Merge "msm:cpp: Enable two paralel to planar output in cpp"
diff --git a/Documentation/ata/ahci_msm.txt b/Documentation/ata/ahci_msm.txt
new file mode 100644
index 0000000..2f84834
--- /dev/null
+++ b/Documentation/ata/ahci_msm.txt
@@ -0,0 +1,322 @@
+Introduction
+============
+The SATA Host Controller developed for Qualcomm SoC is used
+to facilitate SATA storage devices that connect to SoC through a
+standard SATA cable interface. The MSM Advanced Host Controller
+Interface (AHCI) driver interfaces with the generic Linux AHCI driver
+and communicates with the AHCI controller for data movement between
+system memory and SATA devices (persistent storage).
+
+Hardware description
+====================
+Serial Advanced Technology Attachment (SATA) is a communication
+protocol designed to transfer data between a computer and storage
+devices (Hard Disk Drive(HDD), Solid State Drives(SSD) etc.).
+First generation (Gen1) SATA interfaces communicate at a serial
+rate of 1.5Gb/s and use low-voltage differential signaling on
+serial links. With 8b-10b encoding, the effective data throughput
+for Gen1 interface is 150MB/s.
+
+The SATA host controller in Qualcomm chipsets adheres to the AHCI 1.3
+specification which describes the interface between system software
+and the host controller, as well as the functional behavior needed
+for software to communicate with the SATA host controller.
+
+The SATA PHY hardware macro in Qualcomm chipsets adheres to the
+SATA 3.0 specification with Gen1 serial interface. This is used to
+serialize and de-serialize data and communicates with SATA HDD. Also,
+the PHY can detect SATA HDD during hot swap and raise an interrupt to
+the CPU through AHCI controller to notify about the detection/removal.
+
+The following figure shows the SATA architecture block diagram as
+implemented in MSM chipsets.
+
+ +---------+
+ |SATA Disk|
+ | Drive |
+ +---------+
+ ^ ^
+ Tx | | Rx
+ v v
++--------------+ +--------------+ +-----------+
+| System Memory| | SATA PHY | | CPU |
++--------------+ +--------------+ +-----------+
+ ^ ^ ^ ^ ^
+ | | | | |
+ | v v | |
+ | +------------------+(Interrupt)|
+ | | SATA CONTROLLER |-----+ |
+ | +------------------+ |
+ | ^ ^ |
+ | | | |
+ v v v v
+ <--------------------------------------------------------->
+< System Fabric (Control and Data) >
+ <--------------------------------------------------------->
+
+Some controller capabilities:
+- Supports 64-bit addressing
+- Supports native command queueing (upto 32 commands)
+- Supports First-party DMA to move data to and from system memory
+- ATA-7 command set compliant
+- Port multiplier support for some chipsets
+- Supports aggressive power management (partial, slumber modes)
+- Supports asynchronous notification
+
+Software description
+====================
+The SATA driver uses the generic interface to read/write data to
+the Hard Disk Drive (HDD). It uses following components in Linux
+to interface with the generic block layer which then interfaces
+with file system or user processes.
+
+1) AHCI platform Driver (includes MSM-specific glue driver)
+2) LIBAHCI
+3) LIBATA
+4) SCSI
+
+AHCI platform driver registers as a device driver for platform
+device registered during SoC board initialization. It is responsible
+for platform specific tasks like PHY configuration, clock initial-
+ization, claiming memory resources etc. Also, implements certain
+functionality that deviates from the standard specification.
+
+Library "LIBAHCI" implements software layer functionality described
+in the standard AHCI specification. It interfaces with the LIBATA
+framework to execute SATA the command set. It converts ATA task files
+into AHCI command descriptors and pass them to the controller for
+execution. It handles controller interrupts and sends command
+completion events to the upper layers. It implements a controller-
+specific reset and recover mechanism in case of errors. It implements
+link power management policies - partial, slumber modes, runtime power
+management and platform power management. It abstracts the low-level
+controller details from the LIBATA framework.
+
+"LIBATA" is a helper library for implementing ATA and SATA command
+protocol as described in standard ATA and SATA specifications. It
+builds read/write requests from SCSI commands and pass them to the
+low-level controller driver (LLD). It handshakes with the SATA
+device using standard commands to understand capabilities and carry
+out device configurations. It interfaces with the SCSI layer to manage
+underlying disks. It manages different devices connected to each host
+port using a port multiplier. Also, it manages the link PHY component,
+the interconnect interface and any external interface (cables, etc.)
+that follow the SATA electrical specification.
+
+The SCSI layer is a helper library for translating generic block layer
+commands to SCSI commands and pass them on to the LIBATA framework.
+Certain generic stuff like device scan, media change, and hot plug
+detection are handled. This layer handles all types of SCSI devices,
+and SATA storage devices are one class of SCSI devices. It also provides
+the IOCTL interface to manage disks from userspace.
+
+Following is the logical code flow:
+
+ +------------------------+
+ | File System (ext4 etc.)|
+ +------------------------+
+ ^
+ |
+ v
+ +------------------------+
+ | Generic Block Layer |
+ +------------------------+
+ ^
+ |
+ v
+ +------------------------+
+ | SCSI Layer |
+ +------------------------+
+ ^
+ |
+ v
+ +------------------------+
+ | LIBATA library |
+ +------------------------+
+ ^
+ |
+ v
+ +------------------------+
+ | LIBAHCI library |
+ +------------------------+
+ ^
+ |
+ v
+ +------------------------+
+ | AHCI platform driver + |
+ | MSM AHCI glue driver |
+ +------------------------+
+
+Design
+======
+The MSM AHCI driver acts as a glue driver for the Linux
+AHCI controller driver. It provides the following functionality:
+- Registers as a driver for msm_sata device which has an AHCI-compliant
+ controller and PHY as resources.
+- Registers an AHCI platform device in the probe function providing
+ ahci platform data
+- AHCI platform data consists of the following callbacks:
+ - init
+ o PHY resource acquisition
+ o Clock and voltage regulator initialization
+ o PHY calibration
+ - exit
+ o PHY power down
+ o Clock and voltage regulator turn off
+ - suspend
+ - resume
+ o Sequence described in the next section.
+- The Linux AHCI platform driver then probes the AHCI device and
+ initializes it according to the standard AHCI specification.
+- The SATA drive is detected as part of scsi_scan_host() called by
+ LIBAHCI after controller initialization.
+
+Power Management
+================
+Various power modes are supported by this driver.
+
+Platform suspend/resume:
+During suspend:
+- PHY analog blocks are powered down
+- Controller and PHY is kept in Power-on-Reset (POR) mode
+- Clocks and voltage regulators are gated
+
+During resume:
+- Clocks and voltage regulators are ungated
+- PHY is powered up and calibrated to functional mode
+- Controller is re-initialized to process commands.
+
+Runtime suspend/resume:
+- Execute the same steps as in platform suspend/resume.
+- Runtime suspend/resume is disabled by default due to regressions
+ in hot-plug detection (specification limitation). The users can
+ enable runtime power management with following shell commands.
+
+ # cd /sys/devices/platform/msm_sata.0/ahci.0/
+ # echo auto > ./power/control
+ # echo auto > ./ata1/power/control
+ # echo auto > ./ata1/host0/target0:0:0/0:0:0:0/power/control
+
+ Note: The device will be runtime-suspended only when user unmounts
+ all the partitions.
+
+Link power management (defined by AHCI 1.3 specification):
+- Automatic low power mode transition are supported.
+- AHCI supports two power modes: partial and slumber.
+- Software uses Inteface Communication Control (ICC) bits in AHCI
+ register space to enable automatic partial/slumber state.
+- Partial mode:
+ - Software asserts automatic partial mode when the use
+ case demands low latency resume.
+ - Upon receiving partial mode signal, PHY disables byte clocks
+ and re-enables them during resume and thus has low latency.
+- Slumber mode:
+ - Software asserts automatic slumber mode when the use
+ case demands low power consumption and can withstand
+ high resume latencies.
+ - Upon receiving slumber mode signal, PHY disables byte
+ clocks and some internal circuitry. Upon resume PHY
+ enables byte clocks and reacquires the PLL lock.
+- Once the software enables partial/slumber modes, the transitioning
+ into these modes are automatic and is handled by hardware without
+ software intervention while the controller is idle with no outstanding
+ commands to process.
+
+- The Linux AHCI link power management defines three modes:
+ - max_performance (default mode)
+ Doesn't allow partial/slumber transition when host is idle.
+ - medium_power (Partial mode)
+ Following shell commands are used to enable this mode:
+
+ # cd /sys/devices/platform/msm_sata.0/ahci.0/
+ # echo medium_power > ./ata1/host0/scsi_host/host0/link_power_management_policy
+
+ - min_power (Slumber mode)
+ Following shell commands are used to enable this mode:
+
+ # cd /sys/devices/platform/msm_sata.0/ahci.0/
+ # echo min_power > ./ata1/host0/scsi_host/host0/link_power_management_policy
+
+SMP/multi-core
+==============
+The MSM AHCI driver hooks only init, exit, suspend, resume callbacks to
+the AHCI driver which are serialized by design and hence the driver, which
+is inherently SMP safe.
+
+Security
+========
+None.
+
+Performance
+===========
+The theoretical performance with Gen1 SATA PHY is 150MB/s (8b/10b encoding).
+The performance is dependent on various factors, mainly:
+- Capabilities of the external SATA hard disk connected to the MSM SATA port
+- Various system bus frequencies and system loads
+- System memory capabilities
+- Benchmark test applications that collect performance numbers
+
+One example of the maximum performance achieved in a specific system
+configuration follows:
+
+Benchmark: Iozone sequential performance
+Block size: 128K
+File size: 1GB
+Platform: APQ8064 V2 CDP
+CPU Governor: Performance
+
+SanDisk SSD (i100 64GB):
+Read - 135MB/s
+Write - 125MB/s
+
+Western Digital HDD (WD20EURS 2TB):
+Read - 121MB/s
+Write - 98MB/s
+
+Interface
+=========
+The MSM AHCI controller driver provides function pointers as the
+required interface to the Linux AHCI controller driver. The main
+routines implemented are init, exit, suspend, and resume for handling
+MSM-specific initialization, freeing of resources on exit, and
+MSM-specific power management tweaks during suspend power collapse.
+
+Driver parameters
+=================
+None.
+
+Config options
+==============
+Config option SATA_AHCI_MSM in drivers/ata/Kconfig enables this driver.
+
+Dependencies
+============
+The MSM AHCI controller driver is dependent on Linux AHCI driver,
+Linux ATA framework, Linux SCSI framework and Linux generic block layer.
+
+While configuring the kernel, the following options should be set:
+
+- CONFIG_BLOCK
+- CONFIG_SCSI
+- CONFIG_ATA
+- CONFIG_SATA_AHCI_PLATFORM
+
+User space utilities
+====================
+Any user space component that can mount a block device can be used to
+read/write data into persistent storage. However, at the time of this
+writing there are no utilities that author is aware of that can manage
+h/w from userspace.
+
+Other
+=====
+None.
+
+Known issues
+============
+None.
+
+To do
+=====
+- Device tree support.
+- MSM bus frequency voting support.
diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt
index 6aed1ce..b4ae5e6 100644
--- a/Documentation/cpu-freq/governors.txt
+++ b/Documentation/cpu-freq/governors.txt
@@ -198,57 +198,74 @@
The CPUfreq governor "interactive" is designed for latency-sensitive,
interactive workloads. This governor sets the CPU speed depending on
-usage, similar to "ondemand" and "conservative" governors. However,
-the governor is more aggressive about scaling the CPU speed up in
-response to CPU-intensive activity.
-
-Sampling the CPU load every X ms can lead to under-powering the CPU
-for X ms, leading to dropped frames, stuttering UI, etc. Instead of
-sampling the cpu at a specified rate, the interactive governor will
-check whether to scale the cpu frequency up soon after coming out of
-idle. When the cpu comes out of idle, a timer is configured to fire
-within 1-2 ticks. If the cpu is very busy between exiting idle and
-when the timer fires then we assume the cpu is underpowered and ramp
-to MAX speed.
-
-If the cpu was not sufficiently busy to immediately ramp to MAX speed,
-then governor evaluates the cpu load since the last speed adjustment,
-choosing the highest value between that longer-term load or the
-short-term load since idle exit to determine the cpu speed to ramp to.
+usage, similar to "ondemand" and "conservative" governors, but with a
+different set of configurable behaviors.
The tuneable values for this governor are:
+target_loads: CPU load values used to adjust speed to influence the
+current CPU load toward that value. In general, the lower the target
+load, the more often the governor will raise CPU speeds to bring load
+below the target. The format is a single target load, optionally
+followed by pairs of CPU speeds and CPU loads to target at or above
+those speeds. Colons can be used between the speeds and associated
+target loads for readability. For example:
+
+ 85 1000000:90 1700000:99
+
+targets CPU load 85% below speed 1GHz, 90% at or above 1GHz, until
+1.7GHz and above, at which load 99% is targeted. If speeds are
+specified these must appear in ascending order. Higher target load
+values are typically specified for higher speeds, that is, target load
+values also usually appear in an ascending order. The default is
+target load 90% for all speeds.
+
min_sample_time: The minimum amount of time to spend at the current
-frequency before ramping down. This is to ensure that the governor has
-seen enough historic cpu load data to determine the appropriate
-workload. Default is 80000 uS.
+frequency before ramping down. Default is 80000 uS.
hispeed_freq: An intermediate "hi speed" at which to initially ramp
when CPU load hits the value specified in go_hispeed_load. If load
stays high for the amount of time specified in above_hispeed_delay,
-then speed may be bumped higher. Default is maximum speed.
+then speed may be bumped higher. Default is the maximum speed
+allowed by the policy at governor initialization time.
-go_hispeed_load: The CPU load at which to ramp to the intermediate "hi
-speed". Default is 85%.
+go_hispeed_load: The CPU load at which to ramp to hispeed_freq.
+Default is 99%.
-above_hispeed_delay: Once speed is set to hispeed_freq, wait for this
-long before bumping speed higher in response to continued high load.
+above_hispeed_delay: When speed is at or above hispeed_freq, wait for
+this long before raising speed in response to continued high load.
Default is 20000 uS.
-timer_rate: Sample rate for reevaluating cpu load when the system is
-not idle. Default is 20000 uS.
+timer_rate: Sample rate for reevaluating CPU load when the CPU is not
+idle. A deferrable timer is used, such that the CPU will not be woken
+from idle to service this timer until something else needs to run.
+(The maximum time to allow deferring this timer when not running at
+minimum speed is configurable via timer_slack.) Default is 20000 uS.
-input_boost: If non-zero, boost speed of all CPUs to hispeed_freq on
-touchscreen activity. Default is 0.
+timer_slack: Maximum additional time to defer handling the governor
+sampling timer beyond timer_rate when running at speeds above the
+minimum. For platforms that consume additional power at idle when
+CPUs are running at speeds greater than minimum, this places an upper
+bound on how long the timer will be deferred prior to re-evaluating
+load and dropping speed. For example, if timer_rate is 20000uS and
+timer_slack is 10000uS then timers will be deferred for up to 30msec
+when not at lowest speed. A value of -1 means defer timers
+indefinitely at all speeds. Default is 80000 uS.
boost: If non-zero, immediately boost speed of all CPUs to at least
hispeed_freq until zero is written to this attribute. If zero, allow
CPU speeds to drop below hispeed_freq according to load as usual.
+Default is zero.
-boostpulse: Immediately boost speed of all CPUs to hispeed_freq for
-min_sample_time, after which speeds are allowed to drop below
+boostpulse: On each write, immediately boost speed of all CPUs to
+hispeed_freq for at least the period of time specified by
+boostpulse_duration, after which speeds are allowed to drop below
hispeed_freq according to load as usual.
+boostpulse_duration: Length of time to hold CPU speed at hispeed_freq
+on a write to boostpulse, before allowing speed to drop according to
+load as usual. Default is 80000 uS.
+
3. The Governor Interface in the CPUfreq Core
=============================================
diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt
new file mode 100644
index 0000000..6508329
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/cpus.txt
@@ -0,0 +1,78 @@
+* ARM CPUs binding description
+
+The device tree allows to describe the layout of CPUs in a system through
+the "cpus" node, which in turn contains a number of subnodes (ie "cpu")
+defining properties for every cpu.
+
+Bindings for CPU nodes follow the ePAPR standard, available from:
+
+http://devicetree.org
+
+For the ARM architecture every CPU node must contain the following properties:
+
+- device_type: must be "cpu"
+- reg: property matching the CPU MPIDR[23:0] register bits
+ reg[31:24] bits must be set to 0
+- compatible: should be one of:
+ "arm,arm1020"
+ "arm,arm1020e"
+ "arm,arm1022"
+ "arm,arm1026"
+ "arm,arm720"
+ "arm,arm740"
+ "arm,arm7tdmi"
+ "arm,arm920"
+ "arm,arm922"
+ "arm,arm925"
+ "arm,arm926"
+ "arm,arm940"
+ "arm,arm946"
+ "arm,arm9tdmi"
+ "arm,cortex-a5"
+ "arm,cortex-a7"
+ "arm,cortex-a8"
+ "arm,cortex-a9"
+ "arm,cortex-a15"
+ "arm,arm1136"
+ "arm,arm1156"
+ "arm,arm1176"
+ "arm,arm11mpcore"
+ "faraday,fa526"
+ "intel,sa110"
+ "intel,sa1100"
+ "marvell,feroceon"
+ "marvell,mohawk"
+ "marvell,xsc3"
+ "marvell,xscale"
+ "qcom,krait"
+
+Example:
+
+ cpus {
+ #size-cells = <0>;
+ #address-cells = <1>;
+
+ CPU0: cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x0>;
+ };
+
+ CPU1: cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x1>;
+ };
+
+ CPU2: cpu@100 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x100>;
+ };
+
+ CPU3: cpu@101 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x101>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/arm/msm/acpuclock/acpuclock-a7.txt b/Documentation/devicetree/bindings/arm/msm/acpuclock/acpuclock-a7.txt
index 7a15f6a..a77d435 100644
--- a/Documentation/devicetree/bindings/arm/msm/acpuclock/acpuclock-a7.txt
+++ b/Documentation/devicetree/bindings/arm/msm/acpuclock/acpuclock-a7.txt
@@ -10,7 +10,6 @@
- reg-names: name of the bases for the above registers. "rcg_base"
is expected.
- a7_cpu-supply: regulator to supply a7 cpu
-- a7_mem-supply: regulator to supply a7 l2 cache
Example:
qcom,acpuclk@f9011050 {
@@ -18,5 +17,4 @@
reg = <0xf9011050 0x8>;
reg-names = "rcg_base";
a7_cpu-supply = <&pm8026_s2>;
- a7_mem-supply = <&pm8026_l3>;
};
diff --git a/Documentation/devicetree/bindings/arm/msm/core_sleep_status.txt b/Documentation/devicetree/bindings/arm/msm/core_sleep_status.txt
index 6ac80f1..b6fc45a 100644
--- a/Documentation/devicetree/bindings/arm/msm/core_sleep_status.txt
+++ b/Documentation/devicetree/bindings/arm/msm/core_sleep_status.txt
@@ -6,11 +6,6 @@
devices require that the offlined core is power collapsed before turning off
the resources that are used by the offlined core.
-This device is dependent on the pm-8x60 device, which configures the low power
-mode of respective cores. The sleep status is only valid when the core enters
-a low power mode. The device is a child node to pm-8x60 node which is documented
-in Documentation/devicetree/bindings/arm/msm/pm-8x60.txt
-
The required properties of sleep status device are:
- compatible: qcom,cpu-sleep-status
diff --git a/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt b/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
index 203730f..04310ec 100644
--- a/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
+++ b/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
@@ -12,9 +12,12 @@
Required properties:
- compatible: Must be "qcom,cpr-regulator"
-- reg: Register addresses for RBCPR and efuse
-- reg-names: Register names. Must be "rbcpr" and "efuse_phys"
+- reg: Register addresses for RBCPR, RBCPR clock
+ select, PVS eFuse and CPR eFuse
+- reg-names: Register names. Must be "rbcpr", "rbcpr_clk",
+ "pvs_efuse" and "cpr_efuse"
- regulator-name: A string used to describe the regulator
+- interrupts: Interrupt line from RBCPR to interrupt controller.
- regulator-min-microvolt: Minimum corner value as min constraint, which
should be 1 for SVS corner
- regulator-max-microvolt: Maximum corner value as max constraint, which
@@ -23,9 +26,6 @@
represent total number of PVS bins. It should
not exceed a maximum of 5 for total number of
32 bins.
-- qcom,efuse-bit-pos: A list of integers whose length must equal
- to qcom,num-efuse-bits and each integer indicates
- bit position in efuse memory from LSB to MSB
- qcom,pvs-bin-process: A list of integers whose length is equal to 2 to
the power of qcom,num-efuse-bits. The location or
0-based index of an element in the list corresponds
@@ -44,8 +44,25 @@
0 (SVS voltage): 1050000 uV
1 (NORMAL voltage): 1150000 uV
2 (TURBO voltage): 1275000 uV
- 3 (SUPER_TURBO voltage): 1275000 uV
- vdd-apc-supply: Regulator to supply VDD APC power
+- qcom,vdd-apc-step-up-limit: Limit of vdd-apc-supply steps for scaling up.
+- qcom,vdd-apc-step-down-limit: Limit of vdd-apc-supply steps for scaling down.
+- qcom,cpr-ref-clk: The reference clock in kHz.
+- qcom,cpr-timer-delay: The delay in microseconds for the timer interval.
+- qcom,cpr-timer-cons-up: Consecutive number of timer interval (qcom,cpr-timer-delay)
+ occurred before issuing UP interrupt.
+- qcom,cpr-timer-cons-down: Consecutive number of timer interval (qcom,cpr-timer-delay)
+ occurred before issuing DOWN interrupt.
+- qcom,cpr-irq-line: Internal interrupt route signal of RBCPR, one of 0, 1 or 2.
+- qcom,cpr-step-quotient: Number of CPR quotient (RO count) per vdd-apc-supply step
+ to issue error_steps.
+- qcom,cpr-up-threshold: The threshold for CPR to issue interrupt when
+ error_steps is greater than it when stepping up.
+- qcom,cpr-down-threshold: The threshold for CPR to issue interrupt when
+ error_steps is greater than it when stepping down.
+- qcom,cpr-idle-clocks: Idle clock cycles RO can be in.
+- qcom,cpr-gcnt-time: The time for gate count in microseconds.
+- qcom,cpr-apc-volt-step: The voltage in microvolt per CPR step, such as 5000uV.
Optional properties:
@@ -61,28 +78,42 @@
2 => equal to slow speed corner ceiling
3 => equal to qcom,vdd-mx-vmax
This is required when vdd-mx-supply is present.
+- qcom,cpr-enable: Present: CPR enabled by default.
+ Not Present: CPR disable by default.
Example:
apc_vreg_corner: regulator@f9018000 {
status = "okay";
compatible = "qcom,cpr-regulator";
- reg = <0xf9018000 0x1000>,
- <0xfc4b80b0 8>;
- reg-names = "rbcpr", "efuse_phys";
+ reg = <0xf9018000 0x1000>, <0xfc4b80b0 8>, <0xfc4bc450 16>;
+ reg-names = "rbcpr", "pvs_efuse", "cpr_efuse";
+ interrupts = <0 15 0>;
regulator-name = "apc_corner";
regulator-min-microvolt = <1>;
- regulator-max-microvolt = <4>;
+ regulator-max-microvolt = <3>;
qcom,num-efuse-bits = <5>;
- qcom,efuse-bit-pos = <6 7 8 9 10>;
- qcom,pvs-bin-process = <0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2
+ qcom,pvs-bin-process = <0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 2
2 2 2 2 3 3 3 3 3 3 3 3 0 0 0 0>;
- qcom,pvs-corner-ceiling-slow = <1050000 1150000 1275000 1350000>;
- qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000 1200000>;
- qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000 1140000>;
+ qcom,pvs-corner-ceiling-slow = <1050000 1160000 1275000>;
+ qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000>;
+ qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000>;
vdd-apc-supply = <&pm8226_s2>;
vdd-mx-supply = <&pm8226_l3_ao>;
qcom,vdd-mx-vmax = <1350000>;
qcom,vdd-mx-vmin-method = <1>;
+ qcom,vdd-apc-step-up-limit = <1>;
+ qcom,vdd-apc-step-down-limit = <1>;
+ qcom,cpr-ref-clk = <19200>;
+ qcom,cpr-timer-delay = <5000>;
+ qcom,cpr-timer-cons-up = <1>;
+ qcom,cpr-timer-cons-down = <2>;
+ qcom,cpr-irq-line = <0>;
+ qcom,cpr-step-quotient = <15>;
+ qcom,cpr-up-threshold = <1>;
+ qcom,cpr-down-threshold = <2>;
+ qcom,cpr-idle-clocks = <5>;
+ qcom,cpr-gcnt-time = <1>;
+ qcom,cpr-apc-volt-step = <5000>;
};
diff --git a/Documentation/devicetree/bindings/arm/msm/memory-reserve.txt b/Documentation/devicetree/bindings/arm/msm/memory-reserve.txt
index c1b79ae..6dac1b7 100644
--- a/Documentation/devicetree/bindings/arm/msm/memory-reserve.txt
+++ b/Documentation/devicetree/bindings/arm/msm/memory-reserve.txt
@@ -69,3 +69,20 @@
This region is assumed to be a part of a separate hole that has been removed
and this binding specifies the fixed location and size of the region within
that hole.
+
+
+Some drivers may only wish to reserve memory from the system. Reserved memory
+is still tracked internally by the Linux page allocator. The memory is reserved
+from the buddy allocator at bootup but may be freed back at a later point in
+time with memblock_free and free_bootmem_late.
+
+Required parameters:
+-qcom,memblock-reserve: base and size of block to be reserved. Drivers should
+call memblock_is_reserved before attempting to use the base address to ensure
+the memory was completely reserved.
+
+ qcom,a-driver {
+ compatible = "qcom,a-driver";
+ /* reserve a 4MB region @ 0x200000 for use later */
+ qcom,memblock-reserve = <0x200000 0x400000>;
+ };
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt b/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
index 6b2f962..fbf1a1f 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
@@ -91,6 +91,31 @@
(Can be 0/1/2).
qcom,prio1: Priority high signal for a NoC bus master
(Can be 0/1/2)
+qcom,dual-conf: Indicates whether a BIMC/NoC master can be configured
+ in multiple modes at run-time. (Boolean)
+qcom,mode-thresh: Threshold mode for a BIMC/NoC master. Beyond a certain
+ threshold frequency, a threshold mode can be used.
+ (Can be Fixed/Limiter/Bypass/Regulator)
+qcom,bimc,bw: Bandwidth limit for a BIMC master using dual modes.
+ This bandwidth is used to calculate Grant count and
+ other parameters used in Limiter and Regular mode
+ for static BKE configuration. It is defined in KBps.
+qcom,bimc,gp: Grant Period for configuring a master in limiter
+ mode. This is an integer value in micro-seconds.
+qcom,bimc,thmp: Medium threshold percentage for BIMC masters.
+ This percentage is used to calculate medium threshold
+ value for BIMC Masters in Limiter mode for static
+ configuration. This can be any integer value between
+ 1 and 100.
+qcom,thresh: Beyond this threshold frequency, the mode usage is
+ switched from mode specified by property qcom,mode
+ to the one specified by qcom,mode-thresh. In case the
+ requested IB value falls below this threshold, the mode
+ is switched back to qcom,mode. Frequency is specified in
+ KBps.
+
+
+
Example:
@@ -135,6 +160,45 @@
};
};
+ msm-bimc@0xfc380000 {
+ compatible = "msm-bus-fabric";
+ reg = <0xfc380000 0x0006A000>;
+ cell-id = <0>;
+ label = "msm_bimc";
+ qcom,fabclk-dual = "mem_clk";
+ qcom,fabclk-active = "mem_a_clk";
+ qcom,ntieredslaves = <0>;
+ qcom,qos-freq = <19200>;
+ qcom,hw-sel = "BIMC";
+ qcom,rpm-en;
+
+ coresight-id = <55>;
+ coresight-name = "coresight-bimc";
+ coresight-nr-inports = <0>;
+ coresight-outports = <0>;
+ coresight-child-list = <&funnel_in1>;
+ coresight-child-ports = <3>;
+
+ mas-ampss-m0 {
+ cell-id = <1>;
+ label = "mas-ampss-m0";
+ qcom,masterp = <0>;
+ qcom,tier = <2>;
+ qcom,hw-sel = "BIMC";
+ qcom,mode = "Limiter";
+ qcom,qport = <0>;
+ qcom,ws = <10000>;
+ qcom,mas-hw-id = <0>;
+ qcom,prio-rd = <0>;
+ qcom,prio-wr = <0>;
+ qcom,mode-thresh = "Fixed";
+ qcom,thresh = <2000000>;
+ qcom,dual-conf;
+ qcom,bimc,bw = <300000>;
+ qcom,bimc,gp = <5>;
+ qcom,bimc,thmp = <50>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_thermal.txt b/Documentation/devicetree/bindings/arm/msm/msm_thermal.txt
index 31600ca..6ef2b77 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_thermal.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_thermal.txt
@@ -24,6 +24,8 @@
Optional properties
+- qcom,freq-control-mask: The cpu mask that will be used to determine if a
+ core can be used for freq control.
- qcom,core-limit-temp: Threshold temperature to start shutting down cores
in degC
- qcom,core-temp-hysterisis: Degrees C below which the cores will be brought
@@ -56,15 +58,15 @@
Optional child nodes
- qcom,<vdd restriction child node name>: Define the name of the child node.
- If this property exisits, qcom,vdd-rstr-reg, qcom,levels,
- qcom,min-level and qcom,freq-req need to exist, otherwise
- we return an error.
+ If this property exisits, qcom,vdd-rstr-reg, qcom,levels
+ need to exist. qcom,min-level is optional if qcom,freq-req
+ exists, otherwise it's required.
- qcom,vdd-rstr-reg: Name of the rail
- qcom,levels: Array of the level values. Unit is corner voltage for voltage request
or kHz for frequency request.
- qcom,min-level: Request this level as minimum level when disabling voltage
- restriction. Unit is corner voltage for voltage request
- or kHz for frequency request.
+ restriction. Unit is corner voltage for voltage request.
+ This will not be required if qcom,freq-req exists.
- qcom,freq-req: Flag to determine if we should restrict frequency on this rail
instead of voltage.
@@ -77,6 +79,7 @@
qcom,limit-temp = <60>;
qcom,temp-hysteresis = <10>;
qcom,freq-step = <2>;
+ qcom,freq-control-mask = <0xf>
qcom,core-limit-temp = <90>;
qcom,core-temp-hysterisis = <10>;
qcom,core-control-mask = <7>;
@@ -92,5 +95,11 @@
qcom,levels = <5 7 7>; /* Nominal, Super Turbo, Super Turbo */
qcom,min-level = <1>; /* No Request */
};
+
+ qcom,vdd-apps-rstr{
+ qcom,vdd-rstr-reg = "vdd_apps";
+ qcom,levels = <1881600 1958400 2265600>;
+ qcom,freq-req;
+ };
};
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_tspp.txt b/Documentation/devicetree/bindings/arm/msm/msm_tspp.txt
index 2b5e143..5692ad2 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_tspp.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_tspp.txt
@@ -28,6 +28,20 @@
Note: it is assumed that the functionality value (e.g. 1 in 8974 case)
is applicable to all TSIF GPIOs.
+Optional properties:
+
+- vdd_cx-supply: Reference to the regulator that supplies the CX rail.
+ Some hardware platforms (e.g. 8974-v2) require the voltage of the rail
+ supplying power to the TSIF hardware block to be elevated before
+ enabling the TSIF clocks.
+- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
+ the below optional properties:
+ - qcom,msm-bus,name
+ - qcom,msm-bus,num-cases
+ - qcom,msm-bus,active-only
+ - qcom,msm-bus,num-paths
+ - qcom,msm-bus,vectors-KBps
+
Example (for 8974 platform, avaialble at msm8974.dtsi):
tspp: msm_tspp@f99d8000 {
@@ -68,6 +82,14 @@
"tsif_data",
"tsif_sync";
qcom,gpios-func = <1>;
+
+ qcom,msm-bus,name = "tsif";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <82 512 0 0>, /* No vote */
+ <82 512 12288 24576>; /* Max. bandwidth, 2xTSIF, each max of 96Mbps */
};
diff --git a/Documentation/devicetree/bindings/arm/msm/pm_snoc_client.txt b/Documentation/devicetree/bindings/arm/msm/pm_snoc_client.txt
new file mode 100644
index 0000000..4f7111f
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/pm_snoc_client.txt
@@ -0,0 +1,35 @@
+* MSM PM SNOC client
+
+MSM PM SNOC client device is used to setup a bus request for 100 Mhz for the
+SNOC bus when the Apps cores are active. This bus request helps mitigate the
+exit latency from power collapse in cases where there aren't any active bus
+requests for SNOC.
+
+This device is dependent on the pm-8x60 device, which configures the low power
+mode of respective cores.
+
+The required properties of this device are:
+
+- compatible: qcom,pm-snoc-client
+- qcom,msm-bus,name: String representing the client-name
+- qcom,msm-bus,num-cases: Total number of usecases
+- qcom,msm-bus,active-only: Boolean context flag for requests in active or
+ dual (active & sleep) contex
+- qcom,msm-bus,num-paths: Total number of master-slave pairs
+- qcom,msm-bus,vectors-KBps: Arrays of unsigned integers representing:
+ master-id, slave-id, arbitrated bandwidth
+ in KBps, instantaneous bandwidth in KBps
+
+
+Example:
+ qcom,pm-snoc-client {
+ compatible = "qcom,pm-snoc-client";
+ qcom,msm-bus,name = "ocimem_snoc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors =
+ <22 512 0 0>,
+ <22 512 320000 3200000>;
+ };
+
diff --git a/Documentation/devicetree/bindings/arm/pmu.txt b/Documentation/devicetree/bindings/arm/pmu.txt
index 1c044eb..19472c9 100644
--- a/Documentation/devicetree/bindings/arm/pmu.txt
+++ b/Documentation/devicetree/bindings/arm/pmu.txt
@@ -9,6 +9,7 @@
- compatible : should be one of
"arm,cortex-a9-pmu"
"arm,cortex-a8-pmu"
+ "arm,cortex-a7-pmu"
"arm,arm1176-pmu"
"arm,arm1136-pmu"
- interrupts : 1 combined interrupt or 1 per core.
diff --git a/Documentation/devicetree/bindings/bluetooth/bluetooth_power.txt b/Documentation/devicetree/bindings/bluetooth/bluetooth_power.txt
index 88d69e0..86d863c 100644
--- a/Documentation/devicetree/bindings/bluetooth/bluetooth_power.txt
+++ b/Documentation/devicetree/bindings/bluetooth/bluetooth_power.txt
@@ -5,12 +5,26 @@
Required properties:
- compatible: Should be "qca,ar3002"
- qca,bt-reset-gpio: GPIO pin to bring BT Controller out of reset
+ - qca,bt-vdd-io-supply: Bluetooth VDD IO regulator handle
+ - qca,bt-vdd-pa-supply: Bluetooth VDD PA regulator handle
Optional properties:
- None
+ -qca,bt-vdd-ldo-supply: Bluetooth VDD LDO regulator handle. Kept under optional parameters
+ as some of the chipsets doesn't require ldo or it may use from same vddio.
+ - qca,bt-chip-pwd-supply: Chip power down gpio is required when bluetooth module
+ and other modules like wifi co-exist in a singe chip and shares a
+ common gpio to bring chip out of reset.
+ - qca,bt-vdd-io-voltage-level: min and max voltages for the vdd io regulator
+ - qca,bt-vdd-pa-voltage-level: min and max voltages for the vdd pa regulator
+ - qca,bt-vdd-ldo-voltage-level: min and max voltages for the vdd ldo regulator
Example:
bt-ar3002 {
compatible = "qca,ar3002";
qca,bt-reset-gpio = <&pm8941_gpios 34 0>;
+ qca,bt-vdd-io-supply = <&pm8941_s3>;
+ qca,bt-vdd-pa-supply = <&pm8941_l19>;
+ qca,bt-chip-pwd-supply = <&ath_chip_pwd_l>;
+ qca,bt-vdd-io-supply = <1800000 1800000>;
+ qca,bt-vdd-pa-supply = <2900000 2900000>;
};
diff --git a/Documentation/devicetree/bindings/cache/msm_cache_erp.txt b/Documentation/devicetree/bindings/cache/msm_cache_erp.txt
index 400b299..8d00cc2 100644
--- a/Documentation/devicetree/bindings/cache/msm_cache_erp.txt
+++ b/Documentation/devicetree/bindings/cache/msm_cache_erp.txt
@@ -7,6 +7,17 @@
- interrupt-names: Should contain the interrupt names "l1_irq" and
"l2_irq"
+Optional properties:
+- reg: A set of I/O regions to be dumped in the event of a hardware fault being
+ detected. If this property is present, the "reg-names" property is must be
+ present as well.
+- reg-names: Human-readable names assigned to the I/O regions defined by the
+ "reg" property. The names can be completely arbitrary, since they are
+ intended to be human-read during failure analysis, and because the set of I/O
+ regions of interest may vary with the overall system design. This property
+ shall only be present if the "reg" property is present, and must contain as
+ many elements as the "reg" property.
+
Example:
qcom,cache_erp {
compatible = "qcom,cache_erp";
@@ -14,3 +25,17 @@
interrupt-names = "l1_irq", "l2_irq";
};
+Example with "reg" property defined:
+ qcom,cache_erp@f9012000 {
+ reg = <0xf9012000 0x80>,
+ <0xf9089000 0x80>,
+ <0xf9099000 0x80>;
+
+ reg-names = "l2_saw",
+ "krait0_saw",
+ "krait1_saw";
+
+ compatible = "qcom,cache_erp";
+ interrupts = <1 9 0>, <0 2 0>;
+ interrupt-names = "l1_irq", "l2_irq";
+ };
diff --git a/Documentation/devicetree/bindings/coresight/coresight.txt b/Documentation/devicetree/bindings/coresight/coresight.txt
index 25219cd..ce797d3 100644
--- a/Documentation/devicetree/bindings/coresight/coresight.txt
+++ b/Documentation/devicetree/bindings/coresight/coresight.txt
@@ -19,7 +19,8 @@
"arm,coresight-stm" for coresight stm trace device,
"arm,coresight-etm" for coresight etm trace devices,
"qcom,coresight-csr" for coresight csr device,
- "arm,coresight-cti" for coresight cti devices
+ "arm,coresight-cti" for coresight cti devices,
+ "qcom,coresight-hwevent" for coresight hardware event devices
- reg : physical base address and length of the register set(s) of the component
- reg-names : names corresponding to each reg property value. The reg-names that
need to be used with corresponding compatible string for a coresight device
@@ -61,6 +62,12 @@
compatible : should be "arm,coresight-cti"
reg-names : should be:
"cti<num>-base" - physical base address of cti registers
+ - for coresight hardware event devices:
+ compatible : should be "qcom,coresight-hwevent"
+ reg-names : should be:
+ "<ss-mux>" - physical base address of hardware event mux
+ control registers where <ss-mux> is subsystem mux it
+ represents
- coresight-id : unique integer identifier for the component
- coresight-name : unique descriptive name of the component
- coresight-nr-inports : number of input ports on the component
@@ -105,6 +112,7 @@
- qcom,setb-gpios-drv : active drive strength for set B gpios
- qcom,setb-gpios-pull : active pull configuration for set B gpios
- qcom,setb-gpios-dir : active direction for set B gpios
+- qcom,hwevent-clks : list of clocks required by hardware event driver
Examples:
@@ -213,3 +221,17 @@
coresight-name = "coresight-cti1";
coresight-nr-inports = <0>;
};
+
+ hwevent: hwevent@fdf30018 {
+ compatible = "qcom,coresight-hwevent";
+ reg = <0xfdf30018 0x80>,
+ <0xf9011080 0x80>,
+ <0xfd4ab160 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+
+ coresight-id = <29>;
+ coresight-name = "coresight-hwevent";
+ coresight-nr-inports = <0>;
+
+ qcom,hwevent-clks = "core_mmss_clk";
+ };
diff --git a/Documentation/devicetree/bindings/crypto/msm/qcedev.txt b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
index 7eb65d2..3ca9080 100644
--- a/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
@@ -15,8 +15,7 @@
Optional properties:
- qcom,ce-hw-shared : optional, indicates if the hardware is shared between EE.
-
-
+ - qcom,ce-hw-key : optional, indicates if the hardware supports use of HW KEY.
Example:
diff --git a/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt b/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
index 79dc287..01cc798 100644
--- a/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
@@ -15,7 +15,7 @@
Optional properties:
- qcom,ce-hw-shared : optional, indicates if the hardware is shared between EE.
-
+ - qcom,ce-hw-key : optional, indicates if the hardware supports use of HW KEY.
Example:
diff --git a/Documentation/devicetree/bindings/dma/sps/sps.txt b/Documentation/devicetree/bindings/dma/sps/sps.txt
index 094acb1..a979c56 100644
--- a/Documentation/devicetree/bindings/dma/sps/sps.txt
+++ b/Documentation/devicetree/bindings/dma/sps/sps.txt
@@ -14,6 +14,9 @@
1 - With BAM DMA and without pipe memory
2 - With BAM DMA and with pipe memory
3 - Without BAM DMA and without pipe memory
+ - qcom,pipe-attr-ee: BAM pipes are attributed to a specific EE, with
+ which we can know the pipes belong to apps side and can have the
+ error interrupts at the pipe level.
Example:
@@ -24,4 +27,5 @@
<0xfe803000 0x4800>;
interrupts = <0 94 0>;
qcom,device-type = <2>;
+ qcom,pipe-attr-ee;
};
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
index a9528c5..8c87eac 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
@@ -4,7 +4,10 @@
are compatable with MIPI display serial interface specification.
Required properties:
-- compatible: Must be "qcom,mdss-dsi-panel"
+- compatible: Specifies the version for DSI HW. that
+ this panel will be worked with
+ "qcom,mdss-dsi-panel" = DSI v6.0
+ "qcom,dsi-panel-v2" = DSI V2.0
- status: A string that has to be set to "okay/ok" to enable
the panel driver. By default this property will be
set to "disable". Will be set to "ok/okay" status
@@ -56,9 +59,9 @@
- qcom,enable-gpio: Specifies the panel lcd/display enable gpio.
- qcom,rst-gpio: Specifies the panel reset gpio.
- qcom,te-gpio: Specifies the gpio used for TE.
-- qcom,dsi-lpg-channel : LPG channel for backlight.
-- qcom,dsi-pwm-period : PWM period in microseconds.
-- qcom,dsi-pwm-gpio : PWM gpio.
+- qcom,pwm-lpg-channel: LPG channel for backlight.
+- qcom,pwm-period: PWM period in microseconds.
+- qcom,pwm-pmic-gpio: PMIC gpio binding to backlight.
- qcom,mdss-pan-broadcast-mode: Boolean used to enable broadcast mode.
- qcom,cont-splash-enabled: Boolean used to enable continuous splash mode.
- qcom,fbc-enabled: Boolean used to enable frame buffer compression mode.
@@ -174,6 +177,19 @@
- qcom,off-cmds-dsi-state: A string that Specifies the ctrl state for sending ON commands.
Supported modes are "DSI_LP_MODE" and "DSI_HS_MODE".
+
+- qcom,panel-on-cmds: A byte stream formed by multiple dcs packets base on
+ qcom dsi controller protocol.
+ byte 0 : dcs data type
+ byte 1 : set to indicate this is an individual packet
+ (no chain).
+ byte 2 : virtual channel number
+ byte 3 : expect ack from client (dcs read command)
+ byte 4 : wait number of specified ms after dcs command
+ transmitted
+ byte 5, 6: 16 bits length in network byte order
+ byte 7 and beyond: number byte of payload
+
Note, if a given optional qcom,* binding is not present, then the driver will configure
the default values specified.
@@ -204,7 +220,7 @@
qcom,mdss-pan-dsi-mdp-tr = <0x04>;
qcom,mdss-pan-dsi-dma-tr = <0x04>;
qcom,mdss-pan-frame-rate = <60>;
- qcom,panel-on-cmds = [32 01 00 00 00 02 00 00];
+ qcom,panel-on-cmds = [32 01 00 00 00 00 02 00 00];
qcom,on-cmds-dsi-state = "DSI_LP_MODE";
qcom,panel-off-cmds = [22 01 00 00 00 00 00];
qcom,off-cmds-dsi-state = "DSI LP MODE";
diff --git a/Documentation/devicetree/bindings/fb/mdss-mdp.txt b/Documentation/devicetree/bindings/fb/mdss-mdp.txt
index bb19768..9bc949e 100644
--- a/Documentation/devicetree/bindings/fb/mdss-mdp.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-mdp.txt
@@ -112,12 +112,27 @@
compression feature in the rotator.
- qcom,mdss-has-decimation: Boolean property to indicate the presence of
decimation feature in fetch.
+- qcom,mdss-ad-off: Array of offset addresses for the available
+ Assertive Display (AD) blocks. These offsets
+ are calculated from the register "mdp_phys"
+ defined in reg property. The number of AD
+ offsets should be less than or equal to the
+ number of mixers driving interfaces defined in
+ property: qcom,mdss-mixer-intf-off. Assumes
+ that AD blocks are aligned with the mixer
+ offsets as well (i.e. the first mixer offset
+ corresponds to the same pathway as the first
+ AD offset).
+
Optional subnodes:
Child nodes representing the frame buffer virtual devices.
Subnode properties:
- compatible : Must be "qcom,mdss-fb"
- cell-index : Index representing frame buffer
+- qcom,mdss-mixer-swap: A boolean property that indicates if the mixer muxes
+ need to be swapped based on the target panel.
+ By default the property is not defined.
@@ -162,6 +177,7 @@
mdss_fb0: qcom,mdss_fb_primary {
cell-index = <0>;
compatible = "qcom,mdss-fb";
+ qcom,mdss-mixer-swap;
};
};
diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt
index 436dfc7..aa0aa8c 100644
--- a/Documentation/devicetree/bindings/gpu/adreno.txt
+++ b/Documentation/devicetree/bindings/gpu/adreno.txt
@@ -66,12 +66,25 @@
and when coming back out of resume
- qcom,step-pwrlevel: How many qcom,gpu-pwrlevel should be decremented at once
- qcom,idle-timeout: This property represents the time in microseconds for idle timeout.
-- qcom,nap-allowed: Boolean. <0> or <1> to disable/enable nap.
- qcom,chipid: If it exists this property is used to replace
the chip identification read from the GPU hardware.
This is used to override faulty hardware readings.
- qcom,strtstp-sleepwake: Boolean. Enables use of GPU SLUMBER instead of SLEEP for power savings
+The following properties are optional as collecting data via coresight might
+not be supported for every chipset. The documentation for coresight
+properties can be found in:
+Documentation/devicetree/bindings/coresight/coresight.txt
+
+- coresight-id Unique integer identifier for the bus.
+- coresight-name Unique descriptive name of the bus.
+- coresight-nr-inports Number of input ports on the bus.
+- coresight-outports List of output port numbers on the bus.
+- coresight-child-list List of phandles pointing to the children of this
+ component.
+- coresight-child-ports List of input port numbers of the children.
+
+
Example of A330 GPU in MSM8974:
/ {
@@ -91,7 +104,6 @@
qcom,initial-pwrlevel = <1>;
qcom,idle-timeout = <83>; //<HZ/12>
- qcom,nap-allowed = <1>;
qcom,clk-map = <0x00000016>; //KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE
/* Bus Scale Settings */
diff --git a/Documentation/devicetree/bindings/i2c/i2c-qup.txt b/Documentation/devicetree/bindings/i2c/i2c-qup.txt
index a7976e8..fd7b635 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-qup.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-qup.txt
@@ -28,10 +28,18 @@
recovery procedure.
- qcom,sda-gpio : I2C data GPIO number. Required for execution of bus
recovery procedure.
+ - qcom,active-only : Vote for core clock when the application processor goes
+ to active state and remove that vote when it goes to idle
+ state. This flag may improve service time of first i2c
+ request at the expense of power consumption. When this
+ entry is not present, voting is done by the runtime-pm
+ callbacks.
+ - qcom,master-id : Master endpoint number used for voting on clocks using
+ bus-scaling driver.
Example:
- i2c@f9966000 {
- cell-index = <0>;
+ i2c_3: i2c@f9966000 {
+ cell-index = <3>;
compatible = "qcom,i2c-qup";
reg = <0xf9966000 0x1000>;
reg-names = "qup_phys_addr";
diff --git a/Documentation/devicetree/bindings/input/gen_vkeys.txt b/Documentation/devicetree/bindings/input/gen_vkeys.txt
index da99e19..2f8d65e 100644
--- a/Documentation/devicetree/bindings/input/gen_vkeys.txt
+++ b/Documentation/devicetree/bindings/input/gen_vkeys.txt
@@ -12,6 +12,9 @@
- qcom,panel-maxy : Maximum y-coordinate of touch panel
- qcom,key-codes : Array of key codes for virtual keys
+Optional properties:
+ - qcom,y-offset : Offset of y-location for virtual keys, default 0
+
Example:
gen-vkeys {
compatible = "qcom,gen-vkeys";
@@ -21,4 +24,5 @@
qcom,panel-maxx = <760>;
qcom,panel-maxy = <1424>;
qcom,key-codes = <158 139 102 217>;
+ qcom,y-offset = <35>;
};
diff --git a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt
new file mode 100644
index 0000000..d77e96c
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt
@@ -0,0 +1,53 @@
+FocalTech touch controller
+
+The focaltech controller is connected to host processor
+via i2c. The controller generates interrupts when the
+user touches the panel. The host controller is expected
+to read the touch coordinates over i2c and pass the coordinates
+to the rest of the system.
+
+Required properties:
+
+ - compatible : should be "focaltech,5x06"
+ - reg : i2c slave address of the device
+ - interrupt-parent : parent of interrupt
+ - interrupts : touch sample interrupt to indicate presense or release
+ of fingers on the panel.
+ - vdd-supply : Power supply needed to power up the device
+ - vcc_i2c-supply : Power source required to power up i2c bus
+ - focaltech,family-id : family identification of the controller
+ - focaltech,irq-gpio : irq gpio which is to provide interrupts to host,
+ same as "interrupts" node. It will also
+ contain active low or active high information.
+ - focaltech,reset-gpio : reset gpio to control the reset of chip
+ - focaltech,display-coords : display coordinates in pixels. It is a four
+ tuple consisting of min x, min y, max x and
+ max y values
+
+Optional properties:
+
+ - focaltech,panel-coords : panel coordinates for the chip in pixels.
+ It is a four tuple consisting of min x,
+ min y, max x and max y values
+ - focaltech,i2c-pull-up : to specify pull up is required
+ - focaltech,no-force-update : to specify force update is allowed
+ - focaltech,button-map : button map of key codes. The number
+ of key codes depend on panel
+
+Example:
+ i2c@f9924000 {
+ ft5x06_ts@38 {
+ compatible = "focaltech,5x06";
+ reg = <0x38>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <61 0x2>;
+ vdd-supply = <&pm8941_l22>;
+ vcc_i2c-supply = <&pm8941_s3>;
+ focaltech,reset-gpio = <&msmgpio 60 0x00>;
+ focaltech,irq-gpio = <&msmgpio 61 0x00>;
+ focaltech,panel-coords = <0 0 480 800>;
+ focaltech,display-coords = <0 0 480 800>;
+ focaltech,button-map= <158 102 139 217>;
+ focaltech,family-id = <0x0a>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/synaptics_i2c_rmi4.txt b/Documentation/devicetree/bindings/input/touchscreen/synaptics_i2c_rmi4.txt
index d24139b..0f35e73 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/synaptics_i2c_rmi4.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/synaptics_i2c_rmi4.txt
@@ -12,7 +12,6 @@
Optional property:
- vdd-supply : Analog power supply needed to power device
- - synaptics,reg-en : specify to indicate regulator is needed
- vcc_i2c-supply : Power source required to pull up i2c bus
- synaptics,i2c-pull-up : specify to indicate pull up is needed
- synaptics,button-map : virtual key code mappings to be used
@@ -48,6 +47,5 @@
synaptics,irq-gpio = <&msmgpio 17 0x00>;
synaptics,button-map = [8B 66 9E];
synaptics,i2c-pull-up;
- synaptics,reg-en;
};
};
diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu_v0.txt b/Documentation/devicetree/bindings/iommu/msm_iommu_v0.txt
index cc1ffc2..c7c6415 100644
--- a/Documentation/devicetree/bindings/iommu/msm_iommu_v0.txt
+++ b/Documentation/devicetree/bindings/iommu/msm_iommu_v0.txt
@@ -11,17 +11,22 @@
- qcom,iommu-pmu-ngroups: Number of Performance Monitor Unit (PMU) groups.
- qcom,iommu-pmu-ncounters: Number of PMU counters per group.
- qcom,iommu-pmu-event-classes: List of event classes supported.
+- qcom,needs-alt-core-clk : boolean to enable the secondary core clock for
+ access to the IOMMU configuration registers
+- Bus scaling properties: See msm_bus.txt
+
- List of sub nodes, one for each of the translation context banks supported.
- Each sub node has the following required properties:
+ Required properties for each sub-node:
- - reg : offset and length of the register set for the context bank.
- - interrupts : should contain the context bank interrupt.
- - qcom,iommu-ctx-mids : List of machine identifiers associated with this
- translation context.
- - label : Name of the context bank
+ - compatible : "qcom,msm-smmu-v0-ctx"
+ - reg : offset and length of the register set for the context bank.
+ - interrupts : should contain the context bank interrupt.
+ - qcom,iommu-ctx-mids : List of machine identifiers associated with this
+ translation context.
+ - label : Name of the context bank
-Optional properties:
- - none
+ Optional properties for each sub-node:
+ - none
Example:
@@ -39,6 +44,7 @@
0x11>;
qcom,iommu-ctx@fd000000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd000000 0x1000>;
interrupts = <0 250 0>;
qcom,iommu-ctx-mids = <0 3>;
diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt b/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt
index 2c47f74..26a119c 100644
--- a/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt
+++ b/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt
@@ -13,6 +13,7 @@
- qcom,iommu-secure-id : Secure identifier for the IOMMU block
- qcom,secure-context : boolean indicating that a context is secure and
programmed by the secure environment.
+- qcom,vdd-supply: Regulator needed to access IOMMU
- qcom,alt-vdd-supply : Alternative regulator needed to access IOMMU
configuration registers.
- interrupts : should contain the performance monitor overflow interrupt number.
@@ -21,12 +22,17 @@
- qcom,iommu-pmu-ngroups: Number of Performance Monitor Unit (PMU) groups.
- qcom,iommu-pmu-ncounters: Number of PMU counters per group.
- qcom,iommu-pmu-event-classes: List of event classes supported.
+- Bus scaling properties: See msm_bus.txt
- List of sub nodes, one for each of the translation context banks supported.
Each sub node has the following required properties:
+ - compatible : "qcom,msm-smmu-v1-ctx"
- reg : offset and length of the register set for the context bank.
- - interrupts : should contain the context bank interrupt.
+ - interrupts : should contain the context bank interrupt. If this is
+ a secure context bank, this should be a list of 2 3-tuples where
+ the first is the non-secure interrupt, and the second is the
+ secure interrupt.
- qcom,iommu-ctx-sids : List of stream identifiers associated with this
translation context.
- label : Name of the context bank
@@ -65,12 +71,14 @@
0x01>;
qcom,iommu-ctx@fda6c000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda6c000 0x1000>;
interrupts = <0 70 0>;
qcom,iommu-ctx-sids = <0 2>;
label = "ctx_0";
};
qcom,iommu-ctx@fda6d000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda6d000 0x1000>;
interrupts = <0 71 0>;
qcom,iommu-ctx-sids = <1>;
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp.txt b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
index a221433..ff95d43 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
@@ -4,7 +4,7 @@
controlling LEDs that are part of PMIC on Qualcomm reference
platforms. The PMIC is connected to Host processor via
SPMI bus. This driver supports various LED modules such as
-WLED (white LED), RGB LED and flash LED.
+Keypad backlight, WLED (white LED), RGB LED and flash LED.
Each LED module is represented as a node of "leds-qpnp". This
node will further contain the type of LED supported and its
@@ -45,11 +45,13 @@
- qcom,saftey-timer: include for safety timer use, otherwise watchdog timer will be used
- linux,default-trigger: trigger the led from external modules such as display
- qcom,default-state: default state of the led, should be "on" or "off"
+- qcom,torch-enable: set flash led to torch mode
+- flash_boost-supply: SMBB regulator for LED flash mode
RGB Led is a tri-colored led, Red, Blue & Green.
Required properties for RGB led:
-- qcom,mode: mode the led should operate in, options 0 = PWM, 1 = LPG
+- qcom,mode: mode the led should operate in, options "pwm" and "lpg"
- qcom,pwm-channel: pwm channel the led will operate on
Required properties for PWM mode only:
@@ -58,6 +60,8 @@
Required properties for LPG mode only:
- qcom,duty-pcts: array of values for duty cycle to go through
- qcom,start-idx: starting point duty-pcts array
+
+Optional properties for LPG mode only:
- qcom,pause-lo: pause at low end of cycle
- qcom,pause-hi: pause at high end of cycle
- qcom,ramp-step-ms: step between each cycle (ms)
@@ -67,6 +71,7 @@
- linux,default-trigger: trigger the led from external modules such as display
- qcom,default-state: default state of the led, should be "on" or "off"
- qcom,turn-off-delay-ms: delay in millisecond for turning off the led when its default-state is "on". Value is being ignored in case default-state is "off".
+- qcom,use-blink: Use blink sysfs entry for switching into lpg mode. For optimal use, set default mode to pwm. All required lpg parameters must be supplied.
MPP LED is an LED controled through a Multi Purpose Pin.
@@ -75,6 +80,48 @@
- qcom,default-state: default state of the led, should be "on" or "off"
- qcom,source-sel: select power source, default 1 (enabled)
- qcom,mode-ctrl: select operation mode, default 0x60 = Mode Sink
+- qcom,mode: mode the led should operate in, options "pwm", "lpg" and "manual"
+- qcom,use-blink: Use blink sysfs entry for switching into lpg mode. For optimal use, set default mode to pwm. All required lpg parameters must be supplied.
+
+Required properties for PWM mode only:
+- qcom,pwm-channel: pwm channel the led will operate on
+- qcom,pwm-us: time the pwm device will modulate at (us)
+
+Required properties for LPG mode only:
+- qcom,pwm-channel: pwm channel the led will operate on
+- qcom,duty-pcts: array of values for duty cycle to go through
+- qcom,start-idx: starting point duty-pcts array
+
+Optional properties for LPG mode only:
+- qcom,pause-lo: pause at low end of cycle
+- qcom,pause-hi: pause at high end of cycle
+- qcom,ramp-step-ms: step between each cycle (ms)
+- qcom,lut-flags: flags to be used in lut configuration
+
+Keypad backlight is a backlight source for buttons. It supports four rows
+and the required rows are enabled by specifying values in the properties.
+
+Required properties for keypad backlight:
+- qcom,mode: mode the led should operate in, options "pwm" and "lpg"
+- qcom,pwm-channel: pwm channel the led will operate on
+- qcom,pwm-us: time the pwm device will modulate at (us)
+- qcom,row-src-sel-val: select source for rows. One bit is used for each row.
+ Specify 0 for vph_pwr and 1 for vbst for each row.
+- qcom,row-scan-val: select rows for scanning
+- qcom,row-scan-en: row scan enable
+
+Required properties for PWM mode only:
+- qcom,pwm-us: time the pwm device will modulate at (us)
+
+Required properties for LPG mode only:
+- qcom,duty-pcts: array of values for duty cycle to go through
+- qcom,start-idx: starting point duty-pcts array
+
+Optional properties for LPG mode only:
+- qcom,pause-lo: pause at low end of cycle
+- qcom,pause-hi: pause at high end of cycle
+- qcom,ramp-step-ms: step between each cycle (ms)
+- qcom,lut-flags: flags to be used in lut configuration
Example:
@@ -92,14 +139,43 @@
};
};
+ qcom,leds@a300 {
+ status = "okay";
+ qcom,led_mpp_pwm {
+ label = "mpp";
+ linux,name = "green";
+ linux,default-trigger = "none";
+ qcom,default-state = "off";
+ qcom,max-current = <40>;
+ qcom,current-setting = <5>;
+ qcom,id = <6>;
+ qcom,mode = "pwm";
+ qcom,source-sel = <8>;
+ qcom,mode-ctrl = <0x60>;
+ qcom,pwm-channel = <0>;
+ qcom,pwm-us = <1000>;
+ };
+ };
+
qcom,leds@d000 {
status = "okay";
qcom,rgb_pwm {
label = "rgb";
linux,name = "led:rgb_red";
- qcom,mode = <0>;
- qcom,pwm-channel = <6>;
+ qcom,mode = "pwm";
qcom,pwm-us = <1000>;
+ qcom,pwm-channel = <6>;
+ qcom,max-current = <12>;
+ qcom,default-state = "off";
+ qcom,id = <3>;
+ linux,default-trigger =
+ "battery-charging";
+ };
+ qcom,rgb_lpg {
+ label = "rgb";
+ linux,name = "led:rgb_green";
+ qcom,mode = "lpg";
+ qcom,pwm-channel = <5>;
qcom,duty-ms = <20>;
qcom,start-idx = <1>;
qcom,idx-len = <10>;
@@ -112,10 +188,10 @@
"battery-charging";
};
- qcom,rgb_lpg {
+ qcom,rgb_blink {
label = "rgb";
linux,name = "led:rgb_blue";
- qcom,mode = <1>;
+ qcom,mode = "pwm";
qcom,pwm-channel = <4>;
qcom,start-idx = <1>;
qcom,idx-len = <10>;
@@ -130,12 +206,15 @@
qcom,turn-off-delay-ms = <500>;
qcom,id = <5>;
linux,default-trigger = "none";
+ qcom,pwm-us = <1000>;
+ qcom,use-blink;
};
};
qcom,leds@d300 {
compatible = "qcom,leds-qpnp";
status = "okay";
+ flash_boost-supply = <&pm8941_chg_boost>;
qcom,flash_0 {
qcom,max-current = <1000>;
qcom,default-state = "off";
@@ -174,3 +253,18 @@
};
};
+ qcom,leds@e200 {
+ status = "okay";
+ qcom,kpdbl {
+ label = "kpdbl";
+ linux,name = "button-backlight";
+ qcom,mode = <0>;
+ qcom,pwm-channel = <8>;
+ qcom,pwm-us = <1000>;
+ qcom,id = <7>;
+ qcom,max-current = <20>;
+ qcom,row-src-sel-val = <0x00>;
+ qcom,row-scan-en = <0x01>;
+ qcom,row-scan-val = <0x01>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-cci.txt b/Documentation/devicetree/bindings/media/video/msm-cci.txt
index 9a7fa90..a2503cd 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cci.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cci.txt
@@ -91,6 +91,7 @@
- gpios : should contain phandle to gpio controller node and array of
#gpio-cells specifying specific gpio (controller specific)
- qcom,gpio-reset : should contain index to gpio used by sensors reset_n
+- qcom,gpio-standby : should contain index to gpio used by sensors standby_n
- qcom,gpio-req-tbl-num : should contain index to gpios specific to this sensor
- qcom,gpio-req-tbl-flags : should contain direction of gpios present in
qcom,gpio-req-tbl-num property (in the same order)
@@ -129,6 +130,10 @@
property should contain phandle of respective actuator node
- qcom,led-flash-src : if LED flash is supported by this sensor, this
property should contain phandle of respective LED flash node
+- qcom,vdd-cx-supply : should contain regulator from which cx voltage is
+ supplied
+- qcom,vdd-cx-name : should contain names of cx regulator
+
* Qualcomm MSM ACTUATOR
Required properties:
@@ -190,6 +195,8 @@
qcom,led-flash-src = <&led_flash0>;
qcom,mount-angle = <90>;
qcom,sensor-name = "s5k3l1yx";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -201,11 +208,15 @@
qcom,cam-vreg-op-mode = <105000 80000 0 100000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
+ <&msmgpio 90 0>,
+ <&msmgpio 89 0>;
qcom,gpio-reset = <1>;
- qcom,gpio-req-tbl-num = <0 1>;
- qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1";
+ "CAM_RESET1",
+ "CAM_STANDBY";
qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 4000>;
diff --git a/Documentation/devicetree/bindings/pil/pil-pronto.txt b/Documentation/devicetree/bindings/pil/pil-pronto.txt
index 199862f..85ccc5d 100644
--- a/Documentation/devicetree/bindings/pil/pil-pronto.txt
+++ b/Documentation/devicetree/bindings/pil/pil-pronto.txt
@@ -14,6 +14,7 @@
- vdd_pronto_pll-supply: regulator to supply pronto pll.
- qcom,firmware-name: Base name of the firmware image. Ex. "wcnss"
- qcom,gpio-err-fatal: GPIO used by the wcnss to indicate error fatal to the Apps.
+- qcom,gpio-err-ready: GPIO used by the wcnss to indicate error ready to the Apps.
- qcom,gpio-proxy-unvote: GPIO used by the wcnss to trigger proxy unvoting in
the Apps
- qcom,gpio-force-stop: GPIO used by the Apps to force the wcnss to shutdown.
@@ -32,6 +33,7 @@
/* GPIO input from wcnss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_4_in 0 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_4_in 1 0>;
qcom,proxy-unvote = <&smp2pgpio_ssr_smp2p_4_in 2 0>;
/* GPIO output to wcnss */
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
index ac8ea73..a7a3f0c 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
@@ -15,7 +15,9 @@
- vdd_cx-supply: Reference to the regulator that supplies the vdd_cx domain.
- qcom,firmware-name: Base name of the firmware image. Ex. "lpass"
- qcom,gpio-err-fatal: GPIO used by the lpass to indicate error fatal to the apps.
+- qcom,gpio-err-ready: GPIO used by the lpass to indicate apps error service is ready.
- qcom,gpio-force-stop: GPIO used by the apps to force the lpass to shutdown.
+- qcom,gpio-proxy-unvote: GPIO used by the lpass to indicate apps clock is ready.
Optional properties:
- vdd_pll-supply: Reference to the regulator that supplies the PLL's rail.
@@ -32,8 +34,9 @@
vdd_cx-supply = <&pm8841_s2>;
qcom,firmware-name = "lpass";
- /* GPIO input from lpass */
+ /* GPIO inputs from lpass */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_2_in 0 0>;
+ qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_2_in 2 0>;
/* GPIO output to lpass */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>;
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
index 2d20794..d6980ac 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
@@ -12,21 +12,28 @@
- reg-names: Names of the bases for the above registers. "qdsp6_base",
"halt_base", "rmb_base", and "restart_reg" are expected.
- interrupts: The modem watchdog interrupt
-- vdd_mss-supply: Reference to the regulator that supplies the processor.
- vdd_cx-supply: Reference to the regulator that supplies the vdd_cx domain.
- vdd_mx-supply: Reference to the regulator that supplies the memory rail.
- qcom,firmware-name: Base name of the firmware image. Ex. "mdsp"
- qcom,gpio-err-fatal: GPIO used by the modem to indicate error fatal to the apps.
+- qcom,gpio-err-ready: GPIO used by the modem to indicate error ready to the apps.
- qcom,gpio-proxy-unvote: GPIO used by the modem to trigger proxy unvoting in
the apps.
- qcom,gpio-force-stop: GPIO used by the apps to force the modem to shutdown.
+- qcom,gpio-stop-ack: GPIO used by the modem to ack force stop or a graceful stop
+ to the apps.
Optional properties:
+- vdd_mss-supply: Reference to the regulator that supplies the processor.
+ This may be a shared regulator that is already voted
+ on in the PIL proxy voting code (and also managed by the
+ modem on its own), hence we mark it as as optional.
- vdd_pll-supply: Reference to the regulator that supplies the PLL's rail.
- qcom,vdd_pll: Voltage to be set for the PLL's rail.
- reg-names: "cxrail_bhs_reg" - control register for modem power
domain.
-- qcom,is-loadable: Boolean- Present if the image needs to be loaded.
+- qcom,is-not-loadable: Boolean- Present if the image does not need to
+ be loaded.
- qcom,pil-self-auth: Boolean- True if authentication is required.
Example:
@@ -43,12 +50,13 @@
vdd_cx-supply = <&pm8841_s2>;
vdd_mx-supply = <&pm8841_s1>;
- qcom,is-loadable;
+ qcom,is-not-loadable;
qcom,firmware-name = "mba";
qcom,pil-self-auth;
/* GPIO inputs from mss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_1_in 1 0>;
qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_1_in 2 0>;
/* GPIO output to mss */
diff --git a/Documentation/devicetree/bindings/power/qpnp-bms.txt b/Documentation/devicetree/bindings/power/qpnp-bms.txt
index 6d093f0..3cac311 100644
--- a/Documentation/devicetree/bindings/power/qpnp-bms.txt
+++ b/Documentation/devicetree/bindings/power/qpnp-bms.txt
@@ -47,7 +47,7 @@
SoC recalculations when the current SoC is below
qcom,low-soc-calculate-soc-threshold or when battery
voltage is below qcom,low-voltage-threshold.
-- qcom,soc-calculate-soc-ms : The time period between subsequent SoC
+- qcom,calculate-soc-ms : The time period between subsequent SoC
recalculations when the current SoC is above or equal
qcom,low-soc-calculate-soc-threshold.
- qcom,chg-term-ua : current in micro-amps when charging is considered done.
@@ -64,6 +64,19 @@
curve.
- qcom,hold-soc-est: if the voltage-based estimated SoC is above this percent,
the BMS will clamp SoC to be at least 1.
+- qcom,tm-temp-margin: if the pmic die temperature changes by more than this
+ value, recalibrate the ADCs. The unit of this property
+ is in millidegrees celsius.
+- qcom,min-fcc-learning-soc: An interger value which defines the minimum SOC
+ to start FCC learning. This is applicable only if
+ FCC learning is enabled.
+- qcom,min-fcc-ocv-pc: An interger value which defines the minimum PC-lookup(OCV)
+ to start FCC learning. This is applicable only if
+ FCC learning is enabled.
+- qcom,min-fcc-learning-samples: An interger value which defines the minimum
+ number of the FCC measurement cycles required to
+ generate an FCC update. This is applicable only
+ if the FCC learning is enabled.
Parent node optional properties:
- qcom,ignore-shutdown-soc: A boolean that controls whether BMS will
@@ -78,6 +91,8 @@
RDS of the batfet.
- qcom,use-ocv-thresholds : A boolean that controls whether BMS will take
new OCVs only between the defined thresholds.
+- qcom,enable-fcc-learning: A boolean that defines if FCC learning is enabled.
+
All sub node required properties:
- reg : offset and length of the PMIC peripheral register map.
@@ -122,6 +137,7 @@
qcom,low-ocv-correction-limit-uv = <100>;
qcom,high-ocv-correction-limit-uv = <50>;
qcom,hold-soc-est = <3>;
+ qcom,tm-temp-margin = <5000>;
qcom,bms-iadc@3800 {
reg = <0x3800 0x100>;
diff --git a/Documentation/devicetree/bindings/power/qpnp-charger.txt b/Documentation/devicetree/bindings/power/qpnp-charger.txt
index fced0d7..6e125f2 100644
--- a/Documentation/devicetree/bindings/power/qpnp-charger.txt
+++ b/Documentation/devicetree/bindings/power/qpnp-charger.txt
@@ -41,6 +41,8 @@
by default. This can then be overriden
writing the the module parameter
"charging_disabled".
+- qcom,duty-cycle-100p: Set this property to enable the 100% duty
+ cycle feature.
- qcom,use-default-batt-values: Set this flag to force reporting of
battery temperature of 250 decidegree
Celsius, state of charge to be 50%
@@ -56,6 +58,17 @@
- qcom,warm-bat-mv: Warm temperature battery target voltage.
- qcom,cool-bat-mv: Cool temperature battery target voltage.
- qcom,tchg-mins: Maximum total software initialized charge time.
+- qcom,bpd-detection: Select a battery presence detection scheme by
+ specifying either "bpd_thm", "bpd_id" or
+ "bpd_thm_id". "bpd_thm" selects the temperature
+ pin, "bpd_id" uses the id pin for battery presence
+ detection, "bpd_thm_id" selects both.
+ If the property is not set, the temperatue pin will
+ be used.
+- otg-parent-supply Specify a phandle to a parent supply regulator
+ for the OTG regulator.
+- boost-parent-supply Specify a phandle to a parent supply regulator
+ for the boost regulator.
Sub node required structure:
- A qcom,chg node must be a child of an SPMI node that has specified
@@ -78,6 +91,7 @@
qcom,usb-chgpth:
- usbin-valid
+
qcom,chgr:
- chg-done
- chg-failed
@@ -134,6 +148,16 @@
- limit-error: Limiting error on SMBB boost.
- boost-pwr-ok: Status of boost power.
+Sub node optional properties:
+ qcom,usb-chgpth:
+ - regulator-name: A string used as a descriptive name
+ for the OTG regulator.
+ qcom,boost:
+ - regulator-min-microvolt: Minimum boost voltage setting.
+ - regulator-max-microvolt: Maximum boost voltage setting.
+ - regulator-name: A string used as a descriptive name
+ for the boost regulator.
+
Example:
pm8941-chg {
spmi-dev-container;
@@ -141,6 +165,9 @@
#address-cells = <1>;
#size-cells = <1>;
+ otg-parent-supply = <&pm8941_boost>;
+ boost-parent-supply = <&foo_parent_reg>;
+
qcom,vddmax-mv = <4200>;
qcom,vddsafe-mv = <4200>;
qcom,vinmin-mv = <4200>;
@@ -211,7 +238,7 @@
"batt-pres";
};
- qcom,usb-chgpth@1300 {
+ pm8941_chg_otg: qcom,usb-chgpth@1300 {
reg = <0x1300 0x100>;
interrupts = <0 0x13 0x0>,
<0 0x13 0x1>,
@@ -231,7 +258,7 @@
"coarse-det-dc";
};
- qcom,boost@1500 {
+ pm8941_chg_boost: qcom,boost@1500 {
reg = <0x1500 0x100>;
interrupts = <0x0 0x15 0x0>,
<0x0 0x15 0x1>;
@@ -244,3 +271,15 @@
reg = <0x1600 0x100>;
};
};
+
+In regulator specific device tree file:
+
+ &pm8941_chg_boost {
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-name = "8941_smbb_boost";
+ };
+
+ &pm8941_chg_otg {
+ regulator-name = "8941_smbb_otg";
+ };
diff --git a/Documentation/devicetree/bindings/regulator/gdsc-regulator.txt b/Documentation/devicetree/bindings/regulator/gdsc-regulator.txt
index eb62ea1..37140b7 100644
--- a/Documentation/devicetree/bindings/regulator/gdsc-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/gdsc-regulator.txt
@@ -11,9 +11,15 @@
Optional properties:
- parent-supply: phandle to the parent supply/regulator node
- - qcom,retain-mems: For Oxili GDSCs only: Presence currently denotes a hardware
- requirement to assert the forced memory retention signals
- in the core's clock branch control register.
+ - qcom,clock-names: List of string names for core clocks
+ - qcom,retain-mem: Presence denotes a hardware requirement to leave the
+ forced core memory retention signals in the core's clock
+ branch control registers asserted.
+ - qcom,retain-periph: Presence denotes a hardware requirement to leave the
+ forced periph memory retention signal in the core's clock
+ branch control registers asserted.
+ - qcom,skip-logic-collapse: Presence denotes a requirement to leave power to
+ the core's logic enabled.
Example:
gdsc_oxili_gx: qcom,gdsc@fd8c4024 {
@@ -21,4 +27,5 @@
regulator-name = "gdsc_oxili_gx";
parent-supply = <&pm8841_s4>;
reg = <0xfd8c4024 0x4>;
+ qcom,clock-names = "core_clk";
};
diff --git a/Documentation/devicetree/bindings/regulator/krait-regulator.txt b/Documentation/devicetree/bindings/regulator/krait-regulator.txt
index aaa731e..a8195df 100644
--- a/Documentation/devicetree/bindings/regulator/krait-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/krait-regulator.txt
@@ -9,14 +9,27 @@
[First Level Nodes]
Required properties:
- compatible: Must be "qcom,krait-pdn"
-- reg: Specifies the physical address of the APCS GCC
- register base
-- reg-names: "apcs_gcc" -string to identify the area where
- the APCS GCC registers reside.
+- reg: This property contains a list of physical
+ addresses for Krait PDN features. The list
+ should contain the address of the APCS GCC
+ register base and the address of the phase
+ scaling factor eFuse.
+- reg-names: This property contains a list of strings naming
+ the registers listed in the reg property.
+ "apcs_gcc" is a string to identify the area
+ where the APCS GCC registers reside.
+ "phase-scaling-efuse" should be used to identify
+ the phase scaling factor eFuse address.
+- qcom,pfm-threshold The power coeff threshold in abstract power units below which
+ pmic will be made to operate in PFM mode.
Optional properties:
- qcom,use-phase-switching indicates whether the driver should add/shed phases on the PMIC
ganged regulator as cpus are hotplugged.
+- qcom,use-phase-scaling-factor Boolean which indicates if the value stored in
+ the phase scaling eFuse should be used or not.
+ If this property is not specified, then worst
+ case scaling will be assumed.
[Second Level Nodes]
Required properties:
@@ -47,10 +60,13 @@
Example:
krait_pdn: krait-pdn@f9011000 {
- reg = <0xf9011000 0x1000>;
- reg-names = "apcs_gcc";
+ reg = <0xf9011000 0x1000>,
+ <0xfc4b80b0 8>;
+ reg-names = "apcs_gcc", "phase-scaling-efuse";
compatible = "qcom,krait-pdn";
qcom,use-phase-switching;
+ qcom,use-phase-scaling-factor;
+ qcom,pfm-threshold = <376975>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 65de56f..4c6569e 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -449,14 +449,29 @@
- qcom,prim-auxpcm-gpio-sync : GPIO on which Primary AUXPCM SYNC signal is coming.
- qcom,prim-auxpcm-gpio-din : GPIO on which Primary AUXPCM DIN signal is coming.
- qcom,prim-auxpcm-gpio-dout : GPIO on which Primary AUXPCM DOUT signal is coming.
+- qcom,prim-auxpcm-gpio-set : set of GPIO lines used for Primary AUXPCM port
+ Possible Values:
+ prim-gpio-prim : Primary AUXPCM shares GPIOs with Primary MI2S
+ prim-gpio-tert : Primary AUXPCM shares GPIOs with Tertiary MI2S
- qcom,sec-auxpcm-gpio-clk : GPIO on which Secondary AUXPCM clk signal is coming.
- qcom,sec-auxpcm-gpio-sync : GPIO on which Secondary AUXPCM SYNC signal is coming.
- qcom,sec-auxpcm-gpio-din : GPIO on which Secondary AUXPCM DIN signal is coming.
- qcom,sec-auxpcm-gpio-dout : GPIO on which Secondary AUXPCM DOUT signal is coming.
- qcom,us-euro-gpios : GPIO on which gnd/mic swap signal is coming.
-
Optional properties:
- qcom,hdmi-audio-rx: specifies if HDMI audio support is enabled or not.
+- qcom,ext-ult-spk-amp-gpio : GPIO for enabling of speaker path amplifier.
+
+- qcom,ext-ult-lo-amp-gpio: GPIO to enable external ultrasound lineout
+ amplifier.
+
+- qcom,headset-jack-type-NO: Adjust GPIO level based on the headset jack type.
+
+
+* APQ8074 ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,apq8074-audio-taiko"
Example:
@@ -498,11 +513,13 @@
qcom,us-euro-gpios = <&pm8941_gpios 20 0>;
qcom,hdmi-audio-rx;
+ qcom,ext-ult-lo-amp-gpio = <&pm8941_gpios 6 0>;
qcom,prim-auxpcm-gpio-clk = <&msmgpio 65 0>;
qcom,prim-auxpcm-gpio-sync = <&msmgpio 66 0>;
qcom,prim-auxpcm-gpio-din = <&msmgpio 67 0>;
qcom,prim-auxpcm-gpio-dout = <&msmgpio 68 0>;
+ qcom,prim-auxpcm-gpio-set = "prim-gpio-prim";
qcom,sec-auxpcm-gpio-clk = <&msmgpio 79 0>;
qcom,sec-auxpcm-gpio-sync = <&msmgpio 80 0>;
qcom,sec-auxpcm-gpio-din = <&msmgpio 81 0>;
@@ -658,3 +675,36 @@
compatible = "qcom,msm-audio-ion;
qcom,smmu-enabled;
};
+
+* MSM8226 ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,msm8226-audio-tapan"
+- qcom,model : The user-visible name of this sound card.
+- qcom,tapan-mclk-clk-freq : Tapan mclk Freq in Hz. currently only 9600000Hz
+ is supported.
+- qcom,prim-auxpcm-gpio-clk : GPIO on which Primary AUXPCM clk signal is coming.
+- qcom,prim-auxpcm-gpio-sync : GPIO on which Primary AUXPCM SYNC signal is coming.
+- qcom,prim-auxpcm-gpio-din : GPIO on which Primary AUXPCM DIN signal is coming.
+- qcom,prim-auxpcm-gpio-dout : GPIO on which Primary AUXPCM DOUT signal is coming.
+- qcom,prim-auxpcm-gpio-set : set of GPIO lines used for Primary AUXPCM port
+ Possible Values:
+ prim-gpio-prim : Primary AUXPCM shares GPIOs with Primary MI2S
+ prim-gpio-tert : Primary AUXPCM shares GPIOs with Tertiary MI2S
+
+Optional Properties:
+- qcom,us-euro-gpios : GPIO on which gnd/mic swap signal is coming.
+
+Example:
+
+sound {
+ compatible = "qcom,msm8226-audio-tapan";
+ qcom,model = "msm8226-tapan-snd-card";
+ qcom,tapan-mclk-clk-freq = <9600000>;
+ qcom,prim-auxpcm-gpio-clk = <&msmgpio 63 0>;
+ qcom,prim-auxpcm-gpio-sync = <&msmgpio 64 0>;
+ qcom,prim-auxpcm-gpio-din = <&msmgpio 65 0>;
+ qcom,prim-auxpcm-gpio-dout = <&msmgpio 66 0>;
+ qcom,prim-auxpcm-gpio-set = "prim-gpio-prim";
+ qcom,cdc-us-euro-gpios = <&msmgpio 69 0>;
+};
diff --git a/Documentation/devicetree/bindings/sound/taiko_codec.txt b/Documentation/devicetree/bindings/sound/taiko_codec.txt
index 777933a..9abf54e 100644
--- a/Documentation/devicetree/bindings/sound/taiko_codec.txt
+++ b/Documentation/devicetree/bindings/sound/taiko_codec.txt
@@ -76,6 +76,8 @@
dynamically.
Supplies in this list are off by default.
+ - qcom,cdc-micbias2-headset-only: Boolean. Allow micbias 2 only to headset mic.
+
Example:
taiko_codec {
@@ -138,6 +140,7 @@
qcom,cdc-slim-ifd = "taiko-slim-ifd";
qcom,cdc-slim-ifd-elemental-addr = [00 00 A0 00 17 02];
qcom,cdc-dmic-sample-rate = <4800000>;
+ qcom,cdc-micbias2-headset-only;
};
Wcd9xxx audio CODEC in I2C mode
diff --git a/Documentation/devicetree/bindings/spi/spi_qsd.txt b/Documentation/devicetree/bindings/spi/spi_qsd.txt
index 4b912f3..f16bcbc 100644
--- a/Documentation/devicetree/bindings/spi/spi_qsd.txt
+++ b/Documentation/devicetree/bindings/spi/spi_qsd.txt
@@ -23,6 +23,12 @@
- qcom,infinite-mode: When missing or set to zero, QUP uses infinite-mode. When
value is non-zero, the value is the number of words in maximum transfer
length.
+ - qcom,active-only : Vote for core clock when the application processor goes
+ to active state and remove that vote when it goes to idle state. This flag may
+ improve service time of first spi request at the expense of power consumption.
+ When this entry is not present, voting is done by the runtime-pm callbacks.
+ - qcom,master-id : Master endpoint number used for voting on clocks using the
+ bus-scaling driver.
Optional properties which are required for support of BAM-mode:
- qcom,ver-reg-exists : Boolean. When present, allows driver to verify if HW
@@ -81,4 +87,5 @@
qcom,bam-consumer-pipe-index = <12>;
qcom,bam-producer-pipe-index = <13>;
qcom,ver-reg-exists;
+ qcom,master-id = <86>;
};
diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
new file mode 100644
index 0000000..20468b2
--- /dev/null
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -0,0 +1,16 @@
+* Universal Flash Storage (UFS) Host Controller
+
+UFSHC nodes are defined to describe on-chip UFS host controllers.
+Each UFS controller instance should have its own node.
+
+Required properties:
+- compatible : compatible list, contains "jedec,ufs-1.1"
+- interrupts : <interrupt mapping for UFS host controller IRQ>
+- reg : <registers mapping>
+
+Example:
+ ufshc@0xfc598000 {
+ compatible = "jedec,ufs-1.1";
+ reg = <0xfc598000 0x800>;
+ interrupts = <0 28 0>;
+ };
diff --git a/Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt b/Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt
index 8ce31d9..a3a9935 100644
--- a/Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt
@@ -40,6 +40,15 @@
DATA GPIO PAD.
- qcom,phy-sof-workaround : If present then HSIC PHY has h/w BUGs related to
SOFs. Software workarounds are required for the same.
+- qcom,pool-64-bit-align: If present then the pool's memory will be aligned
+ to 64 bits
+- qcom,enable_hbm: if present host bus manager is enabled.
+- qcom,disable-park-mode: if present park mode is enabled. Park mode enables executing
+ up to 3 usb packets from each QH.
+- hsic,consider-ipa-handshake: If present then hsic low power mode is
+ depend on suitable handshake with the IPA peer.
+- qcom,ahb-async-bridge-bypass: if present AHB ASYNC bridge will be bypassed such that
+ the bridge on the slave AHB is always used.
- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
below optional properties:
@@ -71,6 +80,7 @@
hsic,ignore-cal-pad-config;
hsic,strobe-pad-offset = <0x2050>;
hsic,data-pad-offset = <0x2054>;
+ hsic,consider-ipa-handshake;
qcom,msm-bus,name = "hsic";
qcom,msm-bus,num-cases = <2>;
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index 6d06e99..e3a6721 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -61,6 +61,9 @@
Used for allowing USB to respond for remote wakup.
- qcom,hsusb-otg-delay-lpm: If present then USB core will wait one second
after disconnect before entering low power mode.
+- qcom,hsusb-otg-delay-lpm-hndshk-on-disconnect: If present then USB core will
+ wait for the handshake with the IPA to complete before entering low
+ power mode.
- <supply-name>-supply: handle to the regulator device tree node.
Optional "supply-name" is "vbus_otg" to supply vbus in host mode.
- qcom,vdd-voltage-level: This property must be a list of three integer
@@ -118,9 +121,6 @@
Optional properties :
- qcom,usb2-enable-hsphy2: If present, select second PHY for USB operation.
-- qcom,pool-64-bit-align: If present then the pool's memory will be aligned
- to 64 bits
-- qcom,enable_hbm: if present host bus manager is enabled.
Example MSM HSUSB EHCI controller device node :
ehci: qcom,ehci-host@f9a55000 {
@@ -135,6 +135,21 @@
qcom,usb2-power-budget = <500>;
};
+MSM HSUSB controller
+
+Required properties :
+- compatible : should be "qcom,ci13xxx_msm"
+
+Optional properties :
+- qcom,itc-level: value of 2^itc-level will be used for as the interrupt threshold
+ (ITC), when itc-level is between 0 to 6.
+
+Example MSM HSUSB controller device node :
+ msm_hsusb: qcom,ci13xxx_msm {
+ compatible = "qcom,ci13xxx_msm";
+ qcom,itc-level = <4>;
+};
+
ANDROID USB:
Required properties:
@@ -145,6 +160,8 @@
update USB PID and serial numbers used by bootloader in DLOAD mode.
- qcom,android-usb-swfi-latency : value to be used by device to vote
for DMA latency in microsecs.
+- qcom,android-usb-cdrom : if this property is present then device creates
+ a new LUN as CD-ROM
Example Android USB device node :
android_usb@fc42b0c8 {
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index 5391734..282257c 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -45,6 +45,8 @@
bits 6-12 PARAMETER_OVERRIDE_B
bits 13-19 PARAMETER_OVERRIDE_C
bits 20-25 PARAMETER_OVERRIDE_D
+- qcom,skip-charger-detection: If present then charger detection using BC1.2
+ is not supported and attached host should always be assumed as SDP.
Sub nodes:
- Sub node for "DWC3- USB3 controller".
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 82ac057..8d41295 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -15,6 +15,7 @@
denx Denx Software Engineering
epson Seiko Epson Corp.
est ESTeem Wireless Modems
+focaltech Focaltech systems
fsl Freescale Semiconductor
GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc.
gef GE Fanuc Intelligent Platforms Embedded Systems, Inc.
diff --git a/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt b/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt
index c130b26..d0bbc47 100644
--- a/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt
+++ b/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt
@@ -18,8 +18,14 @@
- qcom,iris-vddpa-supply : regulator to supply RF PA.
- qcom,iris-vdddig-supply : regulator to supply RF digital(BT/FM).
- gpios: gpio numbers to configure 5-wire interface of WLAN connectivity
-- qcom,has_48mhz_xo: boolean flag to determine the usage of 24MHz XO from RF
-- qcom,has_pronto_hw: boolean flag to determine the revId of the WLAN subsystem
+- qcom,has-48mhz-xo: boolean flag to determine the usage of 24MHz XO from RF
+- qcom,has-pronto-hw: boolean flag to determine the revId of the WLAN subsystem
+
+Optional properties:
+- qcom,has-autodetect-xo: boolean flag to determine whether Iris XO auto detect
+should be performed during boot up.
+- qcom,wlan-rx-buff-count: WLAN RX buffer count is a configurable value,
+using a smaller count for this buffer will reduce the memory usage.
Example:
@@ -41,6 +47,6 @@
gpios = <&msmgpio 36 0>, <&msmgpio 37 0>, <&msmgpio 38 0>,
<&msmgpio 39 0>, <&msmgpio 40 0>;
- qcom,has_48mhz_xo;
- qcom,has_pronto_hw;
+ qcom,has-48mhz-xo;
+ qcom,has-pronto-hw;
};
diff --git a/Documentation/workqueue.txt b/Documentation/workqueue.txt
index a0b577d..a6ab4b6 100644
--- a/Documentation/workqueue.txt
+++ b/Documentation/workqueue.txt
@@ -89,25 +89,28 @@
The cmwq design differentiates between the user-facing workqueues that
subsystems and drivers queue work items on and the backend mechanism
-which manages thread-pool and processes the queued work items.
+which manages thread-pools and processes the queued work items.
The backend is called gcwq. There is one gcwq for each possible CPU
-and one gcwq to serve work items queued on unbound workqueues.
+and one gcwq to serve work items queued on unbound workqueues. Each
+gcwq has two thread-pools - one for normal work items and the other
+for high priority ones.
Subsystems and drivers can create and queue work items through special
workqueue API functions as they see fit. They can influence some
aspects of the way the work items are executed by setting flags on the
workqueue they are putting the work item on. These flags include
-things like CPU locality, reentrancy, concurrency limits and more. To
-get a detailed overview refer to the API description of
+things like CPU locality, reentrancy, concurrency limits, priority and
+more. To get a detailed overview refer to the API description of
alloc_workqueue() below.
-When a work item is queued to a workqueue, the target gcwq is
-determined according to the queue parameters and workqueue attributes
-and appended on the shared worklist of the gcwq. For example, unless
-specifically overridden, a work item of a bound workqueue will be
-queued on the worklist of exactly that gcwq that is associated to the
-CPU the issuer is running on.
+When a work item is queued to a workqueue, the target gcwq and
+thread-pool is determined according to the queue parameters and
+workqueue attributes and appended on the shared worklist of the
+thread-pool. For example, unless specifically overridden, a work item
+of a bound workqueue will be queued on the worklist of either normal
+or highpri thread-pool of the gcwq that is associated to the CPU the
+issuer is running on.
For any worker pool implementation, managing the concurrency level
(how many execution contexts are active) is an important issue. cmwq
@@ -115,26 +118,26 @@
Minimal to save resources and sufficient in that the system is used at
its full capacity.
-Each gcwq bound to an actual CPU implements concurrency management by
-hooking into the scheduler. The gcwq is notified whenever an active
-worker wakes up or sleeps and keeps track of the number of the
-currently runnable workers. Generally, work items are not expected to
-hog a CPU and consume many cycles. That means maintaining just enough
-concurrency to prevent work processing from stalling should be
-optimal. As long as there are one or more runnable workers on the
-CPU, the gcwq doesn't start execution of a new work, but, when the
-last running worker goes to sleep, it immediately schedules a new
-worker so that the CPU doesn't sit idle while there are pending work
-items. This allows using a minimal number of workers without losing
-execution bandwidth.
+Each thread-pool bound to an actual CPU implements concurrency
+management by hooking into the scheduler. The thread-pool is notified
+whenever an active worker wakes up or sleeps and keeps track of the
+number of the currently runnable workers. Generally, work items are
+not expected to hog a CPU and consume many cycles. That means
+maintaining just enough concurrency to prevent work processing from
+stalling should be optimal. As long as there are one or more runnable
+workers on the CPU, the thread-pool doesn't start execution of a new
+work, but, when the last running worker goes to sleep, it immediately
+schedules a new worker so that the CPU doesn't sit idle while there
+are pending work items. This allows using a minimal number of workers
+without losing execution bandwidth.
Keeping idle workers around doesn't cost other than the memory space
for kthreads, so cmwq holds onto idle ones for a while before killing
them.
For an unbound wq, the above concurrency management doesn't apply and
-the gcwq for the pseudo unbound CPU tries to start executing all work
-items as soon as possible. The responsibility of regulating
+the thread-pools for the pseudo unbound CPU try to start executing all
+work items as soon as possible. The responsibility of regulating
concurrency level is on the users. There is also a flag to mark a
bound wq to ignore the concurrency management. Please refer to the
API section for details.
@@ -205,31 +208,22 @@
WQ_HIGHPRI
- Work items of a highpri wq are queued at the head of the
- worklist of the target gcwq and start execution regardless of
- the current concurrency level. In other words, highpri work
- items will always start execution as soon as execution
- resource is available.
+ Work items of a highpri wq are queued to the highpri
+ thread-pool of the target gcwq. Highpri thread-pools are
+ served by worker threads with elevated nice level.
- Ordering among highpri work items is preserved - a highpri
- work item queued after another highpri work item will start
- execution after the earlier highpri work item starts.
-
- Although highpri work items are not held back by other
- runnable work items, they still contribute to the concurrency
- level. Highpri work items in runnable state will prevent
- non-highpri work items from starting execution.
-
- This flag is meaningless for unbound wq.
+ Note that normal and highpri thread-pools don't interact with
+ each other. Each maintain its separate pool of workers and
+ implements concurrency management among its workers.
WQ_CPU_INTENSIVE
Work items of a CPU intensive wq do not contribute to the
concurrency level. In other words, runnable CPU intensive
- work items will not prevent other work items from starting
- execution. This is useful for bound work items which are
- expected to hog CPU cycles so that their execution is
- regulated by the system scheduler.
+ work items will not prevent other work items in the same
+ thread-pool from starting execution. This is useful for bound
+ work items which are expected to hog CPU cycles so that their
+ execution is regulated by the system scheduler.
Although CPU intensive work items don't contribute to the
concurrency level, start of their executions is still
@@ -239,14 +233,6 @@
This flag is meaningless for unbound wq.
- WQ_HIGHPRI | WQ_CPU_INTENSIVE
-
- This combination makes the wq avoid interaction with
- concurrency management completely and behave as a simple
- per-CPU execution context provider. Work items queued on a
- highpri CPU-intensive wq start execution as soon as resources
- are available and don't affect execution of other work items.
-
@max_active:
@max_active determines the maximum number of execution contexts per
@@ -328,20 +314,7 @@
35 w2 wakes up and finishes
Now, let's assume w1 and w2 are queued to a different wq q1 which has
-WQ_HIGHPRI set,
-
- TIME IN MSECS EVENT
- 0 w1 and w2 start and burn CPU
- 5 w1 sleeps
- 10 w2 sleeps
- 10 w0 starts and burns CPU
- 15 w0 sleeps
- 15 w1 wakes up and finishes
- 20 w2 wakes up and finishes
- 25 w0 wakes up and burns CPU
- 30 w0 finishes
-
-If q1 has WQ_CPU_INTENSIVE set,
+WQ_CPU_INTENSIVE set,
TIME IN MSECS EVENT
0 w0 starts and burns CPU
diff --git a/Makefile b/Makefile
index 75b36ae..be32c2f 100644
--- a/Makefile
+++ b/Makefile
@@ -563,7 +563,7 @@
all: vmlinux
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
-KBUILD_CFLAGS += -Os
+KBUILD_CFLAGS += -Os $(call cc-disable-warning,maybe-uninitialized,)
else
KBUILD_CFLAGS += -O2
endif
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/apq8026-mtp.dts
similarity index 69%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/apq8026-mtp.dts
index e410344..e14a6856 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/apq8026-mtp.dts
@@ -10,20 +10,13 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
-/include/ "msmzinc.dtsi"
+/dts-v1/;
+/include/ "apq8026.dtsi"
+/include/ "msm8226-mtp.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
-
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
+ model = "Qualcomm APQ 8026 MTP";
+ compatible = "qcom,apq8026-mtp", "qcom,apq8026", "qcom,mtp";
+ qcom,msm-id = <199 8 0>;
};
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/apq8026-xpm.dts
similarity index 69%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/apq8026-xpm.dts
index e410344..67152af 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/apq8026-xpm.dts
@@ -10,20 +10,13 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
-/include/ "msmzinc.dtsi"
+/dts-v1/;
+/include/ "apq8026.dtsi"
+/include/ "msm8226-cdp.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
-
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
+ model = "Qualcomm APQ 8026 XPM";
+ compatible = "qcom,apq8026-xpm", "qcom,apq8026", "qcom,xpm";
+ qcom,msm-id = <199 14 0>;
};
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/apq8026.dtsi
similarity index 68%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/apq8026.dtsi
index e410344..db6576a 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/apq8026.dtsi
@@ -10,20 +10,15 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
+/*
+ * Only 8026-specific property overrides should be placed inside this
+ * file. Device definitions should be placed inside the msm8226.dtsi
+ * file.
+ */
-/include/ "msmzinc.dtsi"
+/include/ "msm8226.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
-
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
+ model = "Qualcomm APQ 8026";
+ compatible = "qcom,apq8026";
};
diff --git a/arch/arm/boot/dts/apq8074-dragonboard.dtsi b/arch/arm/boot/dts/apq8074-dragonboard.dtsi
new file mode 100644
index 0000000..a8b3065
--- /dev/null
+++ b/arch/arm/boot/dts/apq8074-dragonboard.dtsi
@@ -0,0 +1,655 @@
+/* Copyright (c) 2013, 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/ "dsi-panel-sharp-qhd-video.dtsi"
+/include/ "msm8974-camera-sensor-dragonboard.dtsi"
+/include/ "msm8974-leds.dtsi"
+
+&soc {
+ serial@f991e000 {
+ status = "ok";
+ };
+
+ qcom,mdss_dsi_sharp_qhd_video {
+ status = "ok";
+ qcom,cont-splash-enabled;
+ };
+
+ qcom,hdmi_tx@fd922100 {
+ status = "ok";
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&pm8941_gpios 5 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ general {
+ label = "general";
+ gpios = <&pm8941_gpios 23 0x1>;
+ linux,input-type = <1>;
+ linux,code = <102>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+ };
+
+ bt_ar3002 {
+ compatible = "qca,ar3002";
+ qca,bt-reset-gpio = <&pm8941_gpios 34 0>;
+ };
+
+ hsic_hub {
+ compatible = "qcom,hsic-smsc-hub";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ smsc,reset-gpio = <&pm8941_gpios 8 0x00>;
+
+ hsic_host: hsic@f9a00000 {
+ compatible = "qcom,hsic-host";
+ reg = <0xf9a00000 0x400>;
+ #address-cells = <0>;
+ interrupt-parent = <&hsic_host>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 136 0
+ 1 &intc 0 148 0
+ 2 &msmgpio 144 0x8>;
+ interrupt-names = "core_irq", "async_irq", "wakeup";
+ HSIC_VDDCX-supply = <&pm8841_s2>;
+ HSIC_GDSC-supply = <&gdsc_usb_hsic>;
+ hsic,strobe-gpio = <&msmgpio 144 0x00>;
+ hsic,data-gpio = <&msmgpio 145 0x00>;
+ hsic,ignore-cal-pad-config;
+ hsic,strobe-pad-offset = <0x2050>;
+ hsic,data-pad-offset = <0x2054>;
+
+ qcom,msm-bus,name = "hsic";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <85 512 0 0>,
+ <85 512 40000 160000>;
+ };
+ };
+
+ i2c@f9923000 {
+ status = "ok";
+ atmel_mxt_ts@4a {
+ compatible = "atmel,mxt-ts";
+ reg = <0x4a>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <61 0x2>;
+ vdd_ana-supply = <&pm8941_l18>;
+ vcc_i2c-supply = <&pm8941_s3>;
+ atmel,reset-gpio = <&msmgpio 60 0x00>;
+ atmel,irq-gpio = <&msmgpio 61 0x00>;
+ atmel,panel-coords = <0 0 566 1067>;
+ atmel,display-coords = <0 0 540 960>;
+ atmel,i2c-pull-up;
+ atmel,cfg_1 {
+ atmel,family-id = <0x81>;
+ atmel,variant-id = <0x19>;
+ atmel,version = <0x10>;
+ atmel,build = <0xaa>;
+ atmel,config = [
+ /* Object 38, Instance = 0 */
+ 0F 02 00 17 04 0C 00 00
+ /* Object 7, Instance = 0 */
+ 30 FF 19
+ /* Object 8, Instance = 0 */
+ 1B 00 05 01 00 00 08 08 00 00
+ /* Object 9, Instance = 0 */
+ 83 00 00 13 0B 00 10 23 01 03
+ 0A 0F 01 0B 04 05 28 0A 2B 04
+ 36 02 00 00 00 00 8F 28 8F 50
+ 12 0F 32 32 02
+ /* Object 15, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00
+ /* Object 18, Instance = 0 */
+ 00 00
+ /* Object 19, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00
+ /* Object 23, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00
+ /* Object 25, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00
+ /* Object 40, Instance = 0 */
+ 00 00 00 00 00
+ /* Object 42, Instance = 0 */
+ 00 00 00 00 00 00 00 00
+ /* Object 46, Instance = 0 */
+ 00 03 10 30 00 00 01 00 00
+ /* Object 47, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ /* Object 48, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00
+ ];
+ };
+ };
+ };
+
+ gen-vkeys {
+ compatible = "qcom,gen-vkeys";
+ label = "atmel_mxt_ts";
+ qcom,disp-maxx = <540>;
+ qcom,disp-maxy = <960>;
+ qcom,panel-maxx = <566>;
+ qcom,panel-maxy = <1067>;
+ qcom,key-codes = <158 139 102 217>;
+ };
+
+ sound {
+ qcom,model = "apq8074-taiko-db-snd-card";
+ qcom,hdmi-audio-rx;
+
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "LDO_H", "MCLK",
+ "AMIC1", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Analog Mic4",
+ "AMIC2", "MIC BIAS2 External",
+ "MIC BIAS2 External", "Headset Mic",
+ "AMIC3", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCLeft Headset Mic",
+ "AMIC5", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Analog Mic6",
+ "AMIC6", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Analog Mic7",
+ "DMIC1", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic1",
+ "DMIC2", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic2",
+ "DMIC3", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic3",
+ "DMIC4", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic4",
+ "DMIC5", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic5",
+ "DMIC6", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic6";
+ };
+
+ qcom,pronto@fb21b000 {
+ status = "disabled";
+ };
+
+ qcom,iris-fm {
+ status = "disabled";
+ };
+
+ qcom,wcnss-wlan@fb000000 {
+ status = "disabled";
+ };
+
+ qcom,smd-wcnss {
+ status = "disabled";
+ };
+
+ qcom,smsm-wcnss {
+ status = "disabled";
+ };
+};
+
+&mdss_fb0 {
+ qcom,memory-reservation-size = <0x1000000>; /* size 16MB */
+};
+
+&sdcc3 {
+ qcom,sup-voltages = <2000 2000>;
+ status = "ok";
+};
+
+&pm8941_l19 {
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ qcom,init-voltage = <3300000>;
+ regulator-always-on;
+};
+
+&pm8941_l10 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+ regulator-always-on;
+};
+
+&uart7 {
+ status = "ok";
+ qcom,tx-gpio = <&msmgpio 41 0x00>;
+ qcom,rx-gpio = <&msmgpio 42 0x00>;
+ qcom,cts-gpio = <&msmgpio 43 0x00>;
+ qcom,rfr-gpio = <&msmgpio 44 0x00>;
+};
+
+&usb_otg {
+ status = "ok";
+ qcom,hsusb-otg-otg-control = <2>;
+ qcom,hsusb-otg-mode = <3>;
+ vbus_otg-supply = <&pm8941_mvs1>;
+ qcom,usb2-enable-hsphy2;
+ qcom,dp-manual-pullup;
+
+ #address-cells = <0>;
+ interrupt-parent = <&usb_otg>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 134 0
+ 1 &intc 0 140 0
+ 2 &spmi_bus 0x0 0x0 0x9 0x0>;
+ interrupt-names = "core_irq", "async_irq", "pmic_id_irq";
+};
+
+&usb3 {
+ qcom,charging-disabled;
+ vbus_dwc3-supply = <0>;
+ dwc3@f9200000 {
+ host-only-mode;
+ };
+};
+
+&slim_msm {
+ taiko_codec {
+ qcom,cdc-micbias2-ext-cap;
+ qcom,cdc-micbias3-ext-cap;
+ };
+};
+
+&pm8941_gpios {
+ gpio@c000 { /* GPIO 1 */
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <2>;
+ qcom,src-sel = <0>;
+ };
+
+ gpio@c100 { /* GPIO 2 */
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <2>;
+ qcom,src-sel = <0>;
+ };
+
+ gpio@c200 { /* GPIO 3 */
+ };
+
+ gpio@c300 { /* GPIO 4 */
+ };
+
+ gpio@c400 { /* GPIO 5 */
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <2>;
+ qcom,src-sel = <0>;
+ };
+
+ gpio@c500 { /* GPIO 6 */
+ /* TUSB3_HUB-RESET */
+ qcom,mode = <1>; /* QPNP_PIN_MODE_DIG_OUT */
+ qcom,output-type = <0>; /* QPNP_PIN_OUT_BUF_CMOS */
+ qcom,pull = <0>; /* QPNP_PIN_PULL_30 */
+ qcom,vin-sel = <0>; /* QPNP_PIN_VIN0 VPH */
+ qcom,out-strength = <2>; /* QPNP_PIN_OUT_STRENGTH_MED */
+ qcom,src-sel = <0>; /* QPNP_PIN_SEL_FUNC_CONSTANT */
+ qcom,invert = <1>; /* Keep it out of reset */
+ qcom,master-en = <1>;
+ };
+
+ gpio@c600 { /* GPIO 7 */
+ };
+
+ gpio@c700 { /* GPIO 8 */
+ /* HSIC_HUB-RESET */
+ qcom,mode = <1>; /* DIG_OUT */
+ qcom,pull = <5>; /* PULL_NO */
+ qcom,out-strength = <2>; /* STRENGTH_MED */
+ qcom,master-en = <1>;
+ };
+
+ gpio@c800 { /* GPIO 9 */
+ /* GbE_RST_N */
+ qcom,mode = <1>; /* QPNP_PIN_MODE_DIG_OUT */
+ qcom,output-type = <0>; /* QPNP_PIN_OUT_BUF_CMOS */
+ qcom,pull = <0>; /* QPNP_PIN_PULL_30 */
+ qcom,vin-sel = <0>; /* QPNP_PIN_VIN0 VPH */
+ qcom,out-strength = <2>; /* QPNP_PIN_OUT_STRENGTH_MED */
+ qcom,src-sel = <0>; /* QPNP_PIN_SEL_FUNC_CONSTANT */
+ qcom,invert = <1>; /* Keep it out of reset */
+ qcom,master-en = <1>;
+ };
+
+ gpio@c900 { /* GPIO 10 */
+ /* SATA_RST_N */
+ qcom,mode = <1>; /* QPNP_PIN_MODE_DIG_OUT */
+ qcom,output-type = <0>; /* QPNP_PIN_OUT_BUF_CMOS */
+ qcom,pull = <0>; /* QPNP_PIN_PULL_30 */
+ qcom,vin-sel = <0>; /* QPNP_PIN_VIN0 VPH */
+ qcom,out-strength = <2>; /* QPNP_PIN_OUT_STRENGTH_MED */
+ qcom,src-sel = <0>; /* QPNP_PIN_SEL_FUNC_CONSTANT */
+ qcom,invert = <1>; /* Keep it out of reset */
+ qcom,master-en = <1>;
+ };
+
+ gpio@ca00 { /* GPIO 11 */
+ };
+
+ gpio@cb00 { /* GPIO 12 */
+ };
+
+ gpio@cc00 { /* GPIO 13 */
+ };
+
+ gpio@cd00 { /* GPIO 14 */
+ };
+
+ gpio@ce00 { /* GPIO 15 */
+ qcom,mode = <1>;
+ qcom,output-type = <0>;
+ qcom,pull = <5>;
+ qcom,vin-sel = <2>;
+ qcom,out-strength = <3>;
+ qcom,src-sel = <2>;
+ qcom,master-en = <1>;
+ };
+
+ gpio@cf00 { /* GPIO 16 */
+ };
+
+ gpio@d000 { /* GPIO 17 */
+ };
+
+ gpio@d100 { /* GPIO 18 */
+ };
+
+ gpio@d200 { /* GPIO 19 */
+ qcom,mode = <1>; /* QPNP_PIN_MODE_DIG_OUT */
+ qcom,output-type = <0>; /* QPNP_PIN_OUT_BUF_CMOS */
+ qcom,pull = <5>; /* QPNP_PIN_PULL_NO */
+ qcom,vin-sel = <2>; /* QPNP_PIN_VIN2 */
+ qcom,out-strength = <2>; /* QPNP_PIN_OUT_STRENGTH_MED */
+ qcom,src-sel = <0>; /* QPNP_PIN_SEL_FUNC_CONSTANT */
+ qcom,master-en = <1>;
+ };
+
+ gpio@d300 { /* GPIO 20 */
+ };
+
+ gpio@d400 { /* GPIO 21 */
+ };
+
+ gpio@d500 { /* GPIO 22 */
+ };
+
+ gpio@d600 { /* GPIO 23 */
+ };
+
+ gpio@d700 { /* GPIO 24 */
+ };
+
+ gpio@d800 { /* GPIO 25 */
+ };
+
+ gpio@d900 { /* GPIO 26 */
+ };
+
+ gpio@da00 { /* GPIO 27 */
+ };
+
+ gpio@db00 { /* GPIO 28 */
+ };
+
+ gpio@dc00 { /* GPIO 29 */
+ qcom,pull = <0>; /* set to default pull */
+ qcom,master-en = <1>;
+ qcom,vin-sel = <2>; /* select 1.8 V source */
+ };
+
+ gpio@dd00 { /* GPIO 30 */
+ };
+
+ gpio@de00 { /* GPIO 31 */
+ };
+
+ gpio@df00 { /* GPIO 32 */
+ };
+
+ gpio@e000 { /* GPIO 33 */
+ qcom,mode = <1>; /* QPNP_PIN_MODE_DIG_OUT */
+ qcom,output-type = <0>; /* QPNP_PIN_OUT_BUF_CMOS */
+ qcom,pull = <5>; /* QPNP_PIN_PULL_NO */
+ qcom,vin-sel = <2>; /* QPNP_PIN_VIN2 */
+ qcom,out-strength = <2>; /* QPNP_PIN_OUT_STRENGTH_MED */
+ qcom,src-sel = <0>; /* QPNP_PIN_SEL_FUNC_CONSTANT */
+ qcom,invert = <1>;
+ qcom,master-en = <1>;
+ };
+
+ gpio@e100 { /* GPIO 34 */
+ qcom,mode = <1>; /* QPNP_PIN_MODE_DIG_OUT */
+ qcom,output-type = <0>; /* QPNP_PIN_OUT_BUF_CMOS */
+ qcom,pull = <5>; /* QPNP_PIN_PULL_NO */
+ qcom,vin-sel = <2>; /* QPNP_PIN_VIN2 */
+ qcom,out-strength = <2>; /* QPNP_PIN_OUT_STRENGTH_MED */
+ qcom,src-sel = <0>; /* QPNP_PIN_SEL_FUNC_CONSTANT */
+ qcom,invert = <0>;
+ qcom,master-en = <1>;
+ };
+
+ gpio@e200 { /* GPIO 35 */
+ };
+
+ gpio@e300 { /* GPIO 36 */
+ qcom,mode = <1>; /* QPNP_PIN_MODE_DIG_OUT */
+ qcom,output-type = <0>; /* QPNP_PIN_OUT_BUF_CMOS */
+ qcom,pull = <5>; /* QPNP_PIN_PULL_NO */
+ qcom,vin-sel = <2>; /* QPNP_PIN_VIN2 */
+ qcom,out-strength = <3>; /* QPNP_PIN_OUT_STRENGTH_HIGH */
+ qcom,src-sel = <3>; /* QPNP_PIN_SEL_FUNC_2 */
+ qcom,master-en = <1>;
+ };
+};
+
+&pm8941_mpps {
+
+ mpp@a000 { /* MPP 1 */
+ };
+
+ mpp@a100 { /* MPP 2 */
+ };
+
+ mpp@a200 { /* MPP 3 */
+ };
+
+ mpp@a300 { /* MPP 4 */
+ };
+
+ mpp@a400 { /* MPP 5 */
+ };
+
+ mpp@a500 { /* MPP 6 */
+ };
+
+ mpp@a600 { /* MPP 7 */
+ };
+
+ mpp@a700 { /* MPP 8 */
+ };
+};
+
+&pm8841_mpps {
+
+ mpp@a000 { /* MPP 1 */
+ };
+
+ mpp@a100 { /* MPP 2 */
+ };
+
+ mpp@a200 { /* MPP 3*/
+ };
+
+ mpp@a300 { /* MPP 4*/
+ };
+};
+
+&spi_epm {
+ epm-adc@0 {
+ compatible = "cy,epm-adc-cy8c5568lti-114";
+ reg = <0>;
+ interrupt-parent = <&msmgpio>;
+ spi-max-frequency = <960000>;
+ qcom,channels = <31>;
+ qcom,gain = <50 50 50 50 50 100 50 50 50 50
+ 50 50 50 50 100 50 50 50 50 100
+ 50 50 50 100 50 50 50 1 1 1
+ 1>;
+ qcom,rsense = <40 10 10 25 10 1000 75 25 10 25
+ 33 500 200 10 500 100 33 200 25 100
+ 75 500 50 200 5 5 3 1 1 1
+ 1>;
+ qcom,channel-type = <0xf0000000>;
+ };
+};
+
+&spmi_bus {
+ qcom,pm8941@1 {
+ qcom,leds@d000 {
+ qcom,rgb_2 {
+ status = "ok";
+ qcom,default-state = "on";
+ qcom,turn-off-delay-ms = <1000>;
+ };
+ };
+
+ qcom,leds@d800 {
+ status = "okay";
+ qcom,wled_0 {
+ label = "wled";
+ linux,name = "wled:backlight";
+ linux,default-trigger = "bkl-trigger";
+ qcom,cs-out-en;
+ qcom,op-fdbck = <1>;
+ qcom,default-state = "on";
+ qcom,max-current = <20>;
+ qcom,ctrl-delay-us = <0>;
+ qcom,boost-curr-lim = <3>;
+ qcom,cp-sel = <0>;
+ qcom,switch-freq = <2>;
+ qcom,ovp-val = <1>;
+ qcom,num-strings = <1>;
+ qcom,id = <0>;
+ };
+ };
+ };
+};
+
+&pm8941_chg {
+ status = "ok";
+
+ qcom,charging-disabled;
+
+ qcom,chgr@1000 {
+ status = "ok";
+ };
+
+ qcom,buck@1100 {
+ status = "ok";
+ };
+
+ qcom,usb-chgpth@1300 {
+ status = "ok";
+ };
+
+ qcom,dc-chgpth@1400 {
+ status = "ok";
+ };
+
+ qcom,boost@1500 {
+ status = "ok";
+ };
+
+ qcom,chg-misc@1600 {
+ status = "ok";
+ };
+};
+
+&sdhc_1 {
+ vdd-supply = <&pm8941_l20>;
+ vdd-io-supply = <&pm8941_s3>;
+
+ qcom,vdd-always-on;
+ qcom,vdd-lpm-sup;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <800 500000>;
+
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <250 154000>;
+
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,nonremovable;
+ status = "ok";
+};
+
+&sdhc_2 {
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_2>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 221 0
+ 2 &msmgpio 62 0x3>;
+ interrupt-names = "hc_irq", "pwr_irq", "status_irq";
+ cd-gpios = <&msmgpio 62 0x1>;
+
+ vdd-supply = <&pm8941_l21>;
+ vdd-io-supply = <&pm8941_l13>;
+
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <9000 800000>;
+
+ qcom,vdd-io-voltage-level = <1800000 2950000>;
+ qcom,vdd-io-current-level = <6 22000>;
+
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/apq8074-v1.dtsi b/arch/arm/boot/dts/apq8074-v1.dtsi
new file mode 100644
index 0000000..c4e7b7c
--- /dev/null
+++ b/arch/arm/boot/dts/apq8074-v1.dtsi
@@ -0,0 +1,48 @@
+/* Copyright (c) 2013, 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.
+ */
+
+/*
+ * As a general rule, only version-specific property overrides should be placed
+ * inside this file. However, device definitions should be placed inside the
+ * msm8974.dtsi file.
+ */
+
+/include/ "msm8974-v1.dtsi"
+
+&soc {
+ qcom,qseecom@a700000 {
+ compatible = "qcom,qseecom";
+ reg = <0x0a700000 0x500000>;
+ reg-names = "secapp-region";
+ qcom,disk-encrypt-pipe-pair = <2>;
+ qcom,hlos-ce-hw-instance = <1>;
+ qcom,qsee-ce-hw-instance = <0>;
+ qcom,msm-bus,name = "qseecom-noc";
+ qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <55 512 0 0>,
+ <55 512 3936000 393600>,
+ <55 512 3936000 393600>,
+ <55 512 3936000 393600>;
+ };
+};
+
+&memory_hole {
+ qcom,memblock-remove = <0x0a700000 0x5800000>; /* Address and size of the hole */
+};
+
+&qseecom {
+ status = "disabled";
+};
+
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/apq8074-v2-dragonboard.dts
similarity index 69%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/apq8074-v2-dragonboard.dts
index e410344..5a6f5f3 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/apq8074-v2-dragonboard.dts
@@ -12,18 +12,11 @@
/dts-v1/;
-/include/ "msmzinc.dtsi"
+/include/ "apq8074-v2.dtsi"
+/include/ "apq8074-dragonboard.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
-
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
+ model = "Qualcomm APQ 8074v2 DRAGONBOARD";
+ compatible = "qcom,apq8074-dragonboard", "qcom,apq8074", "qcom,dragonboard";
+ qcom,msm-id = <184 10 0x20000>;
};
diff --git a/arch/arm/boot/dts/apq8074-v2-liquid.dts b/arch/arm/boot/dts/apq8074-v2-liquid.dts
new file mode 100644
index 0000000..4ec1cdd
--- /dev/null
+++ b/arch/arm/boot/dts/apq8074-v2-liquid.dts
@@ -0,0 +1,34 @@
+/* Copyright (c) 2013, 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.
+ */
+
+/dts-v1/;
+
+/include/ "apq8074-v2.dtsi"
+/include/ "msm8974-liquid.dtsi"
+
+/ {
+ model = "Qualcomm APQ 8074v2 LIQUID";
+ compatible = "qcom,apq8074-liquid", "qcom,apq8074", "qcom,liquid";
+ qcom,msm-id = <184 9 0x20000>;
+};
+
+&usb3 {
+ interrupt-parent = <&usb3>;
+ interrupts = <0 1>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0x0 0xffffffff>;
+ interrupt-map = <0x0 0 &intc 0 133 0
+ 0x0 1 &spmi_bus 0x0 0x0 0x9 0x0>;
+ interrupt-names = "hs_phy_irq", "pmic_id_irq";
+
+ qcom,misc-ref = <&pm8941_misc>;
+};
diff --git a/arch/arm/boot/dts/apq8074-v2.dtsi b/arch/arm/boot/dts/apq8074-v2.dtsi
new file mode 100644
index 0000000..76eb14b
--- /dev/null
+++ b/arch/arm/boot/dts/apq8074-v2.dtsi
@@ -0,0 +1,52 @@
+/* Copyright (c) 2013, 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.
+ */
+
+/*
+ * As a general rule, only version-specific property overrides should be placed
+ * inside this file. However, device definitions should be placed inside the
+ * msm8974.dtsi file.
+ */
+
+/include/ "msm8974-v2.dtsi"
+
+&soc {
+ qcom,qseecom@a700000 {
+ compatible = "qcom,qseecom";
+ reg = <0x0a700000 0x500000>;
+ reg-names = "secapp-region";
+ qcom,disk-encrypt-pipe-pair = <2>;
+ qcom,hlos-ce-hw-instance = <1>;
+ qcom,qsee-ce-hw-instance = <0>;
+ qcom,msm-bus,name = "qseecom-noc";
+ qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <55 512 0 0>,
+ <55 512 3936000 393600>,
+ <55 512 3936000 393600>,
+ <55 512 3936000 393600>;
+ };
+
+ sound {
+ compatible = "qcom,apq8074-audio-taiko";
+ };
+};
+
+&memory_hole {
+ qcom,memblock-remove = <0x0a700000 0x5800000>; /* Address and size of the hole */
+};
+
+&qseecom {
+ status = "disabled";
+};
+
diff --git a/arch/arm/boot/dts/msmzinc-ion.dtsi b/arch/arm/boot/dts/apq8084-ion.dtsi
similarity index 98%
rename from arch/arm/boot/dts/msmzinc-ion.dtsi
rename to arch/arm/boot/dts/apq8084-ion.dtsi
index aac4230..ea954b8 100644
--- a/arch/arm/boot/dts/msmzinc-ion.dtsi
+++ b/arch/arm/boot/dts/apq8084-ion.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,ion {
compatible = "qcom,msm-ion";
#address-cells = <1>;
diff --git a/arch/arm/boot/dts/apq8084-regulator.dtsi b/arch/arm/boot/dts/apq8084-regulator.dtsi
new file mode 100644
index 0000000..998b469
--- /dev/null
+++ b/arch/arm/boot/dts/apq8084-regulator.dtsi
@@ -0,0 +1,377 @@
+/* Copyright (c) 2013, 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.
+ */
+
+
+/* QPNP controlled regulators: */
+
+&spmi_bus {
+ qcom,pma8084@1 {
+ pma8084_s1: regulator@1400 {
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1050000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ qcom,system-load = <100000>;
+ status = "okay";
+ };
+
+ /* PMA8084 S2 + S12 = 2 phase VDD_CX supply */
+ pma8084_s2: regulator@1700 {
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1050000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ qcom,system-load = <100000>;
+ status = "okay";
+ };
+
+ pma8084_s3: regulator@1a00 {
+ regulator-min-microvolt = <1300000>;
+ regulator-max-microvolt = <1300000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ qcom,system-load = <100000>;
+ status = "okay";
+ };
+
+ pma8084_s4: regulator@1d00 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ qcom,system-load = <100000>;
+ status = "okay";
+ };
+
+ pma8084_s5: regulator@2000 {
+ regulator-min-microvolt = <2150000>;
+ regulator-max-microvolt = <2150000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ /* PMA8084 S6 + S7 = 2 phase VDD_GFX supply */
+ pma8084_s6: regulator@2300 {
+ regulator-min-microvolt = <815000>;
+ regulator-max-microvolt = <900000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ /* PMA8084 S8 + S9 + S10 + S11 = 4 phase VDD_APC supply */
+ pma8084_s8: regulator@2900 {
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <1100000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ qcom,system-load = <100000>;
+ status = "okay";
+ };
+
+ /* Output of PMA8084 L1 and L11 is tied together. */
+ pma8084_l1: regulator@4000 {
+ parent-supply = <&pma8084_s3>;
+ regulator-min-microvolt = <1225000>;
+ regulator-max-microvolt = <1225000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ qcom,system-load = <10000>;
+ status = "okay";
+ };
+
+ pma8084_l2: regulator@4100 {
+ parent-supply = <&pma8084_s3>;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l3: regulator@4200 {
+ parent-supply = <&pma8084_s3>;
+ regulator-min-microvolt = <1225000>;
+ regulator-max-microvolt = <1225000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l4: regulator@4300 {
+ parent-supply = <&pma8084_s3>;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l6: regulator@4500 {
+ parent-supply = <&pma8084_s5>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l8: regulator@4700 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l9: regulator@4800 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l10: regulator@4900 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l12: regulator@4b00 {
+ parent-supply = <&pma8084_s5>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l13: regulator@4c00 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l14: regulator@4d00 {
+ parent-supply = <&pma8084_s5>;
+ regulator-min-microvolt = <1225000>;
+ regulator-max-microvolt = <1225000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l15: regulator@4e00 {
+ parent-supply = <&pma8084_s5>;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l16: regulator@4f00 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l17: regulator@5000 {
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l18: regulator@5100 {
+ regulator-min-microvolt = <2850000>;
+ regulator-max-microvolt = <2850000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l19: regulator@5200 {
+ regulator-min-microvolt = <2950000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l20: regulator@5300 {
+ regulator-min-microvolt = <2950000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l21: regulator@5400 {
+ regulator-min-microvolt = <2950000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l22: regulator@5500 {
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l23: regulator@5600 {
+ regulator-min-microvolt = <2700000>;
+ regulator-max-microvolt = <2700000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l24: regulator@5700 {
+ regulator-min-microvolt = <3075000>;
+ regulator-max-microvolt = <3075000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l25: regulator@5800 {
+ regulator-min-microvolt = <2100000>;
+ regulator-max-microvolt = <2100000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l26: regulator@5900 {
+ parent-supply = <&pma8084_s5>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l27: regulator@5a00 {
+ parent-supply = <&pma8084_s3>;
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1050000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_lvs1: regulator@8000 {
+ parent-supply = <&pma8084_s4>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_lvs2: regulator@8100 {
+ parent-supply = <&pma8084_s4>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_lvs3: regulator@8200 {
+ parent-supply = <&pma8084_s4>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_lvs4: regulator@8300 {
+ parent-supply = <&pma8084_s4>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_mvs1: regulator@8400 {
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+ };
+};
+
+&rpm_bus {
+ rpm-regulator-smpb1 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "smpb";
+ qcom,resource-id = <1>;
+ qcom,regulator-type = <1>;
+ qcom,hpm-min-load = <100000>;
+
+ pma8084_s1_ao: regulator-s1-ao {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s1_ao";
+ qcom,set = <1>;
+ regulator-min-microvolt = <675000>;
+ regulator-max-microvolt = <1050000>;
+ };
+ };
+
+ rpm-regulator-smpb2 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "smpb";
+ qcom,resource-id = <2>;
+ qcom,regulator-type = <1>;
+ qcom,hpm-min-load = <100000>;
+
+ pma8084_s2_corner_ao: regulator-s2-corner-ao {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_s2_corner_ao";
+ qcom,set = <1>;
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ qcom,use-voltage-corner;
+ };
+ };
+
+ rpm-regulator-ldoa12 {
+ compatible = "qcom,rpm-regulator-smd-resource";
+ qcom,resource-name = "ldoa";
+ qcom,resource-id = <12>;
+ qcom,regulator-type = <0>;
+ qcom,hpm-min-load = <10000>;
+
+ pma8084_l12_ao: regulator-l12-ao {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8084_l12_ao";
+ qcom,set = <1>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/apq8084-sim.dts b/arch/arm/boot/dts/apq8084-sim.dts
new file mode 100644
index 0000000..e206d4d
--- /dev/null
+++ b/arch/arm/boot/dts/apq8084-sim.dts
@@ -0,0 +1,173 @@
+/* Copyright (c) 2013, 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.
+ */
+
+/dts-v1/;
+
+/include/ "apq8084.dtsi"
+
+/ {
+ model = "Qualcomm APQ 8084 Simulator";
+ compatible = "qcom,apq8084-sim", "qcom,apq8084", "qcom,sim";
+ qcom,msm-id = <178 0 0>;
+
+ aliases {
+ serial0 = &uart0;
+ };
+};
+
+&soc {
+ uart0: serial@f991f000 {
+ status = "ok";
+ };
+};
+
+&sdcc1 {
+ qcom,vdd-always-on;
+ qcom,vdd-lpm-sup;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <800 500000>;
+
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <250 154000>;
+
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,clk-rates = <400000 20000000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+ qcom,nonremovable;
+ qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+
+ status = "ok";
+};
+
+&sdcc2 {
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <9000 800000>;
+ qcom,vdd-io-voltage-level = <1800000 2950000>;
+ qcom,vdd-io-current-level = <6 22000>;
+ qcom,vdd-io-lpm-sup;
+
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,clk-rates = <400000 20000000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+ qcom,xpc;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+ qcom,current-limit = <800>;
+
+ status = "ok";
+};
+
+&pma8084_gpios {
+ gpio@c000 { /* GPIO 1 */
+ };
+
+ gpio@c100 { /* GPIO 2 */
+ };
+
+ gpio@c200 { /* GPIO 3 */
+ };
+
+ gpio@c300 { /* GPIO 4 */
+ };
+
+ gpio@c400 { /* GPIO 5 */
+ };
+
+ gpio@c500 { /* GPIO 6 */
+ };
+
+ gpio@c600 { /* GPIO 7 */
+ };
+
+ gpio@c700 { /* GPIO 8 */
+ };
+
+ gpio@c800 { /* GPIO 9 */
+ };
+
+ gpio@c900 { /* GPIO 10 */
+ };
+
+ gpio@ca00 { /* GPIO 11 */
+ };
+
+ gpio@cb00 { /* GPIO 12 */
+ };
+
+ gpio@cc00 { /* GPIO 13 */
+ };
+
+ gpio@cd00 { /* GPIO 14 */
+ };
+
+ gpio@ce00 { /* GPIO 15 */
+ };
+
+ gpio@cf00 { /* GPIO 16 */
+ };
+
+ gpio@d000 { /* GPIO 17 */
+ };
+
+ gpio@d100 { /* GPIO 18 */
+ };
+
+ gpio@d200 { /* GPIO 19 */
+ };
+
+ gpio@d300 { /* GPIO 20 */
+ };
+
+ gpio@d400 { /* GPIO 21 */
+ };
+
+ gpio@d500 { /* GPIO 22 */
+ };
+};
+
+&pma8084_mpps {
+ mpp@a000 { /* MPP 1 */
+ };
+
+ mpp@a100 { /* MPP 2 */
+ };
+
+ mpp@a200 { /* MPP 3 */
+ };
+
+ mpp@a300 { /* MPP 4 */
+ };
+
+ mpp@a400 { /* MPP 5 */
+ };
+
+ mpp@a500 { /* MPP 6 */
+ };
+
+ mpp@a600 { /* MPP 7 */
+ };
+
+ mpp@a700 { /* MPP 8 */
+ };
+};
+
+&usb3 {
+ qcom,skip-charger-detection;
+};
diff --git a/arch/arm/boot/dts/apq8084-smp2p.dtsi b/arch/arm/boot/dts/apq8084-smp2p.dtsi
new file mode 100644
index 0000000..b1d21ff
--- /dev/null
+++ b/arch/arm/boot/dts/apq8084-smp2p.dtsi
@@ -0,0 +1,82 @@
+/* Copyright (c) 2013, 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.
+ */
+&soc {
+ qcom,smp2p-adsp {
+ compatible = "qcom,smp2p";
+ reg = <0xf9011008 0x4>;
+ qcom,remote-pid = <2>;
+ qcom,irq-bitmask = <0x400>;
+ interrupts = <0 158 1>;
+ };
+
+ smp2pgpio_smp2p_7_in: qcom,smp2pgpio-smp2p-7-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <7>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_7_in {
+ compatible = "qcom,smp2pgpio_test_smp2p_7_in";
+ gpios = <&smp2pgpio_smp2p_7_in 0 0>;
+ };
+
+ smp2pgpio_smp2p_7_out: qcom,smp2pgpio-smp2p-7-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <7>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_7_out {
+ compatible = "qcom,smp2pgpio_test_smp2p_7_out";
+ gpios = <&smp2pgpio_smp2p_7_out 0 0>;
+ };
+
+ smp2pgpio_smp2p_2_in: qcom,smp2pgpio-smp2p-2-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <2>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_2_in {
+ compatible = "qcom,smp2pgpio_test_smp2p_2_in";
+ gpios = <&smp2pgpio_smp2p_2_in 0 0>;
+ };
+
+ smp2pgpio_smp2p_2_out: qcom,smp2pgpio-smp2p-2-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_2_out {
+ compatible = "qcom,smp2pgpio_test_smp2p_2_out";
+ gpios = <&smp2pgpio_smp2p_2_out 0 0>;
+ };
+};
diff --git a/arch/arm/boot/dts/apq8084.dtsi b/arch/arm/boot/dts/apq8084.dtsi
new file mode 100644
index 0000000..b39f569
--- /dev/null
+++ b/arch/arm/boot/dts/apq8084.dtsi
@@ -0,0 +1,276 @@
+/* Copyright (c) 2013, 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/ "skeleton64.dtsi"
+
+/ {
+ model = "Qualcomm APQ 8084";
+ compatible = "qcom,apq8084";
+ interrupt-parent = <&intc>;
+ soc: soc { };
+};
+
+/include/ "apq8084-ion.dtsi"
+/include/ "apq8084-smp2p.dtsi"
+
+&soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0xffffffff>;
+
+ intc: interrupt-controller@f9000000 {
+ compatible = "qcom,msm-qgic2";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = <0xF9000000 0x1000>,
+ <0xF9002000 0x1000>;
+ };
+
+ msmgpio: gpio@fd510000 {
+ compatible = "qcom,msm-gpio";
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0xfd510000 0x4000>;
+ ngpio = <146>;
+ interrupts = <0 208 0>;
+ qcom,direct-connect-irqs = <8>;
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <1 2 0 1 3 0>;
+ clock-frequency = <19200000>;
+ };
+
+ serial@f991f000 {
+ compatible = "qcom,msm-lsuart-v14";
+ reg = <0xf991f000 0x1000>;
+ interrupts = <0 109 0>;
+ status = "disabled";
+ };
+
+ qcom,cache_erp {
+ compatible = "qcom,cache_erp";
+ interrupts = <1 9 0>, <0 2 0>;
+ interrupt-names = "l1_irq", "l2_irq";
+ };
+
+ qcom,cache_dump {
+ compatible = "qcom,cache_dump";
+ qcom,l1-dump-size = <0x100000>;
+ qcom,l2-dump-size = <0x500000>;
+ qcom,memory-reservation-type = "EBI1";
+ qcom,memory-reservation-size = <0x600000>; /* 6M EBI1 buffer */
+ };
+
+ rpm_bus: qcom,rpm-smd {
+ compatible = "qcom,rpm-smd";
+ rpm-channel-name = "rpm_requests";
+ rpm-channel-type = <15>; /* SMD_APPS_RPM */
+ rpm-standalone;
+ };
+
+ qcom,msm-imem@fe805000 {
+ compatible = "qcom,msm-imem";
+ reg = <0xfe805000 0x1000>; /* Address and size of IMEM */
+ };
+
+ qcom,msm-rtb {
+ compatible = "qcom,msm-rtb";
+ qcom,memory-reservation-type = "EBI1";
+ qcom,memory-reservation-size = <0x100000>; /* 1M EBI1 buffer */
+ };
+
+ sdcc1: qcom,sdcc@f9824000 {
+ cell-index = <1>; /* SDC1 eMMC slot */
+ compatible = "qcom,msm-sdcc";
+ reg = <0xf9824000 0x800>;
+ reg-names = "core_mem";
+ interrupts = <0 123 0>;
+ interrupt-names = "core_irq";
+
+ qcom,bus-width = <8>;
+ status = "disabled";
+ };
+
+ sdcc2: qcom,sdcc@f98a4000 {
+ cell-index = <2>; /* SDC2 SD card slot */
+ compatible = "qcom,msm-sdcc";
+ reg = <0xf98a4000 0x800>;
+ reg-names = "core_mem";
+ interrupts = <0 125 0>;
+ interrupt-names = "core_irq";
+
+
+ qcom,bus-width = <4>;
+ status = "disabled";
+ };
+
+ qcom,sps@f9980000 {
+ compatible = "qcom,msm_sps";
+ reg = <0xf9984000 0x15000>,
+ <0xf9999000 0xb000>;
+ interrupts = <0 94 0>;
+ qcom,pipe-attr-ee;
+ };
+
+ spmi_bus: qcom,spmi@fc4c0000 {
+ cell-index = <0>;
+ compatible = "qcom,spmi-pmic-arb";
+ reg-names = "core", "intr", "cnfg";
+ reg = <0xfc4cf000 0x1000>,
+ <0Xfc4cb000 0x1000>,
+ <0Xfc4ca000 0x1000>;
+ /* 190,ee0_krait_hlos_spmi_periph_irq */
+ /* 187,channel_0_krait_hlos_trans_done_irq */
+ interrupts = <0 190 0>, <0 187 0>;
+ qcom,not-wakeup;
+ qcom,pmic-arb-ee = <0>;
+ qcom,pmic-arb-channel = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ };
+
+ i2c_0: i2c@f9925000 { /* BLSP1 QUP3 */
+ cell-index = <0>;
+ compatible = "qcom,i2c-qup";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ reg = <0xf9925000 0x1000>;
+ interrupt-names = "qup_err_intr";
+ interrupts = <0 97 0>;
+ qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-src-freq = <50000000>;
+ qcom,sda-gpio = <&msmgpio 10 0>;
+ qcom,scl-gpio = <&msmgpio 11 0>;
+ };
+
+ usb3: qcom,ssusb@f9200000 {
+ compatible = "qcom,dwc-usb3-msm";
+ reg = <0xf9200000 0xfc000>,
+ <0xfd4ab000 0x4>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ interrupts = <0 133 0>;
+ interrupt-names = "hs_phy_irq";
+ ssusb_vdd_dig-supply = <&pma8084_s1>;
+ SSUSB_1p8-supply = <&pma8084_l6>;
+ hsusb_vdd_dig-supply = <&pma8084_s1>;
+ HSUSB_1p8-supply = <&pma8084_l6>;
+ HSUSB_3p3-supply = <&pma8084_l24>;
+ qcom,dwc-usb3-msm-dbm-eps = <4>;
+ qcom,vdd-voltage-level = <0 900000 1050000>;
+
+ dwc3@f9200000 {
+ compatible = "synopsys,dwc3";
+ reg = <0xf9200000 0xfc000>;
+ interrupt-parent = <&intc>;
+ interrupts = <0 131 0>, <0 179 0>;
+ interrupt-names = "irq", "otg_irq";
+ tx-fifo-resize;
+ };
+ };
+
+ android_usb {
+ compatible = "qcom,android-usb";
+ };
+
+ qcom,ocmem@fdd00000 {
+ compatible = "qcom,msm-ocmem";
+ reg = <0xfdd00000 0x2000>,
+ <0xfdd02000 0x2000>,
+ <0xfe039000 0x400>,
+ <0xfec00000 0x200000>;
+ reg-names = "ocmem_ctrl_physical", "dm_ctrl_physical", "br_ctrl_physical", "ocmem_physical";
+ interrupts = <0 76 0 0 77 0>;
+ interrupt-names = "ocmem_irq", "dm_irq";
+ qcom,ocmem-num-regions = <0x4>;
+ qcom,ocmem-num-macros = <0x20>;
+ qcom,resource-type = <0x706d636f>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0xfec00000 0x200000>;
+
+ partition@0 {
+ reg = <0x0 0x180000>;
+ qcom,ocmem-part-name = "graphics";
+ qcom,ocmem-part-min = <0x80000>;
+ };
+
+ partition@80000 {
+ reg = <0x180000 0x80000>;
+ qcom,ocmem-part-name = "lp_audio";
+ qcom,ocmem-part-min = <0x80000>;
+ };
+
+ partition@100000 {
+ reg = <0x180000 0x80000>;
+ qcom,ocmem-part-name = "video";
+ qcom,ocmem-part-min = <0x55000>;
+ };
+
+ };
+
+ memory_hole: qcom,msm-mem-hole {
+ compatible = "qcom,msm-mem-hole";
+ qcom,memblock-remove = <0x0dc00000 0x2000000>; /* Address and Size of Hole */
+ };
+
+ qcom,ipc-spinlock@fd484000 {
+ compatible = "qcom,ipc-spinlock-sfpb";
+ reg = <0xfd484000 0x400>;
+ qcom,num-locks = <8>;
+ };
+
+ qcom,smem@fa00000 {
+ compatible = "qcom,smem";
+ reg = <0xfa00000 0x200000>,
+ <0xf9011000 0x1000>,
+ <0xfc428000 0x4000>;
+ reg-names = "smem", "irq-reg-base", "aux-mem1";
+
+ qcom,smd-adsp {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <1>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x100>;
+ qcom,pil-string = "adsp";
+ interrupts = <0 156 1>;
+ };
+
+ qcom,smsm-adsp {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <1>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x200>;
+ interrupts = <0 157 1>;
+ };
+
+ qcom,smd-rpm {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <15>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x1>;
+ interrupts = <0 168 1>;
+ qcom,irq-no-suspend;
+ };
+ };
+};
+
+/include/ "msm-pma8084.dtsi"
+/include/ "apq8084-regulator.dtsi"
diff --git a/arch/arm/boot/dts/dsi-panel-nt35590-720p-cmd.dtsi b/arch/arm/boot/dts/dsi-panel-nt35590-720p-cmd.dtsi
new file mode 100644
index 0000000..7942567
--- /dev/null
+++ b/arch/arm/boot/dts/dsi-panel-nt35590-720p-cmd.dtsi
@@ -0,0 +1,530 @@
+/* Copyright (c) 2013, 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.
+ */
+
+/ {
+ qcom,mdss_dsi_nt35590_720p_cmd {
+ compatible = "qcom,mdss-dsi-panel";
+ label = "nt35590 720p command mode dsi panel";
+ status = "disable";
+ qcom,dsi-ctrl-phandle = <&mdss_dsi0>;
+ qcom,rst-gpio = <&msmgpio 25 0>;
+ qcom,te-gpio = <&msmgpio 24 0>;
+ qcom,mdss-pan-res = <720 1280>;
+ qcom,mdss-pan-bpp = <24>;
+ qcom,mdss-pan-dest = "display_1";
+ qcom,mdss-pan-porch-values = <164 8 140 1 1 6>;
+ qcom,mdss-pan-underflow-clr = <0xff>;
+ qcom,mdss-pan-bl-ctrl = "bl_ctrl_wled";
+ qcom,mdss-pan-bl-levels = <1 4095>;
+ qcom,mdss-pan-dsi-mode = <1>;
+ qcom,mdss-vsync-enable = <1>;
+ qcom,mdss-hw-vsync-mode = <1>;
+ qcom,mdss-pan-dsi-h-pulse-mode = <1>;
+ qcom,mdss-pan-dsi-h-power-stop = <0 0 0>;
+ qcom,mdss-pan-dsi-bllp-power-stop = <1 1>;
+ qcom,mdss-pan-dsi-traffic-mode = <2>;
+ qcom,mdss-pan-dsi-dst-format = <8>;
+ qcom,mdss-pan-insert-dcs-cmd = <1>;
+ qcom,mdss-pan-wr-mem-continue = <0x3c>;
+ qcom,mdss-pan-wr-mem-start = <0x2c>;
+ qcom,mdss-pan-te-sel = <1>;
+ qcom,mdss-pan-dsi-vc = <0>;
+ qcom,mdss-pan-dsi-rgb-swap = <0>;
+ qcom,mdss-pan-dsi-data-lanes = <1 1 1 1>; /* 4 lanes */
+ qcom,mdss-pan-dsi-dlane-swap = <0>;
+ qcom,mdss-pan-dsi-t-clk = <0x2c 0x20>;
+ qcom,mdss-pan-dsi-stream = <0>;
+ qcom,mdss-pan-dsi-mdp-tr = <0x0>;
+ qcom,mdss-pan-dsi-dma-tr = <0x04>;
+ qcom,mdss-pan-dsi-frame-rate = <60>;
+ qcom,panel-phy-regulatorSettings = [07 09 03 00 /* Regualotor settings */
+ 20 00 01];
+ qcom,panel-phy-timingSettings = [7d 25 1d 00 37 33
+ 22 27 1e 03 04 00];
+ qcom,panel-phy-strengthCtrl = [ff 06];
+ qcom,panel-phy-bistCtrl = [00 00 b1 ff /* BIST Ctrl settings */
+ 00 00];
+ qcom,panel-phy-laneConfig = [00 00 00 00 00 00 00 01 97 /* lane0 config */
+ 00 00 00 00 05 00 00 01 97 /* lane1 config */
+ 00 00 00 00 0a 00 00 01 97 /* lane2 config */
+ 00 00 00 00 0f 00 00 01 97 /* lane3 config */
+ 00 c0 00 00 00 00 00 01 bb]; /* Clk ln config */
+ qcom,panel-on-cmds = [29 01 00 00 00 02 FF EE
+ 29 01 00 00 00 02 26 08
+ 29 01 00 00 00 02 26 00
+ 29 01 00 00 10 02 FF 00
+ 29 01 00 00 00 02 BA 03
+ 29 01 00 00 00 02 C2 08
+ 29 01 00 00 00 02 FF 01
+ 29 01 00 00 00 02 FB 01
+ 29 01 00 00 00 02 00 4A
+ 29 01 00 00 00 02 01 33
+ 29 01 00 00 00 02 02 53
+ 29 01 00 00 00 02 03 55
+ 29 01 00 00 00 02 04 55
+ 29 01 00 00 00 02 05 33
+ 29 01 00 00 00 02 06 22
+ 29 01 00 00 00 02 08 56
+ 29 01 00 00 00 02 09 8F
+ 29 01 00 00 00 02 36 73
+ 29 01 00 00 00 02 0B 9F
+ 29 01 00 00 00 02 0C 9F
+ 29 01 00 00 00 02 0D 2F
+ 29 01 00 00 00 02 0E 24
+ 29 01 00 00 00 02 11 83
+ 29 01 00 00 00 02 12 03
+ 29 01 00 00 00 02 71 2C
+ 29 01 00 00 00 02 6F 03
+ 29 01 00 00 00 02 0F 0A
+ 29 01 00 00 00 02 FF 05
+ 29 01 00 00 00 02 FB 01
+ 29 01 00 00 00 02 01 00
+ 29 01 00 00 00 02 02 8B
+ 29 01 00 00 00 02 03 82
+ 29 01 00 00 00 02 04 82
+ 29 01 00 00 00 02 05 30
+ 29 01 00 00 00 02 06 33
+ 29 01 00 00 00 02 07 01
+ 29 01 00 00 00 02 08 00
+ 29 01 00 00 00 02 09 46
+ 29 01 00 00 00 02 0A 46
+ 29 01 00 00 00 02 0D 0B
+ 29 01 00 00 00 02 0E 1D
+ 29 01 00 00 00 02 0F 08
+ 29 01 00 00 00 02 10 53
+ 29 01 00 00 00 02 11 00
+ 29 01 00 00 00 02 12 00
+ 29 01 00 00 00 02 14 01
+ 29 01 00 00 00 02 15 00
+ 29 01 00 00 00 02 16 05
+ 29 01 00 00 00 02 17 00
+ 29 01 00 00 00 02 19 7F
+ 29 01 00 00 00 02 1A FF
+ 29 01 00 00 00 02 1B 0F
+ 29 01 00 00 00 02 1C 00
+ 29 01 00 00 00 02 1D 00
+ 29 01 00 00 00 02 1E 00
+ 29 01 00 00 00 02 1F 07
+ 29 01 00 00 00 02 20 00
+ 29 01 00 00 00 02 21 06
+ 29 01 00 00 00 02 22 55
+ 29 01 00 00 00 02 23 4D
+ 29 01 00 00 00 02 2D 02
+ 29 01 00 00 00 02 28 01
+ 29 01 00 00 00 02 2F 02
+ 29 01 00 00 00 02 83 01
+ 29 01 00 00 00 02 9E 58
+ 29 01 00 00 00 02 9F 6A
+ 29 01 00 00 00 02 A0 01
+ 29 01 00 00 00 02 A2 10
+ 29 01 00 00 00 02 BB 0A
+ 29 01 00 00 00 02 BC 0A
+ 29 01 00 00 00 02 32 08
+ 29 01 00 00 00 02 33 B8
+ 29 01 00 00 00 02 36 01
+ 29 01 00 00 00 02 37 00
+ 29 01 00 00 00 02 43 00
+ 29 01 00 00 00 02 4B 21
+ 29 01 00 00 00 02 4C 03
+ 29 01 00 00 00 02 50 21
+ 29 01 00 00 00 02 51 03
+ 29 01 00 00 00 02 58 21
+ 29 01 00 00 00 02 59 03
+ 29 01 00 00 00 02 5D 21
+ 29 01 00 00 00 02 5E 03
+ 29 01 00 00 00 02 6C 00
+ 29 01 00 00 00 02 6D 00
+ 29 01 00 00 00 02 FB 01
+ 29 01 00 00 00 02 FF 01
+ 29 01 00 00 00 02 FB 01
+ 29 01 00 00 00 02 75 00
+ 29 01 00 00 00 02 76 7D
+ 29 01 00 00 00 02 77 00
+ 29 01 00 00 00 02 78 8A
+ 29 01 00 00 00 02 79 00
+ 29 01 00 00 00 02 7A 9C
+ 29 01 00 00 00 02 7B 00
+ 29 01 00 00 00 02 7C B1
+ 29 01 00 00 00 02 7D 00
+ 29 01 00 00 00 02 7E BF
+ 29 01 00 00 00 02 7F 00
+ 29 01 00 00 00 02 80 CF
+ 29 01 00 00 00 02 81 00
+ 29 01 00 00 00 02 82 DD
+ 29 01 00 00 00 02 83 00
+ 29 01 00 00 00 02 84 E8
+ 29 01 00 00 00 02 85 00
+ 29 01 00 00 00 02 86 F2
+ 29 01 00 00 00 02 87 01
+ 29 01 00 00 00 02 88 1F
+ 29 01 00 00 00 02 89 01
+ 29 01 00 00 00 02 8A 41
+ 29 01 00 00 00 02 8B 01
+ 29 01 00 00 00 02 8C 78
+ 29 01 00 00 00 02 8D 01
+ 29 01 00 00 00 02 8E A5
+ 29 01 00 00 00 02 8F 01
+ 29 01 00 00 00 02 90 EE
+ 29 01 00 00 00 02 91 02
+ 29 01 00 00 00 02 92 29
+ 29 01 00 00 00 02 93 02
+ 29 01 00 00 00 02 94 2A
+ 29 01 00 00 00 02 95 02
+ 29 01 00 00 00 02 96 5D
+ 29 01 00 00 00 02 97 02
+ 29 01 00 00 00 02 98 93
+ 29 01 00 00 00 02 99 02
+ 29 01 00 00 00 02 9A B8
+ 29 01 00 00 00 02 9B 02
+ 29 01 00 00 00 02 9C E7
+ 29 01 00 00 00 02 9D 03
+ 29 01 00 00 00 02 9E 07
+ 29 01 00 00 00 02 9F 03
+ 29 01 00 00 00 02 A0 37
+ 29 01 00 00 00 02 A2 03
+ 29 01 00 00 00 02 A3 46
+ 29 01 00 00 00 02 A4 03
+ 29 01 00 00 00 02 A5 56
+ 29 01 00 00 00 02 A6 03
+ 29 01 00 00 00 02 A7 66
+ 29 01 00 00 00 02 A9 03
+ 29 01 00 00 00 02 AA 7A
+ 29 01 00 00 00 02 AB 03
+ 29 01 00 00 00 02 AC 93
+ 29 01 00 00 00 02 AD 03
+ 29 01 00 00 00 02 AE A3
+ 29 01 00 00 00 02 AF 03
+ 29 01 00 00 00 02 B0 B4
+ 29 01 00 00 00 02 B1 03
+ 29 01 00 00 00 02 B2 CB
+ 29 01 00 00 00 02 B3 00
+ 29 01 00 00 00 02 B4 7D
+ 29 01 00 00 00 02 B5 00
+ 29 01 00 00 00 02 B6 8A
+ 29 01 00 00 00 02 B7 00
+ 29 01 00 00 00 02 B8 9C
+ 29 01 00 00 00 02 B9 00
+ 29 01 00 00 00 02 BA B1
+ 29 01 00 00 00 02 BB 00
+ 29 01 00 00 00 02 BC BF
+ 29 01 00 00 00 02 BD 00
+ 29 01 00 00 00 02 BE CF
+ 29 01 00 00 00 02 BF 00
+ 29 01 00 00 00 02 C0 DD
+ 29 01 00 00 00 02 C1 00
+ 29 01 00 00 00 02 C2 E8
+ 29 01 00 00 00 02 C3 00
+ 29 01 00 00 00 02 C4 F2
+ 29 01 00 00 00 02 C5 01
+ 29 01 00 00 00 02 C6 1F
+ 29 01 00 00 00 02 C7 01
+ 29 01 00 00 00 02 C8 41
+ 29 01 00 00 00 02 C9 01
+ 29 01 00 00 00 02 CA 78
+ 29 01 00 00 00 02 CB 01
+ 29 01 00 00 00 02 CC A5
+ 29 01 00 00 00 02 CD 01
+ 29 01 00 00 00 02 CE EE
+ 29 01 00 00 00 02 CF 02
+ 29 01 00 00 00 02 D0 29
+ 29 01 00 00 00 02 D1 02
+ 29 01 00 00 00 02 D2 2A
+ 29 01 00 00 00 02 D3 02
+ 29 01 00 00 00 02 D4 5D
+ 29 01 00 00 00 02 D5 02
+ 29 01 00 00 00 02 D6 93
+ 29 01 00 00 00 02 D7 02
+ 29 01 00 00 00 02 D8 B8
+ 29 01 00 00 00 02 D9 02
+ 29 01 00 00 00 02 DA E7
+ 29 01 00 00 00 02 DB 03
+ 29 01 00 00 00 02 DC 07
+ 29 01 00 00 00 02 DD 03
+ 29 01 00 00 00 02 DE 37
+ 29 01 00 00 00 02 DF 03
+ 29 01 00 00 00 02 E0 46
+ 29 01 00 00 00 02 E1 03
+ 29 01 00 00 00 02 E2 56
+ 29 01 00 00 00 02 E3 03
+ 29 01 00 00 00 02 E4 66
+ 29 01 00 00 00 02 E5 03
+ 29 01 00 00 00 02 E6 7A
+ 29 01 00 00 00 02 E7 03
+ 29 01 00 00 00 02 E8 93
+ 29 01 00 00 00 02 E9 03
+ 29 01 00 00 00 02 EA A3
+ 29 01 00 00 00 02 EB 03
+ 29 01 00 00 00 02 EC B4
+ 29 01 00 00 00 02 ED 03
+ 29 01 00 00 00 02 EE CB
+ 29 01 00 00 00 02 EF 00
+ 29 01 00 00 00 02 F0 ED
+ 29 01 00 00 00 02 F1 00
+ 29 01 00 00 00 02 F2 F3
+ 29 01 00 00 00 02 F3 00
+ 29 01 00 00 00 02 F4 FE
+ 29 01 00 00 00 02 F5 01
+ 29 01 00 00 00 02 F6 09
+ 29 01 00 00 00 02 F7 01
+ 29 01 00 00 00 02 F8 13
+ 29 01 00 00 00 02 F9 01
+ 29 01 00 00 00 02 FA 1D
+ 29 01 00 00 00 02 FF 02
+ 29 01 00 00 00 02 FB 01
+ 29 01 00 00 00 02 00 01
+ 29 01 00 00 00 02 01 26
+ 29 01 00 00 00 02 02 01
+ 29 01 00 00 00 02 03 2F
+ 29 01 00 00 00 02 04 01
+ 29 01 00 00 00 02 05 37
+ 29 01 00 00 00 02 06 01
+ 29 01 00 00 00 02 07 56
+ 29 01 00 00 00 02 08 01
+ 29 01 00 00 00 02 09 70
+ 29 01 00 00 00 02 0A 01
+ 29 01 00 00 00 02 0B 9D
+ 29 01 00 00 00 02 0C 01
+ 29 01 00 00 00 02 0D C2
+ 29 01 00 00 00 02 0E 01
+ 29 01 00 00 00 02 0F FF
+ 29 01 00 00 00 02 10 02
+ 29 01 00 00 00 02 11 31
+ 29 01 00 00 00 02 12 02
+ 29 01 00 00 00 02 13 32
+ 29 01 00 00 00 02 14 02
+ 29 01 00 00 00 02 15 60
+ 29 01 00 00 00 02 16 02
+ 29 01 00 00 00 02 17 94
+ 29 01 00 00 00 02 18 02
+ 29 01 00 00 00 02 19 B5
+ 29 01 00 00 00 02 1A 02
+ 29 01 00 00 00 02 1B E3
+ 29 01 00 00 00 02 1C 03
+ 29 01 00 00 00 02 1D 03
+ 29 01 00 00 00 02 1E 03
+ 29 01 00 00 00 02 1F 2D
+ 29 01 00 00 00 02 20 03
+ 29 01 00 00 00 02 21 3A
+ 29 01 00 00 00 02 22 03
+ 29 01 00 00 00 02 23 48
+ 29 01 00 00 00 02 24 03
+ 29 01 00 00 00 02 25 57
+ 29 01 00 00 00 02 26 03
+ 29 01 00 00 00 02 27 68
+ 29 01 00 00 00 02 28 03
+ 29 01 00 00 00 02 29 7B
+ 29 01 00 00 00 02 2A 03
+ 29 01 00 00 00 02 2B 90
+ 29 01 00 00 00 02 2D 03
+ 29 01 00 00 00 02 2F A0
+ 29 01 00 00 00 02 30 03
+ 29 01 00 00 00 02 31 CB
+ 29 01 00 00 00 02 32 00
+ 29 01 00 00 00 02 33 ED
+ 29 01 00 00 00 02 34 00
+ 29 01 00 00 00 02 35 F3
+ 29 01 00 00 00 02 36 00
+ 29 01 00 00 00 02 37 FE
+ 29 01 00 00 00 02 38 01
+ 29 01 00 00 00 02 39 09
+ 29 01 00 00 00 02 3A 01
+ 29 01 00 00 00 02 3B 13
+ 29 01 00 00 00 02 3D 01
+ 29 01 00 00 00 02 3F 1D
+ 29 01 00 00 00 02 40 01
+ 29 01 00 00 00 02 41 26
+ 29 01 00 00 00 02 42 01
+ 29 01 00 00 00 02 43 2F
+ 29 01 00 00 00 02 44 01
+ 29 01 00 00 00 02 45 37
+ 29 01 00 00 00 02 46 01
+ 29 01 00 00 00 02 47 56
+ 29 01 00 00 00 02 48 01
+ 29 01 00 00 00 02 49 70
+ 29 01 00 00 00 02 4A 01
+ 29 01 00 00 00 02 4B 9D
+ 29 01 00 00 00 02 4C 01
+ 29 01 00 00 00 02 4D C2
+ 29 01 00 00 00 02 4E 01
+ 29 01 00 00 00 02 4F FF
+ 29 01 00 00 00 02 50 02
+ 29 01 00 00 00 02 51 31
+ 29 01 00 00 00 02 52 02
+ 29 01 00 00 00 02 53 32
+ 29 01 00 00 00 02 54 02
+ 29 01 00 00 00 02 55 60
+ 29 01 00 00 00 02 56 02
+ 29 01 00 00 00 02 58 94
+ 29 01 00 00 00 02 59 02
+ 29 01 00 00 00 02 5A B5
+ 29 01 00 00 00 02 5B 02
+ 29 01 00 00 00 02 5C E3
+ 29 01 00 00 00 02 5D 03
+ 29 01 00 00 00 02 5E 03
+ 29 01 00 00 00 02 5F 03
+ 29 01 00 00 00 02 60 2D
+ 29 01 00 00 00 02 61 03
+ 29 01 00 00 00 02 62 3A
+ 29 01 00 00 00 02 63 03
+ 29 01 00 00 00 02 64 48
+ 29 01 00 00 00 02 65 03
+ 29 01 00 00 00 02 66 57
+ 29 01 00 00 00 02 67 03
+ 29 01 00 00 00 02 68 68
+ 29 01 00 00 00 02 69 03
+ 29 01 00 00 00 02 6A 7B
+ 29 01 00 00 00 02 6B 03
+ 29 01 00 00 00 02 6C 90
+ 29 01 00 00 00 02 6D 03
+ 29 01 00 00 00 02 6E A0
+ 29 01 00 00 00 02 6F 03
+ 29 01 00 00 00 02 70 CB
+ 29 01 00 00 00 02 71 00
+ 29 01 00 00 00 02 72 19
+ 29 01 00 00 00 02 73 00
+ 29 01 00 00 00 02 74 36
+ 29 01 00 00 00 02 75 00
+ 29 01 00 00 00 02 76 55
+ 29 01 00 00 00 02 77 00
+ 29 01 00 00 00 02 78 70
+ 29 01 00 00 00 02 79 00
+ 29 01 00 00 00 02 7A 83
+ 29 01 00 00 00 02 7B 00
+ 29 01 00 00 00 02 7C 99
+ 29 01 00 00 00 02 7D 00
+ 29 01 00 00 00 02 7E A8
+ 29 01 00 00 00 02 7F 00
+ 29 01 00 00 00 02 80 B7
+ 29 01 00 00 00 02 81 00
+ 29 01 00 00 00 02 82 C5
+ 29 01 00 00 00 02 83 00
+ 29 01 00 00 00 02 84 F7
+ 29 01 00 00 00 02 85 01
+ 29 01 00 00 00 02 86 1E
+ 29 01 00 00 00 02 87 01
+ 29 01 00 00 00 02 88 60
+ 29 01 00 00 00 02 89 01
+ 29 01 00 00 00 02 8A 95
+ 29 01 00 00 00 02 8B 01
+ 29 01 00 00 00 02 8C E1
+ 29 01 00 00 00 02 8D 02
+ 29 01 00 00 00 02 8E 20
+ 29 01 00 00 00 02 8F 02
+ 29 01 00 00 00 02 90 23
+ 29 01 00 00 00 02 91 02
+ 29 01 00 00 00 02 92 59
+ 29 01 00 00 00 02 93 02
+ 29 01 00 00 00 02 94 94
+ 29 01 00 00 00 02 95 02
+ 29 01 00 00 00 02 96 B4
+ 29 01 00 00 00 02 97 02
+ 29 01 00 00 00 02 98 E1
+ 29 01 00 00 00 02 99 03
+ 29 01 00 00 00 02 9A 01
+ 29 01 00 00 00 02 9B 03
+ 29 01 00 00 00 02 9C 28
+ 29 01 00 00 00 02 9D 03
+ 29 01 00 00 00 02 9E 30
+ 29 01 00 00 00 02 9F 03
+ 29 01 00 00 00 02 A0 37
+ 29 01 00 00 00 02 A2 03
+ 29 01 00 00 00 02 A3 3B
+ 29 01 00 00 00 02 A4 03
+ 29 01 00 00 00 02 A5 40
+ 29 01 00 00 00 02 A6 03
+ 29 01 00 00 00 02 A7 50
+ 29 01 00 00 00 02 A9 03
+ 29 01 00 00 00 02 AA 6D
+ 29 01 00 00 00 02 AB 03
+ 29 01 00 00 00 02 AC 80
+ 29 01 00 00 00 02 AD 03
+ 29 01 00 00 00 02 AE CB
+ 29 01 00 00 00 02 AF 00
+ 29 01 00 00 00 02 B0 19
+ 29 01 00 00 00 02 B1 00
+ 29 01 00 00 00 02 B2 36
+ 29 01 00 00 00 02 B3 00
+ 29 01 00 00 00 02 B4 55
+ 29 01 00 00 00 02 B5 00
+ 29 01 00 00 00 02 B6 70
+ 29 01 00 00 00 02 B7 00
+ 29 01 00 00 00 02 B8 83
+ 29 01 00 00 00 02 B9 00
+ 29 01 00 00 00 02 BA 99
+ 29 01 00 00 00 02 BB 00
+ 29 01 00 00 00 02 BC A8
+ 29 01 00 00 00 02 BD 00
+ 29 01 00 00 00 02 BE B7
+ 29 01 00 00 00 02 BF 00
+ 29 01 00 00 00 02 C0 C5
+ 29 01 00 00 00 02 C1 00
+ 29 01 00 00 00 02 C2 F7
+ 29 01 00 00 00 02 C3 01
+ 29 01 00 00 00 02 C4 1E
+ 29 01 00 00 00 02 C5 01
+ 29 01 00 00 00 02 C6 60
+ 29 01 00 00 00 02 C7 01
+ 29 01 00 00 00 02 C8 95
+ 29 01 00 00 00 02 C9 01
+ 29 01 00 00 00 02 CA E1
+ 29 01 00 00 00 02 CB 02
+ 29 01 00 00 00 02 CC 20
+ 29 01 00 00 00 02 CD 02
+ 29 01 00 00 00 02 CE 23
+ 29 01 00 00 00 02 CF 02
+ 29 01 00 00 00 02 D0 59
+ 29 01 00 00 00 02 D1 02
+ 29 01 00 00 00 02 D2 94
+ 29 01 00 00 00 02 D3 02
+ 29 01 00 00 00 02 D4 B4
+ 29 01 00 00 00 02 D5 02
+ 29 01 00 00 00 02 D6 E1
+ 29 01 00 00 00 02 D7 03
+ 29 01 00 00 00 02 D8 01
+ 29 01 00 00 00 02 D9 03
+ 29 01 00 00 00 02 DA 28
+ 29 01 00 00 00 02 DB 03
+ 29 01 00 00 00 02 DC 30
+ 29 01 00 00 00 02 DD 03
+ 29 01 00 00 00 02 DE 37
+ 29 01 00 00 00 02 DF 03
+ 29 01 00 00 00 02 E0 3B
+ 29 01 00 00 00 02 E1 03
+ 29 01 00 00 00 02 E2 40
+ 29 01 00 00 00 02 E3 03
+ 29 01 00 00 00 02 E4 50
+ 29 01 00 00 00 02 E5 03
+ 29 01 00 00 00 02 E6 6D
+ 29 01 00 00 00 02 E7 03
+ 29 01 00 00 00 02 E8 80
+ 29 01 00 00 00 02 E9 03
+ 29 01 00 00 00 02 EA CB
+ 29 01 00 00 00 02 FF 01
+ 29 01 00 00 00 02 FB 01
+ 29 01 00 00 00 02 FF 02
+ 29 01 00 00 00 02 FB 01
+ 29 01 00 00 00 02 FF 04
+ 29 01 00 00 00 02 FB 01
+ 29 01 00 00 00 02 FF 00
+ 29 01 00 00 64 02 11 00
+ 29 01 00 00 00 02 FF EE
+ 29 01 00 00 00 02 12 50
+ 29 01 00 00 00 02 13 02
+ 29 01 00 00 00 02 6A 60
+ 29 01 00 00 00 02 FF 00
+ 29 01 00 00 78 02 29 00];
+ qcom,on-cmds-dsi-state = "DSI_LP_MODE";
+ qcom,panel-off-cmds = [05 01 00 00 32 02 28 00
+ 05 01 00 00 78 02 10 00];
+ qcom,off-cmds-dsi-state = "DSI_HS_MODE";
+ };
+};
diff --git a/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi b/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi
index c0c9107..7bc748d 100644
--- a/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,mdss_dsi_nt35590_720p_video {
compatible = "qcom,mdss-dsi-panel";
label = "nt35590 720p video mode dsi panel";
@@ -51,474 +51,474 @@
00 00 00 00 0a 00 00 01 97 /* lane2 config */
00 00 00 00 0f 00 00 01 97 /* lane3 config */
00 c0 00 00 00 00 00 01 bb]; /* Clk ln config */
- qcom,panel-on-cmds = [29 01 00 00 00 02 FF EE
- 29 01 00 00 00 02 26 08
- 29 01 00 00 00 02 26 00
- 29 01 00 00 10 02 FF 00
- 29 01 00 00 00 02 BA 03
- 29 01 00 00 00 02 C2 03
- 29 01 00 00 00 02 FF 01
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 00 4A
- 29 01 00 00 00 02 01 33
- 29 01 00 00 00 02 02 53
- 29 01 00 00 00 02 03 55
- 29 01 00 00 00 02 04 55
- 29 01 00 00 00 02 05 33
- 29 01 00 00 00 02 06 22
- 29 01 00 00 00 02 08 56
- 29 01 00 00 00 02 09 8F
- 29 01 00 00 00 02 36 73
- 29 01 00 00 00 02 0B 9F
- 29 01 00 00 00 02 0C 9F
- 29 01 00 00 00 02 0D 2F
- 29 01 00 00 00 02 0E 24
- 29 01 00 00 00 02 11 83
- 29 01 00 00 00 02 12 03
- 29 01 00 00 00 02 71 2C
- 29 01 00 00 00 02 6F 03
- 29 01 00 00 00 02 0F 0A
- 29 01 00 00 00 02 FF 05
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 01 00
- 29 01 00 00 00 02 02 8B
- 29 01 00 00 00 02 03 82
- 29 01 00 00 00 02 04 82
- 29 01 00 00 00 02 05 30
- 29 01 00 00 00 02 06 33
- 29 01 00 00 00 02 07 01
- 29 01 00 00 00 02 08 00
- 29 01 00 00 00 02 09 46
- 29 01 00 00 00 02 0A 46
- 29 01 00 00 00 02 0D 0B
- 29 01 00 00 00 02 0E 1D
- 29 01 00 00 00 02 0F 08
- 29 01 00 00 00 02 10 53
- 29 01 00 00 00 02 11 00
- 29 01 00 00 00 02 12 00
- 29 01 00 00 00 02 14 01
- 29 01 00 00 00 02 15 00
- 29 01 00 00 00 02 16 05
- 29 01 00 00 00 02 17 00
- 29 01 00 00 00 02 19 7F
- 29 01 00 00 00 02 1A FF
- 29 01 00 00 00 02 1B 0F
- 29 01 00 00 00 02 1C 00
- 29 01 00 00 00 02 1D 00
- 29 01 00 00 00 02 1E 00
- 29 01 00 00 00 02 1F 07
- 29 01 00 00 00 02 20 00
- 29 01 00 00 00 02 21 06
- 29 01 00 00 00 02 22 55
- 29 01 00 00 00 02 23 4D
- 29 01 00 00 00 02 2D 02
- 29 01 00 00 00 02 28 01
- 29 01 00 00 00 02 2F 02
- 29 01 00 00 00 02 83 01
- 29 01 00 00 00 02 9E 58
- 29 01 00 00 00 02 9F 6A
- 29 01 00 00 00 02 A0 01
- 29 01 00 00 00 02 A2 10
- 29 01 00 00 00 02 BB 0A
- 29 01 00 00 00 02 BC 0A
- 29 01 00 00 00 02 32 08
- 29 01 00 00 00 02 33 B8
- 29 01 00 00 00 02 36 01
- 29 01 00 00 00 02 37 00
- 29 01 00 00 00 02 43 00
- 29 01 00 00 00 02 4B 21
- 29 01 00 00 00 02 4C 03
- 29 01 00 00 00 02 50 21
- 29 01 00 00 00 02 51 03
- 29 01 00 00 00 02 58 21
- 29 01 00 00 00 02 59 03
- 29 01 00 00 00 02 5D 21
- 29 01 00 00 00 02 5E 03
- 29 01 00 00 00 02 6C 00
- 29 01 00 00 00 02 6D 00
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 FF 01
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 75 00
- 29 01 00 00 00 02 76 7D
- 29 01 00 00 00 02 77 00
- 29 01 00 00 00 02 78 8A
- 29 01 00 00 00 02 79 00
- 29 01 00 00 00 02 7A 9C
- 29 01 00 00 00 02 7B 00
- 29 01 00 00 00 02 7C B1
- 29 01 00 00 00 02 7D 00
- 29 01 00 00 00 02 7E BF
- 29 01 00 00 00 02 7F 00
- 29 01 00 00 00 02 80 CF
- 29 01 00 00 00 02 81 00
- 29 01 00 00 00 02 82 DD
- 29 01 00 00 00 02 83 00
- 29 01 00 00 00 02 84 E8
- 29 01 00 00 00 02 85 00
- 29 01 00 00 00 02 86 F2
- 29 01 00 00 00 02 87 01
- 29 01 00 00 00 02 88 1F
- 29 01 00 00 00 02 89 01
- 29 01 00 00 00 02 8A 41
- 29 01 00 00 00 02 8B 01
- 29 01 00 00 00 02 8C 78
- 29 01 00 00 00 02 8D 01
- 29 01 00 00 00 02 8E A5
- 29 01 00 00 00 02 8F 01
- 29 01 00 00 00 02 90 EE
- 29 01 00 00 00 02 91 02
- 29 01 00 00 00 02 92 29
- 29 01 00 00 00 02 93 02
- 29 01 00 00 00 02 94 2A
- 29 01 00 00 00 02 95 02
- 29 01 00 00 00 02 96 5D
- 29 01 00 00 00 02 97 02
- 29 01 00 00 00 02 98 93
- 29 01 00 00 00 02 99 02
- 29 01 00 00 00 02 9A B8
- 29 01 00 00 00 02 9B 02
- 29 01 00 00 00 02 9C E7
- 29 01 00 00 00 02 9D 03
- 29 01 00 00 00 02 9E 07
- 29 01 00 00 00 02 9F 03
- 29 01 00 00 00 02 A0 37
- 29 01 00 00 00 02 A2 03
- 29 01 00 00 00 02 A3 46
- 29 01 00 00 00 02 A4 03
- 29 01 00 00 00 02 A5 56
- 29 01 00 00 00 02 A6 03
- 29 01 00 00 00 02 A7 66
- 29 01 00 00 00 02 A9 03
- 29 01 00 00 00 02 AA 7A
- 29 01 00 00 00 02 AB 03
- 29 01 00 00 00 02 AC 93
- 29 01 00 00 00 02 AD 03
- 29 01 00 00 00 02 AE A3
- 29 01 00 00 00 02 AF 03
- 29 01 00 00 00 02 B0 B4
- 29 01 00 00 00 02 B1 03
- 29 01 00 00 00 02 B2 CB
- 29 01 00 00 00 02 B3 00
- 29 01 00 00 00 02 B4 7D
- 29 01 00 00 00 02 B5 00
- 29 01 00 00 00 02 B6 8A
- 29 01 00 00 00 02 B7 00
- 29 01 00 00 00 02 B8 9C
- 29 01 00 00 00 02 B9 00
- 29 01 00 00 00 02 BA B1
- 29 01 00 00 00 02 BB 00
- 29 01 00 00 00 02 BC BF
- 29 01 00 00 00 02 BD 00
- 29 01 00 00 00 02 BE CF
- 29 01 00 00 00 02 BF 00
- 29 01 00 00 00 02 C0 DD
- 29 01 00 00 00 02 C1 00
- 29 01 00 00 00 02 C2 E8
- 29 01 00 00 00 02 C3 00
- 29 01 00 00 00 02 C4 F2
- 29 01 00 00 00 02 C5 01
- 29 01 00 00 00 02 C6 1F
- 29 01 00 00 00 02 C7 01
- 29 01 00 00 00 02 C8 41
- 29 01 00 00 00 02 C9 01
- 29 01 00 00 00 02 CA 78
- 29 01 00 00 00 02 CB 01
- 29 01 00 00 00 02 CC A5
- 29 01 00 00 00 02 CD 01
- 29 01 00 00 00 02 CE EE
- 29 01 00 00 00 02 CF 02
- 29 01 00 00 00 02 D0 29
- 29 01 00 00 00 02 D1 02
- 29 01 00 00 00 02 D2 2A
- 29 01 00 00 00 02 D3 02
- 29 01 00 00 00 02 D4 5D
- 29 01 00 00 00 02 D5 02
- 29 01 00 00 00 02 D6 93
- 29 01 00 00 00 02 D7 02
- 29 01 00 00 00 02 D8 B8
- 29 01 00 00 00 02 D9 02
- 29 01 00 00 00 02 DA E7
- 29 01 00 00 00 02 DB 03
- 29 01 00 00 00 02 DC 07
- 29 01 00 00 00 02 DD 03
- 29 01 00 00 00 02 DE 37
- 29 01 00 00 00 02 DF 03
- 29 01 00 00 00 02 E0 46
- 29 01 00 00 00 02 E1 03
- 29 01 00 00 00 02 E2 56
- 29 01 00 00 00 02 E3 03
- 29 01 00 00 00 02 E4 66
- 29 01 00 00 00 02 E5 03
- 29 01 00 00 00 02 E6 7A
- 29 01 00 00 00 02 E7 03
- 29 01 00 00 00 02 E8 93
- 29 01 00 00 00 02 E9 03
- 29 01 00 00 00 02 EA A3
- 29 01 00 00 00 02 EB 03
- 29 01 00 00 00 02 EC B4
- 29 01 00 00 00 02 ED 03
- 29 01 00 00 00 02 EE CB
- 29 01 00 00 00 02 EF 00
- 29 01 00 00 00 02 F0 ED
- 29 01 00 00 00 02 F1 00
- 29 01 00 00 00 02 F2 F3
- 29 01 00 00 00 02 F3 00
- 29 01 00 00 00 02 F4 FE
- 29 01 00 00 00 02 F5 01
- 29 01 00 00 00 02 F6 09
- 29 01 00 00 00 02 F7 01
- 29 01 00 00 00 02 F8 13
- 29 01 00 00 00 02 F9 01
- 29 01 00 00 00 02 FA 1D
- 29 01 00 00 00 02 FF 02
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 00 01
- 29 01 00 00 00 02 01 26
- 29 01 00 00 00 02 02 01
- 29 01 00 00 00 02 03 2F
- 29 01 00 00 00 02 04 01
- 29 01 00 00 00 02 05 37
- 29 01 00 00 00 02 06 01
- 29 01 00 00 00 02 07 56
- 29 01 00 00 00 02 08 01
- 29 01 00 00 00 02 09 70
- 29 01 00 00 00 02 0A 01
- 29 01 00 00 00 02 0B 9D
- 29 01 00 00 00 02 0C 01
- 29 01 00 00 00 02 0D C2
- 29 01 00 00 00 02 0E 01
- 29 01 00 00 00 02 0F FF
- 29 01 00 00 00 02 10 02
- 29 01 00 00 00 02 11 31
- 29 01 00 00 00 02 12 02
- 29 01 00 00 00 02 13 32
- 29 01 00 00 00 02 14 02
- 29 01 00 00 00 02 15 60
- 29 01 00 00 00 02 16 02
- 29 01 00 00 00 02 17 94
- 29 01 00 00 00 02 18 02
- 29 01 00 00 00 02 19 B5
- 29 01 00 00 00 02 1A 02
- 29 01 00 00 00 02 1B E3
- 29 01 00 00 00 02 1C 03
- 29 01 00 00 00 02 1D 03
- 29 01 00 00 00 02 1E 03
- 29 01 00 00 00 02 1F 2D
- 29 01 00 00 00 02 20 03
- 29 01 00 00 00 02 21 3A
- 29 01 00 00 00 02 22 03
- 29 01 00 00 00 02 23 48
- 29 01 00 00 00 02 24 03
- 29 01 00 00 00 02 25 57
- 29 01 00 00 00 02 26 03
- 29 01 00 00 00 02 27 68
- 29 01 00 00 00 02 28 03
- 29 01 00 00 00 02 29 7B
- 29 01 00 00 00 02 2A 03
- 29 01 00 00 00 02 2B 90
- 29 01 00 00 00 02 2D 03
- 29 01 00 00 00 02 2F A0
- 29 01 00 00 00 02 30 03
- 29 01 00 00 00 02 31 CB
- 29 01 00 00 00 02 32 00
- 29 01 00 00 00 02 33 ED
- 29 01 00 00 00 02 34 00
- 29 01 00 00 00 02 35 F3
- 29 01 00 00 00 02 36 00
- 29 01 00 00 00 02 37 FE
- 29 01 00 00 00 02 38 01
- 29 01 00 00 00 02 39 09
- 29 01 00 00 00 02 3A 01
- 29 01 00 00 00 02 3B 13
- 29 01 00 00 00 02 3D 01
- 29 01 00 00 00 02 3F 1D
- 29 01 00 00 00 02 40 01
- 29 01 00 00 00 02 41 26
- 29 01 00 00 00 02 42 01
- 29 01 00 00 00 02 43 2F
- 29 01 00 00 00 02 44 01
- 29 01 00 00 00 02 45 37
- 29 01 00 00 00 02 46 01
- 29 01 00 00 00 02 47 56
- 29 01 00 00 00 02 48 01
- 29 01 00 00 00 02 49 70
- 29 01 00 00 00 02 4A 01
- 29 01 00 00 00 02 4B 9D
- 29 01 00 00 00 02 4C 01
- 29 01 00 00 00 02 4D C2
- 29 01 00 00 00 02 4E 01
- 29 01 00 00 00 02 4F FF
- 29 01 00 00 00 02 50 02
- 29 01 00 00 00 02 51 31
- 29 01 00 00 00 02 52 02
- 29 01 00 00 00 02 53 32
- 29 01 00 00 00 02 54 02
- 29 01 00 00 00 02 55 60
- 29 01 00 00 00 02 56 02
- 29 01 00 00 00 02 58 94
- 29 01 00 00 00 02 59 02
- 29 01 00 00 00 02 5A B5
- 29 01 00 00 00 02 5B 02
- 29 01 00 00 00 02 5C E3
- 29 01 00 00 00 02 5D 03
- 29 01 00 00 00 02 5E 03
- 29 01 00 00 00 02 5F 03
- 29 01 00 00 00 02 60 2D
- 29 01 00 00 00 02 61 03
- 29 01 00 00 00 02 62 3A
- 29 01 00 00 00 02 63 03
- 29 01 00 00 00 02 64 48
- 29 01 00 00 00 02 65 03
- 29 01 00 00 00 02 66 57
- 29 01 00 00 00 02 67 03
- 29 01 00 00 00 02 68 68
- 29 01 00 00 00 02 69 03
- 29 01 00 00 00 02 6A 7B
- 29 01 00 00 00 02 6B 03
- 29 01 00 00 00 02 6C 90
- 29 01 00 00 00 02 6D 03
- 29 01 00 00 00 02 6E A0
- 29 01 00 00 00 02 6F 03
- 29 01 00 00 00 02 70 CB
- 29 01 00 00 00 02 71 00
- 29 01 00 00 00 02 72 19
- 29 01 00 00 00 02 73 00
- 29 01 00 00 00 02 74 36
- 29 01 00 00 00 02 75 00
- 29 01 00 00 00 02 76 55
- 29 01 00 00 00 02 77 00
- 29 01 00 00 00 02 78 70
- 29 01 00 00 00 02 79 00
- 29 01 00 00 00 02 7A 83
- 29 01 00 00 00 02 7B 00
- 29 01 00 00 00 02 7C 99
- 29 01 00 00 00 02 7D 00
- 29 01 00 00 00 02 7E A8
- 29 01 00 00 00 02 7F 00
- 29 01 00 00 00 02 80 B7
- 29 01 00 00 00 02 81 00
- 29 01 00 00 00 02 82 C5
- 29 01 00 00 00 02 83 00
- 29 01 00 00 00 02 84 F7
- 29 01 00 00 00 02 85 01
- 29 01 00 00 00 02 86 1E
- 29 01 00 00 00 02 87 01
- 29 01 00 00 00 02 88 60
- 29 01 00 00 00 02 89 01
- 29 01 00 00 00 02 8A 95
- 29 01 00 00 00 02 8B 01
- 29 01 00 00 00 02 8C E1
- 29 01 00 00 00 02 8D 02
- 29 01 00 00 00 02 8E 20
- 29 01 00 00 00 02 8F 02
- 29 01 00 00 00 02 90 23
- 29 01 00 00 00 02 91 02
- 29 01 00 00 00 02 92 59
- 29 01 00 00 00 02 93 02
- 29 01 00 00 00 02 94 94
- 29 01 00 00 00 02 95 02
- 29 01 00 00 00 02 96 B4
- 29 01 00 00 00 02 97 02
- 29 01 00 00 00 02 98 E1
- 29 01 00 00 00 02 99 03
- 29 01 00 00 00 02 9A 01
- 29 01 00 00 00 02 9B 03
- 29 01 00 00 00 02 9C 28
- 29 01 00 00 00 02 9D 03
- 29 01 00 00 00 02 9E 30
- 29 01 00 00 00 02 9F 03
- 29 01 00 00 00 02 A0 37
- 29 01 00 00 00 02 A2 03
- 29 01 00 00 00 02 A3 3B
- 29 01 00 00 00 02 A4 03
- 29 01 00 00 00 02 A5 40
- 29 01 00 00 00 02 A6 03
- 29 01 00 00 00 02 A7 50
- 29 01 00 00 00 02 A9 03
- 29 01 00 00 00 02 AA 6D
- 29 01 00 00 00 02 AB 03
- 29 01 00 00 00 02 AC 80
- 29 01 00 00 00 02 AD 03
- 29 01 00 00 00 02 AE CB
- 29 01 00 00 00 02 AF 00
- 29 01 00 00 00 02 B0 19
- 29 01 00 00 00 02 B1 00
- 29 01 00 00 00 02 B2 36
- 29 01 00 00 00 02 B3 00
- 29 01 00 00 00 02 B4 55
- 29 01 00 00 00 02 B5 00
- 29 01 00 00 00 02 B6 70
- 29 01 00 00 00 02 B7 00
- 29 01 00 00 00 02 B8 83
- 29 01 00 00 00 02 B9 00
- 29 01 00 00 00 02 BA 99
- 29 01 00 00 00 02 BB 00
- 29 01 00 00 00 02 BC A8
- 29 01 00 00 00 02 BD 00
- 29 01 00 00 00 02 BE B7
- 29 01 00 00 00 02 BF 00
- 29 01 00 00 00 02 C0 C5
- 29 01 00 00 00 02 C1 00
- 29 01 00 00 00 02 C2 F7
- 29 01 00 00 00 02 C3 01
- 29 01 00 00 00 02 C4 1E
- 29 01 00 00 00 02 C5 01
- 29 01 00 00 00 02 C6 60
- 29 01 00 00 00 02 C7 01
- 29 01 00 00 00 02 C8 95
- 29 01 00 00 00 02 C9 01
- 29 01 00 00 00 02 CA E1
- 29 01 00 00 00 02 CB 02
- 29 01 00 00 00 02 CC 20
- 29 01 00 00 00 02 CD 02
- 29 01 00 00 00 02 CE 23
- 29 01 00 00 00 02 CF 02
- 29 01 00 00 00 02 D0 59
- 29 01 00 00 00 02 D1 02
- 29 01 00 00 00 02 D2 94
- 29 01 00 00 00 02 D3 02
- 29 01 00 00 00 02 D4 B4
- 29 01 00 00 00 02 D5 02
- 29 01 00 00 00 02 D6 E1
- 29 01 00 00 00 02 D7 03
- 29 01 00 00 00 02 D8 01
- 29 01 00 00 00 02 D9 03
- 29 01 00 00 00 02 DA 28
- 29 01 00 00 00 02 DB 03
- 29 01 00 00 00 02 DC 30
- 29 01 00 00 00 02 DD 03
- 29 01 00 00 00 02 DE 37
- 29 01 00 00 00 02 DF 03
- 29 01 00 00 00 02 E0 3B
- 29 01 00 00 00 02 E1 03
- 29 01 00 00 00 02 E2 40
- 29 01 00 00 00 02 E3 03
- 29 01 00 00 00 02 E4 50
- 29 01 00 00 00 02 E5 03
- 29 01 00 00 00 02 E6 6D
- 29 01 00 00 00 02 E7 03
- 29 01 00 00 00 02 E8 80
- 29 01 00 00 00 02 E9 03
- 29 01 00 00 00 02 EA CB
- 29 01 00 00 00 02 FF 01
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 FF 02
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 FF 04
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 FF 00
- 29 01 00 00 64 02 11 00
- 29 01 00 00 00 02 FF EE
- 29 01 00 00 00 02 12 50
- 29 01 00 00 00 02 13 02
- 29 01 00 00 00 02 6A 60
- 29 01 00 00 00 02 FF 00
- 29 01 00 00 78 02 29 00];
+ qcom,panel-on-cmds = [29 01 00 00 00 00 02 FF EE
+ 29 01 00 00 00 00 02 26 08
+ 29 01 00 00 00 00 02 26 00
+ 29 01 00 00 10 00 02 FF 00
+ 29 01 00 00 00 00 02 BA 03
+ 29 01 00 00 00 00 02 C2 03
+ 29 01 00 00 00 00 02 FF 01
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 00 4A
+ 29 01 00 00 00 00 02 01 33
+ 29 01 00 00 00 00 02 02 53
+ 29 01 00 00 00 00 02 03 55
+ 29 01 00 00 00 00 02 04 55
+ 29 01 00 00 00 00 02 05 33
+ 29 01 00 00 00 00 02 06 22
+ 29 01 00 00 00 00 02 08 56
+ 29 01 00 00 00 00 02 09 8F
+ 29 01 00 00 00 00 02 36 73
+ 29 01 00 00 00 00 02 0B 9F
+ 29 01 00 00 00 00 02 0C 9F
+ 29 01 00 00 00 00 02 0D 2F
+ 29 01 00 00 00 00 02 0E 24
+ 29 01 00 00 00 00 02 11 83
+ 29 01 00 00 00 00 02 12 03
+ 29 01 00 00 00 00 02 71 2C
+ 29 01 00 00 00 00 02 6F 03
+ 29 01 00 00 00 00 02 0F 0A
+ 29 01 00 00 00 00 02 FF 05
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 01 00
+ 29 01 00 00 00 00 02 02 8B
+ 29 01 00 00 00 00 02 03 82
+ 29 01 00 00 00 00 02 04 82
+ 29 01 00 00 00 00 02 05 30
+ 29 01 00 00 00 00 02 06 33
+ 29 01 00 00 00 00 02 07 01
+ 29 01 00 00 00 00 02 08 00
+ 29 01 00 00 00 00 02 09 46
+ 29 01 00 00 00 00 02 0A 46
+ 29 01 00 00 00 00 02 0D 0B
+ 29 01 00 00 00 00 02 0E 1D
+ 29 01 00 00 00 00 02 0F 08
+ 29 01 00 00 00 00 02 10 53
+ 29 01 00 00 00 00 02 11 00
+ 29 01 00 00 00 00 02 12 00
+ 29 01 00 00 00 00 02 14 01
+ 29 01 00 00 00 00 02 15 00
+ 29 01 00 00 00 00 02 16 05
+ 29 01 00 00 00 00 02 17 00
+ 29 01 00 00 00 00 02 19 7F
+ 29 01 00 00 00 00 02 1A FF
+ 29 01 00 00 00 00 02 1B 0F
+ 29 01 00 00 00 00 02 1C 00
+ 29 01 00 00 00 00 02 1D 00
+ 29 01 00 00 00 00 02 1E 00
+ 29 01 00 00 00 00 02 1F 07
+ 29 01 00 00 00 00 02 20 00
+ 29 01 00 00 00 00 02 21 06
+ 29 01 00 00 00 00 02 22 55
+ 29 01 00 00 00 00 02 23 4D
+ 29 01 00 00 00 00 02 2D 02
+ 29 01 00 00 00 00 02 28 01
+ 29 01 00 00 00 00 02 2F 02
+ 29 01 00 00 00 00 02 83 01
+ 29 01 00 00 00 00 02 9E 58
+ 29 01 00 00 00 00 02 9F 6A
+ 29 01 00 00 00 00 02 A0 01
+ 29 01 00 00 00 00 02 A2 10
+ 29 01 00 00 00 00 02 BB 0A
+ 29 01 00 00 00 00 02 BC 0A
+ 29 01 00 00 00 00 02 32 08
+ 29 01 00 00 00 00 02 33 B8
+ 29 01 00 00 00 00 02 36 01
+ 29 01 00 00 00 00 02 37 00
+ 29 01 00 00 00 00 02 43 00
+ 29 01 00 00 00 00 02 4B 21
+ 29 01 00 00 00 00 02 4C 03
+ 29 01 00 00 00 00 02 50 21
+ 29 01 00 00 00 00 02 51 03
+ 29 01 00 00 00 00 02 58 21
+ 29 01 00 00 00 00 02 59 03
+ 29 01 00 00 00 00 02 5D 21
+ 29 01 00 00 00 00 02 5E 03
+ 29 01 00 00 00 00 02 6C 00
+ 29 01 00 00 00 00 02 6D 00
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 FF 01
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 75 00
+ 29 01 00 00 00 00 02 76 7D
+ 29 01 00 00 00 00 02 77 00
+ 29 01 00 00 00 00 02 78 8A
+ 29 01 00 00 00 00 02 79 00
+ 29 01 00 00 00 00 02 7A 9C
+ 29 01 00 00 00 00 02 7B 00
+ 29 01 00 00 00 00 02 7C B1
+ 29 01 00 00 00 00 02 7D 00
+ 29 01 00 00 00 00 02 7E BF
+ 29 01 00 00 00 00 02 7F 00
+ 29 01 00 00 00 00 02 80 CF
+ 29 01 00 00 00 00 02 81 00
+ 29 01 00 00 00 00 02 82 DD
+ 29 01 00 00 00 00 02 83 00
+ 29 01 00 00 00 00 02 84 E8
+ 29 01 00 00 00 00 02 85 00
+ 29 01 00 00 00 00 02 86 F2
+ 29 01 00 00 00 00 02 87 01
+ 29 01 00 00 00 00 02 88 1F
+ 29 01 00 00 00 00 02 89 01
+ 29 01 00 00 00 00 02 8A 41
+ 29 01 00 00 00 00 02 8B 01
+ 29 01 00 00 00 00 02 8C 78
+ 29 01 00 00 00 00 02 8D 01
+ 29 01 00 00 00 00 02 8E A5
+ 29 01 00 00 00 00 02 8F 01
+ 29 01 00 00 00 00 02 90 EE
+ 29 01 00 00 00 00 02 91 02
+ 29 01 00 00 00 00 02 92 29
+ 29 01 00 00 00 00 02 93 02
+ 29 01 00 00 00 00 02 94 2A
+ 29 01 00 00 00 00 02 95 02
+ 29 01 00 00 00 00 02 96 5D
+ 29 01 00 00 00 00 02 97 02
+ 29 01 00 00 00 00 02 98 93
+ 29 01 00 00 00 00 02 99 02
+ 29 01 00 00 00 00 02 9A B8
+ 29 01 00 00 00 00 02 9B 02
+ 29 01 00 00 00 00 02 9C E7
+ 29 01 00 00 00 00 02 9D 03
+ 29 01 00 00 00 00 02 9E 07
+ 29 01 00 00 00 00 02 9F 03
+ 29 01 00 00 00 00 02 A0 37
+ 29 01 00 00 00 00 02 A2 03
+ 29 01 00 00 00 00 02 A3 46
+ 29 01 00 00 00 00 02 A4 03
+ 29 01 00 00 00 00 02 A5 56
+ 29 01 00 00 00 00 02 A6 03
+ 29 01 00 00 00 00 02 A7 66
+ 29 01 00 00 00 00 02 A9 03
+ 29 01 00 00 00 00 02 AA 7A
+ 29 01 00 00 00 00 02 AB 03
+ 29 01 00 00 00 00 02 AC 93
+ 29 01 00 00 00 00 02 AD 03
+ 29 01 00 00 00 00 02 AE A3
+ 29 01 00 00 00 00 02 AF 03
+ 29 01 00 00 00 00 02 B0 B4
+ 29 01 00 00 00 00 02 B1 03
+ 29 01 00 00 00 00 02 B2 CB
+ 29 01 00 00 00 00 02 B3 00
+ 29 01 00 00 00 00 02 B4 7D
+ 29 01 00 00 00 00 02 B5 00
+ 29 01 00 00 00 00 02 B6 8A
+ 29 01 00 00 00 00 02 B7 00
+ 29 01 00 00 00 00 02 B8 9C
+ 29 01 00 00 00 00 02 B9 00
+ 29 01 00 00 00 00 02 BA B1
+ 29 01 00 00 00 00 02 BB 00
+ 29 01 00 00 00 00 02 BC BF
+ 29 01 00 00 00 00 02 BD 00
+ 29 01 00 00 00 00 02 BE CF
+ 29 01 00 00 00 00 02 BF 00
+ 29 01 00 00 00 00 02 C0 DD
+ 29 01 00 00 00 00 02 C1 00
+ 29 01 00 00 00 00 02 C2 E8
+ 29 01 00 00 00 00 02 C3 00
+ 29 01 00 00 00 00 02 C4 F2
+ 29 01 00 00 00 00 02 C5 01
+ 29 01 00 00 00 00 02 C6 1F
+ 29 01 00 00 00 00 02 C7 01
+ 29 01 00 00 00 00 02 C8 41
+ 29 01 00 00 00 00 02 C9 01
+ 29 01 00 00 00 00 02 CA 78
+ 29 01 00 00 00 00 02 CB 01
+ 29 01 00 00 00 00 02 CC A5
+ 29 01 00 00 00 00 02 CD 01
+ 29 01 00 00 00 00 02 CE EE
+ 29 01 00 00 00 00 02 CF 02
+ 29 01 00 00 00 00 02 D0 29
+ 29 01 00 00 00 00 02 D1 02
+ 29 01 00 00 00 00 02 D2 2A
+ 29 01 00 00 00 00 02 D3 02
+ 29 01 00 00 00 00 02 D4 5D
+ 29 01 00 00 00 00 02 D5 02
+ 29 01 00 00 00 00 02 D6 93
+ 29 01 00 00 00 00 02 D7 02
+ 29 01 00 00 00 00 02 D8 B8
+ 29 01 00 00 00 00 02 D9 02
+ 29 01 00 00 00 00 02 DA E7
+ 29 01 00 00 00 00 02 DB 03
+ 29 01 00 00 00 00 02 DC 07
+ 29 01 00 00 00 00 02 DD 03
+ 29 01 00 00 00 00 02 DE 37
+ 29 01 00 00 00 00 02 DF 03
+ 29 01 00 00 00 00 02 E0 46
+ 29 01 00 00 00 00 02 E1 03
+ 29 01 00 00 00 00 02 E2 56
+ 29 01 00 00 00 00 02 E3 03
+ 29 01 00 00 00 00 02 E4 66
+ 29 01 00 00 00 00 02 E5 03
+ 29 01 00 00 00 00 02 E6 7A
+ 29 01 00 00 00 00 02 E7 03
+ 29 01 00 00 00 00 02 E8 93
+ 29 01 00 00 00 00 02 E9 03
+ 29 01 00 00 00 00 02 EA A3
+ 29 01 00 00 00 00 02 EB 03
+ 29 01 00 00 00 00 02 EC B4
+ 29 01 00 00 00 00 02 ED 03
+ 29 01 00 00 00 00 02 EE CB
+ 29 01 00 00 00 00 02 EF 00
+ 29 01 00 00 00 00 02 F0 ED
+ 29 01 00 00 00 00 02 F1 00
+ 29 01 00 00 00 00 02 F2 F3
+ 29 01 00 00 00 00 02 F3 00
+ 29 01 00 00 00 00 02 F4 FE
+ 29 01 00 00 00 00 02 F5 01
+ 29 01 00 00 00 00 02 F6 09
+ 29 01 00 00 00 00 02 F7 01
+ 29 01 00 00 00 00 02 F8 13
+ 29 01 00 00 00 00 02 F9 01
+ 29 01 00 00 00 00 02 FA 1D
+ 29 01 00 00 00 00 02 FF 02
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 00 01
+ 29 01 00 00 00 00 02 01 26
+ 29 01 00 00 00 00 02 02 01
+ 29 01 00 00 00 00 02 03 2F
+ 29 01 00 00 00 00 02 04 01
+ 29 01 00 00 00 00 02 05 37
+ 29 01 00 00 00 00 02 06 01
+ 29 01 00 00 00 00 02 07 56
+ 29 01 00 00 00 00 02 08 01
+ 29 01 00 00 00 00 02 09 70
+ 29 01 00 00 00 00 02 0A 01
+ 29 01 00 00 00 00 02 0B 9D
+ 29 01 00 00 00 00 02 0C 01
+ 29 01 00 00 00 00 02 0D C2
+ 29 01 00 00 00 00 02 0E 01
+ 29 01 00 00 00 00 02 0F FF
+ 29 01 00 00 00 00 02 10 02
+ 29 01 00 00 00 00 02 11 31
+ 29 01 00 00 00 00 02 12 02
+ 29 01 00 00 00 00 02 13 32
+ 29 01 00 00 00 00 02 14 02
+ 29 01 00 00 00 00 02 15 60
+ 29 01 00 00 00 00 02 16 02
+ 29 01 00 00 00 00 02 17 94
+ 29 01 00 00 00 00 02 18 02
+ 29 01 00 00 00 00 02 19 B5
+ 29 01 00 00 00 00 02 1A 02
+ 29 01 00 00 00 00 02 1B E3
+ 29 01 00 00 00 00 02 1C 03
+ 29 01 00 00 00 00 02 1D 03
+ 29 01 00 00 00 00 02 1E 03
+ 29 01 00 00 00 00 02 1F 2D
+ 29 01 00 00 00 00 02 20 03
+ 29 01 00 00 00 00 02 21 3A
+ 29 01 00 00 00 00 02 22 03
+ 29 01 00 00 00 00 02 23 48
+ 29 01 00 00 00 00 02 24 03
+ 29 01 00 00 00 00 02 25 57
+ 29 01 00 00 00 00 02 26 03
+ 29 01 00 00 00 00 02 27 68
+ 29 01 00 00 00 00 02 28 03
+ 29 01 00 00 00 00 02 29 7B
+ 29 01 00 00 00 00 02 2A 03
+ 29 01 00 00 00 00 02 2B 90
+ 29 01 00 00 00 00 02 2D 03
+ 29 01 00 00 00 00 02 2F A0
+ 29 01 00 00 00 00 02 30 03
+ 29 01 00 00 00 00 02 31 CB
+ 29 01 00 00 00 00 02 32 00
+ 29 01 00 00 00 00 02 33 ED
+ 29 01 00 00 00 00 02 34 00
+ 29 01 00 00 00 00 02 35 F3
+ 29 01 00 00 00 00 02 36 00
+ 29 01 00 00 00 00 02 37 FE
+ 29 01 00 00 00 00 02 38 01
+ 29 01 00 00 00 00 02 39 09
+ 29 01 00 00 00 00 02 3A 01
+ 29 01 00 00 00 00 02 3B 13
+ 29 01 00 00 00 00 02 3D 01
+ 29 01 00 00 00 00 02 3F 1D
+ 29 01 00 00 00 00 02 40 01
+ 29 01 00 00 00 00 02 41 26
+ 29 01 00 00 00 00 02 42 01
+ 29 01 00 00 00 00 02 43 2F
+ 29 01 00 00 00 00 02 44 01
+ 29 01 00 00 00 00 02 45 37
+ 29 01 00 00 00 00 02 46 01
+ 29 01 00 00 00 00 02 47 56
+ 29 01 00 00 00 00 02 48 01
+ 29 01 00 00 00 00 02 49 70
+ 29 01 00 00 00 00 02 4A 01
+ 29 01 00 00 00 00 02 4B 9D
+ 29 01 00 00 00 00 02 4C 01
+ 29 01 00 00 00 00 02 4D C2
+ 29 01 00 00 00 00 02 4E 01
+ 29 01 00 00 00 00 02 4F FF
+ 29 01 00 00 00 00 02 50 02
+ 29 01 00 00 00 00 02 51 31
+ 29 01 00 00 00 00 02 52 02
+ 29 01 00 00 00 00 02 53 32
+ 29 01 00 00 00 00 02 54 02
+ 29 01 00 00 00 00 02 55 60
+ 29 01 00 00 00 00 02 56 02
+ 29 01 00 00 00 00 02 58 94
+ 29 01 00 00 00 00 02 59 02
+ 29 01 00 00 00 00 02 5A B5
+ 29 01 00 00 00 00 02 5B 02
+ 29 01 00 00 00 00 02 5C E3
+ 29 01 00 00 00 00 02 5D 03
+ 29 01 00 00 00 00 02 5E 03
+ 29 01 00 00 00 00 02 5F 03
+ 29 01 00 00 00 00 02 60 2D
+ 29 01 00 00 00 00 02 61 03
+ 29 01 00 00 00 00 02 62 3A
+ 29 01 00 00 00 00 02 63 03
+ 29 01 00 00 00 00 02 64 48
+ 29 01 00 00 00 00 02 65 03
+ 29 01 00 00 00 00 02 66 57
+ 29 01 00 00 00 00 02 67 03
+ 29 01 00 00 00 00 02 68 68
+ 29 01 00 00 00 00 02 69 03
+ 29 01 00 00 00 00 02 6A 7B
+ 29 01 00 00 00 00 02 6B 03
+ 29 01 00 00 00 00 02 6C 90
+ 29 01 00 00 00 00 02 6D 03
+ 29 01 00 00 00 00 02 6E A0
+ 29 01 00 00 00 00 02 6F 03
+ 29 01 00 00 00 00 02 70 CB
+ 29 01 00 00 00 00 02 71 00
+ 29 01 00 00 00 00 02 72 19
+ 29 01 00 00 00 00 02 73 00
+ 29 01 00 00 00 00 02 74 36
+ 29 01 00 00 00 00 02 75 00
+ 29 01 00 00 00 00 02 76 55
+ 29 01 00 00 00 00 02 77 00
+ 29 01 00 00 00 00 02 78 70
+ 29 01 00 00 00 00 02 79 00
+ 29 01 00 00 00 00 02 7A 83
+ 29 01 00 00 00 00 02 7B 00
+ 29 01 00 00 00 00 02 7C 99
+ 29 01 00 00 00 00 02 7D 00
+ 29 01 00 00 00 00 02 7E A8
+ 29 01 00 00 00 00 02 7F 00
+ 29 01 00 00 00 00 02 80 B7
+ 29 01 00 00 00 00 02 81 00
+ 29 01 00 00 00 00 02 82 C5
+ 29 01 00 00 00 00 02 83 00
+ 29 01 00 00 00 00 02 84 F7
+ 29 01 00 00 00 00 02 85 01
+ 29 01 00 00 00 00 02 86 1E
+ 29 01 00 00 00 00 02 87 01
+ 29 01 00 00 00 00 02 88 60
+ 29 01 00 00 00 00 02 89 01
+ 29 01 00 00 00 00 02 8A 95
+ 29 01 00 00 00 00 02 8B 01
+ 29 01 00 00 00 00 02 8C E1
+ 29 01 00 00 00 00 02 8D 02
+ 29 01 00 00 00 00 02 8E 20
+ 29 01 00 00 00 00 02 8F 02
+ 29 01 00 00 00 00 02 90 23
+ 29 01 00 00 00 00 02 91 02
+ 29 01 00 00 00 00 02 92 59
+ 29 01 00 00 00 00 02 93 02
+ 29 01 00 00 00 00 02 94 94
+ 29 01 00 00 00 00 02 95 02
+ 29 01 00 00 00 00 02 96 B4
+ 29 01 00 00 00 00 02 97 02
+ 29 01 00 00 00 00 02 98 E1
+ 29 01 00 00 00 00 02 99 03
+ 29 01 00 00 00 00 02 9A 01
+ 29 01 00 00 00 00 02 9B 03
+ 29 01 00 00 00 00 02 9C 28
+ 29 01 00 00 00 00 02 9D 03
+ 29 01 00 00 00 00 02 9E 30
+ 29 01 00 00 00 00 02 9F 03
+ 29 01 00 00 00 00 02 A0 37
+ 29 01 00 00 00 00 02 A2 03
+ 29 01 00 00 00 00 02 A3 3B
+ 29 01 00 00 00 00 02 A4 03
+ 29 01 00 00 00 00 02 A5 40
+ 29 01 00 00 00 00 02 A6 03
+ 29 01 00 00 00 00 02 A7 50
+ 29 01 00 00 00 00 02 A9 03
+ 29 01 00 00 00 00 02 AA 6D
+ 29 01 00 00 00 00 02 AB 03
+ 29 01 00 00 00 00 02 AC 80
+ 29 01 00 00 00 00 02 AD 03
+ 29 01 00 00 00 00 02 AE CB
+ 29 01 00 00 00 00 02 AF 00
+ 29 01 00 00 00 00 02 B0 19
+ 29 01 00 00 00 00 02 B1 00
+ 29 01 00 00 00 00 02 B2 36
+ 29 01 00 00 00 00 02 B3 00
+ 29 01 00 00 00 00 02 B4 55
+ 29 01 00 00 00 00 02 B5 00
+ 29 01 00 00 00 00 02 B6 70
+ 29 01 00 00 00 00 02 B7 00
+ 29 01 00 00 00 00 02 B8 83
+ 29 01 00 00 00 00 02 B9 00
+ 29 01 00 00 00 00 02 BA 99
+ 29 01 00 00 00 00 02 BB 00
+ 29 01 00 00 00 00 02 BC A8
+ 29 01 00 00 00 00 02 BD 00
+ 29 01 00 00 00 00 02 BE B7
+ 29 01 00 00 00 00 02 BF 00
+ 29 01 00 00 00 00 02 C0 C5
+ 29 01 00 00 00 00 02 C1 00
+ 29 01 00 00 00 00 02 C2 F7
+ 29 01 00 00 00 00 02 C3 01
+ 29 01 00 00 00 00 02 C4 1E
+ 29 01 00 00 00 00 02 C5 01
+ 29 01 00 00 00 00 02 C6 60
+ 29 01 00 00 00 00 02 C7 01
+ 29 01 00 00 00 00 02 C8 95
+ 29 01 00 00 00 00 02 C9 01
+ 29 01 00 00 00 00 02 CA E1
+ 29 01 00 00 00 00 02 CB 02
+ 29 01 00 00 00 00 02 CC 20
+ 29 01 00 00 00 00 02 CD 02
+ 29 01 00 00 00 00 02 CE 23
+ 29 01 00 00 00 00 02 CF 02
+ 29 01 00 00 00 00 02 D0 59
+ 29 01 00 00 00 00 02 D1 02
+ 29 01 00 00 00 00 02 D2 94
+ 29 01 00 00 00 00 02 D3 02
+ 29 01 00 00 00 00 02 D4 B4
+ 29 01 00 00 00 00 02 D5 02
+ 29 01 00 00 00 00 02 D6 E1
+ 29 01 00 00 00 00 02 D7 03
+ 29 01 00 00 00 00 02 D8 01
+ 29 01 00 00 00 00 02 D9 03
+ 29 01 00 00 00 00 02 DA 28
+ 29 01 00 00 00 00 02 DB 03
+ 29 01 00 00 00 00 02 DC 30
+ 29 01 00 00 00 00 02 DD 03
+ 29 01 00 00 00 00 02 DE 37
+ 29 01 00 00 00 00 02 DF 03
+ 29 01 00 00 00 00 02 E0 3B
+ 29 01 00 00 00 00 02 E1 03
+ 29 01 00 00 00 00 02 E2 40
+ 29 01 00 00 00 00 02 E3 03
+ 29 01 00 00 00 00 02 E4 50
+ 29 01 00 00 00 00 02 E5 03
+ 29 01 00 00 00 00 02 E6 6D
+ 29 01 00 00 00 00 02 E7 03
+ 29 01 00 00 00 00 02 E8 80
+ 29 01 00 00 00 00 02 E9 03
+ 29 01 00 00 00 00 02 EA CB
+ 29 01 00 00 00 00 02 FF 01
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 FF 02
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 FF 04
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 FF 00
+ 29 01 00 00 64 00 02 11 00
+ 29 01 00 00 00 00 02 FF EE
+ 29 01 00 00 00 00 02 12 50
+ 29 01 00 00 00 00 02 13 02
+ 29 01 00 00 00 00 02 6A 60
+ 29 01 00 00 00 00 02 FF 00
+ 29 01 00 00 78 00 02 29 00];
qcom,on-cmds-dsi-state = "DSI_LP_MODE";
- qcom,panel-off-cmds = [05 01 00 00 32 02 28 00
- 05 01 00 00 78 02 10 00];
+ qcom,panel-off-cmds = [05 01 00 00 32 00 02 28 00
+ 05 01 00 00 78 00 02 10 00];
qcom,off-cmds-dsi-state = "DSI_HS_MODE";
};
};
diff --git a/arch/arm/boot/dts/dsi-panel-orise-720p-video.dtsi b/arch/arm/boot/dts/dsi-panel-orise-720p-video.dtsi
index 448d357..478541f 100644
--- a/arch/arm/boot/dts/dsi-panel-orise-720p-video.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-orise-720p-video.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,mdss_dsi_orise_720p_video {
compatible = "qcom,mdss-dsi-panel";
label = "orise 720p video mode dsi panel";
@@ -50,11 +50,11 @@
00 c2 45 00 00 00 00 01 75 /* lane3 config */
00 02 45 00 00 00 00 01 97]; /* Clk ln config */
- qcom,panel-on-cmds = [05 01 00 00 78 02 11 00
- 05 01 00 00 78 02 29 00];
+ qcom,panel-on-cmds = [05 01 00 00 78 00 02 11 00
+ 05 01 00 00 78 00 02 29 00];
qcom,on-cmds-dsi-state = "DSI_LP_MODE";
- qcom,panel-off-cmds = [05 01 00 00 32 02 28 00
- 05 01 00 00 78 02 10 00];
+ qcom,panel-off-cmds = [05 01 00 00 32 00 02 28 00
+ 05 01 00 00 78 00 02 10 00];
qcom,off-cmds-dsi-state = "DSI_LP_MODE";
};
};
diff --git a/arch/arm/boot/dts/dsi-panel-sharp-qhd-video.dtsi b/arch/arm/boot/dts/dsi-panel-sharp-qhd-video.dtsi
index f853285..45d396c 100644
--- a/arch/arm/boot/dts/dsi-panel-sharp-qhd-video.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-sharp-qhd-video.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,mdss_dsi_sharp_qhd_video {
compatible = "qcom,mdss-dsi-panel";
label = "sharp QHD LS043T1LE01 video mode dsi panel";
@@ -52,16 +52,16 @@
00 00 00 00 0a 00 00 01 97 /* lane2 config */
00 00 00 00 0f 00 00 01 97 /* lane3 config */
00 c0 00 00 00 00 00 01 bb]; /* Clk ln config */
- qcom,panel-on-cmds = [05 01 00 00 32 02 01 00 /* sw reset */
- 05 01 00 00 0a 02 11 00 /* exit sleep */
- 15 01 00 00 0a 02 53 2c /* backlight on */
- 15 01 00 00 0a 02 51 ff /* brightness max */
- 05 01 00 00 0a 02 29 00 /* display on */
- 15 01 00 00 0a 02 ae 03 /* set num of lanes */
- 15 01 00 00 0a 02 3a 77 /* rgb_888 */];
+ qcom,panel-on-cmds = [05 01 00 00 32 00 02 01 00 /* sw reset */
+ 05 01 00 00 0a 00 02 11 00 /* exit sleep */
+ 15 01 00 00 0a 00 02 53 2c /* backlight on */
+ 15 01 00 00 0a 00 02 51 ff /* brightness max */
+ 05 01 00 00 0a 00 02 29 00 /* display on */
+ 15 01 00 00 0a 00 02 ae 03 /* set num of lanes */
+ 15 01 00 00 0a 00 02 3a 77 /* rgb_888 */];
qcom,on-cmds-dsi-state = "DSI_LP_MODE";
- qcom,panel-off-cmds = [05 01 00 00 0a 02 28 00 /* display off */
- 05 01 00 00 78 02 10 00 /* enter sleep */];
+ qcom,panel-off-cmds = [05 01 00 00 0a 00 02 28 00 /* display off */
+ 05 01 00 00 78 00 02 10 00 /* enter sleep */];
qcom,off-cmds-dsi-state = "DSI_HS_MODE";
};
};
diff --git a/arch/arm/boot/dts/dsi-panel-sim-video.dtsi b/arch/arm/boot/dts/dsi-panel-sim-video.dtsi
index 9a734a0..271e373 100644
--- a/arch/arm/boot/dts/dsi-panel-sim-video.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-sim-video.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,mdss_dsi_sim_video {
compatible = "qcom,mdss-dsi-panel";
@@ -38,9 +38,9 @@
qcom,mdss-pan-dsi-mdp-tr = <0x04>;
qcom,mdss-pan-dsi-dma-tr = <0x04>;
qcom,mdss-pan-dsi-frame-rate = <60>;
- qcom,panel-on-cmds = [32 01 00 00 00 02 00 00];
+ qcom,panel-on-cmds = [32 01 00 00 00 00 02 00 00];
qcom,on-cmds-dsi-state = "DSI_LP_MODE";
- qcom,panel-off-cmds = [22 01 00 00 00 02 00 00];
+ qcom,panel-off-cmds = [22 01 00 00 00 00 02 00 00];
qcom,off-cmds-dsi-state = "DSI_LP_MODE";
};
};
diff --git a/arch/arm/boot/dts/dsi-panel-toshiba-720p-video.dtsi b/arch/arm/boot/dts/dsi-panel-toshiba-720p-video.dtsi
index 2937cde..5c37cf8 100644
--- a/arch/arm/boot/dts/dsi-panel-toshiba-720p-video.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-toshiba-720p-video.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,mdss_dsi_toshiba_720p_video {
compatible = "qcom,mdss-dsi-panel";
@@ -54,71 +54,71 @@
00 00 00 00 0f 00 00 01 97 /* lane3 config */
00 c0 00 00 00 00 00 01 bb]; /* Clk ln config */
- qcom,panel-on-cmds = [23 01 00 00 0a 02 b0 00
- 23 01 00 00 0a 02 b2 00
- 23 01 00 00 0a 02 b3 0c
- 23 01 00 00 0a 02 b4 02
- 29 01 00 00 00 06
+ qcom,panel-on-cmds = [23 01 00 00 0a 00 02 b0 00
+ 23 01 00 00 0a 00 02 b2 00
+ 23 01 00 00 0a 00 02 b3 0c
+ 23 01 00 00 0a 00 02 b4 02
+ 29 01 00 00 00 00 06
c0 40 02 7f c8 08
- 29 01 00 00 00 10
+ 29 01 00 00 00 00 10
c1 00 a8 00 00 00
00 00 9d 08 27 00
00 00 00 00
- 29 01 00 00 00 06
+ 29 01 00 00 00 00 06
c2 00 00 09 00 00
- 23 01 00 00 0a 02 c3 04
- 29 01 00 00 00 04
+ 23 01 00 00 0a 00 02 c3 04
+ 29 01 00 00 00 00 04
c4 4d 83 00
- 29 01 00 00 00 0b
+ 29 01 00 00 00 00 0b
c6 12 00 08 71 00
00 00 80 00 04
- 23 01 00 00 0a 02 c7 22
- 29 01 00 00 00 05
+ 23 01 00 00 0a 00 02 c7 22
+ 29 01 00 00 00 00 05
c8 4c 0c 0c 0c
- 29 01 00 00 00 0e
+ 29 01 00 00 00 00 0e
c9 00 40 00 16 32
2e 3a 43 3e 3c 45
79 3f
- 29 01 00 00 00 0e
+ 29 01 00 00 00 00 0e
ca 00 46 1a 23 21
1c 25 31 2d 49 5f
7f 3f
- 29 01 00 00 00 0e
+ 29 01 00 00 00 00 0e
cb 00 4c 20 3a 42
40 47 4b 42 3e 46
7e 3f
- 29 01 00 00 00 0e
+ 29 01 00 00 00 00 0e
cc 00 41 19 21 1d
14 18 1f 1d 25 3f
73 3f
- 29 01 00 00 00 0e
+ 29 01 00 00 00 00 0e
cd 23 79 5a 5f 57
4c 51 51 45 3f 4b
7f 3f
- 29 01 00 00 00 0e
+ 29 01 00 00 00 00 0e
ce 00 40 14 20 1a
0e 0e 13 08 00 05
46 1c
- 29 01 00 00 00 04
+ 29 01 00 00 00 00 04
d0 6a 64 01
- 29 01 00 00 00 03 d1 77 d4
- 23 01 00 00 0a 02 d3 33
- 29 01 00 00 00 03 d5 0f 0f
- 29 01 00 00 00 07
+ 29 01 00 00 00 00 03 d1 77 d4
+ 23 01 00 00 0a 00 02 d3 33
+ 29 01 00 00 00 00 03 d5 0f 0f
+ 29 01 00 00 00 00 07
d8 34 64 23 25 62
32
- 29 01 00 00 00 0c
+ 29 01 00 00 00 00 0c
de 10 7b 11 0a 00
00 00 00 00 00 00
- 29 01 00 00 00 09
+ 29 01 00 00 00 00 09
fd 04 55 53 00 70
ff 10 73
- 23 01 00 00 0a 02 e2 00
- 05 01 00 00 78 02 11 00
- 05 01 00 00 32 02 29 00];
+ 23 01 00 00 0a 00 02 e2 00
+ 05 01 00 00 78 00 02 11 00
+ 05 01 00 00 32 00 02 29 00];
qcom,on-cmds-dsi-state = "DSI_LP_MODE";
- qcom,panel-off-cmds = [05 01 00 00 32 02 28 00
- 05 01 00 00 78 02 10 00];
+ qcom,panel-off-cmds = [05 01 00 00 32 00 02 28 00
+ 05 01 00 00 78 00 02 10 00];
qcom,off-cmds-dsi-state = "DSI_HS_MODE";
};
};
diff --git a/arch/arm/boot/dts/dsi-v2-panel-hx8379a-wvga-video.dtsi b/arch/arm/boot/dts/dsi-v2-panel-hx8379a-wvga-video.dtsi
new file mode 100644
index 0000000..b9ed050
--- /dev/null
+++ b/arch/arm/boot/dts/dsi-v2-panel-hx8379a-wvga-video.dtsi
@@ -0,0 +1,117 @@
+/* Copyright (c) 2013, 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.
+ */
+
+/ {
+ qcom,dsi_v2_hx8379a_wvga_video {
+ compatible = "qcom,dsi-panel-v2";
+ label = "HX8379A WVGA video mode dsi panel";
+ qcom,dsi-ctrl-phandle = <&mdss_dsi0>;
+ qcom,rst-gpio = <&msmgpio 41 0>;
+ vdda-supply = <&pm8110_l19>;
+ vddio-supply=<&pm8110_l14>;
+ qcom,mdss-pan-res = <480 800>;
+ qcom,mdss-pan-bpp = <24>;
+ qcom,mdss-pan-dest = "display_1";
+ qcom,mdss-pan-porch-values = <90 17 90 2 3 11>;
+ qcom,mdss-pan-underflow-clr = <0xff>;
+ qcom,mdss-pan-bl-levels = <1 255>;
+ qcom,mdss-pan-bl-ctrl = "bl_ctrl_wled";
+ qcom,mdss-pan-dsi-mode = <0>;
+ qcom,mdss-pan-dsi-h-pulse-mode = <1>;
+ qcom,mdss-pan-dsi-h-power-stop = <0 0 0>;
+ qcom,mdss-pan-dsi-bllp-power-stop = <1 1>;
+ qcom,mdss-pan-dsi-traffic-mode = <2>;
+ qcom,mdss-pan-dsi-dst-format = <3>;
+ qcom,mdss-pan-dsi-vc = <0>;
+ qcom,mdss-pan-dsi-rgb-swap = <0>;
+ qcom,mdss-pan-dsi-data-lanes = <1 1 0 0>;
+ qcom,mdss-pan-dsi-dlane-swap = <1>;
+ qcom,mdss-pan-dsi-t-clk = <0x1b 0x04>;
+ qcom,mdss-pan-dsi-stream = <0>;
+ qcom,mdss-pan-dsi-mdp-tr = <0x0>;/*todo*/
+ qcom,mdss-pan-dsi-dma-tr = <0x04>;
+ qcom,mdss-pan-dsi-frame-rate = <60>;
+ qcom,panel-phy-regulatorSettings =[09 08 05 00 20 03];
+ qcom,panel-phy-timingSettings = [5D 12 0C 00 33 39
+ 10 16 15 03 04 00];
+ qcom,panel-phy-strengthCtrl = [ff 06];
+ qcom,panel-phy-bistCtrl = [03 03 00 00 0f 00];
+ qcom,panel-phy-laneConfig =
+ [80 45 00 00 01 66 /*lane0**/
+ 80 45 00 00 01 66 /*lane1*/
+ 80 45 00 00 01 66 /*lane2*/
+ 80 45 00 00 01 66 /*lane3*/
+ 40 67 00 00 01 88]; /*Clk*/
+
+ qcom,on-cmds-dsi-state = "DSI_LP_MODE";
+ qcom,panel-on-cmds = [
+ 29 01 00 00 01 04
+ B9 FF 83 79
+ 23 01 00 00 01 02
+ BA 51
+ 29 01 00 00 01 14
+ B1 00 50 44
+ EA 8D 08 11
+ 0F 0F 24 2C
+ 9A 1A 42 0B
+ 6E F1 00 E6
+ 29 01 00 00 01 0e
+ B2 00 00 3C
+ 08 04 19 22
+ 00 FF 08 04
+ 19 20
+ 29 01 00 00 01 20
+ B4 80 08 00
+ 32 10 03 32
+ 13 70 32 10
+ 08 37 01 28
+ 05 37 08 3C
+ 20 44 44 08
+ 00 40 08 28
+ 08 30 30 04
+ 23 01 00 00 01 02
+ cc 02
+ 29 01 00 00 01 30
+ D5 00 00 08
+ 00 01 05 00
+ 03 00 88 88
+ 88 88 23 01
+ 67 45 02 13
+ 88 88 88 88
+ 88 88 88 88
+ 88 88 54 76
+ 10 32 31 20
+ 88 88 88 88
+ 88 88 00 00
+ 00 00 00 00
+ 29 01 00 00 01 24
+ E0 79 00 00
+ 02 1C 1F 33
+ 28 3E 07 0E
+ 0F 15 17 16
+ 16 13 19 00
+ 00 02 1C 1F
+ 33 28 3E 07
+ 0E 0F 15 17
+ 16 16 13 19
+ 29 01 00 00 01 05
+ B6 00 A6 00 A6
+ 05 01 00 00 96 02
+ 11 00
+ 05 01 00 00 78 02
+ 29 00
+ ];
+ qcom,panel-off-cmds = [05 01 00 00 32 02 28 00
+ 05 01 00 00 78 02 10 00];
+ qcom,off-cmds-dsi-state = "DSI_LP_MODE";
+ };
+};
diff --git a/arch/arm/boot/dts/dsi-v2-panel-truly-wvga-video.dtsi b/arch/arm/boot/dts/dsi-v2-panel-truly-wvga-video.dtsi
new file mode 100644
index 0000000..891eac3
--- /dev/null
+++ b/arch/arm/boot/dts/dsi-v2-panel-truly-wvga-video.dtsi
@@ -0,0 +1,120 @@
+/* Copyright (c) 2013, 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.
+ */
+
+/ {
+ qcom,dsi_v2_truly_wvga_video {
+ compatible = "qcom,dsi-panel-v2";
+ label = "Truly WVGA video mode dsi panel";
+ qcom,dsi-ctrl-phandle = <&mdss_dsi0>;
+ qcom,rst-gpio = <&msmgpio 41 0>;
+ qcom,mode-selection-gpio = <&msmgpio 7 0>;
+ vdda-supply = <&pm8110_l19>;
+ vddio-supply=<&pm8110_l14>;
+ qcom,mdss-pan-res = <480 800>;
+ qcom,mdss-pan-bpp = <24>;
+ qcom,mdss-pan-dest = "display_1";
+ qcom,mdss-pan-porch-values = <40 8 160 10 2 12>;
+ qcom,mdss-pan-underflow-clr = <0xff>;
+ qcom,mdss-pan-bl-levels = <1 255>;
+ qcom,mdss-pan-bl-ctrl = "bl_ctrl_wled";
+ qcom,mdss-pan-dsi-mode = <0>;
+ qcom,mdss-pan-dsi-h-pulse-mode = <0>;
+ qcom,mdss-pan-dsi-h-power-stop = <0 0 0>;
+ qcom,mdss-pan-dsi-bllp-power-stop = <1 1>;
+ qcom,mdss-pan-dsi-traffic-mode = <1>;
+ qcom,mdss-pan-dsi-dst-format = <3>;
+ qcom,mdss-pan-dsi-vc = <0>;
+ qcom,mdss-pan-dsi-rgb-swap = <0>;
+ qcom,mdss-pan-dsi-data-lanes = <1 1 0 0>;
+ qcom,mdss-pan-dsi-dlane-swap = <0>;
+ qcom,mdss-pan-dsi-t-clk = <0x1b 0x04>;
+ qcom,mdss-pan-dsi-stream = <0>;
+ qcom,mdss-pan-dsi-mdp-tr = <0x0>;/*todo*/
+ qcom,mdss-pan-dsi-dma-tr = <0x04>;
+ qcom,mdss-pan-dsi-frame-rate = <60>;
+ qcom,panel-phy-regulatorSettings =[09 08 05 00 20 03];
+ qcom,panel-phy-timingSettings = [5D 12 0C 00 33 38
+ 10 16 1E 03 04 00];
+ qcom,panel-phy-strengthCtrl = [ff 06];
+ qcom,panel-phy-bistCtrl = [03 03 00 00 0f 00];
+ qcom,panel-phy-laneConfig =
+ [80 45 00 00 01 66 /*lane0**/
+ 80 45 00 00 01 66 /*lane1*/
+ 80 45 00 00 01 66 /*lane2*/
+ 80 45 00 00 01 66 /*lane3*/
+ 40 67 00 00 01 88]; /*Clk*/
+
+ qcom,on-cmds-dsi-state = "DSI_LP_MODE";
+ qcom,panel-on-cmds = [
+ 05 01 00 00 00 02
+ 01 00
+ 23 01 00 00 00 02
+ b0 04
+ 29 01 00 00 00 03
+ b3 02 00
+ 23 01 00 00 00 02
+ bd 00
+ 29 01 00 00 00 03
+ c0 18 66
+ 29 01 00 00 00 10
+ c1 23 31 99 21 20 00 30 28 0c 0c
+ 00 00 00 21 01
+ 29 01 00 00 00 07
+ c2 10 06 06 01 03 00
+ 29 01 00 00 00 19
+ c8 04 10 18 20 2e 46 3c 28 1f 18
+ 10 04 04 10 18 20 2e 46 3c 28 1f 18 10 04
+ 29 01 00 00 00 19
+ c9 04 10 18 20 2e 46 3c 28 1f 18
+ 10 04 04 10 18 20 2e 46 3c 28 1f 18 10 04
+ 29 01 00 00 00 19
+ ca 04 10 18 20 2e 46 3c 28 1f 18
+ 10 04 04 10 18 20 2e 46 3c 28 1f 18 10 04
+ 29 01 00 00 00 11
+ d0 29 03 ce a6 00 43 20 10 01 00
+ 01 01 00 03 01 00
+ 29 01 00 00 00 08
+ d1 18 0C 23 03 75 02 50
+ 23 01 00 00 00 02
+ d3 11
+ 29 01 00 00 00 03
+ d5 2a 2a
+ 29 01 00 00 00 03
+ de 01 41
+ 23 01 00 00 00 02
+ e6 51
+ 23 01 00 00 00 02
+ fa 03
+ 23 01 00 00 64 02
+ d6 28
+ 39 01 00 00 00 05
+ 2a 00 00 01 df
+ 39 01 00 00 00 05
+ 2b 00 00 03 1f
+ 15 01 00 00 00 02
+ 35 00
+ 39 01 00 00 00 03
+ 44 00 50
+ 15 01 00 00 00 02
+ 36 c1
+ 15 01 00 00 00 02
+ 3a 77
+ 05 01 00 00 7D 02
+ 11 00
+ 05 01 00 00 14 02
+ 29 00
+ ];
+ qcom,panel-off-cmds = [05 01 00 00 32 02 28 00
+ 05 01 00 00 78 02 10 00];
+ qcom,off-cmds-dsi-state = "DSI_LP_MODE";
+ };
+};
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/fsm9900-rumi.dts
similarity index 76%
rename from arch/arm/boot/dts/msmzinc-sim.dts
rename to arch/arm/boot/dts/fsm9900-rumi.dts
index e410344..2b380c7 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/fsm9900-rumi.dts
@@ -12,18 +12,20 @@
/dts-v1/;
-/include/ "msmzinc.dtsi"
+/include/ "fsm9900.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
+ model = "Qualcomm FSM9900 Rumi";
+ compatible = "qcom,fsm9900-rumi", "qcom,fsm9900", "qcom-sim";
+ qcom,msm-id = <188 0 0>;
aliases {
serial0 = &uart0;
};
+};
- uart0: serial@f991f000 {
+&soc {
+ uart0: serial@f9960000 {
status = "ok";
};
};
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/fsm9900-sim.dts
similarity index 73%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/fsm9900-sim.dts
index e410344..050929e 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/fsm9900-sim.dts
@@ -12,18 +12,21 @@
/dts-v1/;
-/include/ "msmzinc.dtsi"
+/include/ "fsm9900.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
+ model = "Qualcomm FSM9900 Simulator";
+ compatible = "qcom,fsm9900-sim", "qcom,fsm9900", "qcom-sim";
+ qcom,msm-id = <188 0 0>;
aliases {
serial0 = &uart0;
};
+};
- uart0: serial@f991f000 {
+&soc {
+ uart0: serial@f9960000 {
+ interrupts = <0 116 0>;
status = "ok";
};
};
diff --git a/arch/arm/boot/dts/msmzinc.dtsi b/arch/arm/boot/dts/fsm9900.dtsi
similarity index 68%
rename from arch/arm/boot/dts/msmzinc.dtsi
rename to arch/arm/boot/dts/fsm9900.dtsi
index 642597d..766db36 100644
--- a/arch/arm/boot/dts/msmzinc.dtsi
+++ b/arch/arm/boot/dts/fsm9900.dtsi
@@ -10,13 +10,19 @@
* GNU General Public License for more details.
*/
-/include/ "skeleton.dtsi"
-/include/ "msmzinc-ion.dtsi"
+/include/ "skeleton64.dtsi"
/ {
- model = "Qualcomm MSM ZINC";
- compatible = "qcom,msmzinc";
+ model = "Qualcomm FSM9900";
+ compatible = "qcom,fsm9900";
interrupt-parent = <&intc>;
+ soc: soc { };
+};
+
+&soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0xffffffff>;
intc: interrupt-controller@f9000000 {
compatible = "qcom,msm-qgic2";
@@ -33,9 +39,9 @@
interrupt-controller;
#interrupt-cells = <2>;
reg = <0xfd510000 0x4000>;
- ngpio = <146>;
+ ngpio = <142>;
interrupts = <0 208 0>;
- qcom,direct-connect-irqs = <8>;
+ qcom,direct-connect-irqs = <5>;
};
timer {
@@ -44,13 +50,24 @@
clock-frequency = <19200000>;
};
- serial@f991f000 {
+ serial@f9960000 {
compatible = "qcom,msm-lsuart-v14";
- reg = <0xf991f000 0x1000>;
- interrupts = <0 109 0>;
+ reg = <0xf9960000 0x1000>;
+ interrupts = <0 104 0>;
status = "disabled";
};
+ cpu-pmu {
+ compatible = "qcom,krait-pmu";
+ qcom,irq-is-percpu;
+ interrupts = <1 7 0xf00>;
+ };
+
+ qcom,msm-imem@fe805000 {
+ compatible = "qcom,msm-imem";
+ reg = <0xfe805000 0x1000>; /* Address and size of IMEM */
+ };
+
qcom,cache_erp {
compatible = "qcom,cache_erp";
interrupts = <1 9 0>, <0 2 0>;
@@ -65,22 +82,13 @@
qcom,memory-reservation-size = <0x600000>; /* 6M EBI1 buffer */
};
- rpm_bus: qcom,rpm-smd {
- compatible = "qcom,rpm-smd";
- rpm-channel-name = "rpm_requests";
- rpm-channel-type = <15>; /* SMD_APPS_RPM */
- rpm-standalone;
- };
+ qcom,ion {
+ compatible = "qcom,msm-ion";
+ #address-cells = <1>;
+ #size-cells = <0>;
- qcom,msm-imem@fe805000 {
- compatible = "qcom,msm-imem";
- reg = <0xfe805000 0x1000>; /* Address and size of IMEM */
+ qcom,ion-heap@30 { /* SYSTEM HEAP */
+ reg = <30>;
+ };
};
-
- qcom,msm-rtb {
- compatible = "qcom,msm-rtb";
- qcom,memory-reservation-type = "EBI1";
- qcom,memory-reservation-size = <0x100000>; /* 1M EBI1 buffer */
- };
-
};
diff --git a/arch/arm/boot/dts/mpq8092-iommu.dtsi b/arch/arm/boot/dts/mpq8092-iommu.dtsi
index c6693e1..56369dc 100644
--- a/arch/arm/boot/dts/mpq8092-iommu.dtsi
+++ b/arch/arm/boot/dts/mpq8092-iommu.dtsi
@@ -14,20 +14,27 @@
&jpeg_iommu {
status = "ok";
+ vdd-supply = <&gdsc_jpeg>;
};
&mdp_iommu {
status = "ok";
+ vdd-supply = <&gdsc_mdss>;
};
&venus_iommu {
status = "ok";
+ vdd-supply = <&gdsc_venus>;
};
&kgsl_iommu {
status = "ok";
+ qcom,needs-alt-core-clk;
+ vdd-supply = <&gdsc_oxili_cx>;
+ qcom,alt-vdd-supply = <&gdsc_oxili_gx>;
};
&vfe_iommu {
status = "ok";
+ vdd-supply = <&gdsc_vfe>;
};
diff --git a/arch/arm/boot/dts/mpq8092-ion.dtsi b/arch/arm/boot/dts/mpq8092-ion.dtsi
index 2cd2f7b..f9f5985 100644
--- a/arch/arm/boot/dts/mpq8092-ion.dtsi
+++ b/arch/arm/boot/dts/mpq8092-ion.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,ion {
compatible = "qcom,msm-ion";
#address-cells = <1>;
@@ -20,58 +20,14 @@
reg = <30>;
};
- qcom,ion-heap@8 { /* CP_MM HEAP */
- compatible = "qcom,msm-ion-reserve";
- reg = <8>;
- qcom,heap-align = <0x1000>;
- qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0x7800000>;
- };
-
- qcom,ion-heap@29 { /* FIRMWARE HEAP */
- compatible = "qcom,msm-ion-reserve";
- reg = <29>;
- qcom,heap-align = <0x20000>;
- qcom,heap-adjacent = <8>;
- qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0xA00000>;
- };
-
- qcom,ion-heap@12 { /* MFC HEAP */
- compatible = "qcom,msm-ion-reserve";
- reg = <12>;
- qcom,heap-align = <0x1000>;
- qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0x2000>;
- };
-
- qcom,ion-heap@24 { /* SF HEAP */
- compatible = "qcom,msm-ion-reserve";
- reg = <24>;
- qcom,heap-align = <0x1000>;
- qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0x2800000>;
+ qcom,ion-heap@21 { /* SYSTEM CONTIG HEAP */
+ reg = <21>;
};
qcom,ion-heap@25 { /* IOMMU HEAP */
reg = <25>;
};
- qcom,ion-heap@27 { /* QSECOM HEAP */
- compatible = "qcom,msm-ion-reserve";
- reg = <27>;
- qcom,heap-align = <0x1000>;
- qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0x600000>;
- };
-
- qcom,ion-heap@28 { /* AUDIO HEAP */
- compatible = "qcom,msm-ion-reserve";
- reg = <28>;
- qcom,heap-align = <0x1000>;
- qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0x2B4000>;
- };
};
};
diff --git a/arch/arm/boot/dts/mpq8092-regulator.dtsi b/arch/arm/boot/dts/mpq8092-regulator.dtsi
index b724a3d..63896e9 100644
--- a/arch/arm/boot/dts/mpq8092-regulator.dtsi
+++ b/arch/arm/boot/dts/mpq8092-regulator.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -15,9 +15,17 @@
&spmi_bus {
- qcom,pm8644@1 {
+ qcom,pma8084@1 {
+ pma8084_s1: regulator@1400 {
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ qcom,system-load = <100000>;
+ status = "okay";
+ };
- pm8644_s3: regulator@1a00 {
+ pma8084_s3: regulator@1a00 {
regulator-min-microvolt = <1350000>;
regulator-max-microvolt = <1350000>;
qcom,enable-time = <500>;
@@ -27,7 +35,7 @@
status = "okay";
};
- pm8644_s4: regulator@1d00 {
+ pma8084_s4: regulator@1d00 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
qcom,enable-time = <500>;
@@ -37,7 +45,7 @@
status = "okay";
};
- pm8644_s5: regulator@2000 {
+ pma8084_s5: regulator@2000 {
regulator-min-microvolt = <2150000>;
regulator-max-microvolt = <2150000>;
qcom,enable-time = <500>;
@@ -45,7 +53,7 @@
status = "okay";
};
- pm8644_s6: regulator@2300 {
+ pma8084_s6: regulator@2300 {
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <900000>;
qcom,enable-time = <500>;
@@ -53,24 +61,26 @@
status = "okay";
};
- pm8644_s7: regulator@2600 {
- regulator-min-microvolt = <900000>;
- regulator-max-microvolt = <900000>;
+ pma8084_s8: regulator@2900 {
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1050000>;
qcom,enable-time = <500>;
qcom,pull-down-enable = <1>;
status = "okay";
};
- pm8644_s8: regulator@2900 {
+ pma8084_s12: regulator@3500 {
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <900000>;
qcom,enable-time = <500>;
qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ qcom,system-load = <100000>;
status = "okay";
};
- pm8644_l1: regulator@4000 {
- parent-supply = <&pm8644_s3>;
+ pma8084_l1: regulator@4000 {
+ parent-supply = <&pma8084_s3>;
regulator-min-microvolt = <1225000>;
regulator-max-microvolt = <1225000>;
qcom,enable-time = <200>;
@@ -80,17 +90,17 @@
status = "okay";
};
- pm8644_l2: regulator@4100 {
- parent-supply = <&pm8644_s3>;
- regulator-min-microvolt = <900000>;
- regulator-max-microvolt = <900000>;
+ pma8084_l2: regulator@4100 {
+ parent-supply = <&pma8084_s3>;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
qcom,enable-time = <200>;
qcom,pull-down-enable = <1>;
status = "okay";
};
- pm8644_l3: regulator@4200 {
- parent-supply = <&pm8644_s3>;
+ pma8084_l3: regulator@4200 {
+ parent-supply = <&pma8084_s3>;
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
qcom,enable-time = <200>;
@@ -98,50 +108,8 @@
status = "okay";
};
- pm8644_l4: regulator@4300 {
- parent-supply = <&pm8644_s3>;
- regulator-min-microvolt = <1000000>;
- regulator-max-microvolt = <1000000>;
- qcom,enable-time = <200>;
- qcom,pull-down-enable = <1>;
- status = "okay";
- };
-
- pm8644_l6: regulator@4500 {
- parent-supply = <&pm8644_s5>;
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
- qcom,enable-time = <200>;
- qcom,pull-down-enable = <1>;
- status = "okay";
- };
-
- pm8644_l8: regulator@4700 {
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
- qcom,enable-time = <200>;
- qcom,pull-down-enable = <1>;
- status = "okay";
- };
-
- pm8644_l9: regulator@4800 {
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <2950000>;
- qcom,enable-time = <200>;
- qcom,pull-down-enable = <1>;
- status = "okay";
- };
-
- pm8644_l10: regulator@4900 {
- regulator-min-microvolt = <2000000>;
- regulator-max-microvolt = <2000000>;
- qcom,enable-time = <200>;
- qcom,pull-down-enable = <1>;
- status = "okay";
- };
-
- pm8644_l11: regulator@4a00 {
- parent-supply = <&pm8644_s3>;
+ pma8084_l4: regulator@4300 {
+ parent-supply = <&pma8084_s3>;
regulator-min-microvolt = <1300000>;
regulator-max-microvolt = <1300000>;
qcom,enable-time = <200>;
@@ -149,8 +117,7 @@
status = "okay";
};
- pm8644_l12: regulator@4b00 {
- parent-supply = <&pm8644_s5>;
+ pma8084_l6: regulator@4500 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
qcom,enable-time = <200>;
@@ -158,7 +125,7 @@
status = "okay";
};
- pm8644_l13: regulator@4c00 {
+ pma8084_l9: regulator@4800 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2950000>;
qcom,enable-time = <200>;
@@ -166,17 +133,7 @@
status = "okay";
};
- pm8644_l14: regulator@4d00 {
- parent-supply = <&pm8644_s5>;
- regulator-min-microvolt = <1000000>;
- regulator-max-microvolt = <1000000>;
- qcom,enable-time = <200>;
- qcom,pull-down-enable = <1>;
- status = "okay";
- };
-
- pm8644_l15: regulator@4e00 {
- parent-supply = <&pm8644_s5>;
+ pma8084_l10: regulator@4900 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
qcom,enable-time = <200>;
@@ -184,59 +141,16 @@
status = "okay";
};
- pm8644_l16: regulator@4f00 {
- parent-supply = <&pm8644_s4>;
- regulator-min-microvolt = <750000>;
- regulator-max-microvolt = <750000>;
+ pma8084_l11: regulator@4a00 {
+ parent-supply = <&pma8084_s3>;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
qcom,enable-time = <200>;
qcom,pull-down-enable = <1>;
status = "okay";
};
- pm8644_l17: regulator@5000 {
- regulator-min-microvolt = <3150000>;
- regulator-max-microvolt = <3150000>;
- qcom,enable-time = <200>;
- qcom,pull-down-enable = <1>;
- regulator-always-on;
- qcom,system-load = <100000>;
- status = "okay";
- };
-
- pm8644_l18: regulator@5100 {
- regulator-min-microvolt = <2850000>;
- regulator-max-microvolt = <2850000>;
- qcom,enable-time = <200>;
- qcom,pull-down-enable = <1>;
- status = "okay";
- };
-
- pm8644_l19: regulator@5200 {
- parent-supply = <&pm8644_s4>;
- regulator-min-microvolt = <1500000>;
- regulator-max-microvolt = <1500000>;
- qcom,enable-time = <200>;
- qcom,pull-down-enable = <1>;
- status = "okay";
- };
-
- pm8644_l20: regulator@5300 {
- regulator-min-microvolt = <2950000>;
- regulator-max-microvolt = <2950000>;
- qcom,enable-time = <200>;
- qcom,pull-down-enable = <1>;
- status = "okay";
- };
-
- pm8644_l21: regulator@5400 {
- regulator-min-microvolt = <2950000>;
- regulator-max-microvolt = <2950000>;
- qcom,enable-time = <200>;
- qcom,pull-down-enable = <1>;
- status = "okay";
- };
-
- pm8644_l22: regulator@5500 {
+ pma8084_l12: regulator@4b00 {
regulator-min-microvolt = <2500000>;
regulator-max-microvolt = <2500000>;
qcom,enable-time = <200>;
@@ -244,7 +158,94 @@
status = "okay";
};
- pm8644_l23: regulator@5600 {
+ pma8084_l13: regulator@4c00 {
+ regulator-min-microvolt = <2950000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ status = "okay";
+ };
+
+ pma8084_l14: regulator@4d00 {
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l15: regulator@4e00 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l16: regulator@4f00 {
+ parent-supply = <&pma8084_s5>;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <750000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l17: regulator@5000 {
+ regulator-min-microvolt = <3150000>;
+ regulator-max-microvolt = <3150000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ qcom,system-load = <100000>;
+ status = "okay";
+ };
+
+ pma8084_l18: regulator@5100 {
+ regulator-min-microvolt = <2850000>;
+ regulator-max-microvolt = <2850000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l19: regulator@5200 {
+ parent-supply = <&pma8084_s5>;
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <1500000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_l20: regulator@5300 {
+ regulator-min-microvolt = <2950000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ status = "okay";
+ };
+
+ pma8084_l21: regulator@5400 {
+ regulator-min-microvolt = <2950000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ status = "okay";
+ };
+
+ pma8084_l22: regulator@5500 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ status = "okay";
+ };
+
+ pma8084_l23: regulator@5600 {
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3000000>;
qcom,enable-time = <200>;
@@ -252,35 +253,68 @@
status = "okay";
};
- pm8644_l24: regulator@5700 {
+ pma8084_l24: regulator@5700 {
regulator-min-microvolt = <3075000>;
regulator-max-microvolt = <3075000>;
qcom,enable-time = <200>;
qcom,pull-down-enable = <1>;
+ regulator-always-on;
status = "okay";
};
- pm8644_lvs1: regulator@8000 {
- parent-supply = <&pm8644_s4>;
+ pma8084_l25: regulator@5800 {
+ parent-supply = <&pma8084_s5>;
+ regulator-min-microvolt = <2000000>;
+ regulator-max-microvolt = <2000000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+ pma8084_l26: regulator@5900 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+ pma8084_l27: regulator@5A00 {
+ parent-supply = <&pma8084_s3>;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
qcom,enable-time = <200>;
qcom,pull-down-enable = <1>;
status = "okay";
};
- pm8644_lvs2: regulator@8100 {
- parent-supply = <&pm8644_s4>;
+ pma8084_lvs1: regulator@8000 {
+ parent-supply = <&pma8084_s4>;
qcom,enable-time = <200>;
qcom,pull-down-enable = <1>;
status = "okay";
};
- pm8644_mvs1: regulator@8200 {
+ pma8084_lvs2: regulator@8100 {
+ parent-supply = <&pma8084_s4>;
qcom,enable-time = <200>;
qcom,pull-down-enable = <1>;
status = "okay";
};
- pm8644_mvs2: regulator@8300 {
+ pma8084_lvs3: regulator@8200 {
+ parent-supply = <&pma8084_s4>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_lvs4: regulator@8300 {
+ parent-supply = <&pma8084_s4>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pma8084_mvs1: regulator@8400 {
qcom,enable-time = <200>;
qcom,pull-down-enable = <1>;
status = "okay";
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/mpq8092-rumi.dts
similarity index 72%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/mpq8092-rumi.dts
index e410344..1abaf55 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/mpq8092-rumi.dts
@@ -12,18 +12,17 @@
/dts-v1/;
-/include/ "msmzinc.dtsi"
+/include/ "mpq8092.dtsi"
+/include/ "mpq8092-rumi.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
+ model = "Qualcomm MPQ8092 RUMI";
+ compatible = "qcom,mpq8092-rumi", "qcom,mpq8092", "qcom,rumi";
+ qcom,msm-id = <146 16 0>;
+};
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
+&soc {
+ serial@f9922000 {
status = "ok";
};
};
diff --git a/arch/arm/boot/dts/mpq8092-rumi.dtsi b/arch/arm/boot/dts/mpq8092-rumi.dtsi
new file mode 100644
index 0000000..2016998
--- /dev/null
+++ b/arch/arm/boot/dts/mpq8092-rumi.dtsi
@@ -0,0 +1,134 @@
+/* Copyright (c) 2013, 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.
+ */
+
+&soc {
+ timer {
+ clock-frequency = <5000000>;
+ };
+
+ timer@f9020000 {
+ clock-frequency = <5000000>;
+ };
+
+ usb@f9a55000 {
+ status = "disable";
+ };
+
+ qcom,sdcc@f9824000 {
+ status = "disabled";
+ qcom,clk-rates = <400000 19200000>;
+ };
+
+ qcom,sdcc@f98a4000 {
+ status = "disabled";
+ qcom,clk-rates = <400000 19200000>;
+ };
+
+ qcom,sps@f998000 {
+ status = "disable";
+ };
+
+ spi@f9924000 {
+ status = "disable";
+ };
+
+ spi@f9923000 {
+ compatible = "qcom,spi-qup-v2";
+ reg = <0xf9923000 0x1000>;
+ interrupts = <0 95 0>;
+ spi-max-frequency = <24000000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ gpios = <&msmgpio 3 0>, /* CLK */
+ <&msmgpio 1 0>, /* MISO */
+ <&msmgpio 0 0>; /* MOSI */
+ cs-gpios = <&msmgpio 9 0>;
+
+ ethernet-switch@2 {
+ compatible = "simtec,ks8851";
+ reg = <2>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <90 0>;
+ spi-max-frequency = <5000000>;
+ };
+ };
+
+ i2c@f9966000 {
+ status = "disable";
+ };
+
+ i2c@f9967000 {
+ status = "disable";
+ cell-index = <0>;
+ compatible = "qcom,i2c-qup";
+ reg = <0Xf9967000 0x1000>;
+ reg-names = "qup_phys_addr";
+ interrupts = <0 105 0>;
+ interrupt-names = "qup_err_intr";
+ qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-src-freq = <19200000>;
+ gpios = <&msmgpio 83 0>,/* DAT */
+ <&msmgpio 84 0>;/* CLK */
+ };
+
+ slim@fe12f000 {
+ status = "disable";
+ };
+
+ qcom,mdss_dsi@fd922800 {
+ status = "disable";
+ };
+
+ qcom,spmi@fc4c0000 {
+ status = "disable";
+ };
+
+ qcom,ssusb@F9200000 {
+ status = "disable";
+ };
+
+ qcom,lpass@fe200000 {
+ status = "disable";
+ };
+
+ qcom,pronto@fb21b000 {
+ status = "disable";
+ };
+
+ qcom,mss@fc880000 {
+ status = "disable";
+ };
+
+ qcom,kgsl-3d0@fdb00000 {
+ status = "disabled";
+ };
+};
+
+&gdsc_venus {
+ status = "disabled";
+};
+
+&gdsc_jpeg {
+ status = "disabled";
+};
+
+&gdsc_oxili_gx {
+ status = "disabled";
+};
+
+&gdsc_oxili_cx {
+ status = "disabled";
+};
+
+&gdsc_usb_hsic {
+ status = "disabled";
+};
diff --git a/arch/arm/boot/dts/mpq8092-sim.dts b/arch/arm/boot/dts/mpq8092-sim.dts
index 0cbfa33..676ef3b 100644
--- a/arch/arm/boot/dts/mpq8092-sim.dts
+++ b/arch/arm/boot/dts/mpq8092-sim.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -16,9 +16,11 @@
/ {
model = "Qualcomm MPQ8092 Simulator";
- compatible = "qcom,mpq8092-sim", "qcom,mpq8092";
+ compatible = "qcom,mpq8092-sim", "qcom,mpq8092", "qcom,sim";
qcom,msm-id = <126 16 0>;
+};
+&soc {
serial@f991f000 {
status = "ok";
};
@@ -28,7 +30,7 @@
};
};
-&pm8644_gpios {
+&pma8084_gpios {
gpio@c000 { /* GPIO 1 */
};
@@ -94,72 +96,9 @@
gpio@d500 { /* GPIO 22 */
};
-
- gpio@d600 { /* GPIO 23 */
- };
-
- gpio@d700 { /* GPIO 24 */
- };
-
- gpio@d800 { /* GPIO 25 */
- };
-
- gpio@d900 { /* GPIO 26 */
- };
-
- gpio@da00 { /* GPIO 27 */
- };
-
- gpio@db00 { /* GPIO 28 */
- };
-
- gpio@dc00 { /* GPIO 29 */
- };
-
- gpio@dd00 { /* GPIO 30 */
- };
-
- gpio@de00 { /* GPIO 31 */
- };
-
- gpio@df00 { /* GPIO 32 */
- };
-
- gpio@e000 { /* GPIO 33 */
- };
-
- gpio@e100 { /* GPIO 34 */
- };
-
- gpio@e200 { /* GPIO 35 */
- };
-
- gpio@e300 { /* GPIO 36 */
- };
-
- gpio@e400 { /* GPIO 37 */
- };
-
- gpio@e500 { /* GPIO 38 */
- };
-
- gpio@e600 { /* GPIO 39 */
- };
-
- gpio@e700 { /* GPIO 40 */
- };
-
- gpio@e800 { /* GPIO 41 */
- };
-
- gpio@e900 { /* GPIO 42 */
- };
-
- gpio@ea00 { /* GPIO 43 */
- };
};
-&pm8644_mpps {
+&pma8084_mpps {
mpp@a000 { /* MPP 1 */
};
@@ -177,4 +116,10 @@
mpp@a500 { /* MPP 6 */
};
+
+ mpp@a600 { /* MPP 7 */
+ };
+
+ mpp@a700 { /* MPP 8 */
+ };
};
diff --git a/arch/arm/boot/dts/mpq8092.dtsi b/arch/arm/boot/dts/mpq8092.dtsi
index 75f168d..e8674a0 100644
--- a/arch/arm/boot/dts/mpq8092.dtsi
+++ b/arch/arm/boot/dts/mpq8092.dtsi
@@ -11,15 +11,24 @@
*/
/include/ "skeleton.dtsi"
-/include/ "mpq8092-iommu.dtsi"
-/include/ "msm-gdsc.dtsi"
-/include/ "mpq8092-ion.dtsi"
/ {
model = "Qualcomm MPQ8092";
compatible = "qcom,mpq8092";
interrupt-parent = <&intc>;
+ soc: soc { };
+};
+
+/include/ "mpq8092-iommu.dtsi"
+/include/ "msm-gdsc.dtsi"
+/include/ "mpq8092-ion.dtsi"
+
+&soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
intc: interrupt-controller@f9000000 {
compatible = "qcom,msm-qgic2";
interrupt-controller;
@@ -46,6 +55,65 @@
clock-frequency = <19200000>;
};
+ timer@f9020000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ compatible = "arm,armv7-timer-mem";
+ reg = <0xf9020000 0x1000>;
+ clock-frequency = <19200000>;
+
+ frame@f9021000 {
+ frame-number = <0>;
+ interrupts = <0 8 0x4>,
+ <0 7 0x4>;
+ reg = <0xf9021000 0x1000>,
+ <0xf9022000 0x1000>;
+ };
+
+ frame@f9023000 {
+ frame-number = <1>;
+ interrupts = <0 9 0x4>;
+ reg = <0xf9023000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@f9024000 {
+ frame-number = <2>;
+ interrupts = <0 10 0x4>;
+ reg = <0xf9024000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@f9025000 {
+ frame-number = <3>;
+ interrupts = <0 11 0x4>;
+ reg = <0xf9025000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@f9026000 {
+ frame-number = <4>;
+ interrupts = <0 12 0x4>;
+ reg = <0xf9026000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@f9027000 {
+ frame-number = <5>;
+ interrupts = <0 13 0x4>;
+ reg = <0xf9027000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@f9028000 {
+ frame-number = <6>;
+ interrupts = <0 14 0x4>;
+ reg = <0xf9028000 0x1000>;
+ status = "disabled";
+ };
+ };
+
serial@f991f000 {
compatible = "qcom,msm-lsuart-v14";
reg = <0xf991f000 0x1000>;
@@ -53,6 +121,13 @@
status = "disabled";
};
+ serial@f9922000 {
+ compatible = "qcom,msm-lsuart-v14";
+ reg = <0xf9922000 0x1000>;
+ interrupts = <0 112 0>;
+ status = "disabled";
+ };
+
serial@f995e000 {
compatible = "qcom,msm-lsuart-v14";
reg = <0xf995e000 0x1000>;
@@ -60,6 +135,11 @@
status = "disabled";
};
+ qcom,msm-imem@fe805000 {
+ compatible = "qcom,msm-imem";
+ reg = <0xfe805000 0x1000>; /* Address and size of IMEM */
+ };
+
spmi_bus: qcom,spmi@fc4c0000 {
cell-index = <0>;
compatible = "qcom,spmi-pmic-arb";
@@ -85,7 +165,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
@@ -105,7 +185,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
@@ -121,6 +201,15 @@
reg = <0xfc580000 0x17c>;
interrupts = <0 243 0>;
};
+
+ qcom,wdt@f9017000 {
+ compatible = "qcom,msm-watchdog";
+ reg = <0xf9017000 0x1000>;
+ interrupts = <0 3 0>, <0 4 0>;
+ qcom,bark-time = <11000>;
+ qcom,pet-time = <10000>;
+ qcom,ipi-ping;
+ };
};
&gdsc_venus {
@@ -135,10 +224,6 @@
status = "ok";
};
-&gdsc_vfe {
- status = "ok";
-};
-
&gdsc_oxili_gx {
status = "ok";
};
@@ -151,5 +236,5 @@
status = "ok";
};
-/include/ "msm-pm8644.dtsi"
+/include/ "msm-pma8084.dtsi"
/include/ "mpq8092-regulator.dtsi"
diff --git a/arch/arm/boot/dts/msm-gdsc.dtsi b/arch/arm/boot/dts/msm-gdsc.dtsi
index cfd68fa..78234e8 100644
--- a/arch/arm/boot/dts/msm-gdsc.dtsi
+++ b/arch/arm/boot/dts/msm-gdsc.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, 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
@@ -11,9 +11,7 @@
* GNU General Public License for more details.
*/
-/include/ "skeleton.dtsi"
-
-/ {
+&soc {
gdsc_venus: qcom,gdsc@fd8c1024 {
compatible = "qcom,gdsc";
regulator-name = "gdsc_venus";
diff --git a/arch/arm/boot/dts/msm-iommu-v0.dtsi b/arch/arm/boot/dts/msm-iommu-v0.dtsi
index 0c44fb5..2cfc5cf 100644
--- a/arch/arm/boot/dts/msm-iommu-v0.dtsi
+++ b/arch/arm/boot/dts/msm-iommu-v0.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
lpass_iommu: qcom,iommu@fd000000 {
compatible = "qcom,msm-smmu-v0";
#address-cells = <1>;
@@ -27,9 +27,17 @@
0x10
0x12
0x80>;
+ qcom,msm-bus,name = "lpass_ebi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <11 512 0 0>,
+ <11 512 0 1000>;
status = "disabled";
lpass_q6_fw: qcom,iommu-ctx@fd000000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd000000 0x1000>;
interrupts = <0 250 0>;
qcom,iommu-ctx-mids = <0 15>;
@@ -37,6 +45,7 @@
};
lpass_audio_shared: qcom,iommu-ctx@fd001000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd001000 0x1000>;
interrupts = <0 250 0>;
qcom,iommu-ctx-mids = <1>;
@@ -44,6 +53,7 @@
};
lpass_video_shared: qcom,iommu-ctx@fd002000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd002000 0x1000>;
interrupts = <0 250 0>;
qcom,iommu-ctx-mids = <2>;
@@ -51,6 +61,7 @@
};
lpass_q6_spare: qcom,iommu-ctx@fd003000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd003000 0x1000>;
interrupts = <0 250 0>;
qcom,iommu-ctx-mids = <3 4 5 6 7 8 9 10 11 12 13 14>;
@@ -74,9 +85,18 @@
0x10
0x12
0x80>;
+ qcom,msm-bus,name = "copss_ebi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <88 512 0 0>,
+ <88 512 0 1000>;
+
status = "disabled";
qcom,iommu-ctx@fd010000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd010000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <0>;
@@ -84,6 +104,7 @@
};
qcom,iommu-ctx@fd011000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd011000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <1>;
@@ -91,6 +112,7 @@
};
qcom,iommu-ctx@fd012000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd012000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <2>;
@@ -98,6 +120,7 @@
};
qcom,iommu-ctx@fd013000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd013000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <3>;
@@ -105,6 +128,7 @@
};
qcom,iommu-ctx@fd014000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd014000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <4>;
@@ -112,6 +136,7 @@
};
qcom,iommu-ctx@fd015000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd015000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <5>;
@@ -119,6 +144,7 @@
};
qcom,iommu-ctx@fd016000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd016000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <6>;
@@ -126,6 +152,7 @@
};
qcom,iommu-ctx@fd017000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd017000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <7>;
@@ -149,9 +176,17 @@
0x10
0x12
0x80>;
+ qcom,msm-bus,name = "mdpe_ebi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <92 512 0 0>,
+ <92 512 0 1000>;
status = "disabled";
qcom,iommu-ctx@fd860000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd860000 0x1000>;
interrupts = <0 247 0>;
qcom,iommu-ctx-mids = <0 1 3>;
@@ -159,6 +194,7 @@
};
qcom,iommu-ctx@fd861000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd861000 0x1000>;
interrupts = <0 247 0>;
qcom,iommu-ctx-mids = <2>;
@@ -182,9 +218,17 @@
0x10
0x12
0x80>;
+ qcom,msm-bus,name = "mdps_ebi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>,
+ <22 512 0 1000>;
status = "disabled";
qcom,iommu-ctx@fd870000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd870000 0x1000>;
interrupts = <0 47 0>;
qcom,iommu-ctx-mids = <0>;
@@ -192,6 +236,7 @@
};
qcom,iommu-ctx@fd871000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd871000 0x1000>;
interrupts = <0 47 0>;
qcom,iommu-ctx-mids = <1>;
@@ -207,6 +252,7 @@
reg = <0xfd880000 0x10000>;
interrupts = <0 38 0>;
qcom,glb-offset = <0xF000>;
+ qcom,needs-alt-core-clk;
label = "gfx_iommu";
qcom,iommu-pmu-ngroups = <1>;
qcom,iommu-pmu-ncounters = <4>;
@@ -215,9 +261,17 @@
0x10
0x12
0x80>;
+ qcom,msm-bus,name = "gfx_ebi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <26 512 0 0>,
+ <26 512 0 1000>;
status = "disabled";
qcom,iommu-ctx@fd880000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd880000 0x1000>;
interrupts = <0 241 0>;
qcom,iommu-ctx-mids = <0 1 2 3 4 5 6 7 8 9 10 11 12 13
@@ -226,6 +280,7 @@
};
qcom,iommu-ctx@fd881000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd881000 0x1000>;
interrupts = <0 241 0>;
qcom,iommu-ctx-mids = <16 17 18 19 20 21 22 23 24 25
@@ -234,6 +289,7 @@
};
qcom,iommu-ctx@fd882000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd882000 0x1000>;
interrupts = <0 241 0>;
qcom,iommu-ctx-mids = <>;
@@ -257,20 +313,21 @@
0x10
0x12
0x80>;
+ qcom,msm-bus,name = "vfe_ebi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <29 512 0 0>,
+ <29 512 0 1000>;
status = "disabled";
qcom,iommu-ctx@fd890000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd890000 0x1000>;
interrupts = <0 65 0>;
- qcom,iommu-ctx-mids = <0>;
+ qcom,iommu-ctx-mids = <0 1 2 3 4 5 6 7 8 9>;
label = "vfe0";
};
-
- qcom,iommu-ctx@fd891000 {
- reg = <0xfd891000 0x1000>;
- interrupts = <0 65 0>;
- qcom,iommu-ctx-mids = <1>;
- label = "vfe1";
- };
};
};
diff --git a/arch/arm/boot/dts/msm-iommu-v1.dtsi b/arch/arm/boot/dts/msm-iommu-v1.dtsi
index 71dcc6a..4492077 100644
--- a/arch/arm/boot/dts/msm-iommu-v1.dtsi
+++ b/arch/arm/boot/dts/msm-iommu-v1.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
jpeg_iommu: qcom,iommu@fda64000 {
compatible = "qcom,msm-smmu-v1";
#address-cells = <1>;
@@ -19,10 +19,16 @@
reg = <0xfda64000 0x10000>;
reg-names = "iommu_base";
interrupts = <0 67 0>;
- vdd-supply = <&gdsc_jpeg>;
qcom,needs-alt-core-clk;
label = "jpeg_iommu";
status = "disabled";
+ qcom,msm-bus,name = "jpeg_ebi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <62 512 0 0>,
+ <62 512 0 1000>;
qcom,iommu-pmu-ngroups = <1>;
qcom,iommu-pmu-ncounters = <8>;
@@ -77,6 +83,7 @@
0x0>;
qcom,iommu-ctx@fda6c000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda6c000 0x1000>;
interrupts = <0 70 0>;
qcom,iommu-ctx-sids = <0>;
@@ -84,6 +91,7 @@
};
qcom,iommu-ctx@fda6d000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda6d000 0x1000>;
interrupts = <0 70 0>;
qcom,iommu-ctx-sids = <1>;
@@ -91,6 +99,7 @@
};
qcom,iommu-ctx@fda6e000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda6e000 0x1000>;
interrupts = <0 70 0>;
qcom,iommu-ctx-sids = <2>;
@@ -106,9 +115,15 @@
reg = <0xfd928000 0x10000>;
reg-names = "iommu_base";
interrupts = <0 73 0>;
- vdd-supply = <&gdsc_mdss>;
qcom,iommu-secure-id = <1>;
label = "mdp_iommu";
+ qcom,msm-bus,name = "mdp_ebi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>,
+ <22 512 0 1000>;
status = "disabled";
qcom,iommu-pmu-ngroups = <1>;
@@ -170,6 +185,7 @@
0x0>;
qcom,iommu-ctx@fd930000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfd930000 0x1000>;
interrupts = <0 47 0>;
qcom,iommu-ctx-sids = <0>;
@@ -177,16 +193,18 @@
};
qcom,iommu-ctx@fd931000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfd931000 0x1000>;
- interrupts = <0 47 0>;
+ interrupts = <0 47 0>, <0 46 0>;
qcom,iommu-ctx-sids = <1>;
label = "mdp_1";
qcom,secure-context;
};
qcom,iommu-ctx@fd932000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfd932000 0x1000>;
- interrupts = <0 47 0>;
+ interrupts = <0 47 0>, <0 46 0>;
qcom,iommu-ctx-sids = <>;
label = "mdp_2";
qcom,secure-context;
@@ -202,10 +220,16 @@
0xfdce0004 0x4>;
reg-names = "iommu_base", "clk_base";
interrupts = <0 45 0>;
- vdd-supply = <&gdsc_venus>;
qcom,iommu-secure-id = <0>;
qcom,needs-alt-core-clk;
label = "venus_iommu";
+ qcom,msm-bus,name = "venus_ebi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <63 512 0 0>,
+ <63 512 0 1000>;
status = "disabled";
qcom,iommu-pmu-ngroups = <1>;
@@ -279,6 +303,7 @@
0x0>;
venus_ns: qcom,iommu-ctx@fdc8c000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfdc8c000 0x1000>;
interrupts = <0 42 0>;
qcom,iommu-ctx-sids = <0 1 2 3 4 5>;
@@ -286,16 +311,18 @@
};
venus_cp: qcom,iommu-ctx@fdc8d000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfdc8d000 0x1000>;
- interrupts = <0 42 0>;
+ interrupts = <0 42 0>, <0 43 0>;
qcom,iommu-ctx-sids = <0x80 0x81 0x82 0x83 0x84 0x85>;
label = "venus_cp";
qcom,secure-context;
};
venus_fw: qcom,iommu-ctx@fdc8e000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfdc8e000 0x1000>;
- interrupts = <0 42 0>;
+ interrupts = <0 42 0>, <0 43 0>;
qcom,iommu-ctx-sids = <0xc0 0xc6>;
label = "venus_fw";
qcom,secure-context;
@@ -310,10 +337,14 @@
reg = <0xfdb10000 0x10000>;
reg-names = "iommu_base";
interrupts = <0 38 0>;
- vdd-supply = <&gdsc_oxili_cx>;
- qcom,alt-vdd-supply = <&gdsc_oxili_gx>;
- qcom,needs-alt-core-clk;
label = "kgsl_iommu";
+ qcom,msm-bus,name = "kgsl_ebi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <26 512 0 0>,
+ <26 512 0 1000>;
status = "disabled";
qcom,iommu-pmu-ngroups = <1>;
@@ -363,6 +394,7 @@
0x0>;
qcom,iommu-ctx@fdb18000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfdb18000 0x1000>;
interrupts = <0 241 0>;
qcom,iommu-ctx-sids = <0>;
@@ -370,6 +402,7 @@
};
qcom,iommu-ctx@fdb19000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfdb19000 0x1000>;
interrupts = <0 241 0>;
qcom,iommu-ctx-sids = <1>;
@@ -385,9 +418,15 @@
reg = <0xfda44000 0x10000>;
reg-names = "iommu_base";
interrupts = <0 62 0>;
- vdd-supply = <&gdsc_vfe>;
qcom,needs-alt-core-clk;
label = "vfe_iommu";
+ qcom,msm-bus,name = "vfe_ebi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <29 512 0 0>,
+ <29 512 0 1000>;
status = "disabled";
qcom,iommu-pmu-ngroups = <1>;
@@ -449,6 +488,7 @@
0x0>;
qcom,iommu-ctx@fda4c000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda4c000 0x1000>;
interrupts = <0 65 0>;
qcom,iommu-ctx-sids = <0>;
@@ -456,6 +496,7 @@
};
qcom,iommu-ctx@fda4d000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda4d000 0x1000>;
interrupts = <0 65 0>;
qcom,iommu-ctx-sids = <1>;
@@ -463,6 +504,7 @@
};
qcom,iommu-ctx@fda4e000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda4e000 0x1000>;
interrupts = <0 65 0>;
qcom,iommu-ctx-sids = <2>;
diff --git a/arch/arm/boot/dts/msm-pm8019.dtsi b/arch/arm/boot/dts/msm-pm8019.dtsi
index 689bf0f..fad9d86 100755
--- a/arch/arm/boot/dts/msm-pm8019.dtsi
+++ b/arch/arm/boot/dts/msm-pm8019.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -196,6 +196,21 @@
qcom,fast-avg-setup = <0>;
};
};
+
+ pm8019_adc_tm: vadc@3400 {
+ compatible = "qcom,qpnp-adc-tm";
+ reg = <0x3400 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x34 0x0>,
+ <0x0 0x34 0x3>,
+ <0x0 0x34 0x4>;
+ interrupt-names = "eoc-int-en-set",
+ "high-thr-en-set",
+ "low-thr-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1800>;
+ };
};
qcom,pm8019@1 {
diff --git a/arch/arm/boot/dts/msm-pm8110.dtsi b/arch/arm/boot/dts/msm-pm8110.dtsi
index fe4cada..0e446e2 100644
--- a/arch/arm/boot/dts/msm-pm8110.dtsi
+++ b/arch/arm/boot/dts/msm-pm8110.dtsi
@@ -22,6 +22,34 @@
#address-cells = <1>;
#size-cells = <1>;
+ qcom,revid@100 {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100 0x100>;
+ };
+
+ qcom,power-on@800 {
+ compatible = "qcom,qpnp-power-on";
+ reg = <0x800 0x100>;
+ interrupts = <0x0 0x8 0x0>,
+ <0x0 0x8 0x1>,
+ <0x0 0x8 0x4>;
+ interrupt-names = "kpdpwr", "resin", "resin-bark";
+ qcom,pon-dbc-delay = <15625>;
+ qcom,system-reset;
+
+ qcom,pon_1 {
+ qcom,pon-type = <0>;
+ qcom,pull-up = <1>;
+ linux,code = <116>;
+ };
+
+ qcom,pon_2 {
+ qcom,pon-type = <1>;
+ qcom,pull-up = <1>;
+ linux,code = <114>;
+ };
+ };
+
pm8110_chg: qcom,charger {
spmi-dev-container;
compatible = "qcom,qpnp-charger";
@@ -29,16 +57,18 @@
#size-cells = <1>;
status = "disabled";
- qcom,chg-vddmax-mv = <4200>;
- qcom,chg-vddsafe-mv = <4200>;
- qcom,chg-vinmin-mv = <4200>;
- qcom,chg-vbatdet-mv = <4100>;
- qcom,chg-ibatmax-ma = <1500>;
- qcom,chg-ibatterm-ma = <200>;
- qcom,chg-ibatsafe-ma = <1500>;
- qcom,chg-thermal-mitigation = <1500 700 600 325>;
+ qcom,vddmax-mv = <4200>;
+ qcom,vddsafe-mv = <4230>;
+ qcom,vinmin-mv = <4200>;
+ qcom,vbatdet-mv = <4100>;
+ qcom,ibatmax-ma = <1500>;
+ qcom,ibatterm-ma = <200>;
+ qcom,ibatsafe-ma = <1500>;
+ qcom,thermal-mitigation = <1500 700 600 325>;
+ qcom,vbatdet-delta-mv = <350>;
+ qcom,tchg-mins = <150>;
- qcom,chg-chgr@1000 {
+ qcom,chgr@1000 {
status = "disabled";
reg = <0x1000 0x100>;
interrupts = <0x0 0x10 0x0>,
@@ -60,7 +90,7 @@
"chg-done";
};
- qcom,chg-buck@1100 {
+ qcom,buck@1100 {
status = "disabled";
reg = <0x1100 0x100>;
interrupts = <0x0 0x11 0x0>,
@@ -80,7 +110,7 @@
"vdd-loop";
};
- qcom,chg-bat-if@1200 {
+ qcom,bat-if@1200 {
status = "disabled";
reg = <0x1200 0x100>;
interrupts = <0x0 0x12 0x0>,
@@ -96,7 +126,7 @@
"psi";
};
- qcom,chg-usb-chgpth@1300 {
+ qcom,usb-chgpth@1300 {
status = "disabled";
reg = <0x1300 0x100>;
interrupts = <0 0x13 0x0>,
@@ -114,6 +144,66 @@
};
};
+ pm8110_gpios: gpios {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pm8110-gpio";
+
+ gpio@c000 {
+ reg = <0xc000 0x100>;
+ qcom,pin-num = <1>;
+ };
+
+ gpio@c100 {
+ reg = <0xc100 0x100>;
+ qcom,pin-num = <2>;
+ };
+
+ gpio@c200 {
+ reg = <0xc200 0x100>;
+ qcom,pin-num = <3>;
+ };
+
+ gpio@c300 {
+ reg = <0xc300 0x100>;
+ qcom,pin-num = <4>;
+ };
+ };
+
+ pm8110_mpps: mpps {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pm8110-mpp";
+
+ mpp@a000 {
+ reg = <0xa000 0x100>;
+ qcom,pin-num = <1>;
+ };
+
+ mpp@a100 {
+ reg = <0xa100 0x100>;
+ qcom,pin-num = <2>;
+ };
+
+ mpp@a200 {
+ reg = <0xa200 0x100>;
+ qcom,pin-num = <3>;
+ };
+
+ mpp@a300 {
+ reg = <0xa300 0x100>;
+ qcom,pin-num = <4>;
+ };
+ };
+
pm8110_vadc: vadc@3100 {
compatible = "qcom,qpnp-vadc";
reg = <0x3100 0x100>;
@@ -180,6 +270,82 @@
};
};
+ pm8110_adc_tm: vadc@3400 {
+ compatible = "qcom,qpnp-adc-tm";
+ reg = <0x3400 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x34 0x0>,
+ <0x0 0x34 0x3>,
+ <0x0 0x34 0x4>;
+ interrupt-names = "eoc-int-en-set",
+ "high-thr-en-set",
+ "low-thr-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1800>;
+ };
+
+ qcom,temp-alarm@2400 {
+ compatible = "qcom,qpnp-temp-alarm";
+ reg = <0x2400 0x100>;
+ interrupts = <0x0 0x24 0x0>;
+ label = "pm8110_tz";
+ qcom,channel-num = <8>;
+ qcom,threshold-set = <0>;
+ };
+
+ pm8110_bms: qcom,bms {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-bms";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ status = "disabled";
+
+ qcom,r-sense-uohm = <10000>;
+ qcom,v-cutoff-uv = <3400000>;
+ qcom,max-voltage-uv = <4200000>;
+ qcom,r-conn-mohm = <0>;
+ qcom,shutdown-soc-valid-limit = <20>;
+ qcom,adjust-soc-low-threshold = <15>;
+ qcom,ocv-voltage-high-threshold-uv = <3750000>;
+ qcom,ocv-voltage-low-threshold-uv = <3650000>;
+ qcom,low-soc-calculate-soc-threshold = <15>;
+ qcom,low-soc-calculate-soc-ms = <5000>;
+ qcom,calculate-soc-ms = <20000>;
+ qcom,chg-term-ua = <100000>;
+ qcom,batt-type = <0>;
+ qcom,low-voltage-threshold = <3420000>;
+ qcom,tm-temp-margin = <5000>;
+ qcom,low-ocv-correction-limit-uv = <100>;
+ qcom,high-ocv-correction-limit-uv = <50>;
+ qcom,hold-soc-est = <3>;
+
+ qcom,bms-iadc@3800 {
+ reg = <0x3800 0x100>;
+ };
+
+ qcom,bms-bms@4000 {
+ reg = <0x4000 0x100>;
+ interrupts = <0x0 0x40 0x0>,
+ <0x0 0x40 0x1>,
+ <0x0 0x40 0x2>,
+ <0x0 0x40 0x3>,
+ <0x0 0x40 0x4>,
+ <0x0 0x40 0x5>,
+ <0x0 0x40 0x6>,
+ <0x0 0x40 0x7>;
+
+ interrupt-names = "vsense_for_r",
+ "vsense_avg",
+ "sw_cc_thr",
+ "ocv_thr",
+ "charge_begin",
+ "good_ocv",
+ "ocv_for_r",
+ "cc_thr";
+ };
+ };
+
qcom,pm8110_rtc {
spmi-dev-container;
compatible = "qcom,qpnp-rtc";
@@ -198,6 +364,12 @@
};
};
+ qcom,leds@a100 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xa100 0x100>;
+ label = "mpp";
+ };
+
qcom,leds@a200 {
compatible = "qcom,leds-qpnp";
reg = <0xa200 0x100>;
@@ -430,5 +602,12 @@
reg = <0x5500 0x100>;
status = "disabled";
};
+
+ qcom,vibrator@c000 {
+ compatible = "qcom,qpnp-vibrator";
+ reg = <0xc000 0x100>;
+ label = "vibrator";
+ status = "disabled";
+ };
};
};
diff --git a/arch/arm/boot/dts/msm-pm8226.dtsi b/arch/arm/boot/dts/msm-pm8226.dtsi
index 99410b3..2008e1e 100644
--- a/arch/arm/boot/dts/msm-pm8226.dtsi
+++ b/arch/arm/boot/dts/msm-pm8226.dtsi
@@ -22,6 +22,11 @@
#address-cells = <1>;
#size-cells = <1>;
+ qcom,revid@100 {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100 0x100>;
+ };
+
qcom,power-on@800 {
compatible = "qcom,qpnp-power-on";
reg = <0x800 0x100>;
@@ -57,11 +62,11 @@
status = "disabled";
qcom,vddmax-mv = <4200>;
- qcom,vddsafe-mv = <4200>;
+ qcom,vddsafe-mv = <4230>;
qcom,vinmin-mv = <4200>;
qcom,vbatdet-delta-mv = <150>;
qcom,ibatmax-ma = <1500>;
- qcom,ibatterm-ma = <200>;
+ qcom,ibatterm-ma = <100>;
qcom,ibatsafe-ma = <1500>;
qcom,thermal-mitigation = <1500 700 600 325>;
qcom,tchg-mins = <150>;
@@ -125,7 +130,7 @@
};
- qcom,usb-chgpth@1300 {
+ pm8226_chg_otg: qcom,usb-chgpth@1300 {
status = "disabled";
reg = <0x1300 0x100>;
interrupts = <0 0x13 0x0>,
@@ -137,7 +142,7 @@
"chg-gone";
};
- qcom,boost@1500 {
+ pm8226_chg_boost: qcom,boost@1500 {
status = "disabled";
reg = <0x1500 0x100>;
interrupts = <0x0 0x15 0x0>,
@@ -173,6 +178,7 @@
qcom,calculate-soc-ms = <20000>;
qcom,chg-term-ua = <100000>;
qcom,batt-type = <0>;
+ qcom,tm-temp-margin = <5000>;
qcom,low-ocv-correction-limit-uv = <100>;
qcom,high-ocv-correction-limit-uv = <50>;
qcom,hold-soc-est = <3>;
@@ -204,6 +210,24 @@
};
};
+ qcom,leds@a100 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xa100 0x100>;
+ label = "mpp";
+ };
+
+ qcom,leds@a300 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xa300 0x100>;
+ label = "mpp";
+ };
+
+ qcom,leds@a500 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xa500 0x100>;
+ label = "mpp";
+ };
+
pm8226_gpios: gpios {
spmi-dev-container;
compatible = "qcom,qpnp-pin";
@@ -381,6 +405,30 @@
};
};
+ pm8226_adc_tm: vadc@3400 {
+ compatible = "qcom,qpnp-adc-tm";
+ reg = <0x3400 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x34 0x0>,
+ <0x0 0x34 0x3>,
+ <0x0 0x34 0x4>;
+ interrupt-names = "eoc-int-en-set",
+ "high-thr-en-set",
+ "low-thr-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1800>;
+ };
+
+ qcom,temp-alarm@2400 {
+ compatible = "qcom,qpnp-temp-alarm";
+ reg = <0x2400 0x100>;
+ interrupts = <0x0 0x24 0x0>;
+ label = "pm8226_tz";
+ qcom,channel-num = <8>;
+ qcom,threshold-set = <0>;
+ };
+
qcom,pm8226_rtc {
spmi-dev-container;
compatible = "qcom,qpnp-rtc";
@@ -686,6 +734,54 @@
label = "wled";
};
+ pwm@b100 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb100 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <0>;
+ };
+
+ pwm@b200 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb200 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <1>;
+ };
+
+ pwm@b300 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb300 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <2>;
+ };
+
+ pwm@b400 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb400 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <3>;
+ };
+
+ pwm@b500 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb500 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <4>;
+ };
+
+ pwm@b600 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb600 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <5>;
+ };
+
regulator@8000 {
regulator-name = "8226_lvs1";
reg = <0x8000 0x100>;
@@ -702,9 +798,10 @@
qcom,leds@d300 {
compatible = "qcom,leds-qpnp";
- status = "disable";
+ status = "okay";
reg = <0xd300 0x100>;
label = "flash";
+ flash_boost-supply = <&pm8226_chg_boost>;
pm8226_flash0: qcom,flash_0 {
qcom,max-current = <1000>;
qcom,default-state = "off";
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index 43b7d03..b4e557e 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -116,6 +116,7 @@
qcom,chg-term-ua = <100000>;
qcom,batt-type = <0>;
qcom,low-voltage-threshold = <3420000>;
+ qcom,tm-temp-margin = <5000>;
qcom,low-ocv-correction-limit-uv = <100>;
qcom,high-ocv-correction-limit-uv = <50>;
qcom,hold-soc-est = <3>;
@@ -172,9 +173,10 @@
status = "disabled";
qcom,vddmax-mv = <4200>;
- qcom,vddsafe-mv = <4200>;
- qcom,vinmin-mv = <4200>;
+ qcom,vddsafe-mv = <4230>;
+ qcom,vinmin-mv = <4300>;
qcom,ibatmax-ma = <1500>;
+ qcom,ibatterm-ma = <100>;
qcom,ibatsafe-ma = <1500>;
qcom,thermal-mitigation = <1500 700 600 325>;
qcom,cool-bat-decidegc = <100>;
@@ -183,7 +185,7 @@
qcom,warm-bat-decidegc = <450>;
qcom,warm-bat-mv = <4100>;
qcom,ibatmax-cool-ma = <350>;
- qcom,vbatdet-delta-mv = <350>;
+ qcom,vbatdet-delta-mv = <100>;
qcom,tchg-mins = <150>;
qcom,chgr@1000 {
@@ -245,7 +247,7 @@
};
- qcom,usb-chgpth@1300 {
+ pm8941_chg_otg: qcom,usb-chgpth@1300 {
status = "disabled";
reg = <0x1300 0x100>;
interrupts = <0 0x13 0x0>,
@@ -267,7 +269,7 @@
"dcin-valid";
};
- qcom,boost@1500 {
+ pm8941_chg_boost: qcom,boost@1500 {
status = "disabled";
reg = <0x1500 0x100>;
interrupts = <0x0 0x15 0x0>,
@@ -584,10 +586,10 @@
};
chan@3 {
- label = "spare1";
+ label = "spare1_div3";
reg = <3>;
qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <6>;
+ qcom,pre-div-channel-scaling = <1>;
qcom,calibration-type = "absolute";
qcom,scale-function = <0>;
qcom,hw-settle-time = <0>;
@@ -595,10 +597,10 @@
};
chan@4 {
- label = "spare2";
+ label = "usb_id_mv";
reg = <4>;
qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <6>;
+ qcom,pre-div-channel-scaling = <1>;
qcom,calibration-type = "absolute";
qcom,scale-function = <0>;
qcom,hw-settle-time = <0>;
@@ -849,7 +851,7 @@
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "absolute";
- qcom,scale-function = <1>;
+ qcom,scale-function = <3>;
qcom,hw-settle-time = <0>;
qcom,fast-avg-setup = <3>;
qcom,btm-channel-number = <0x70>;
@@ -872,7 +874,7 @@
reg = <0xb5>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "absolute";
+ qcom,calibration-type = "ratiometric";
qcom,scale-function = <2>;
qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <3>;
@@ -1228,6 +1230,7 @@
compatible = "qcom,leds-qpnp";
reg = <0xd300 0x100>;
label = "flash";
+ flash_boost-supply = <&pm8941_chg_boost>;
};
qcom,leds@d400 {
diff --git a/arch/arm/boot/dts/msm-pm8644.dtsi b/arch/arm/boot/dts/msm-pma8084.dtsi
similarity index 76%
rename from arch/arm/boot/dts/msm-pm8644.dtsi
rename to arch/arm/boot/dts/msm-pma8084.dtsi
index 17a6b0b..30525aa 100644
--- a/arch/arm/boot/dts/msm-pm8644.dtsi
+++ b/arch/arm/boot/dts/msm-pma8084.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -16,20 +16,20 @@
interrupt-controller;
#interrupt-cells = <3>;
- qcom,pm8644@0 {
+ qcom,pma8084@0 {
spmi-slave-container;
reg = <0x0>;
#address-cells = <1>;
#size-cells = <1>;
- pm8644_gpios: gpios {
+ pma8084_gpios: gpios {
spmi-dev-container;
compatible = "qcom,qpnp-pin";
gpio-controller;
#gpio-cells = <2>;
#address-cells = <1>;
#size-cells = <1>;
- label = "pm8644-gpio";
+ label = "pma8084-gpio";
gpio@c000 {
reg = <0xc000 0x100>;
@@ -140,121 +140,16 @@
reg = <0xd500 0x100>;
qcom,pin-num = <22>;
};
-
- gpio@d600 {
- reg = <0xd600 0x100>;
- qcom,pin-num = <23>;
- };
-
- gpio@d700 {
- reg = <0xd700 0x100>;
- qcom,pin-num = <24>;
- };
-
- gpio@d800 {
- reg = <0xd800 0x100>;
- qcom,pin-num = <25>;
- };
-
- gpio@d900 {
- reg = <0xd900 0x100>;
- qcom,pin-num = <26>;
- };
-
- gpio@da00 {
- reg = <0xda00 0x100>;
- qcom,pin-num = <27>;
- };
-
- gpio@db00 {
- reg = <0xdb00 0x100>;
- qcom,pin-num = <28>;
- };
-
- gpio@dc00 {
- reg = <0xdc00 0x100>;
- qcom,pin-num = <29>;
- };
-
- gpio@dd00 {
- reg = <0xdd00 0x100>;
- qcom,pin-num = <30>;
- };
-
- gpio@de00 {
- reg = <0xde00 0x100>;
- qcom,pin-num = <31>;
- };
-
- gpio@df00 {
- reg = <0xdf00 0x100>;
- qcom,pin-num = <32>;
- };
-
- gpio@e000 {
- reg = <0xe000 0x100>;
- qcom,pin-num = <33>;
- };
-
- gpio@e100 {
- reg = <0xe100 0x100>;
- qcom,pin-num = <34>;
- };
-
- gpio@e200 {
- reg = <0xe200 0x100>;
- qcom,pin-num = <35>;
- };
-
- gpio@e300 {
- reg = <0xe300 0x100>;
- qcom,pin-num = <36>;
- };
-
- gpio@e400 {
- reg = <0xe400 0x100>;
- qcom,pin-num = <37>;
- };
-
- gpio@e500 {
- reg = <0xe500 0x100>;
- qcom,pin-num = <38>;
- };
-
- gpio@e600 {
- reg = <0xe600 0x100>;
- qcom,pin-num = <39>;
- };
-
- gpio@e700 {
- reg = <0xe700 0x100>;
- qcom,pin-num = <40>;
- };
-
- gpio@e800 {
- reg = <0xe800 0x100>;
- qcom,pin-num = <41>;
- };
-
- gpio@e900 {
- reg = <0xe900 0x100>;
- qcom,pin-num = <42>;
- };
-
- gpio@ea00 {
- reg = <0xea00 0x100>;
- qcom,pin-num = <43>;
- };
};
- pm8644_mpps: mpps {
+ pma8084_mpps: mpps {
spmi-dev-container;
compatible = "qcom,qpnp-pin";
gpio-controller;
#gpio-cells = <2>;
#address-cells = <1>;
#size-cells = <1>;
- label = "pm8644-mpp";
+ label = "pma8084-mpp";
mpp@a000 {
reg = <0xa000 0x100>;
@@ -285,17 +180,27 @@
reg = <0xa500 0x100>;
qcom,pin-num = <6>;
};
+
+ mpp@a600 {
+ reg = <0xa600 0x100>;
+ qcom,pin-num = <7>;
+ };
+
+ mpp@a700 {
+ reg = <0xa700 0x100>;
+ qcom,pin-num = <8>;
+ };
};
};
- qcom,pm8644@1 {
+ qcom,pma8084@1 {
spmi-slave-container;
reg = <0x1>;
#address-cells = <1>;
#size-cells = <1>;
regulator@1400 {
- regulator-name = "8644_s1";
+ regulator-name = "8084_s1";
spmi-dev-container;
#address-cells = <1>;
#size-cells = <1>;
@@ -315,7 +220,7 @@
};
regulator@1700 {
- regulator-name = "8644_s2";
+ regulator-name = "8084_s2";
spmi-dev-container;
#address-cells = <1>;
#size-cells = <1>;
@@ -335,7 +240,7 @@
};
regulator@1a00 {
- regulator-name = "8644_s3";
+ regulator-name = "8084_s3";
spmi-dev-container;
#address-cells = <1>;
#size-cells = <1>;
@@ -355,7 +260,7 @@
};
regulator@1d00 {
- regulator-name = "8644_s4";
+ regulator-name = "8084_s4";
spmi-dev-container;
#address-cells = <1>;
#size-cells = <1>;
@@ -375,7 +280,7 @@
};
regulator@2000 {
- regulator-name = "8644_s5";
+ regulator-name = "8084_s5";
spmi-dev-container;
#address-cells = <1>;
#size-cells = <1>;
@@ -395,7 +300,7 @@
};
regulator@2300 {
- regulator-name = "8644_s6";
+ regulator-name = "8084_s6";
spmi-dev-container;
#address-cells = <1>;
#size-cells = <1>;
@@ -415,7 +320,7 @@
};
regulator@2600 {
- regulator-name = "8644_s7";
+ regulator-name = "8084_s7";
spmi-dev-container;
#address-cells = <1>;
#size-cells = <1>;
@@ -435,7 +340,7 @@
};
regulator@2900 {
- regulator-name = "8644_s8";
+ regulator-name = "8084_s8";
spmi-dev-container;
#address-cells = <1>;
#size-cells = <1>;
@@ -455,7 +360,7 @@
};
regulator@2c00 {
- regulator-name = "8644_s9";
+ regulator-name = "8084_s9";
spmi-dev-container;
#address-cells = <1>;
#size-cells = <1>;
@@ -475,7 +380,7 @@
};
regulator@2f00 {
- regulator-name = "8644_s10";
+ regulator-name = "8084_s10";
spmi-dev-container;
#address-cells = <1>;
#size-cells = <1>;
@@ -495,7 +400,7 @@
};
regulator@3200 {
- regulator-name = "8644_s11";
+ regulator-name = "8084_s11";
spmi-dev-container;
#address-cells = <1>;
#size-cells = <1>;
@@ -514,209 +419,248 @@
};
};
+ regulator@3500 {
+ regulator-name = "8084_s12";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x3500 0x300>;
+ status = "disabled";
+
+ qcom,ctl@3500 {
+ reg = <0x3500 0x100>;
+ };
+ qcom,ps@3600 {
+ reg = <0x3600 0x100>;
+ };
+ qcom,freq@3700 {
+ reg = <0x3700 0x100>;
+ };
+ };
+
regulator@4000 {
- regulator-name = "8644_l1";
+ regulator-name = "8084_l1";
reg = <0x4000 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@4100 {
- regulator-name = "8644_l2";
+ regulator-name = "8084_l2";
reg = <0x4100 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@4200 {
- regulator-name = "8644_l3";
+ regulator-name = "8084_l3";
reg = <0x4200 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@4300 {
- regulator-name = "8644_l4";
+ regulator-name = "8084_l4";
reg = <0x4300 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@4400 {
- regulator-name = "8644_l5";
+ regulator-name = "8084_l5";
reg = <0x4400 0x100>;
compatible = "qcom,qpnp-regulator";
- qcom,force-type = <0x04 0x10>;
status = "disabled";
};
regulator@4500 {
- regulator-name = "8644_l6";
+ regulator-name = "8084_l6";
reg = <0x4500 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@4600 {
- regulator-name = "8644_l7";
+ regulator-name = "8084_l7";
reg = <0x4600 0x100>;
compatible = "qcom,qpnp-regulator";
- qcom,force-type = <0x04 0x10>;
status = "disabled";
};
regulator@4700 {
- regulator-name = "8644_l8";
+ regulator-name = "8084_l8";
reg = <0x4700 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@4800 {
- regulator-name = "8644_l9";
+ regulator-name = "8084_l9";
reg = <0x4800 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@4900 {
- regulator-name = "8644_l10";
+ regulator-name = "8084_l10";
reg = <0x4900 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@4a00 {
- regulator-name = "8644_l11";
+ regulator-name = "8084_l11";
reg = <0x4a00 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@4b00 {
- regulator-name = "8644_l12";
+ regulator-name = "8084_l12";
reg = <0x4b00 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@4c00 {
- regulator-name = "8644_l13";
+ regulator-name = "8084_l13";
reg = <0x4c00 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@4d00 {
- regulator-name = "8644_l14";
+ regulator-name = "8084_l14";
reg = <0x4d00 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@4e00 {
- regulator-name = "8644_l15";
+ regulator-name = "8084_l15";
reg = <0x4e00 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@4f00 {
- regulator-name = "8644_l16";
+ regulator-name = "8084_l16";
reg = <0x4f00 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@5000 {
- regulator-name = "8644_l17";
+ regulator-name = "8084_l17";
reg = <0x5000 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@5100 {
- regulator-name = "8644_l18";
+ regulator-name = "8084_l18";
reg = <0x5100 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@5200 {
- regulator-name = "8644_l19";
+ regulator-name = "8084_l19";
reg = <0x5200 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@5300 {
- regulator-name = "8644_l20";
+ regulator-name = "8084_l20";
reg = <0x5300 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@5400 {
- regulator-name = "8644_l21";
+ regulator-name = "8084_l21";
reg = <0x5400 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@5500 {
- regulator-name = "8644_l22";
+ regulator-name = "8084_l22";
reg = <0x5500 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@5600 {
- regulator-name = "8644_l23";
+ regulator-name = "8084_l23";
reg = <0x5600 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@5700 {
- regulator-name = "8644_l24";
+ regulator-name = "8084_l24";
reg = <0x5700 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@5800 {
- regulator-name = "8644_l25";
+ regulator-name = "8084_l25";
reg = <0x5800 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
+ regulator@5900 {
+ regulator-name = "8084_l26";
+ reg = <0x5900 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5a00 {
+ regulator-name = "8084_l27";
+ reg = <0x5a00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
regulator@8000 {
- regulator-name = "8644_lvs1";
+ regulator-name = "8084_lvs1";
reg = <0x8000 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@8100 {
- regulator-name = "8644_lvs2";
+ regulator-name = "8084_lvs2";
reg = <0x8100 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@8200 {
- regulator-name = "8644_mvs1";
+ regulator-name = "8084_lvs3";
reg = <0x8200 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
regulator@8300 {
- regulator-name = "8644_mvs2";
+ regulator-name = "8084_lvs4";
reg = <0x8300 0x100>;
compatible = "qcom,qpnp-regulator";
status = "disabled";
};
+
+ regulator@8400 {
+ regulator-name = "8084_mvs1";
+ reg = <0x8400 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
};
};
diff --git a/arch/arm/boot/dts/msm8226-bus.dtsi b/arch/arm/boot/dts/msm8226-bus.dtsi
index 3c41e9e..d87aa3e 100644
--- a/arch/arm/boot/dts/msm8226-bus.dtsi
+++ b/arch/arm/boot/dts/msm8226-bus.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
msm-mmss-noc@fc478000 {
compatible = "msm-bus-fabric";
reg = <0xfc478000 0x00004000>;
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
index c47d48d..41d6b7e 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
@@ -11,7 +11,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
led_flash0: qcom,camera-led-flash {
cell-index = <0>;
@@ -73,7 +73,7 @@
reg = <0x6d>;
qcom,slave-id = <0x20 0x0 0x9724>;
qcom,csiphy-sd-index = <1>;
- qcom,csid-sd-index = <0>;
+ qcom,csid-sd-index = <1>;
qcom,mount-angle = <0>;
qcom,sensor-name = "ov9724";
cam_vdig-supply = <&pm8226_l5>;
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi
index 1f7ba89..53860ac 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi
@@ -11,7 +11,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
led_flash0: qcom,camera-led-flash {
cell-index = <0>;
@@ -73,8 +73,8 @@
reg = <0x6d>;
qcom,slave-id = <0x20 0x0 0x9724>;
qcom,csiphy-sd-index = <1>;
- qcom,csid-sd-index = <0>;
- qcom,mount-angle = <90>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <270>;
qcom,sensor-name = "ov9724";
cam_vdig-supply = <&pm8226_l5>;
cam_vana-supply = <&pm8226_l19>;
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi
index 5ea02b4..3935dbb 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi
@@ -11,7 +11,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
led_flash0: qcom,camera-led-flash {
cell-index = <0>;
@@ -73,8 +73,8 @@
reg = <0x6d>;
qcom,slave-id = <0x20 0x0 0x9724>;
qcom,csiphy-sd-index = <1>;
- qcom,csid-sd-index = <0>;
- qcom,mount-angle = <90>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <270>;
qcom,sensor-name = "ov9724";
cam_vdig-supply = <&pm8226_l5>;
cam_vana-supply = <&pm8226_l19>;
diff --git a/arch/arm/boot/dts/msm8226-camera.dtsi b/arch/arm/boot/dts/msm8226-camera.dtsi
index e94459e..ec0092d 100644
--- a/arch/arm/boot/dts/msm8226-camera.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera.dtsi
@@ -11,7 +11,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,msm-cam@fd8c0000 {
compatible = "qcom,msm-cam";
reg = <0xfd8c0000 0x10000>;
@@ -21,8 +21,9 @@
qcom,csiphy@fda0ac00 {
cell-index = <0>;
compatible = "qcom,csiphy";
- reg = <0xfda0ac00 0x200>;
- reg-names = "csiphy";
+ reg = <0xfda0ac00 0x200>,
+ <0xfda00030 0x4>;
+ reg-names = "csiphy", "csiphy_clk_mux";
interrupts = <0 78 0>;
interrupt-names = "csiphy";
};
@@ -30,8 +31,9 @@
qcom,csiphy@fda0b000 {
cell-index = <1>;
compatible = "qcom,csiphy";
- reg = <0xfda0b000 0x200>;
- reg-names = "csiphy";
+ reg = <0xfda0b000 0x200>,
+ <0xfda00038 0x4>;
+ reg-names = "csiphy", "csiphy_clk_mux";
interrupts = <0 79 0>;
interrupt-names = "csiphy";
};
@@ -61,8 +63,9 @@
qcom,ispif@fda0a000 {
cell-index = <0>;
compatible = "qcom,ispif";
- reg = <0xfda0a000 0x500>;
- reg-names = "ispif";
+ reg = <0xfda0a000 0x500>,
+ <0xfda00020 0x10>;
+ reg-names = "ispif", "csi_clk_mux";
interrupts = <0 55 0>;
interrupt-names = "ispif";
};
diff --git a/arch/arm/boot/dts/msm8226-cdp.dts b/arch/arm/boot/dts/msm8226-cdp.dtsi
similarity index 82%
rename from arch/arm/boot/dts/msm8226-cdp.dts
rename to arch/arm/boot/dts/msm8226-cdp.dtsi
index 19d451b..2f3fae2 100644
--- a/arch/arm/boot/dts/msm8226-cdp.dts
+++ b/arch/arm/boot/dts/msm8226-cdp.dtsi
@@ -10,16 +10,10 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
-/include/ "msm8226.dtsi"
/include/ "dsi-panel-nt35590-720p-video.dtsi"
/include/ "msm8226-camera-sensor-cdp.dtsi"
-/ {
- model = "Qualcomm MSM 8226 CDP";
- compatible = "qcom,msm8226-cdp", "qcom,msm8226", "qcom,cdp";
- qcom,msm-id = <145 1 0>;
-
+&soc {
serial@f991f000 {
status = "ok";
};
@@ -37,10 +31,9 @@
vdd-supply = <&pm8226_l19>;
vcc_i2c-supply = <&pm8226_lvs1>;
synaptics,reset-gpio = <&msmgpio 16 0x00>;
- synaptics,irq-gpio = <&msmgpio 17 0x00>;
+ synaptics,irq-gpio = <&msmgpio 17 0x2008>;
synaptics,button-map = <139 102 158>;
synaptics,i2c-pull-up;
- synaptics,reg-en;
};
};
@@ -114,6 +107,7 @@
qcom,cdc-mclk-gpios = <&pm8226_gpios 1 0>;
qcom,cdc-vdd-spkr-gpios = <&pm8226_gpios 2 0>;
+ qcom,headset-jack-type-NO;
};
};
@@ -131,7 +125,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x4 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
@@ -173,14 +167,12 @@
qcom,vdd-current-level = <9000 800000>;
vdd-io-supply = <&pm8226_l21>;
- qcom,vdd-io-always-on;
- qcom,vdd-io-lpm-sup;
qcom,vdd-io-voltage-level = <1800000 2950000>;
qcom,vdd-io-current-level = <6 22000>;
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x4 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
@@ -210,8 +202,6 @@
qcom,vdd-current-level = <9000 800000>;
vdd-io-supply = <&pm8226_l21>;
- qcom,vdd-io-always-on;
- qcom,vdd-io-lpm-sup;
qcom,vdd-io-voltage-level = <1800000 2950000>;
qcom,vdd-io-current-level = <6 22000>;
@@ -237,6 +227,68 @@
};
&spmi_bus {
+ qcom,pm8226@0 {
+ qcom,leds@a100 {
+ status = "okay";
+ qcom,led_mpp_2 {
+ label = "mpp";
+ linux,name = "button-backlight";
+ linux,default-trigger = "none";
+ qcom,default-state = "off";
+ qcom,max-current = <40>;
+ qcom,current-setting = <5>;
+ qcom,id = <6>;
+ qcom,mode = "manual";
+ qcom,source-sel = <1>;
+ qcom,mode-ctrl = <0x60>;
+ };
+ };
+
+ qcom,leds@a300 {
+ status = "okay";
+ qcom,led_mpp_4 {
+ label = "mpp";
+ linux,name = "green";
+ linux,default-trigger = "none";
+ qcom,default-state = "off";
+ qcom,max-current = <40>;
+ qcom,current-setting = <5>;
+ qcom,id = <6>;
+ qcom,mode = "pwm";
+ qcom,pwm-us = <1000>;
+ qcom,source-sel = <8>;
+ qcom,mode-ctrl = <0x60>;
+ qcom,pwm-channel = <0>;
+ qcom,start-idx = <1>;
+ qcom,duty-pcts = [00 00 00 00 64
+ 64 00 00 00 00];
+ qcom,use-blink;
+ };
+ };
+
+ qcom,leds@a500 {
+ status = "okay";
+ qcom,led_mpp_6 {
+ label = "mpp";
+ linux,name = "red";
+ linux,default-trigger = "none";
+ qcom,default-state = "off";
+ qcom,max-current = <40>;
+ qcom,current-setting = <5>;
+ qcom,id = <6>;
+ qcom,mode = "pwm";
+ qcom,pwm-us = <1000>;
+ qcom,mode-ctrl = <0x60>;
+ qcom,source-sel = <10>;
+ qcom,pwm-channel = <5>;
+ qcom,start-idx = <1>;
+ qcom,duty-pcts = [00 00 00 00 64
+ 64 00 00 00 00];
+ qcom,use-blink;
+ };
+ };
+ };
+
qcom,pm8226@1 {
qcom,leds@d800 {
status = "okay";
@@ -247,7 +299,7 @@
qcom,cs-out-en;
qcom,op-fdbck = <1>;
qcom,default-state = "on";
- qcom,max-current = <25>;
+ qcom,max-current = <20>;
qcom,ctrl-delay-us = <0>;
qcom,boost-curr-lim = <3>;
qcom,cp-sel = <0>;
@@ -331,3 +383,7 @@
qcom,charging-disabled;
qcom,use-default-batt-values;
};
+
+&usb_otg_sw {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/msm8226-coresight.dtsi b/arch/arm/boot/dts/msm8226-coresight.dtsi
index 993b4e6..e11c963 100644
--- a/arch/arm/boot/dts/msm8226-coresight.dtsi
+++ b/arch/arm/boot/dts/msm8226-coresight.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
tmc_etr: tmc@fc322000 {
compatible = "arm,coresight-tmc";
reg = <0xfc322000 0x1000>,
@@ -34,6 +34,11 @@
coresight-id = <1>;
coresight-name = "coresight-tpiu";
coresight-nr-inports = <1>;
+
+ vdd-supply = <&pm8226_l18>;
+
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <9000 800000>;
};
replicator: replicator@fc31c000 {
@@ -355,4 +360,18 @@
coresight-name = "coresight-cti-cpu3";
coresight-nr-inports = <0>;
};
+
+ hwevent: hwevent@fd828018 {
+ compatible = "qcom,coresight-hwevent";
+ reg = <0xfd828018 0x80>,
+ <0xf9011080 0x80>,
+ <0xfd4ab160 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+
+ coresight-id = <29>;
+ coresight-name = "coresight-hwevent";
+ coresight-nr-inports = <0>;
+
+ qcom,hwevent-clks = "core_mmss_clk";
+ };
};
diff --git a/arch/arm/boot/dts/msm8226-fluid.dts b/arch/arm/boot/dts/msm8226-fluid.dts
index d70ef6e..c58b43b 100644
--- a/arch/arm/boot/dts/msm8226-fluid.dts
+++ b/arch/arm/boot/dts/msm8226-fluid.dts
@@ -16,8 +16,13 @@
/ {
model = "Qualcomm MSM 8226 FLUID";
compatible = "qcom,msm8226-fluid", "qcom,msm8226", "qcom,fluid";
- qcom,msm-id = <145 3 0>;
+ qcom,msm-id = <145 3 0>,
+ <158 3 0>,
+ <159 3 0>,
+ <198 3 0>;
+};
+&soc {
serial@f991f000 {
status = "disabled";
};
diff --git a/arch/arm/boot/dts/msm8226-gpu.dtsi b/arch/arm/boot/dts/msm8226-gpu.dtsi
index bb2f0d4..590f733 100644
--- a/arch/arm/boot/dts/msm8226-gpu.dtsi
+++ b/arch/arm/boot/dts/msm8226-gpu.dtsi
@@ -9,7 +9,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-/ {
+&soc {
msm_gpu: qcom,kgsl-3d0@fdb00000 {
label = "kgsl-3d0";
compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
@@ -22,11 +22,9 @@
qcom,chipid = <0x03000510>;
- qcom,initial-pwrlevel = <2>;
- qcom,step-pwrlevel = <2>;
+ qcom,initial-pwrlevel = <1>;
qcom,idle-timeout = <8>; //<HZ/12>
- qcom,nap-allowed = <1>;
qcom,strtstp-sleepwake;
qcom,clk-map = <0x00000016>; /* KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE */
diff --git a/arch/arm/boot/dts/msm8226-iommu-domains.dtsi b/arch/arm/boot/dts/msm8226-iommu-domains.dtsi
index 6ea5b9e..25fca2a 100644
--- a/arch/arm/boot/dts/msm8226-iommu-domains.dtsi
+++ b/arch/arm/boot/dts/msm8226-iommu-domains.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,iommu-domains {
compatible = "qcom,iommu-domains";
diff --git a/arch/arm/boot/dts/msm8226-iommu.dtsi b/arch/arm/boot/dts/msm8226-iommu.dtsi
index 460068b..ff3e0a5 100644
--- a/arch/arm/boot/dts/msm8226-iommu.dtsi
+++ b/arch/arm/boot/dts/msm8226-iommu.dtsi
@@ -14,6 +14,7 @@
&jpeg_iommu {
status = "ok";
+ vdd-supply = <&gdsc_jpeg>;
qcom,iommu-enable-halt;
qcom,iommu-bfb-regs = <0x204c
@@ -51,6 +52,7 @@
&mdp_iommu {
status = "ok";
+ vdd-supply = <&gdsc_mdss>;
qcom,iommu-enable-halt;
qcom,iommu-bfb-regs = <0x204c
@@ -94,6 +96,7 @@
&venus_iommu {
status = "ok";
+ vdd-supply = <&gdsc_venus>;
qcom,iommu-enable-halt;
qcom,iommu-bfb-regs = <0x204c
@@ -157,7 +160,10 @@
&kgsl_iommu {
status = "ok";
+ vdd-supply = <&gdsc_oxili_cx>;
+ qcom,alt-vdd-supply = <&gdsc_oxili_gx>;
qcom,iommu-enable-halt;
+ qcom,needs-alt-core-clk;
qcom,iommu-bfb-regs = <0x204c
0x2050
@@ -188,6 +194,7 @@
&vfe_iommu {
status = "ok";
+ vdd-supply = <&gdsc_vfe>;
qcom,iommu-enable-halt;
qcom,iommu-bfb-regs = <0x204c
diff --git a/arch/arm/boot/dts/msm8226-ion.dtsi b/arch/arm/boot/dts/msm8226-ion.dtsi
index 9cef5a9..dee64e5 100644
--- a/arch/arm/boot/dts/msm8226-ion.dtsi
+++ b/arch/arm/boot/dts/msm8226-ion.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,ion {
compatible = "qcom,msm-ion";
#address-cells = <1>;
@@ -38,9 +38,7 @@
qcom,ion-heap@27 { /* QSECOM HEAP */
compatible = "qcom,msm-ion-reserve";
reg = <27>;
- qcom,heap-align = <0x1000>;
- qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0x780000>;
+ linux,contiguous-region = <&qsecom_mem>;
};
qcom,ion-heap@28 { /* AUDIO HEAP */
@@ -50,5 +48,19 @@
qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
qcom,memory-reservation-size = <0x314000>;
};
+ qcom,ion-heap@23 { /* OTHER PIL HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <23>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-fixed = <0x06400000 0x2000000>;
+ };
+ qcom,ion-heap@26 { /* MODEM PIL HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <26>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-fixed = <0x08400000 0x4E00000>;
+
+ };
+
};
};
diff --git a/arch/arm/boot/dts/msm8226-mdss.dtsi b/arch/arm/boot/dts/msm8226-mdss.dtsi
index 5aa39d3..f580897 100644
--- a/arch/arm/boot/dts/msm8226-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8226-mdss.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,mdss_mdp@fd900000 {
compatible = "qcom,mdss_mdp";
reg = <0xfd900000 0x22100>,
diff --git a/arch/arm/boot/dts/msm8226-mtp.dts b/arch/arm/boot/dts/msm8226-mtp.dtsi
similarity index 80%
rename from arch/arm/boot/dts/msm8226-mtp.dts
rename to arch/arm/boot/dts/msm8226-mtp.dtsi
index 156c72d..acab5e4 100644
--- a/arch/arm/boot/dts/msm8226-mtp.dts
+++ b/arch/arm/boot/dts/msm8226-mtp.dtsi
@@ -10,16 +10,10 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
-/include/ "msm8226.dtsi"
/include/ "dsi-panel-nt35590-720p-video.dtsi"
/include/ "msm8226-camera-sensor-mtp.dtsi"
-/ {
- model = "Qualcomm MSM 8226 MTP";
- compatible = "qcom,msm8226-mtp", "qcom,msm8226", "qcom,mtp";
- qcom,msm-id = <145 8 0>;
-
+&soc {
serial@f991f000 {
status = "ok";
};
@@ -37,10 +31,9 @@
vdd-supply = <&pm8226_l19>;
vcc_i2c-supply = <&pm8226_lvs1>;
synaptics,reset-gpio = <&msmgpio 16 0x00>;
- synaptics,irq-gpio = <&msmgpio 17 0x00>;
+ synaptics,irq-gpio = <&msmgpio 17 0x2008>;
synaptics,button-map = <139 102 158>;
synaptics,i2c-pull-up;
- synaptics,reg-en;
};
};
@@ -109,6 +102,21 @@
};
};
+&usb_otg {
+ #address-cells = <0>;
+ interrupt-parent = <&usb_otg>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 134 0
+ 1 &intc 0 140 0
+ 2 &spmi_bus 0x0 0x0 0x9 0x0>;
+ interrupt-names = "core_irq", "async_irq", "pmic_id_irq";
+
+ qcom,hsusb-otg-mode = <3>;
+ vbus_otg-supply = <&usb_otg_sw>;
+};
+
&sdcc1 {
vdd-supply = <&pm8226_l17>;
qcom,vdd-always-on;
@@ -123,7 +131,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x4 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
@@ -165,14 +173,12 @@
qcom,vdd-current-level = <9000 800000>;
vdd-io-supply = <&pm8226_l21>;
- qcom,vdd-io-always-on;
- qcom,vdd-io-lpm-sup;
qcom,vdd-io-voltage-level = <1800000 2950000>;
qcom,vdd-io-current-level = <6 22000>;
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x4 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
@@ -199,8 +205,6 @@
qcom,vdd-current-level = <9000 800000>;
vdd-io-supply = <&pm8226_l21>;
- qcom,vdd-io-always-on;
- qcom,vdd-io-lpm-sup;
qcom,vdd-io-voltage-level = <1800000 2950000>;
qcom,vdd-io-current-level = <6 22000>;
@@ -226,6 +230,68 @@
};
&spmi_bus {
+ qcom,pm8226@0 {
+ qcom,leds@a100 {
+ status = "okay";
+ qcom,led_mpp_2 {
+ label = "mpp";
+ linux,name = "button-backlight";
+ linux,default-trigger = "none";
+ qcom,default-state = "off";
+ qcom,max-current = <40>;
+ qcom,current-setting = <5>;
+ qcom,id = <6>;
+ qcom,mode = "manual";
+ qcom,source-sel = <1>;
+ qcom,mode-ctrl = <0x60>;
+ };
+ };
+
+ qcom,leds@a300 {
+ status = "okay";
+ qcom,led_mpp_4 {
+ label = "mpp";
+ linux,name = "green";
+ linux,default-trigger = "none";
+ qcom,default-state = "off";
+ qcom,max-current = <40>;
+ qcom,current-setting = <5>;
+ qcom,id = <6>;
+ qcom,mode = "pwm";
+ qcom,pwm-us = <1000>;
+ qcom,source-sel = <8>;
+ qcom,mode-ctrl = <0x60>;
+ qcom,pwm-channel = <0>;
+ qcom,start-idx = <1>;
+ qcom,duty-pcts = [00 00 00 00 64
+ 64 00 00 00 00];
+ qcom,use-blink;
+ };
+ };
+
+ qcom,leds@a500 {
+ status = "okay";
+ qcom,led_mpp_6 {
+ label = "mpp";
+ linux,name = "red";
+ linux,default-trigger = "none";
+ qcom,default-state = "off";
+ qcom,max-current = <40>;
+ qcom,current-setting = <5>;
+ qcom,id = <6>;
+ qcom,mode = "pwm";
+ qcom,pwm-us = <1000>;
+ qcom,mode-ctrl = <0x60>;
+ qcom,source-sel = <10>;
+ qcom,pwm-channel = <5>;
+ qcom,start-idx = <1>;
+ qcom,duty-pcts = [00 00 00 00 64
+ 64 00 00 00 00];
+ qcom,use-blink;
+ };
+ };
+ };
+
qcom,pm8226@1 {
qcom,leds@d300 {
status = "okay";
@@ -240,7 +306,7 @@
qcom,cs-out-en;
qcom,op-fdbck = <1>;
qcom,default-state = "on";
- qcom,max-current = <25>;
+ qcom,max-current = <20>;
qcom,ctrl-delay-us = <0>;
qcom,boost-curr-lim = <3>;
qcom,cp-sel = <0>;
@@ -363,3 +429,13 @@
&pm8226_chg {
qcom,charging-disabled;
};
+
+&usb_otg_sw {
+ status = "okay";
+};
+
+&slim_msm {
+ tapan_codec {
+ qcom,cdc-micbias1-ext-cap;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226-pm.dtsi b/arch/arm/boot/dts/msm8226-pm.dtsi
index 97b22aa..703ba4b 100644
--- a/arch/arm/boot/dts/msm8226-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-pm.dtsi
@@ -10,9 +10,7 @@
* GNU General Public License for more details.
*/
-/include/ "skeleton.dtsi"
-
-/ {
+&soc {
qcom,spm@f9089000 {
compatible = "qcom,spm-v2";
#address-cells = <1>;
@@ -114,7 +112,7 @@
qcom,type = <0x61706d73>; /* "smpa" */
qcom,id = <0x01>;
qcom,key = <0x6e726f63>; /* "corn" */
- qcom,init-value = <5>; /* Super Turbo */
+ qcom,init-value = <3>; /* SVS SOC */
};
qcom,lpm-resources@1 {
@@ -123,7 +121,7 @@
qcom,type = <0x616F646C>; /* "ldoa" */
qcom,id = <0x03>;
qcom,key = <0x6e726f63>; /* "corn" */
- qcom,init-value = <3>; /* Active */
+ qcom,init-value = <3>; /* SVS SOC */
};
qcom,lpm-resources@2 {
@@ -153,10 +151,10 @@
qcom,mode = "wfi";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_active";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <1>;
@@ -170,10 +168,10 @@
qcom,mode = "standalone_pc";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_active";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <3000>;
@@ -187,10 +185,10 @@
qcom,mode = "pc";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_retention";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <8000>;
@@ -204,10 +202,10 @@
qcom,mode = "pc";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <3>; /* NORMAL */
- qcom,vdd-mem-lower-bound = <2>; /* SVS SOC */
- qcom,vdd-dig-upper-bound = <3>; /* NORMAL */
- qcom,vdd-dig-lower-bound = <2>; /* SVS SOC */
+ qcom,vdd-mem-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-mem-lower-bound = <3>; /* SVS SOC */
+ qcom,vdd-dig-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <9000>;
@@ -221,10 +219,10 @@
qcom,mode = "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,latency-us = <16300>;
qcom,ss-power = <63>;
qcom,energy-overhead = <2128000>;
@@ -236,10 +234,10 @@
qcom,mode = "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <3>; /* NORMAL */
- qcom,vdd-mem-lower-bound = <2>; /* SVS SOC */
- qcom,vdd-dig-upper-bound = <3>; /* NORMAL */
- qcom,vdd-dig-lower-bound = <2>; /* SVS SOC */
+ qcom,vdd-mem-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-mem-lower-bound = <3>; /* SVS SOC */
+ qcom,vdd-dig-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
qcom,latency-us = <24000>;
qcom,ss-power = <10>;
qcom,energy-overhead = <3202600>;
@@ -251,10 +249,10 @@
qcom,mode = "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <2>; /* SVS SOC */
- qcom,vdd-mem-lower-bound = <0>; /* RETENTION */
- qcom,vdd-dig-upper-bound = <2>; /* SVS SOC */
- qcom,vdd-dig-lower-bound = <0>; /* RETENTION */
+ qcom,vdd-mem-upper-bound = <3>; /* SVS SOC */
+ qcom,vdd-mem-lower-bound = <1>; /* RETENTION */
+ qcom,vdd-dig-upper-bound = <3>; /* SVS SOC */
+ qcom,vdd-dig-lower-bound = <1>; /* RETENTION */
qcom,latency-us = <26000>;
qcom,ss-power = <2>;
qcom,energy-overhead = <4252000>;
@@ -380,9 +378,9 @@
qcom,offset-page-indices = <56>;
};
- qcom,rpm-stats@0xfc19dbd0{
+ qcom,rpm-stats@fc19dba0 {
compatible = "qcom,rpm-stats";
- reg = <0xfc19dbd0 0x1000>;
+ reg = <0xfc19dba0 0x1000>;
reg-names = "phys_addr_base";
qcom,sleep-stats-version = <2>;
};
diff --git a/arch/arm/boot/dts/msm8226-qrd.dts b/arch/arm/boot/dts/msm8226-qrd.dtsi
similarity index 80%
rename from arch/arm/boot/dts/msm8226-qrd.dts
rename to arch/arm/boot/dts/msm8226-qrd.dtsi
index 64cc869..7018c6a 100644
--- a/arch/arm/boot/dts/msm8226-qrd.dts
+++ b/arch/arm/boot/dts/msm8226-qrd.dtsi
@@ -10,16 +10,10 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
-/include/ "msm8226.dtsi"
/include/ "dsi-panel-nt35590-720p-video.dtsi"
/include/ "msm8226-camera-sensor-qrd.dtsi"
-/ {
- model = "Qualcomm MSM 8226 QRD";
- compatible = "qcom,msm8226-qrd", "qcom,msm8226", "qcom,qrd";
- qcom,msm-id = <145 11 0>;
-
+&soc {
serial@f991f000 {
status = "ok";
};
@@ -37,10 +31,9 @@
vdd-supply = <&pm8226_l19>;
vcc_i2c-supply = <&pm8226_lvs1>;
synaptics,reset-gpio = <&msmgpio 16 0x00>;
- synaptics,irq-gpio = <&msmgpio 17 0x00>;
+ synaptics,irq-gpio = <&msmgpio 17 0x2008>;
synaptics,button-map = <139 102 158>;
synaptics,i2c-pull-up;
- synaptics,reg-en;
};
};
@@ -106,6 +99,7 @@
qcom,cdc-mclk-gpios = <&pm8226_gpios 1 0>;
qcom,cdc-vdd-spkr-gpios = <&pm8226_gpios 2 0>;
+ qcom,cdc-us-euro-gpios = <&msmgpio 69 0>;
};
};
@@ -123,7 +117,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x4 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
@@ -165,8 +159,6 @@
qcom,vdd-current-level = <9000 800000>;
vdd-io-supply = <&pm8226_l21>;
- qcom,vdd-io-always-on;
- qcom,vdd-io-lpm-sup;
qcom,vdd-io-voltage-level = <1800000 2950000>;
qcom,vdd-io-current-level = <6 22000>;
@@ -202,8 +194,6 @@
qcom,vdd-current-level = <9000 800000>;
vdd-io-supply = <&pm8226_l21>;
- qcom,vdd-io-always-on;
- qcom,vdd-io-lpm-sup;
qcom,vdd-io-voltage-level = <1800000 2950000>;
qcom,vdd-io-current-level = <6 22000>;
@@ -229,6 +219,68 @@
};
&spmi_bus {
+ qcom,pm8226@0 {
+ qcom,leds@a100 {
+ status = "okay";
+ qcom,led_mpp_2 {
+ label = "mpp";
+ linux,name = "button-backlight";
+ linux,default-trigger = "none";
+ qcom,default-state = "off";
+ qcom,max-current = <40>;
+ qcom,current-setting = <5>;
+ qcom,id = <6>;
+ qcom,mode = "manual";
+ qcom,source-sel = <1>;
+ qcom,mode-ctrl = <0x60>;
+ };
+ };
+
+ qcom,leds@a300 {
+ status = "okay";
+ qcom,led_mpp_4 {
+ label = "mpp";
+ linux,name = "green";
+ linux,default-trigger = "none";
+ qcom,default-state = "off";
+ qcom,max-current = <40>;
+ qcom,current-setting = <5>;
+ qcom,id = <6>;
+ qcom,mode = "pwm";
+ qcom,pwm-us = <1000>;
+ qcom,source-sel = <8>;
+ qcom,mode-ctrl = <0x60>;
+ qcom,pwm-channel = <0>;
+ qcom,start-idx = <1>;
+ qcom,duty-pcts = [00 00 00 00 64
+ 64 00 00 00 00];
+ qcom,use-blink;
+ };
+ };
+
+ qcom,leds@a500 {
+ status = "okay";
+ qcom,led_mpp_6 {
+ label = "mpp";
+ linux,name = "red";
+ linux,default-trigger = "none";
+ qcom,default-state = "off";
+ qcom,max-current = <40>;
+ qcom,current-setting = <5>;
+ qcom,id = <6>;
+ qcom,mode = "pwm";
+ qcom,pwm-us = <1000>;
+ qcom,mode-ctrl = <0x60>;
+ qcom,source-sel = <10>;
+ qcom,pwm-channel = <5>;
+ qcom,start-idx = <1>;
+ qcom,duty-pcts = [00 00 00 00 64
+ 64 00 00 00 00];
+ qcom,use-blink;
+ };
+ };
+ };
+
qcom,pm8226@1 {
qcom,leds@d300 {
status = "okay";
@@ -243,7 +295,7 @@
qcom,cs-out-en;
qcom,op-fdbck = <1>;
qcom,default-state = "on";
- qcom,max-current = <25>;
+ qcom,max-current = <20>;
qcom,ctrl-delay-us = <0>;
qcom,boost-curr-lim = <3>;
qcom,cp-sel = <0>;
@@ -263,6 +315,18 @@
};
};
+&pm8226_bms {
+ status = "okay";
+ qcom,batt-type = <4>;
+ qcom,max-voltage-uv = <4350000>;
+};
+
+&pm8226_chg {
+ status = "okay";
+ qcom,chg-vddmax-mv = <4350>;
+ qcom,chg-vddsafe-mv = <4350>;
+};
+
&pm8226_gpios {
gpio@c000 { /* GPIO 1 */
/* XO_PMIC_CDC_MCLK enable for tapan codec */
@@ -329,3 +393,10 @@
mpp@a700 { /* MPP 8 */
};
};
+
+&slim_msm {
+ tapan_codec {
+ qcom,cdc-micbias1-ext-cap;
+ };
+
+};
diff --git a/arch/arm/boot/dts/msm8226-regulator.dtsi b/arch/arm/boot/dts/msm8226-regulator.dtsi
index 70731d2..e1a0f0b 100644
--- a/arch/arm/boot/dts/msm8226-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8226-regulator.dtsi
@@ -26,27 +26,44 @@
/* CPR controlled regulator */
-/ {
+&soc {
apc_vreg_corner: regulator@f9018000 {
status = "okay";
compatible = "qcom,cpr-regulator";
- reg = <0xf9018000 0x1000>,
- <0xfc4b80b0 8>;
- reg-names = "rbcpr", "efuse_phys";
+ reg = <0xf9018000 0x1000>, <0xf9011064 4>, <0xfc4b80b0 8>,
+ <0xfc4bc450 16>;
+ reg-names = "rbcpr", "rbcpr_clk", "pvs_efuse", "cpr_efuse";
+ interrupts = <0 15 0>;
regulator-name = "apc_corner";
regulator-min-microvolt = <1>;
- regulator-max-microvolt = <4>;
+ regulator-max-microvolt = <3>;
qcom,num-efuse-bits = <5>;
- qcom,efuse-bit-pos = <6 7 8 9 10>;
- qcom,pvs-bin-process = <0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2
+ qcom,pvs-bin-process = <0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 2
2 2 2 2 3 3 3 3 3 3 3 3 0 0 0 0>;
- qcom,pvs-corner-ceiling-slow = <1050000 1150000 1275000 1350000>;
- qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000 1200000>;
- qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000 1140000>;
+ qcom,pvs-corner-ceiling-slow = <1155000 1160000 1275000>;
+ qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000>;
+ qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000>;
vdd-apc-supply = <&pm8226_s2>;
+
vdd-mx-supply = <&pm8226_l3_ao>;
qcom,vdd-mx-vmax = <1350000>;
qcom,vdd-mx-vmin-method = <1>;
+
+ qcom,cpr-ref-clk = <19200>;
+ qcom,cpr-timer-delay = <5000>;
+ qcom,cpr-timer-cons-up = <0>;
+ qcom,cpr-timer-cons-down = <2>;
+ qcom,cpr-irq-line = <0>;
+ qcom,cpr-step-quotient = <15>;
+ qcom,cpr-up-threshold = <0>;
+ qcom,cpr-down-threshold = <10>;
+ qcom,cpr-idle-clocks = <0>;
+ qcom,cpr-gcnt-time = <1>;
+ qcom,vdd-apc-step-up-limit = <1>;
+ qcom,vdd-apc-step-down-limit = <1>;
+ qcom,cpr-apc-volt-step = <5000>;
+
+ qcom,cpr-enable;
};
};
@@ -67,7 +84,7 @@
regulator-min-microvolt = <1>;
regulator-max-microvolt = <7>;
qcom,use-voltage-corner;
- qcom,consumer-supplies = "vdd_dig", "", "vdd_sr2_dig", "";
+ qcom,consumer-supplies = "vdd_dig", "";
};
pm8226_s1_corner_ao: regulator-s1-corner-ao {
compatible = "qcom,rpm-regulator-smd";
@@ -76,6 +93,7 @@
regulator-min-microvolt = <1>;
regulator-max-microvolt = <7>;
qcom,use-voltage-corner;
+ qcom,consumer-supplies = "vdd_sr2_dig", "";
};
};
@@ -208,8 +226,27 @@
regulator-max-microvolt = <1800000>;
qcom,init-voltage = <1800000>;
status = "okay";
+ };
+
+ pm8226_l8_ao: regulator-l8-ao {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8226_l8_ao";
+ qcom,set = <1>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
qcom,consumer-supplies = "vdd_sr2_pll", "";
};
+
+ pm8226_l8_so: regulator-l8-so {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8226_l8_so";
+ qcom,set = <2>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+ qcom,init-enable = <0>;
+ };
};
rpm-regulator-ldoa9 {
@@ -405,3 +442,21 @@
};
};
};
+
+&pm8226_chg_boost {
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-name = "8226_smbbp_boost";
+};
+
+&soc {
+ usb_otg_sw: regulator-ncp380 {
+ compatible = "regulator-fixed";
+ regulator-name = "usb_otg_sw";
+ gpio = <&msmgpio 67 0>;
+ parent-supply = <&pm8226_chg_boost>;
+ startup-delay-us = <4000>;
+ enable-active-high;
+ status = "disabled";
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226-sim.dts b/arch/arm/boot/dts/msm8226-sim.dts
index a86dc48..2405646 100644
--- a/arch/arm/boot/dts/msm8226-sim.dts
+++ b/arch/arm/boot/dts/msm8226-sim.dts
@@ -17,8 +17,13 @@
/ {
model = "Qualcomm MSM 8226 Simulator";
compatible = "qcom,msm8226-sim", "qcom,msm8226", "qcom,sim";
- qcom,msm-id = <145 16 0>;
+ qcom,msm-id = <145 16 0>,
+ <158 16 0>,
+ <159 16 0>,
+ <198 16 0>;
+};
+&soc {
serial@f991f000 {
status = "ok";
};
@@ -36,7 +41,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
vdd-supply = <&pm8226_l17>;
@@ -62,7 +67,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
diff --git a/arch/arm/boot/dts/msm8226-smp2p.dtsi b/arch/arm/boot/dts/msm8226-smp2p.dtsi
index 079e4ca..3921a68 100644
--- a/arch/arm/boot/dts/msm8226-smp2p.dtsi
+++ b/arch/arm/boot/dts/msm8226-smp2p.dtsi
@@ -9,7 +9,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,smp2p-modem {
compatible = "qcom,smp2p";
reg = <0xf9011008 0x4>;
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/msm8226-v1-cdp.dts
similarity index 68%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/msm8226-v1-cdp.dts
index e410344..9c49840 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/msm8226-v1-cdp.dts
@@ -11,19 +11,15 @@
*/
/dts-v1/;
-
-/include/ "msmzinc.dtsi"
+/include/ "msm8226-v1.dtsi"
+/include/ "msm8226-cdp.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
-
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
+ model = "Qualcomm MSM 8226 CDP";
+ compatible = "qcom,msm8226-cdp", "qcom,msm8226", "qcom,cdp";
+ qcom,msm-id = <145 1 0>,
+ <158 1 0>,
+ <159 1 0>,
+ <198 1 0>,
+ <205 1 0>;
};
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/msm8226-v1-mtp.dts
similarity index 68%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/msm8226-v1-mtp.dts
index e410344..b1d46b1 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/msm8226-v1-mtp.dts
@@ -11,19 +11,15 @@
*/
/dts-v1/;
-
-/include/ "msmzinc.dtsi"
+/include/ "msm8226-v1.dtsi"
+/include/ "msm8226-mtp.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
-
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
+ model = "Qualcomm MSM 8226 MTP";
+ compatible = "qcom,msm8226-mtp", "qcom,msm8226", "qcom,mtp";
+ qcom,msm-id = <145 8 0>,
+ <158 8 0>,
+ <159 8 0>,
+ <198 8 0>,
+ <205 8 0>;
};
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/msm8226-v1-qrd.dts
similarity index 67%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/msm8226-v1-qrd.dts
index e410344..d2aabac 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/msm8226-v1-qrd.dts
@@ -11,19 +11,15 @@
*/
/dts-v1/;
-
-/include/ "msmzinc.dtsi"
+/include/ "msm8226-v1.dtsi"
+/include/ "msm8226-qrd.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
-
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
+ model = "Qualcomm MSM 8226 QRD";
+ compatible = "qcom,msm8226-qrd", "qcom,msm8226", "qcom,qrd";
+ qcom,msm-id = <145 11 0>,
+ <158 11 0>,
+ <159 11 0>,
+ <198 11 0>,
+ <205 11 0>;
};
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/msm8226-v1.dtsi
similarity index 67%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/msm8226-v1.dtsi
index e410344..d471bec 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/msm8226-v1.dtsi
@@ -10,20 +10,10 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
+/*
+ * As a general rule, only version-specific property overrides should be placed
+ * inside this file. However, device definitions should be placed inside the
+ * msm8226.dtsi file.
+ */
-/include/ "msmzinc.dtsi"
-
-/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
-
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
-};
+/include/ "msm8226.dtsi"
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/msm8226-v2-cdp.dts
similarity index 65%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/msm8226-v2-cdp.dts
index e410344..2b18491 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/msm8226-v2-cdp.dts
@@ -11,19 +11,15 @@
*/
/dts-v1/;
-
-/include/ "msmzinc.dtsi"
+/include/ "msm8226-v2.dtsi"
+/include/ "msm8226-cdp.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
-
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
+ model = "Qualcomm MSM 8226v2 CDP";
+ compatible = "qcom,msm8226-cdp", "qcom,msm8226", "qcom,cdp";
+ qcom,msm-id = <145 1 0x20000>,
+ <158 1 0x20000>,
+ <159 1 0x20000>,
+ <198 1 0x20000>,
+ <205 1 0x20000>;
};
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/msm8226-v2-mtp.dts
similarity index 65%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/msm8226-v2-mtp.dts
index e410344..f15dd4c 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/msm8226-v2-mtp.dts
@@ -11,19 +11,15 @@
*/
/dts-v1/;
-
-/include/ "msmzinc.dtsi"
+/include/ "msm8226-v2.dtsi"
+/include/ "msm8226-mtp.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
-
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
+ model = "Qualcomm MSM 8226v2 MTP";
+ compatible = "qcom,msm8226-mtp", "qcom,msm8226", "qcom,mtp";
+ qcom,msm-id = <145 8 0x20000>,
+ <158 8 0x20000>,
+ <159 8 0x20000>,
+ <198 8 0x20000>,
+ <205 8 0x20000>;
};
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/msm8226-v2-qrd.dts
similarity index 65%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/msm8226-v2-qrd.dts
index e410344..1a89d78 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/msm8226-v2-qrd.dts
@@ -11,19 +11,15 @@
*/
/dts-v1/;
-
-/include/ "msmzinc.dtsi"
+/include/ "msm8226-v2.dtsi"
+/include/ "msm8226-qrd.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
-
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
+ model = "Qualcomm MSM 8226v2 QRD";
+ compatible = "qcom,msm8226-qrd", "qcom,msm8226", "qcom,qrd";
+ qcom,msm-id = <145 11 0x20000>,
+ <158 11 0x20000>,
+ <159 11 0x20000>,
+ <198 11 0x20000>,
+ <205 11 0x20000>;
};
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/msm8226-v2.dtsi
similarity index 67%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/msm8226-v2.dtsi
index e410344..d471bec 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/msm8226-v2.dtsi
@@ -10,20 +10,10 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
+/*
+ * As a general rule, only version-specific property overrides should be placed
+ * inside this file. However, device definitions should be placed inside the
+ * msm8226.dtsi file.
+ */
-/include/ "msmzinc.dtsi"
-
-/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
-
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
-};
+/include/ "msm8226.dtsi"
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index b949d3b..7c98104 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -11,6 +11,35 @@
*/
/include/ "skeleton.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8226";
+ compatible = "qcom,msm8226";
+ interrupt-parent = <&intc>;
+
+ aliases {
+ spi0 = &spi_0;
+ sdhc1 = &sdhc_1; /* SDC1 eMMC slot */
+ sdhc2 = &sdhc_2; /* SDC2 SD card slot */
+ };
+
+ memory {
+ secure_mem: secure_region {
+ linux,contiguous-region;
+ reg = <0 0x6D00000>;
+ label = "secure_mem";
+ };
+
+ qsecom_mem: qsecom_region {
+ linux,contiguous-region;
+ reg = <0 0x780000>;
+ label = "qsecom_mem";
+ };
+ };
+
+ soc: soc { };
+};
+
/include/ "msm8226-ion.dtsi"
/include/ "msm8226-camera.dtsi"
/include/ "msm-gdsc.dtsi"
@@ -22,10 +51,11 @@
/include/ "msm8226-mdss.dtsi"
/include/ "msm8226-coresight.dtsi"
/include/ "msm8226-iommu-domains.dtsi"
-/ {
- model = "Qualcomm MSM 8226";
- compatible = "qcom,msm8226";
- interrupt-parent = <&intc>;
+
+&soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
intc: interrupt-controller@f9000000 {
compatible = "qcom,msm-qgic2";
@@ -47,18 +77,10 @@
qcom,direct-connect-irqs = <8>;
};
- aliases {
- spi0 = &spi_0;
- sdhc1 = &sdhc_1; /* SDC1 eMMC slot */
- sdhc2 = &sdhc_2; /* SDC2 SD card slot */
- };
-
- memory {
- secure_mem: secure_region {
- linux,contiguous-region;
- reg = <0 0x3800000>;
- label = "secure_mem";
- };
+ qcom,mpm2-sleep-counter@fc4a3000 {
+ compatible = "qcom,mpm2-sleep-counter";
+ reg = <0xfc4a3000 0x1000>;
+ clock-frequency = <32768>;
};
timer {
@@ -219,7 +241,7 @@
};
};
- usb@f9a55000 {
+ usb_otg: usb@f9a55000 {
compatible = "qcom,hsusb-otg";
reg = <0xf9a55000 0x400>;
interrupts = <0 134 0>, <0 140 0>;
@@ -229,13 +251,15 @@
HSUSB_3p3-supply = <&pm8226_l20>;
qcom,vdd-voltage-level = <1 5 7>;
+ qcom,hsusb-otg-phy-init-seq =
+ <0x44 0x80 0x68 0x81 0x24 0x82 0x13 0x83 0xffffffff>;
qcom,hsusb-otg-phy-type = <2>;
qcom,hsusb-otg-mode = <1>;
qcom,hsusb-otg-otg-control = <2>;
qcom,hsusb-otg-disable-reset;
qcom,dp-manual-pullup;
- qcom,msm-bus,name = "usb2";
+ qcom,msm-bus,name = "usb";
qcom,msm-bus,num-cases = <2>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps =
@@ -246,6 +270,8 @@
android_usb@fe8050c8 {
compatible = "qcom,android-usb";
reg = <0xfe8050c8 0xc8>;
+ qcom,android-usb-cdrom;
+ qcom,android-usb-swfi-latency = <1>;
};
wcd9xxx_intc: wcd9xxx-irq {
@@ -257,7 +283,7 @@
interrupt-names = "cdc-int";
};
- slim@fe12f000 {
+ slim_msm: slim@fe12f000 {
cell-index = <1>;
compatible = "qcom,slim-ngd";
reg = <0xfe12f000 0x35000>,
@@ -303,7 +329,7 @@
qcom,cdc-micbias-ldoh-v = <0x3>;
qcom,cdc-micbias-cfilt1-mv = <1800>;
- qcom,cdc-micbias-cfilt2-mv = <1800>;
+ qcom,cdc-micbias-cfilt2-mv = <2700>;
qcom,cdc-micbias-cfilt3-mv = <1800>;
qcom,cdc-micbias1-cfilt-sel = <0x0>;
@@ -325,6 +351,11 @@
compatible = "qcom,msm8226-audio-tapan";
qcom,model = "msm8226-tapan-snd-card";
qcom,tapan-mclk-clk-freq = <9600000>;
+ qcom,prim-auxpcm-gpio-clk = <&msmgpio 63 0>;
+ qcom,prim-auxpcm-gpio-sync = <&msmgpio 64 0>;
+ qcom,prim-auxpcm-gpio-din = <&msmgpio 65 0>;
+ qcom,prim-auxpcm-gpio-dout = <&msmgpio 66 0>;
+ qcom,prim-auxpcm-gpio-set = "prim-gpio-prim";
};
qcom,msm-pcm {
@@ -477,6 +508,28 @@
compatible = "qcom,msm-pcm-hostless";
};
+ qcom,msm-auxpcm {
+ compatible = "qcom,msm-auxpcm-resource";
+ qcom,msm-cpudai-auxpcm-clk = "pcm_clk";
+ qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
+ qcom,msm-cpudai-auxpcm-quant = <2>, <2>;
+ qcom,msm-cpudai-auxpcm-slot = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-data = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
+
+ qcom,msm-prim-auxpcm-rx {
+ qcom,msm-auxpcm-dev-id = <4106>;
+ compatible = "qcom,msm-auxpcm-dev";
+ };
+
+ qcom,msm-prim-auxpcm-tx {
+ qcom,msm-auxpcm-dev-id = <4107>;
+ compatible = "qcom,msm-auxpcm-dev";
+ };
+ };
+
qcom,wcnss-wlan@fb000000 {
compatible = "qcom,wcnss_wlan";
reg = <0xfb000000 0x280000>,
@@ -494,7 +547,8 @@
qcom,iris-vdddig-supply = <&pm8226_l24>;
gpios = <&msmgpio 40 0>, <&msmgpio 41 0>, <&msmgpio 42 0>, <&msmgpio 43 0>, <&msmgpio 44 0>;
- qcom,has_pronto_hw;
+ qcom,has-pronto-hw;
+ qcom,has-autodetect-xo;
};
qcom,msm-adsp-sensors {
@@ -516,7 +570,7 @@
qcom,smem@fa00000 {
compatible = "qcom,smem";
- reg = <0xfa00000 0x200000>,
+ reg = <0xfa00000 0x100000>,
<0xf9011000 0x1000>,
<0xfc428000 0x4000>;
reg-names = "smem", "irq-reg-base", "aux-mem1";
@@ -588,6 +642,10 @@
rpm-channel-type = <15>; /* SMD_APPS_RPM */
};
+ qcom,bcl {
+ compatible = "qcom,bcl";
+ };
+
sdcc1: qcom,sdcc@f9824000 {
cell-index = <1>; /* SDC1 eMMC slot */
compatible = "qcom,msm-sdcc";
@@ -677,7 +735,7 @@
reg = <0xf9927000 0x1000>;
interrupt-names = "qup_err_intr";
interrupts = <0 99 0>;
- qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-bus-freq = <384000>;
qcom,i2c-src-freq = <19200000>;
};
@@ -686,7 +744,6 @@
reg = <0xf9011050 0x8>;
reg-names = "rcg_base";
a7_cpu-supply = <&apc_vreg_corner>;
- a7_mem-supply = <&pm8226_l3>;
};
qcom,ocmem@fdd00000 {
@@ -735,6 +792,7 @@
/* GPIO inputs from wcnss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_4_in 0 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_4_in 1 0>;
qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_4_in 2 0>;
/* GPIO output to wcnss */
@@ -756,8 +814,10 @@
qcom,firmware-name = "adsp";
- /* GPIO input from lpass */
+ /* GPIO inputs from lpass */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_2_in 0 0>;
+ qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_2_in 2 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_2_in 1 0>;
/* GPIO output to lpass */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>;
@@ -774,7 +834,6 @@
"restart_reg", "cxrail_bhs_reg";
interrupts = <0 24 1>;
- vdd_mss-supply = <&pm8226_s1>;
vdd_cx-supply = <&pm8226_s1_corner>;
vdd_mx-supply = <&pm8226_l3>;
vdd_pll-supply = <&pm8226_l8>;
@@ -786,7 +845,9 @@
/* GPIO inputs from mss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_1_in 1 0>;
qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_1_in 2 0>;
+ qcom,gpio-stop-ack = <&smp2pgpio_ssr_smp2p_1_in 3 0>;
/* GPIO output to mss */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
@@ -794,7 +855,7 @@
qcom,msm-mem-hole {
compatible = "qcom,msm-mem-hole";
- qcom,memblock-remove = <0x8400000 0x7b00000>; /* Address and Size of Hole */
+ qcom,memblock-remove = <0x6400000 0x9b00000>; /* Address and Size of Hole */
};
tsens: tsens@fc4a8000 {
@@ -815,6 +876,7 @@
qcom,limit-temp = <60>;
qcom,temp-hysteresis = <10>;
qcom,freq-step = <2>;
+ qcom,freq-control-mask = <0xf>;
};
spi_0: spi@f9923000 { /* BLSP1 QUP1 */
@@ -914,25 +976,72 @@
<55 512 3936000 393600>,
<55 512 3936000 393600>;
};
+
+ qcom,qcrypto@fd404000 {
+ compatible = "qcom,qcrypto";
+ reg = <0xfd400000 0x20000>,
+ <0xfd404000 0x8000>;
+ reg-names = "crypto-base","crypto-bam-base";
+ interrupts = <0 207 0>;
+ qcom,bam-pipe-pair = <2>;
+ qcom,ce-hw-instance = <0>;
+ qcom,ce-hw-shared;
+ qcom,msm-bus,name = "qcrypto-noc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <55 512 0 0>,
+ <55 512 3936000 393600>;
+ };
+
+ qcom,qcedev@fd400000 {
+ compatible = "qcom,qcedev";
+ reg = <0xfd400000 0x20000>,
+ <0xfd404000 0x8000>;
+ reg-names = "crypto-base","crypto-bam-base";
+ interrupts = <0 207 0>;
+ qcom,bam-pipe-pair = <1>;
+ qcom,ce-hw-instance = <0>;
+ qcom,ce-hw-shared;
+ qcom,msm-bus,name = "qcedev-noc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <55 512 0 0>,
+ <55 512 3936000 393600>;
+ };
+
+ cpu-pmu {
+ compatible = "arm,cortex-a7-pmu";
+ qcom,irq-is-percpu;
+ interrupts = <1 7 0xf00>;
+ };
};
&gdsc_venus {
+ qcom,clock-names = "core_clk";
status = "ok";
};
&gdsc_mdss {
+ qcom,clock-names = "core_clk", "lut_clk";
status = "ok";
};
&gdsc_jpeg {
+ qcom,clock-names = "core_clk";
status = "ok";
};
&gdsc_vfe {
+ qcom,clock-names = "core_clk", "csi_clk", "cpp_clk";
status = "ok";
};
&gdsc_oxili_cx {
+ qcom,clock-names = "core_clk";
status = "ok";
};
@@ -1045,6 +1154,71 @@
};
};
+&pm8226_adc_tm {
+ /* Channel Node */
+ chan@30 {
+ label = "batt_therm";
+ reg = <0x30>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <1>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <3>;
+ qcom,btm-channel-number = <0x48>;
+ };
+
+ chan@8 {
+ label = "die_temp";
+ reg = <8>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <3>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <3>;
+ qcom,btm-channel-number = <0x68>;
+ };
+
+ chan@6 {
+ label = "vbat_sns";
+ reg = <6>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <3>;
+ qcom,btm-channel-number = <0x70>;
+ };
+
+ chan@14 {
+ label = "pa_therm0";
+ reg = <0x14>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x78>;
+ qcom,thermal-node;
+ };
+
+ chan@17 {
+ label = "pa_therm1";
+ reg = <0x17>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x80>;
+ qcom,thermal-node;
+ };
+};
+
&pm8226_chg {
status = "ok";
diff --git a/arch/arm/boot/dts/msm8610-bus.dtsi b/arch/arm/boot/dts/msm8610-bus.dtsi
index 50066f3..d9bb6ab 100644
--- a/arch/arm/boot/dts/msm8610-bus.dtsi
+++ b/arch/arm/boot/dts/msm8610-bus.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
msm-mmss-noc@fc478000 {
compatible = "msm-bus-fabric";
reg = <0xfc478000 0x00004000>;
@@ -23,17 +23,26 @@
qcom,hw-sel = "NoC";
qcom,rpm-en;
+ coresight-id = <52>;
+ coresight-name = "coresight-mnoc";
+ coresight-nr-inports = <0>;
+ coresight-outports = <0>;
+ coresight-child-list = <&funnel_in0>;
+ coresight-child-ports = <5>;
+
mas-mdp-port0 {
cell-id = <22>;
label = "mas-mdp-port0";
qcom,masterp = <2>;
qcom,tier = <2>;
qcom,hw-sel = "NoC";
- qcom,perm-mode = "Bypass";
- qcom,mode = "Bypass";
+ qcom,perm-mode = "Fixed";
+ qcom,mode = "Fixed";
qcom,qport = <0>;
qcom,ws = <10000>;
qcom,mas-hw-id = <8>;
+ qcom,prio1 = <2>;
+ qcom,prio0 = <2>;
};
mas-vfe {
@@ -47,6 +56,8 @@
qcom,ws = <10000>;
qcom,qport = <2>;
qcom,mas-hw-id = <11>;
+ qcom,prio1 = <2>;
+ qcom,prio0 = <2>;
};
mas-mdpe {
@@ -55,11 +66,13 @@
qcom,masterp = <4>;
qcom,tier = <2>;
qcom,hw-sel = "NoC";
- qcom,perm-mode = "Bypass";
- qcom,mode = "Bypass";
+ qcom,perm-mode = "Fixed";
+ qcom,mode = "Fixed";
qcom,ws = <10000>;
qcom,qport = <7>;
qcom,mas-hw-id = <11>;
+ qcom,prio1 = <2>;
+ qcom,prio0 = <2>;
};
fab-bimc {
@@ -67,7 +80,7 @@
label = "fab-bimc";
qcom,gateway;
qcom,slavep = <16>;
- qcom,buswidth = <16>;
+ qcom,buswidth = <8>;
qcom,hw-sel = "NoC";
qcom,slv-hw-id = <16>;
};
@@ -77,7 +90,7 @@
label = "slv-camera-cfg";
qcom,slavep = <0>;
qcom,tier = <2>;
- qcom,buswidth = <16>;
+ qcom,buswidth = <8>;
qcom,hw-sel = "NoC";
qcom,slv-hw-id = <3>;
};
@@ -87,7 +100,7 @@
label = "slv-display-cfg";
qcom,slavep = <1>;
qcom,tier = <2>;
- qcom,buswidth = <16>;
+ qcom,buswidth = <8>;
qcom,hw-sel = "NoC";
qcom,slv-hw-id = <4>;
};
@@ -97,7 +110,7 @@
label = "slv-cpr-cfg";
qcom,slavep = <3>;
qcom,tier = <2>;
- qcom,buswidth = <16>;
+ qcom,buswidth = <8>;
qcom,hw-sel = "NoC";
qcom,slv-hw-id = <6>;
};
@@ -107,7 +120,7 @@
label = "slv-cpr-xpu-cfg";
qcom,slavep = <4>;
qcom,tier = <2>;
- qcom,buswidth = <16>;
+ qcom,buswidth = <8>;
qcom,hw-sel = "NoC";
qcom,slv-hw-id = <7>;
};
@@ -117,7 +130,7 @@
label = "slv-misc-cfg";
qcom,slavep = <6>;
qcom,tier = <2>;
- qcom,buswidth = <16>;
+ qcom,buswidth = <8>;
qcom,hw-sel = "NoC";
qcom,slv-hw-id = <8>;
};
@@ -127,7 +140,7 @@
label = "slv-misc-xpu-cfg";
qcom,slavep = <7>;
qcom,tier = <2>;
- qcom,buswidth = <16>;
+ qcom,buswidth = <8>;
qcom,hw-sel = "NoC";
qcom,slv-hw-id = <9>;
};
@@ -137,7 +150,7 @@
label = "slv-gfx3d-cfg";
qcom,slavep = <9>;
qcom,tier = <2>;
- qcom,buswidth = <16>;
+ qcom,buswidth = <8>;
qcom,hw-sel = "NoC";
qcom,slv-hw-id = <11>;
};
@@ -147,7 +160,7 @@
label = "slv-mmss-clk-cfg";
qcom,slavep = <11>;
qcom,tier = <2>;
- qcom,buswidth = <16>;
+ qcom,buswidth = <8>;
qcom,hw-sel = "NoC";
qcom,slv-hw-id = <12>;
};
@@ -157,7 +170,7 @@
label = "slv-mmss-clk-xpu-cfg";
qcom,slavep = <12>;
qcom,tier = <2>;
- qcom,buswidth = <16>;
+ qcom,buswidth = <8>;
qcom,hw-sel = "NoC";
qcom,slv-hw-id = <13>;
};
@@ -167,7 +180,7 @@
label = "slv-mnoc-mpu-cfg";
qcom,slavep = <13>;
qcom,tier = <2>;
- qcom,buswidth = <16>;
+ qcom,buswidth = <8>;
qcom,hw-sel = "NoC";
qcom,slv-hw-id = <14>;
};
@@ -177,7 +190,7 @@
label = "slv-onoc-mpu-cfg";
qcom,slavep = <14>;
qcom,tier = <2>;
- qcom,buswidth = <16>;
+ qcom,buswidth = <8>;
qcom,hw-sel = "NoC";
qcom,slv-hw-id = <15>;
};
@@ -187,7 +200,7 @@
label = "slv-service-mnoc";
qcom,slavep = <18>;
qcom,tier = <2>;
- qcom,buswidth = <16>;
+ qcom,buswidth = <8>;
qcom,hw-sel = "NoC";
qcom,slv-hw-id = <17>;
};
@@ -197,7 +210,7 @@
label = "slv-dsi-cfg";
qcom,slavep = <19>;
qcom,tier = <2>;
- qcom,buswidth = <16>;
+ qcom,buswidth = <8>;
qcom,hw-sel = "NoC";
qcom,slv-hw-id = <19>;
};
@@ -215,6 +228,13 @@
qcom,hw-sel = "NoC";
qcom,rpm-en;
+ coresight-id = <50>;
+ coresight-name = "coresight-snoc";
+ coresight-nr-inports = <0>;
+ coresight-outports = <0>;
+ coresight-child-list = <&funnel_in0>;
+ coresight-child-ports = <3>;
+
mas-lpass-ahb {
cell-id = <52>;
label = "mas-lpass-ahb";
@@ -272,8 +292,8 @@
qcom,mas-hw-id = <29>;
qcom,slv-hw-id = <28>;
qcom,mode = "Fixed";
- qcom,prio-rd = <2>;
- qcom,prio-wr = <2>;
+ qcom,prio0 = <2>;
+ qcom,prio1 = <2>;
};
fab-ovnoc {
@@ -291,6 +311,9 @@
qcom,masterp = <5>;
qcom,tier = <2>;
qcom,mas-hw-id = <23>;
+ qcom,hw-sel = "NoC";
+ qcom,prio0 = <1>;
+ qcom,prio1 = <1>;
};
mas-mss {
@@ -413,6 +436,13 @@
qcom,hw-sel = "NoC";
qcom,rpm-en;
+ coresight-id = <54>;
+ coresight-name = "coresight-pnoc";
+ coresight-nr-inports = <0>;
+ coresight-outports = <0>;
+ coresight-child-list = <&funnel_in0>;
+ coresight-child-ports = <6>;
+
mas-pnoc-cfg {
cell-id = <88>;
label = "mas-pnoc-cfg";
@@ -887,6 +917,13 @@
qcom,hw-sel = "BIMC";
qcom,rpm-en;
+ coresight-id = <55>;
+ coresight-name = "coresight-bimc";
+ coresight-nr-inports = <0>;
+ coresight-outports = <0>;
+ coresight-child-list = <&funnel_in1>;
+ coresight-child-ports = <3>;
+
mas-ampss-m0 {
cell-id = <1>;
label = "mas-ampss-m0";
@@ -899,6 +936,7 @@
qcom,mas-hw-id = <0>;
qcom,prio-rd = <1>;
qcom,prio-wr = <1>;
+ qcom,prio-lvl = <1>;
};
mas-mss-proc {
@@ -943,8 +981,8 @@
qcom,qport = <4>;
qcom,mas-hw-id = <25>;
qcom,mode = "Fixed";
- qcom,prio-rd = <1>;
- qcom,prio-wr = <1>;
+ qcom,prio0 = <1>;
+ qcom,prio1 = <1>;
};
mas-gfx3d {
@@ -952,13 +990,11 @@
label = "mas-gfx3d";
qcom,masterp = <5>;
qcom,tier = <2>;
- qcom,hw-sel = "NoC";
- qcom,perm-mode = "Bypass";
- qcom,mode = "Bypass";
+ qcom,hw-sel = "BIMC";
+ qcom,perm-mode = "Fixed";
+ qcom,mode = "Fixed";
qcom,ws = <10000>;
qcom,qport = <5>;
- qcom,prio-rd = <1>;
- qcom,prio-wr = <1>;
qcom,mas-hw-id = <6>;
};
diff --git a/arch/arm/boot/dts/msm8610-camera-sensor-cdp-mtp.dtsi b/arch/arm/boot/dts/msm8610-camera-sensor-cdp-mtp.dtsi
new file mode 100644
index 0000000..1b4a594
--- /dev/null
+++ b/arch/arm/boot/dts/msm8610-camera-sensor-cdp-mtp.dtsi
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2013, 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.
+ */
+
+&i2c {
+
+ led_flash0: qcom,led-flash@60 {
+ cell-index = <0>;
+ reg = <0x60>;
+ qcom,slave-id = <0x60 0x00 0x0011>;
+ compatible = "qcom,led-flash";
+ qcom,flash-name = "adp1600";
+ qcom,flash-type = <1>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 18 0>,
+ <&msmgpio 19 0>;
+ qcom,gpio-flash-en = <0>;
+ qcom,gpio-flash-now = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <0 0>;
+ qcom,gpio-req-tbl-label = "FLASH_EN",
+ "FLASH_NOW";
+ };
+
+ actuator0: qcom,actuator@6e {
+ cell-index = <3>;
+ reg = <0x6c>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ };
+
+ qcom,camera@6f {
+ compatible = "qcom,ov8825";
+ reg = <0x6f>;
+ qcom,slave-id = <0x6c 0x300a 0x8825>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,actuator-src = <&actuator0>;
+ qcom,led-flash-src = <&led_flash0>;
+ qcom,mount-angle = <90>;
+ qcom,sensor-name = "ov8825";
+ cam_vdig-supply = <&pm8110_l2>;
+ cam_vana-supply = <&pm8110_l19>;
+ cam_vio-supply = <&pm8110_l14>;
+ cam_vaf-supply = <&pm8110_l16>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-type = <0 0 0 0>;
+ qcom,cam-vreg-min-voltage = <1200000 1800000 2850000 3000000>;
+ qcom,cam-vreg-max-voltage = <1200000 1800000 2850000 3000000>;
+ qcom,cam-vreg-op-mode = <200000 8000 80000 100000>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 13 0>,
+ <&msmgpio 21 0>,
+ <&msmgpio 20 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1",
+ "CAM_STANDBY";
+ qcom,csi-lane-assign = <0xe4>;
+ qcom,csi-lane-mask = <0x3>;
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <0>;
+ };
+
+ qcom,camera@6d {
+ compatible = "qcom,ov9724";
+ reg = <0x6d>;
+ qcom,slave-id = <0x20 0x0 0x9724>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <90>;
+ qcom,sensor-name = "ov9724";
+ cam_vdig-supply = <&pm8110_l4>;
+ cam_vana-supply = <&pm8110_l19>;
+ cam_vio-supply = <&pm8110_l14>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-type = <0 1 0>;
+ qcom,cam-vreg-min-voltage = <1200000 0 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 14 0>,
+ <&msmgpio 15 0>,
+ <&msmgpio 8 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET",
+ "CAM_STANDBY";
+ qcom,gpio-set-tbl-num = <1 1>;
+ qcom,gpio-set-tbl-flags = <0 2>;
+ qcom,gpio-set-tbl-delay = <1000 4000>;
+ qcom,csi-lane-assign = <0xe4>;
+ qcom,csi-lane-mask = <0x1>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ };
+
+ qcom,camera@78 {
+ compatible = "qcom,sp1628";
+ reg = <0x78>;
+ qcom,slave-id = <0x78 0x0 0x1628>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <90>;
+ qcom,sensor-name = "sp1628";
+ cam_vdig-supply = <&pm8110_l2>;
+ cam_vana-supply = <&pm8110_l19>;
+ cam_vio-supply = <&pm8110_l14>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-type = <0 0 0>;
+ qcom,cam-vreg-min-voltage = <1200000 1800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 1800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 80000 80000>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 14 0>,
+ <&msmgpio 15 0>,
+ <&msmgpio 8 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET",
+ "CAM_STANDBY";
+ qcom,gpio-set-tbl-num = <1 1>;
+ qcom,gpio-set-tbl-flags = <0 2>;
+ qcom,gpio-set-tbl-delay = <1000 4000>;
+ qcom,csi-lane-assign = <0xe4>;
+ qcom,csi-lane-mask = <0x3>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <0>;
+ };
+
+};
diff --git a/arch/arm/boot/dts/msm8610-camera.dtsi b/arch/arm/boot/dts/msm8610-camera.dtsi
new file mode 100644
index 0000000..b1c94dd
--- /dev/null
+++ b/arch/arm/boot/dts/msm8610-camera.dtsi
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2013, 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.
+ */
+
+&soc{
+ qcom,msm-cam@fd8c0000 {
+ compatible = "qcom,msm-cam";
+ reg = <0xfd8C0000 0x10000>;
+ reg-names = "msm-cam";
+ };
+
+ qcom,csiphy@fda00c00 {
+ cell-index = <0>;
+ compatible = "qcom,csiphy";
+ reg = <0xfda00c00 0x1f4>;
+ reg-names = "csiphy";
+ interrupts = <0 78 0>;
+ interrupt-names = "csiphy";
+ };
+
+ qcom,csiphy@fda01000 {
+ cell-index = <1>;
+ compatible = "qcom,csiphy";
+ reg = <0xfda01000 0x1f4>;
+ reg-names = "csiphy";
+ interrupts = <0 79 0>;
+ interrupt-names = "csiphy";
+ };
+
+ qcom,csid@fda00000 {
+ cell-index = <0>;
+ compatible = "qcom,csid";
+ reg = <0xfda00000 0x100>;
+ reg-names = "csid";
+ interrupts = <0 50 0>;
+ interrupt-names = "csid";
+ qcom,csi-vdd-voltage = <1200000>;
+ qcom,mipi-csi-vdd-supply = <&pm8110_l4>;
+ };
+
+ qcom,csid@fda00400 {
+ cell-index = <1>;
+ compatible = "qcom,csid";
+ reg = <0xfda00400 0x100>;
+ reg-names = "csid";
+ interrupts = <0 51 0>;
+ interrupt-names = "csid";
+ qcom,csi-vdd-voltage = <1200000>;
+ qcom,mipi-csi-vdd-supply = <&pm8110_l4>;
+ };
+
+ qcom,ispif@fda00800 {
+ cell-index = <0>;
+ compatible = "qcom,ispif";
+ reg = <0xfda00800 0x200>;
+ reg-names = "ispif";
+ interrupts = <0 52 0>;
+ interrupt-names = "ispif";
+ };
+
+ qcom,vfe@fde00000 {
+ cell-index = <0>;
+ compatible = "qcom,vfe32";
+ reg = <0xfde00000 0x800>;
+ reg-names = "vfe", "vfe_vbif";
+ interrupts = <0 49 0>;
+ interrupt-names = "vfe";
+ vdd-supply = <&gdsc_vfe>;
+ };
+
+};
diff --git a/arch/arm/boot/dts/msm8610-cdp.dts b/arch/arm/boot/dts/msm8610-cdp.dts
index a8a2446..6891b90 100644
--- a/arch/arm/boot/dts/msm8610-cdp.dts
+++ b/arch/arm/boot/dts/msm8610-cdp.dts
@@ -13,35 +13,226 @@
/dts-v1/;
/include/ "msm8610.dtsi"
+/include/ "dsi-v2-panel-truly-wvga-video.dtsi"
+/include/ "msm8610-camera-sensor-cdp-mtp.dtsi"
/ {
model = "Qualcomm MSM 8610 CDP";
compatible = "qcom,msm8610-cdp", "qcom,msm8610", "qcom,cdp";
- qcom,msm-id = <147 1 0>, <165 1 0>;
+ qcom,msm-id = <147 1 0>, <165 1 0>, <161 1 0>, <162 1 0>,
+ <163 1 0>, <164 1 0>, <166 1 0>;
+};
+&soc {
serial@f991e000 {
status = "ok";
};
+
+ i2c@f9923000{
+ atmel_mxt_ts@4a {
+ compatible = "atmel,mxt-ts";
+ reg = <0x4a>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <1 0x2>;
+ vdd_ana-supply = <&pm8110_l19>;
+ vcc_i2c-supply = <&pm8110_l14>;
+ atmel,reset-gpio = <&msmgpio 0 0x00>;
+ atmel,irq-gpio = <&msmgpio 1 0x00>;
+ atmel,panel-coords = <0 0 508 880>;
+ atmel,display-coords = <0 0 480 800>;
+ atmel,i2c-pull-up;
+ atmel,no-force-update;
+ atmel,cfg_1 {
+ atmel,family-id = <0x81>;
+ atmel,variant-id = <0x15>;
+ atmel,version = <0x11>;
+ atmel,build = <0xaa>;
+ atmel,config = [
+ /* Object 6, Instance = 0 */
+ 00 00 00 00 00 00
+ /* Object 38, Instance = 0 */
+ 1D 02 00 0A 06 0D 00 00
+ /* Object 7, Instance = 0 */
+ 20 08 32
+ /* Object 8, Instance = 0 */
+ 19 00 14 14 FF 00 FF 00 00 00
+ /* Object 9, Instance = 0 */
+ 83 00 00 13 0B 00 20 32 01 03
+ 00 32 05 30 0A 05 0A 00 70 03
+ FC 01 00 36 2F D8 00 00 40 00
+ 00 0A 00 00 02
+ /* Object 18, Instance = 0 */
+ 00 00
+ /* Object 19, Instance = 0 */
+ 00 00 00 00 00 00
+ /* Object 25, Instance = 0 */
+ 03 00 18 79 A8 61
+ /* Object 58, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00
+ /* Object 42, Instance = 0 */
+ 00 00 00 00 00 00 00 00
+ /* Object 46, Instance = 0 */
+ 04 03 08 10 00 00 00 00 00
+ /* Object 47, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ /* Object 48, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00
+ /* Object 55, Instance = 0 */
+ 00 00 00 00
+ ];
+ };
+ };
+ };
+
+ gen-vkeys {
+ compatible = "qcom,gen-vkeys";
+ label = "atmel_mxt_ts";
+ qcom,disp-maxx = <480>;
+ qcom,disp-maxy = <800>;
+ qcom,panel-maxx = <508>;
+ qcom,panel-maxy = <880>;
+ qcom,key-codes = <158 102 139>;
+ qcom,y-offset = <35>;
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+
+ camera_snapshot {
+ label = "camera_snapshot";
+ gpios = <&msmgpio 73 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x2fe>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ camera_focus {
+ label = "camera_focus";
+ gpios = <&msmgpio 74 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x210>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&msmgpio 72 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+ };
+
+ sound {
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "INT_LDO_H", "MCLK",
+ "MIC BIAS External", "Handset Mic",
+ "MIC BIAS Internal2", "Headset Mic",
+ "AMIC1", "MIC BIAS External",
+ "AMIC2", "MIC BIAS Internal2";
+ };
+};
+
+&i2c_cdc {
+ msm8x10_wcd_codec@0d{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x0d>;
+ cdc-vdda-cp-supply = <&pm8110_s4>;
+ qcom,cdc-vdda-cp-voltage = <2150000 2150000>;
+ qcom,cdc-vdda-cp-current = <650000>;
+
+ cdc-vdda-h-supply = <&pm8110_l6>;
+ qcom,cdc-vdda-h-voltage = <1800000 1800000>;
+ qcom,cdc-vdda-h-current = <250000>;
+
+ cdc-vdd-px-supply = <&pm8110_l6>;
+ qcom,cdc-vdd-px-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-px-current = <10000>;
+
+ cdc-vdd-1p2v-supply = <&pm8110_l4>;
+ qcom,cdc-vdd-1p2v-voltage = <1200000 1200000>;
+ qcom,cdc-vdd-1p2v-current = <5000>;
+
+ cdc-vdd-mic-bias-supply = <&pm8110_l20>;
+ qcom,cdc-vdd-mic-bias-voltage = <3075000 3075000>;
+ qcom,cdc-vdd-mic-bias-current = <25000>;
+
+ qcom,cdc-micbias-cfilt-sel = <0x0>;
+ qcom,cdc-micbias-cfilt-mv = <1800000>;
+ qcom,cdc-mclk-clk-rate = <12288000>;
+ };
+
+ msm8x10_wcd_codec@77{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x77>;
+ };
+
+ msm8x10_wcd_codec@66{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x66>;
+ };
+
+ msm8x10_wcd_codec@55{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x55>;
+ };
};
&spmi_bus {
qcom,pm8110@0 {
+ qcom,leds@a100 {
+ status = "okay";
+ qcom,led_mpp_2 {
+ label = "mpp";
+ linux,name = "button-backlight";
+ linux-default-trigger = "hr-trigger";
+ qcom,default-state = "off";
+ qcom,max-current = <40>;
+ qcom,id = <6>;
+ qcom,source-sel = <1>;
+ qcom,mode-ctrl = <0x60>;
+ qcom,mode = "manual";
+ };
+ };
+
qcom,leds@a200 {
status = "okay";
qcom,led_mpp_3 {
label = "mpp";
linux,name = "wled-backlight";
- linux-default-trigger = "none";
+ linux,default-trigger = "bkl-trigger";
qcom,default-state = "on";
qcom,max-current = <40>;
qcom,id = <6>;
qcom,source-sel = <1>;
qcom,mode-ctrl = <0x10>;
+ qcom,mode = "manual";
};
};
};
};
+&spmi_bus {
+ qcom,pm8110@1 {
+ qcom,vibrator@c000 {
+ status = "okay";
+ qcom,vib-timeout-ms = <15000>;
+ qcom,vib-vtg-level-mV = <3100>;
+ };
+ };
+};
+
&sdhc_1 {
vdd-supply = <&pm8110_l17>;
qcom,vdd-always-on;
@@ -51,6 +242,7 @@
vdd-io-supply = <&pm8110_l6>;
qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
qcom,vdd-io-voltage-level = <1800000 1800000>;
qcom,vdd-io-current-level = <200 60000>;
@@ -72,8 +264,6 @@
qcom,vdd-current-level = <15000 400000>;
vdd-io-supply = <&pm8110_l21>;
- qcom,vdd-io-always-on;
- qcom,vdd-io-lpm-sup;
qcom,vdd-io-voltage-level = <1800000 2950000>;
qcom,vdd-io-current-level = <200 50000>;
@@ -100,18 +290,18 @@
&pm8110_chg {
status = "ok";
- qcom,chg-charging-disabled;
- qcom,chg-use-default-batt-values;
+ qcom,charging-disabled;
+ qcom,use-default-batt-values;
- qcom,chg-chgr@1000 {
+ qcom,chgr@1000 {
status = "ok";
};
- qcom,chg-buck@1100 {
+ qcom,buck@1100 {
status = "ok";
};
- qcom,chg-usb-chgpth@1300 {
+ qcom,usb-chgpth@1300 {
status = "ok";
};
@@ -119,3 +309,88 @@
status = "ok";
};
};
+
+&pm8110_gpios {
+ gpio@c000 { /* GPIO 1 */
+ };
+
+ gpio@c100 { /* GPIO 2 */
+ };
+
+ gpio@c200 { /* GPIO 3 */
+ };
+
+ gpio@c300 { /* GPIO 4 */
+ };
+};
+
+&pm8110_mpps {
+ mpp@a000 { /* MPP 1 */
+ };
+
+ mpp@a100 { /* MPP 2 */
+ status = "disabled";
+ };
+
+ mpp@a200 { /* MPP 3 */
+ status = "disabled";
+ };
+
+ mpp@a300 { /* MPP 4 */
+ /* PA_THERM config */
+ qcom,mode = <4>; /* AIN input */
+ qcom,invert = <1>; /* Enable MPP */
+ qcom,ain-route = <3>; /* AMUX 8 */
+ qcom,master-en = <1>;
+ qcom,src-sel = <0>; /* Function constant */
+ };
+};
+
+/* CoreSight */
+&tpiu {
+ qcom,seta-gpios = <&msmgpio 4 0>,
+ <&msmgpio 5 0>,
+ <&msmgpio 6 0>,
+ <&msmgpio 7 0>,
+ <&msmgpio 22 0>,
+ <&msmgpio 23 0>,
+ <&msmgpio 24 0>,
+ <&msmgpio 25 0>,
+ <&msmgpio 26 0>,
+ <&msmgpio 27 0>,
+ <&msmgpio 28 0>,
+ <&msmgpio 29 0>,
+ <&msmgpio 30 0>,
+ <&msmgpio 31 0>,
+ <&msmgpio 94 0>,
+ <&msmgpio 95 0>,
+ <&msmgpio 96 0>,
+ <&msmgpio 97 0>;
+ qcom,seta-gpios-func = <9 9 8 11 2 2 2 2 2 2 3 2 3 3 4 4 4 4>;
+ qcom,seta-gpios-drv = <7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7>;
+ qcom,seta-gpios-pull = <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ qcom,seta-gpios-dir = <2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2>;
+
+ qcom,setb-gpios = <&msmgpio 8 0>,
+ <&msmgpio 10 0>,
+ <&msmgpio 11 0>,
+ <&msmgpio 13 0>,
+ <&msmgpio 14 0>,
+ <&msmgpio 15 0>,
+ <&msmgpio 16 0>,
+ <&msmgpio 17 0>,
+ <&msmgpio 18 0>,
+ <&msmgpio 19 0>,
+ <&msmgpio 20 0>,
+ <&msmgpio 21 0>,
+ <&msmgpio 42 0>,
+ <&msmgpio 80 0>,
+ <&msmgpio 81 0>,
+ <&msmgpio 82 0>,
+ <&msmgpio 83 0>,
+ <&msmgpio 84 0>;
+ qcom,setb-gpios-func = <10 8 8 6 9 9 9 9 9 9 9 9 5 7 7 8 8 8>;
+ qcom,setb-gpios-drv = <7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7>;
+ qcom,setb-gpios-pull = <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ qcom,setb-gpios-dir = <2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2>;
+};
diff --git a/arch/arm/boot/dts/msm8610-coresight.dtsi b/arch/arm/boot/dts/msm8610-coresight.dtsi
index a0a2c14..516522e 100644
--- a/arch/arm/boot/dts/msm8610-coresight.dtsi
+++ b/arch/arm/boot/dts/msm8610-coresight.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
tmc_etr: tmc@fc326000 {
compatible = "arm,coresight-tmc";
reg = <0xfc326000 0x1000>,
@@ -34,6 +34,11 @@
coresight-id = <1>;
coresight-name = "coresight-tpiu";
coresight-nr-inports = <1>;
+
+ vdd-supply = <&pm8110_l18>;
+
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <15000 400000>;
};
replicator: replicator@fc324000 {
@@ -335,4 +340,18 @@
coresight-name = "coresight-cti-cpu3";
coresight-nr-inports = <0>;
};
+
+ hwevent: hwevent@fd820018 {
+ compatible = "qcom,coresight-hwevent";
+ reg = <0xfd820018 0x80>,
+ <0xf9011080 0x80>,
+ <0xfd4ab160 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+
+ coresight-id = <27>;
+ coresight-name = "coresight-hwevent";
+ coresight-nr-inports = <0>;
+
+ qcom,hwevent-clks = "core_mmss_clk";
+ };
};
diff --git a/arch/arm/boot/dts/msm8610-gpu.dtsi b/arch/arm/boot/dts/msm8610-gpu.dtsi
index 5e57430..7e3ee0d 100644
--- a/arch/arm/boot/dts/msm8610-gpu.dtsi
+++ b/arch/arm/boot/dts/msm8610-gpu.dtsi
@@ -9,7 +9,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-/ {
+&soc {
msm_gpu: qcom,kgsl-3d0@fdc00000 {
label = "kgsl-3d0";
compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
@@ -25,10 +25,10 @@
qcom,initial-pwrlevel = <1>;
qcom,idle-timeout = <8>; /* <HZ/12> */
- qcom,nap-allowed = <1>;
qcom,strtstp-sleepwake;
- qcom,clk-map = <0x000001E>; /* KGSL_CLK_CORE |
- KGSL_CLK_IFACE | KGSL_CLK_MEM | KGSL_CLK_MEM_IFACE */
+ qcom,clk-map = <0x000005E>; /* KGSL_CLK_CORE |
+ KGSL_CLK_IFACE | KGSL_CLK_MEM | KGSL_CLK_MEM_IFACE |
+ KGSL_CLK_ALT_MEM_IFACE */
/* Bus Scale Settings */
qcom,msm-bus,name = "grp3d";
diff --git a/arch/arm/boot/dts/msm8610-iommu-domains.dtsi b/arch/arm/boot/dts/msm8610-iommu-domains.dtsi
index 0f48517..6f43897 100644
--- a/arch/arm/boot/dts/msm8610-iommu-domains.dtsi
+++ b/arch/arm/boot/dts/msm8610-iommu-domains.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,iommu-domains {
compatible = "qcom,iommu-domains";
diff --git a/arch/arm/boot/dts/msm8610-ion.dtsi b/arch/arm/boot/dts/msm8610-ion.dtsi
index 41b58da..456b60c 100644
--- a/arch/arm/boot/dts/msm8610-ion.dtsi
+++ b/arch/arm/boot/dts/msm8610-ion.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,ion {
compatible = "qcom,msm-ion";
#address-cells = <1>;
@@ -31,9 +31,7 @@
qcom,ion-heap@27 { /* QSECOM HEAP */
compatible = "qcom,msm-ion-reserve";
reg = <27>;
- qcom,heap-align = <0x1000>;
- qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0x100000>;
+ linux,contiguous-region = <&qsecom_mem>;
};
};
};
diff --git a/arch/arm/boot/dts/msm8610-mdss.dtsi b/arch/arm/boot/dts/msm8610-mdss.dtsi
new file mode 100644
index 0000000..af0e3e4
--- /dev/null
+++ b/arch/arm/boot/dts/msm8610-mdss.dtsi
@@ -0,0 +1,37 @@
+/* Copyright (c) 2012-2013, 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.
+ */
+
+&soc {
+ qcom,mdss_mdp@fd900000 {
+ compatible = "qcom,mdss_mdp3";
+ reg = <0xfd900000 0x100000>;
+ reg-names = "mdp_phys";
+ interrupts = <0 72 0>;
+
+ mdss_fb0: qcom,mdss_fb_primary {
+ cell-index = <0>;
+ compatible = "qcom,mdss-fb";
+ qcom,memory-reservation-type = "EBI1";
+ qcom,memory-reservation-size = <0x300000>;
+ };
+ };
+
+ mdss_dsi0: qcom,mdss_dsi@fdd00000 {
+ compatible = "qcom,msm-dsi-v2";
+ label = "MDSS DSI CTRL->0";
+ cell-index = <0>;
+ reg = <0xfdd00000 0x100000>;
+ interrupts = <0 30 0>;
+ vdda-supply = <&pm8110_l4>;
+ qcom,mdss-fb-map = <&mdss_fb0>;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8610-mtp.dts b/arch/arm/boot/dts/msm8610-mtp.dts
index b1511ff..ddbe3a0 100644
--- a/arch/arm/boot/dts/msm8610-mtp.dts
+++ b/arch/arm/boot/dts/msm8610-mtp.dts
@@ -13,35 +13,226 @@
/dts-v1/;
/include/ "msm8610.dtsi"
+/include/ "dsi-v2-panel-truly-wvga-video.dtsi"
+/include/ "msm8610-camera-sensor-cdp-mtp.dtsi"
/ {
model = "Qualcomm MSM 8610 MTP";
compatible = "qcom,msm8610-mtp", "qcom,msm8610", "qcom,mtp";
- qcom,msm-id = <147 8 0>, <165 8 0>;
+ qcom,msm-id = <147 8 0>, <165 8 0>, <161 8 0>, <162 8 0>,
+ <163 8 0>, <164 8 0>, <166 8 0>;
+};
- serial@f991f000 {
+&soc {
+ serial@f991e000 {
status = "ok";
};
+
+ i2c@f9923000{
+ atmel_mxt_ts@4a {
+ compatible = "atmel,mxt-ts";
+ reg = <0x4a>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <1 0x2>;
+ vdd_ana-supply = <&pm8110_l19>;
+ vcc_i2c-supply = <&pm8110_l14>;
+ atmel,reset-gpio = <&msmgpio 0 0x00>;
+ atmel,irq-gpio = <&msmgpio 1 0x00>;
+ atmel,panel-coords = <0 0 508 880>;
+ atmel,display-coords = <0 0 480 800>;
+ atmel,i2c-pull-up;
+ atmel,no-force-update;
+ atmel,cfg_1 {
+ atmel,family-id = <0x81>;
+ atmel,variant-id = <0x15>;
+ atmel,version = <0x11>;
+ atmel,build = <0xaa>;
+ atmel,config = [
+ /* Object 6, Instance = 0 */
+ 00 00 00 00 00 00
+ /* Object 38, Instance = 0 */
+ 1D 02 00 0A 06 0D 00 00
+ /* Object 7, Instance = 0 */
+ 20 08 32
+ /* Object 8, Instance = 0 */
+ 19 00 14 14 FF 00 FF 00 00 00
+ /* Object 9, Instance = 0 */
+ 83 00 00 13 0B 00 20 32 01 03
+ 00 32 05 30 0A 05 0A 00 70 03
+ FC 01 00 36 2F D8 00 00 40 00
+ 00 0A 00 00 02
+ /* Object 18, Instance = 0 */
+ 00 00
+ /* Object 19, Instance = 0 */
+ 00 00 00 00 00 00
+ /* Object 25, Instance = 0 */
+ 03 00 18 79 A8 61
+ /* Object 58, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00
+ /* Object 42, Instance = 0 */
+ 00 00 00 00 00 00 00 00
+ /* Object 46, Instance = 0 */
+ 04 03 08 10 00 00 00 00 00
+ /* Object 47, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ /* Object 48, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00
+ /* Object 55, Instance = 0 */
+ 00 00 00 00
+ ];
+ };
+ };
+ };
+
+ gen-vkeys {
+ compatible = "qcom,gen-vkeys";
+ label = "atmel_mxt_ts";
+ qcom,disp-maxx = <480>;
+ qcom,disp-maxy = <800>;
+ qcom,panel-maxx = <508>;
+ qcom,panel-maxy = <880>;
+ qcom,key-codes = <158 102 139>;
+ qcom,y-offset = <35>;
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+
+ camera_snapshot {
+ label = "camera_snapshot";
+ gpios = <&msmgpio 73 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x2fe>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ camera_focus {
+ label = "camera_focus";
+ gpios = <&msmgpio 74 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x210>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&msmgpio 72 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+ };
+
+ sound {
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "INT_LDO_H", "MCLK",
+ "MIC BIAS External", "Handset Mic",
+ "MIC BIAS Internal2", "Headset Mic",
+ "AMIC1", "MIC BIAS External",
+ "AMIC2", "MIC BIAS Internal2";
+ };
+};
+
+&i2c_cdc {
+ msm8x10_wcd_codec@0d{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x0d>;
+ cdc-vdda-cp-supply = <&pm8110_s4>;
+ qcom,cdc-vdda-cp-voltage = <2150000 2150000>;
+ qcom,cdc-vdda-cp-current = <650000>;
+
+ cdc-vdda-h-supply = <&pm8110_l6>;
+ qcom,cdc-vdda-h-voltage = <1800000 1800000>;
+ qcom,cdc-vdda-h-current = <250000>;
+
+ cdc-vdd-px-supply = <&pm8110_l6>;
+ qcom,cdc-vdd-px-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-px-current = <10000>;
+
+ cdc-vdd-1p2v-supply = <&pm8110_l4>;
+ qcom,cdc-vdd-1p2v-voltage = <1200000 1200000>;
+ qcom,cdc-vdd-1p2v-current = <5000>;
+
+ cdc-vdd-mic-bias-supply = <&pm8110_l20>;
+ qcom,cdc-vdd-mic-bias-voltage = <3075000 3075000>;
+ qcom,cdc-vdd-mic-bias-current = <25000>;
+
+ qcom,cdc-micbias-cfilt-sel = <0x0>;
+ qcom,cdc-micbias-cfilt-mv = <1800000>;
+ qcom,cdc-mclk-clk-rate = <12288000>;
+ };
+
+ msm8x10_wcd_codec@77{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x77>;
+ };
+
+ msm8x10_wcd_codec@66{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x66>;
+ };
+
+ msm8x10_wcd_codec@55{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x55>;
+ };
};
&spmi_bus {
qcom,pm8110@0 {
+ qcom,leds@a100 {
+ status = "okay";
+ qcom,led_mpp_2 {
+ label = "mpp";
+ linux,name = "button-backlight";
+ linux-default-trigger = "hr-trigger";
+ qcom,default-state = "off";
+ qcom,max-current = <40>;
+ qcom,id = <6>;
+ qcom,source-sel = <1>;
+ qcom,mode-ctrl = <0x60>;
+ qcom,mode = "manual";
+ };
+ };
+
qcom,leds@a200 {
status = "okay";
qcom,led_mpp_3 {
label = "mpp";
linux,name = "wled-backlight";
- linux-default-trigger = "none";
+ linux,default-trigger = "bkl-trigger";
qcom,default-state = "on";
qcom,max-current = <40>;
qcom,id = <6>;
qcom,source-sel = <1>;
qcom,mode-ctrl = <0x10>;
+ qcom,mode = "manual";
};
};
};
};
+&spmi_bus {
+ qcom,pm8110@1 {
+ qcom,vibrator@c000 {
+ status = "okay";
+ qcom,vib-timeout-ms = <15000>;
+ qcom,vib-vtg-level-mV = <3100>;
+ };
+ };
+};
+
&sdhc_1 {
vdd-supply = <&pm8110_l17>;
qcom,vdd-always-on;
@@ -51,6 +242,7 @@
vdd-io-supply = <&pm8110_l6>;
qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
qcom,vdd-io-voltage-level = <1800000 1800000>;
qcom,vdd-io-current-level = <200 60000>;
@@ -72,8 +264,6 @@
qcom,vdd-current-level = <15000 400000>;
vdd-io-supply = <&pm8110_l21>;
- qcom,vdd-io-always-on;
- qcom,vdd-io-lpm-sup;
qcom,vdd-io-voltage-level = <1800000 2950000>;
qcom,vdd-io-current-level = <200 50000>;
@@ -100,21 +290,21 @@
&pm8110_chg {
status = "ok";
- qcom,chg-charging-disabled;
+ qcom,charging-disabled;
- qcom,chg-chgr@1000 {
+ qcom,chgr@1000 {
status = "ok";
};
- qcom,chg-buck@1100 {
+ qcom,buck@1100 {
status = "ok";
};
- qcom,chg-bat-if@1200 {
+ qcom,bat-if@1200 {
status = "ok";
};
- qcom,chg-usb-chgpth@1300 {
+ qcom,usb-chgpth@1300 {
status = "ok";
};
@@ -122,3 +312,43 @@
status = "ok";
};
};
+
+&pm8110_gpios {
+ gpio@c000 { /* GPIO 1 */
+ };
+
+ gpio@c100 { /* GPIO 2 */
+ };
+
+ gpio@c200 { /* GPIO 3 */
+ };
+
+ gpio@c300 { /* GPIO 4 */
+ };
+};
+
+&pm8110_mpps {
+ mpp@a000 { /* MPP 1 */
+ };
+
+ mpp@a100 { /* MPP 2 */
+ status = "disabled";
+ };
+
+ mpp@a200 { /* MPP 3 */
+ status = "disabled";
+ };
+
+ mpp@a300 { /* MPP 4 */
+ /* PA_THERM config */
+ qcom,mode = <4>; /* AIN input */
+ qcom,invert = <1>; /* Enable MPP */
+ qcom,ain-route = <3>; /* AMUX 8 */
+ qcom,master-en = <1>;
+ qcom,src-sel = <0>; /* Function constant */
+ };
+};
+
+&pm8110_bms {
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/msm8610-pm.dtsi b/arch/arm/boot/dts/msm8610-pm.dtsi
index e8849f6..faa7a41 100644
--- a/arch/arm/boot/dts/msm8610-pm.dtsi
+++ b/arch/arm/boot/dts/msm8610-pm.dtsi
@@ -10,9 +10,7 @@
* GNU General Public License for more details.
*/
-/include/ "skeleton.dtsi"
-
-/ {
+&soc {
qcom,spm@f9089000 {
compatible = "qcom,spm-v2";
#address-cells = <1>;
@@ -114,7 +112,7 @@
qcom,type = <0x61706d73>; /* "smpa" */
qcom,id = <0x01>;
qcom,key = <0x6e726f63>; /* "corn" */
- qcom,init-value = <5>; /* Super Turbo */
+ qcom,init-value = <3>; /* SVS SOC */
};
qcom,lpm-resources@1 {
@@ -123,7 +121,7 @@
qcom,type = <0x616F646C>; /* "ldoa" */
qcom,id = <0x03>;
qcom,key = <0x6e726f63>; /* "corn" */
- qcom,init-value = <3>; /* Active */
+ qcom,init-value = <3>; /* SVS SOC */
};
qcom,lpm-resources@2 {
@@ -153,10 +151,10 @@
qcom,mode = "wfi";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_active";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <1>;
@@ -170,10 +168,10 @@
qcom,mode = "standalone_pc";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_active";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <3000>;
@@ -187,10 +185,10 @@
qcom,mode = "pc";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_retention";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <8000>;
@@ -204,10 +202,10 @@
qcom,mode = "pc";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <3>; /* NORMAL */
- qcom,vdd-mem-lower-bound = <2>; /* SVS SOC */
- qcom,vdd-dig-upper-bound = <3>; /* NORMAL */
- qcom,vdd-dig-lower-bound = <2>; /* SVS SOC */
+ qcom,vdd-mem-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-mem-lower-bound = <3>; /* SVS SOC */
+ qcom,vdd-dig-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <9000>;
@@ -221,10 +219,10 @@
qcom,mode = "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,latency-us = <16300>;
qcom,ss-power = <63>;
qcom,energy-overhead = <2128000>;
@@ -236,10 +234,10 @@
qcom,mode = "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <3>; /* NORMAL */
- qcom,vdd-mem-lower-bound = <2>; /* SVS SOC */
- qcom,vdd-dig-upper-bound = <3>; /* NORMAL */
- qcom,vdd-dig-lower-bound = <2>; /* SVS SOC */
+ qcom,vdd-mem-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-mem-lower-bound = <3>; /* SVS SOC */
+ qcom,vdd-dig-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
qcom,latency-us = <24000>;
qcom,ss-power = <10>;
qcom,energy-overhead = <3202600>;
@@ -251,10 +249,10 @@
qcom,mode = "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <2>; /* SVS SOC */
- qcom,vdd-mem-lower-bound = <0>; /* RETENTION */
- qcom,vdd-dig-upper-bound = <2>; /* SVS SOC */
- qcom,vdd-dig-lower-bound = <0>; /* RETENTION */
+ qcom,vdd-mem-upper-bound = <3>; /* SVS SOC */
+ qcom,vdd-mem-lower-bound = <1>; /* RETENTION */
+ qcom,vdd-dig-upper-bound = <3>; /* SVS SOC */
+ qcom,vdd-dig-lower-bound = <1>; /* RETENTION */
qcom,latency-us = <26000>;
qcom,ss-power = <2>;
qcom,energy-overhead = <4252000>;
@@ -382,9 +380,9 @@
qcom,offset-page-indices = <56>;
};
- qcom,rpm-stats@0xfc19dbd0{
+ qcom,rpm-stats@fc19dba0 {
compatible = "qcom,rpm-stats";
- reg = <0xfc19dbd0 0x1000>;
+ reg = <0xfc19dba0 0x1000>;
reg-names = "phys_addr_base";
qcom,sleep-stats-version = <2>;
};
diff --git a/arch/arm/boot/dts/msm8610-qrd.dts b/arch/arm/boot/dts/msm8610-qrd.dts
new file mode 100644
index 0000000..42367b3
--- /dev/null
+++ b/arch/arm/boot/dts/msm8610-qrd.dts
@@ -0,0 +1,299 @@
+/* Copyright (c) 2013, 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.
+ */
+
+/dts-v1/;
+
+/include/ "msm8610.dtsi"
+/include/ "dsi-v2-panel-hx8379a-wvga-video.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8610 QRD";
+ compatible = "qcom,msm8610-qrd", "qcom,msm8610", "qcom,qrd";
+ qcom,msm-id = <147 11 0>, <165 11 0>, <161 11 0>, <162 11 0>,
+ <163 11 0>, <164 11 0>, <166 11 0>;
+};
+
+&soc {
+ i2c@f9923000{
+ focaltech@38{
+ compatible = "focaltech,5x06";
+ reg = <0x38>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <1 0x2>;
+ vdd-supply = <&pm8110_l19>;
+ vcc_i2c-supply = <&pm8110_l14>;
+ focaltech,family-id = <0x06>;
+ focaltech,reset-gpio = <&msmgpio 0 0x00>;
+ focaltech,irq-gpio = <&msmgpio 1 0x00>;
+ focaltech,display-coords = <0 0 480 800>;
+ focaltech,panel-coords = <0 0 480 800>;
+ focaltech,button-map= <139 102 158>;
+ focaltech,no-force-update;
+ focaltech,i2c-pull-up;
+ };
+ };
+
+ gen-vkeys {
+ compatible = "qcom,gen-vkeys";
+ label = "ft5x06_ts";
+ qcom,disp-maxx = <480>;
+ qcom,disp-maxy = <800>;
+ qcom,panel-maxx = <481>;
+ qcom,panel-maxy = <940>;
+ qcom,key-codes = <139 0 102 158 0 0 0>;
+ qcom,y-offset = <0>;
+ };
+ serial@f991e000 {
+ status = "ok";
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+
+ camera_snapshot {
+ label = "camera_snapshot";
+ gpios = <&msmgpio 73 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x2fe>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ camera_focus {
+ label = "camera_focus";
+ gpios = <&msmgpio 74 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x210>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&msmgpio 72 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+ };
+
+ i2c@f9927000 {
+ msm8x10_wcd_codec@0d{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x0d>;
+ cdc-vdda-cp-supply = <&pm8110_s4>;
+ qcom,cdc-vdda-cp-voltage = <2150000 2150000>;
+ qcom,cdc-vdda-cp-current = <650000>;
+
+ cdc-vdda-h-supply = <&pm8110_l6>;
+ qcom,cdc-vdda-h-voltage = <1800000 1800000>;
+ qcom,cdc-vdda-h-current = <250000>;
+
+ cdc-vdd-px-supply = <&pm8110_l6>;
+ qcom,cdc-vdd-px-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-px-current = <10000>;
+
+ cdc-vdd-1p2v-supply = <&pm8110_l4>;
+ qcom,cdc-vdd-1p2v-voltage = <1200000 1200000>;
+ qcom,cdc-vdd-1p2v-current = <5000>;
+
+ cdc-vdd-mic-bias-supply = <&pm8110_l20>;
+ qcom,cdc-vdd-mic-bias-voltage = <3075000 3075000>;
+ qcom,cdc-vdd-mic-bias-current = <25000>;
+
+ qcom,cdc-micbias-cfilt-sel = <0x0>;
+ qcom,cdc-micbias-cfilt-mv = <1800000>;
+ qcom,cdc-mclk-clk-rate = <12288000>;
+ };
+
+ msm8x10_wcd_codec@77{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x77>;
+ };
+
+ msm8x10_wcd_codec@66{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x66>;
+ };
+
+ msm8x10_wcd_codec@55{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x55>;
+ };
+ };
+
+ sound {
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "INT_LDO_H", "MCLK",
+ "MIC BIAS Internal1", "Handset Mic",
+ "MIC BIAS Internal2", "Headset Mic",
+ "AMIC1", "MIC BIAS Internal1",
+ "AMIC2", "MIC BIAS Internal2";
+ };
+};
+
+&spmi_bus {
+ qcom,pm8110@0 {
+ qcom,leds@a100 {
+ status = "okay";
+ qcom,led_mpp_2 {
+ label = "mpp";
+ linux,name = "wled-homerow";
+ linux-default-trigger = "hr-trigger";
+ qcom,default-state = "off";
+ qcom,max-current = <40>;
+ qcom,id = <6>;
+ qcom,source-sel = <1>;
+ qcom,mode-ctrl = <0x61>;
+ qcom,mode = "manual";
+ };
+ };
+
+ qcom,leds@a200 {
+ status = "okay";
+ qcom,led_mpp_3 {
+ label = "mpp";
+ linux,name = "wled-backlight";
+ linux,default-trigger = "bkl-trigger";
+ qcom,default-state = "on";
+ qcom,max-current = <40>;
+ qcom,id = <6>;
+ qcom,source-sel = <1>;
+ qcom,mode-ctrl = <0x10>;
+ qcom,mode = "manual";
+ };
+ };
+ };
+};
+
+&spmi_bus {
+ qcom,pm8110@1 {
+ qcom,vibrator@c000 {
+ status = "okay";
+ qcom,vib-timeout-ms = <15000>;
+ qcom,vib-vtg-level-mV = <3100>;
+ };
+ };
+};
+
+&sdhc_1 {
+ vdd-supply = <&pm8110_l17>;
+ qcom,vdd-always-on;
+ qcom,vdd-lpm-sup;
+ qcom,vdd-voltage-level = <2900000 2900000>;
+ qcom,vdd-current-level = <200 400000>;
+
+ vdd-io-supply = <&pm8110_l6>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <200 60000>;
+
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ qcom,nonremovable;
+
+ status = "ok";
+};
+
+&sdhc_2 {
+ vdd-supply = <&pm8110_l18>;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <15000 400000>;
+
+ vdd-io-supply = <&pm8110_l21>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
+ qcom,vdd-io-voltage-level = <1800000 2950000>;
+ qcom,vdd-io-current-level = <200 50000>;
+
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_2>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 221 0
+ 2 &msmgpio 42 0x3>;
+ interrupt-names = "hc_irq", "pwr_irq", "status_irq";
+ cd-gpios = <&msmgpio 42 0x1>;
+
+ status = "ok";
+};
+
+&pm8110_chg {
+ status = "ok";
+
+ qcom,chgr@1000 {
+ status = "ok";
+ };
+
+ qcom,buck@1100 {
+ status = "ok";
+ };
+
+ qcom,bat-if@1200 {
+ status = "ok";
+ };
+
+ qcom,usb-chgpth@1300 {
+ status = "ok";
+ };
+
+ qcom,chg-misc@1600 {
+ status = "ok";
+ };
+};
+
+&pm8110_gpios {
+ gpio@c000 { /* GPIO 1 */
+ };
+
+ gpio@c100 { /* GPIO 2 */
+ };
+
+ gpio@c200 { /* GPIO 3 */
+ };
+
+ gpio@c300 { /* GPIO 4 */
+ };
+};
+
+&pm8110_mpps {
+ mpp@a000 { /* MPP 1 */
+ };
+
+ mpp@a100 { /* MPP 2 */
+ status = "disabled";
+ };
+
+ mpp@a200 { /* MPP 3 */
+ status = "disabled";
+ };
+
+ mpp@a300 { /* MPP 4 */
+ };
+};
diff --git a/arch/arm/boot/dts/msm8610-regulator.dtsi b/arch/arm/boot/dts/msm8610-regulator.dtsi
index 67eee5c..09520c5 100644
--- a/arch/arm/boot/dts/msm8610-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8610-regulator.dtsi
@@ -26,24 +26,42 @@
/* CPR controlled regulator */
-/ {
+&soc {
apc_vreg_corner: regulator@f9018000 {
status = "okay";
compatible = "qcom,cpr-regulator";
- reg = <0xf9018000 0x1000>,
- <0xfc4b80b0 8>;
- reg-names = "rbcpr", "efuse_phys";
+ reg = <0xf9018000 0x1000>, <0xf9011064 4>, <0xfc4b80b0 8>,
+ <0xfc4bc450 16>;
+ reg-names = "rbcpr", "rbcpr_clk", "pvs_efuse", "cpr_efuse";
+ interrupts = <0 15 0>;
regulator-name = "apc_corner";
regulator-min-microvolt = <1>;
- regulator-max-microvolt = <4>;
+ regulator-max-microvolt = <3>;
qcom,num-efuse-bits = <5>;
- qcom,efuse-bit-pos = <6 7 8 9 10>;
- qcom,pvs-bin-process = <0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2
+ qcom,pvs-bin-process = <0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 2
2 2 2 2 3 3 3 3 3 3 3 3 0 0 0 0>;
- qcom,pvs-corner-ceiling-slow = <1050000 1150000 1275000 1350000>;
- qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000 1200000>;
- qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000 1140000>;
+ qcom,pvs-corner-ceiling-slow = <1150000 1150000 1275000>;
+ qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000>;
+ qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000>;
vdd-apc-supply = <&pm8110_s2>;
+
+ vdd-mx-supply = <&pm8110_l3_ao>;
+ qcom,vdd-mx-vmax = <1350000>;
+ qcom,vdd-mx-vmin-method = <1>;
+
+ qcom,cpr-ref-clk = <19200>;
+ qcom,cpr-timer-delay = <5000>;
+ qcom,cpr-timer-cons-up = <1>;
+ qcom,cpr-timer-cons-down = <2>;
+ qcom,cpr-irq-line = <0>;
+ qcom,cpr-step-quotient = <15>;
+ qcom,cpr-up-threshold = <1>;
+ qcom,cpr-down-threshold = <2>;
+ qcom,cpr-idle-clocks = <5>;
+ qcom,cpr-gcnt-time = <1>;
+ qcom,vdd-apc-step-up-limit = <1>;
+ qcom,vdd-apc-step-down-limit = <1>;
+ qcom,cpr-apc-volt-step = <5000>;
};
};
@@ -66,7 +84,7 @@
regulator-min-microvolt = <1>;
regulator-max-microvolt = <7>;
qcom,use-voltage-corner;
- qcom,consumer-supplies = "vdd_dig", "", "vdd_sr2_dig", "";
+ qcom,consumer-supplies = "vdd_dig", "";
};
pm8110_s1_corner_ao: regulator-s1-corner-ao {
@@ -76,6 +94,7 @@
regulator-min-microvolt = <1>;
regulator-max-microvolt = <7>;
qcom,use-voltage-corner;
+ qcom,consumer-supplies = "vdd_sr2_dig", "";
};
};
@@ -214,8 +233,27 @@
regulator-max-microvolt = <1800000>;
qcom,init-voltage = <1800000>;
status = "okay";
+ };
+
+ pm8110_l10_ao: regulator-l10-ao {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8110_l10_ao";
+ qcom,set = <1>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
qcom,consumer-supplies = "vdd_sr2_pll", "";
};
+
+ pm8110_l10_so: regulator-l10-so {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8110_l10_so";
+ qcom,set = <2>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+ qcom,init-enable = <0>;
+ };
};
rpm-regulator-ldoa12 {
diff --git a/arch/arm/boot/dts/msm8610-rumi.dts b/arch/arm/boot/dts/msm8610-rumi.dts
index cab7560..7f06485 100644
--- a/arch/arm/boot/dts/msm8610-rumi.dts
+++ b/arch/arm/boot/dts/msm8610-rumi.dts
@@ -18,7 +18,9 @@
model = "Qualcomm MSM 8610 Rumi";
compatible = "qcom,msm8610-rumi", "qcom,msm8610", "qcom,rumi";
qcom,msm-id = <147 15 0>;
+};
+&soc {
serial@f991f000 {
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8610-sim.dts b/arch/arm/boot/dts/msm8610-sim.dts
index 1838b94..7c57fe6 100644
--- a/arch/arm/boot/dts/msm8610-sim.dts
+++ b/arch/arm/boot/dts/msm8610-sim.dts
@@ -18,8 +18,55 @@
model = "Qualcomm MSM 8610 Simulator";
compatible = "qcom,msm8610-sim", "qcom,msm8610", "qcom,sim";
qcom,msm-id = <147 16 0>;
+};
+&soc {
serial@f991f000 {
status = "ok";
};
};
+
+&i2c_cdc {
+ msm8x10_wcd_codec@0d{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x0d>;
+ cdc-vdda-cp-supply = <&pm8110_s4>;
+ qcom,cdc-vdda-cp-voltage = <2150000 2150000>;
+ qcom,cdc-vdda-cp-current = <650000>;
+
+ cdc-vdda-h-supply = <&pm8110_l6>;
+ qcom,cdc-vdda-h-voltage = <1800000 1800000>;
+ qcom,cdc-vdda-h-current = <250000>;
+
+ cdc-vdd-px-supply = <&pm8110_l6>;
+ qcom,cdc-vdd-px-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-px-current = <10000>;
+
+ cdc-vdd-1p2v-supply = <&pm8110_l4>;
+ qcom,cdc-vdd-1p2v-voltage = <1200000 1200000>;
+ qcom,cdc-vdd-1p2v-current = <5000>;
+
+ cdc-vdd-mic-bias-supply = <&pm8110_l20>;
+ qcom,cdc-vdd-mic-bias-voltage = <3075000 3075000>;
+ qcom,cdc-vdd-mic-bias-current = <25000>;
+
+ qcom,cdc-micbias-cfilt-sel = <0x0>;
+ qcom,cdc-micbias-cfilt-mv = <1800000>;
+ qcom,cdc-mclk-clk-rate = <12288000>;
+ };
+
+ msm8x10_wcd_codec@77{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x77>;
+ };
+
+ msm8x10_wcd_codec@66{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x66>;
+ };
+
+ msm8x10_wcd_codec@55{
+ compatible = "qcom,msm8x10-wcd-i2c";
+ reg = <0x55>;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8610-smp2p.dtsi b/arch/arm/boot/dts/msm8610-smp2p.dtsi
index 079e4ca..3921a68 100644
--- a/arch/arm/boot/dts/msm8610-smp2p.dtsi
+++ b/arch/arm/boot/dts/msm8610-smp2p.dtsi
@@ -9,7 +9,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,smp2p-modem {
compatible = "qcom,smp2p";
reg = <0xf9011008 0x4>;
diff --git a/arch/arm/boot/dts/msm8610.dtsi b/arch/arm/boot/dts/msm8610.dtsi
index b6a4315..a62df58 100644
--- a/arch/arm/boot/dts/msm8610.dtsi
+++ b/arch/arm/boot/dts/msm8610.dtsi
@@ -11,6 +11,30 @@
*/
/include/ "skeleton.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8610";
+ compatible = "qcom,msm8610";
+ interrupt-parent = <&intc>;
+
+ memory {
+ qsecom_mem: qsecom_region {
+ linux,contiguous-region;
+ reg = <0 0x100000>;
+ label = "qsecom_mem";
+ };
+ };
+
+ aliases {
+ sdhc1 = &sdhc_1; /* SDC1 eMMC slot */
+ sdhc2 = &sdhc_2; /* SDC2 SD card slot */
+ spi4 = &spi_4;
+ };
+
+ soc: soc { };
+};
+
+/include/ "msm8610-camera.dtsi"
/include/ "msm-iommu-v0.dtsi"
/include/ "msm8610-ion.dtsi"
/include/ "msm8610-gpu.dtsi"
@@ -19,11 +43,12 @@
/include/ "msm8610-pm.dtsi"
/include/ "msm8610-smp2p.dtsi"
/include/ "msm8610-bus.dtsi"
+/include/ "msm8610-mdss.dtsi"
-/ {
- model = "Qualcomm MSM 8610";
- compatible = "qcom,msm8610";
- interrupt-parent = <&intc>;
+&soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
intc: interrupt-controller@f9000000 {
compatible = "qcom,msm-qgic2";
@@ -45,10 +70,10 @@
qcom,direct-connect-irqs = <8>;
};
- aliases {
- spi0 = &spi_0;
- sdhc1 = &sdhc_1; /* SDC1 eMMC slot */
- sdhc2 = &sdhc_2; /* SDC2 SD card slot */
+ qcom,mpm2-sleep-counter@fc4a3000 {
+ compatible = "qcom,mpm2-sleep-counter";
+ reg = <0xfc4a3000 0x1000>;
+ clock-frequency = <32768>;
};
timer {
@@ -121,6 +146,11 @@
qcom,adsp-state = <0>;
};
+ qcom,msm-audio-ion {
+ compatible = "qcom,msm-audio-ion";
+ qcom,smmu-enabled;
+ };
+
qcom,msm-imem@fe805000 {
compatible = "qcom,msm-imem";
reg = <0xfe805000 0x1000>; /* Address and size of IMEM */
@@ -148,7 +178,7 @@
qcom,buffer-type-tz-usage-map = <0x1 0x1>,
<0x1fe 0x2>;
qcom,hfi = "q6";
- qcom,max-hw-load = <97200>; /* FWVGA @ 30 * 2 */
+ qcom,max-hw-load = <108000>; /* 720p @ 30 * 1 */
};
qcom,usbbam@f9a44000 {
@@ -233,7 +263,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
@@ -265,7 +295,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
@@ -309,7 +339,7 @@
qcom,smem@d900000 {
compatible = "qcom,smem";
- reg = <0xd900000 0x200000>,
+ reg = <0xd900000 0x100000>,
<0xf9011000 0x1000>,
<0xfc428000 0x4000>;
reg-names = "smem", "irq-reg-base", "aux-mem1";
@@ -382,6 +412,10 @@
rpm-standalone;
};
+ qcom,bcl {
+ compatible = "qcom,bcl";
+ };
+
qcom,msm-mem-hole {
compatible = "qcom,msm-mem-hole";
qcom,memblock-remove = <0x07B00000 0x6400000>; /* Address and Size of Hole */
@@ -401,7 +435,6 @@
reg = <0xf9011050 0x8>;
reg-names = "rcg_base";
a7_cpu-supply = <&apc_vreg_corner>;
- a7_mem-supply = <&pm8110_l3_ao>;
};
spmi_bus: qcom,spmi@fc4c0000 {
@@ -414,11 +447,52 @@
/* 190,ee0_krait_hlos_spmi_periph_irq */
/* 187,channel_0_krait_hlos_trans_done_irq */
interrupts = <0 190 0>, <0 187 0>;
- qcom,not-wakeup;
qcom,pmic-arb-ee = <0>;
qcom,pmic-arb-channel = <0>;
};
+ i2c@f9923000 { /* BLSP-1 QUP-1 */
+ cell-index = <1>;
+ compatible = "qcom,i2c-qup";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ reg = <0xf9923000 0x1000>;
+ interrupt-names = "qup_err_intr";
+ interrupts = <0 95 0>;
+ qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-src-freq = <19200000>;
+ qcom,sda-gpio = <&msmgpio 2 0>;
+ qcom,scl-gpio = <&msmgpio 3 0>;
+ };
+
+ i2c_cdc: i2c@f9927000 { /* BLSP1 QUP5 */
+ cell-index = <5>;
+ compatible = "qcom,i2c-qup";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ reg = <0xf9927000 0x1000>;
+ interrupt-names = "qup_err_intr";
+ interrupts = <0 99 0>;
+ qcom,i2c-bus-freq = <100000>;
+ };
+
+ i2c: i2c@f9928000 { /* BLSP1 QUP6 */
+ cell-index = <6>;
+ compatible = "qcom,i2c-qup";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ reg = <0xf9928000 0x1000>;
+ interrupt-names = "qup_err_intr";
+ interrupts = <0 100 0>;
+ qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-src-freq = <19200000>;
+ qcom,sda-gpio = <&msmgpio 16 0>;
+ qcom,scl-gpio = <&msmgpio 17 0>;
+ };
+
i2c@f9925000 { /* BLSP-1 QUP-3 */
cell-index = <0>;
compatible = "qcom,i2c-qup";
@@ -431,28 +505,27 @@
qcom,i2c-bus-freq = <100000>;
};
-
- spi_0: spi@f9923000 { /* BLSP1 QUP1 */
+ spi_4: spi@f9926000 { /* BLSP1 QUP4 */
compatible = "qcom,spi-qup-v2";
#address-cells = <1>;
#size-cells = <0>;
reg-names = "spi_physical", "spi_bam_physical";
- reg = <0xf9923000 0x1000>,
- <0xf9904000 0xF000>;
+ reg = <0xf9926000 0x1000>,
+ <0xf9904000 0x15000>;
interrupt-names = "spi_irq", "spi_bam_irq";
- interrupts = <0 95 0>, <0 238 0>;
- spi-max-frequency = <19200000>;
+ interrupts = <0 98 0>, <0 238 0>;
+ spi-max-frequency = <50000000>;
- gpios = <&msmgpio 3 0>, /* CLK */
- <&msmgpio 1 0>, /* MISO */
- <&msmgpio 0 0>; /* MOSI */
- cs-gpios = <&msmgpio 2 0>;
+ gpios = <&msmgpio 89 0>, /* CLK */
+ <&msmgpio 87 0>, /* MISO */
+ <&msmgpio 86 0>; /* MOSI */
+ cs-gpios = <&msmgpio 88 0>;
qcom,infinite-mode = <0>;
qcom,use-bam;
qcom,ver-reg-exists;
- qcom,bam-consumer-pipe-index = <12>;
- qcom,bam-producer-pipe-index = <13>;
+ qcom,bam-consumer-pipe-index = <18>;
+ qcom,bam-producer-pipe-index = <19>;
};
qcom,pronto@fb21b000 {
@@ -468,6 +541,7 @@
/* GPIO inputs from wcnss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_4_in 0 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_4_in 1 0>;
qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_4_in 2 0>;
/* GPIO output to wcnss */
@@ -485,6 +559,13 @@
qcom,msm-pcm {
compatible = "qcom,msm-pcm-dsp";
+ qcom,msm-pcm-dsp-id = <0>;
+ };
+
+ qcom,msm-pcm-low-latency {
+ compatible = "qcom,msm-pcm-dsp";
+ qcom,msm-pcm-dsp-id = <1>;
+ qcom,msm-pcm-low-latency;
};
qcom,msm-pcm-routing {
@@ -524,15 +605,15 @@
qcom,msm-dai-q6-mi2s-prim {
compatible = "qcom,msm-dai-q6-mi2s";
qcom,msm-dai-q6-mi2s-dev-id = <0>;
- qcom,msm-mi2s-rx-lines = <1>;
- qcom,msm-mi2s-tx-lines = <0>;
+ qcom,msm-mi2s-rx-lines = <0>;
+ qcom,msm-mi2s-tx-lines = <3>;
};
qcom,msm-dai-q6-mi2s-sec {
compatible = "qcom,msm-dai-q6-mi2s";
qcom,msm-dai-q6-mi2s-dev-id = <1>;
- qcom,msm-mi2s-rx-lines = <0>;
- qcom,msm-mi2s-tx-lines = <3>;
+ qcom,msm-mi2s-rx-lines = <3>;
+ qcom,msm-mi2s-tx-lines = <0>;
};
};
@@ -577,6 +658,21 @@
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <240>;
};
+
+ qcom,msm-dai-q6-incall-record-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <32771>;
+ };
+
+ qcom,msm-dai-q6-incall-record-tx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <32772>;
+ };
+
+ qcom,msm-dai-q6-incall-music-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <32773>;
+ };
};
qcom,msm-pcm-hostless {
@@ -585,8 +681,9 @@
qcom,wcnss-wlan@fb000000 {
compatible = "qcom,wcnss_wlan";
- reg = <0xfb000000 0x280000>;
- reg-names = "wcnss_mmio";
+ reg = <0xfb000000 0x280000>,
+ <0xf9011008 0x04>;
+ reg-names = "wcnss_mmio", "wcnss_fiq";
interrupts = <0 145 0>, <0 146 0>;
interrupt-names = "wcnss_wlantx_irq", "wcnss_wlanrx_irq";
@@ -599,7 +696,8 @@
qcom,iris-vdddig-supply = <&pm8110_l5>;
gpios = <&msmgpio 23 0>, <&msmgpio 24 0>, <&msmgpio 25 0>, <&msmgpio 26 0>, <&msmgpio 27 0>;
- qcom,has_pronto_hw;
+ qcom,has-pronto-hw;
+ qcom,wlan-rx-buff-count = <256>;
};
qcom,mss@fc880000 {
@@ -613,7 +711,6 @@
"restart_reg", "cxrail_bhs_reg";
interrupts = <0 24 1>;
- vdd_mss-supply = <&pm8110_s1>;
vdd_cx-supply = <&pm8110_s1_corner>;
vdd_mx-supply = <&pm8110_l3>;
vdd_pll-supply = <&pm8110_l10>;
@@ -624,7 +721,9 @@
/* GPIO inputs from mss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_1_in 1 0>;
qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_1_in 2 0>;
+ qcom,gpio-stop-ack = <&smp2pgpio_ssr_smp2p_1_in 3 0>;
/* GPIO output to mss */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
@@ -640,8 +739,10 @@
vdd_cx-supply = <&pm8110_s1_corner>;
qcom,firmware-name = "adsp";
- /* GPIO input from lpass */
+ /* GPIO inputs from lpass */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_2_in 0 0>;
+ qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_2_in 2 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_2_in 1 0>;
/* GPIO output to lpass */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>;
@@ -656,18 +757,17 @@
qcom,sensors = <2>;
qcom,slope = <2901 2846>;
qcom,calib-mode = "fuse_map3";
- qcom,calibration-less-mode;
- qcom,tsens-local-init;
qcom,sensor-id = <0 5>;
};
qcom,msm-thermal {
compatible = "qcom,msm-thermal";
- qcom,sensor-id = <0>;
+ qcom,sensor-id = <5>;
qcom,poll-ms = <250>;
qcom,limit-temp = <60>;
qcom,temp-hysteresis = <10>;
qcom,freq-step = <2>;
+ qcom,freq-control-mask = <0xf>;
};
qcom,ipc-spinlock@fd484000 {
@@ -706,6 +806,12 @@
qcom,msm-rng-iface-clk;
};
+ qcom,msm-rtb {
+ compatible = "qcom,msm-rtb";
+ qcom,memory-reservation-type = "EBI1";
+ qcom,memory-reservation-size = <0x100000>; /* 1M EBI1 buffer */
+ };
+
jtag_mm0: jtagmm@fc34c000 {
compatible = "qcom,jtag-mm";
reg = <0xfc34c000 0x1000>,
@@ -738,6 +844,43 @@
compatible = "qcom,tz-log";
reg = <0x0fe805720 0x1000>;
};
+
+ qcom,qcrypto@fd404000 {
+ compatible = "qcom,qcrypto";
+ reg = <0xfd400000 0x20000>,
+ <0xfd404000 0x8000>;
+ reg-names = "crypto-base","crypto-bam-base";
+ interrupts = <0 207 0>;
+ qcom,bam-pipe-pair = <2>;
+ qcom,ce-hw-instance = <1>;
+ qcom,ce-hw-shared;
+ qcom,msm-bus,name = "qcrypto-noc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <55 512 0 0>,
+ <55 512 393600 3936000>;
+ };
+
+ qcom,qcedev@fd400000 {
+ compatible = "qcom,qcedev";
+ reg = <0xfd400000 0x20000>,
+ <0xfd404000 0x8000>;
+ reg-names = "crypto-base","crypto-bam-base";
+ interrupts = <0 207 0>;
+ qcom,bam-pipe-pair = <1>;
+ qcom,ce-hw-instance = <1>;
+ qcom,ce-hw-shared;
+ qcom,msm-bus,name = "qcedev-noc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <55 512 0 0>,
+ <55 512 393600 3936000>;
+ };
+
};
&gdsc_vfe {
@@ -794,7 +937,7 @@
label = "vchg_sns";
reg = <2>;
qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <3>;
+ qcom,pre-div-channel-scaling = <2>;
qcom,calibration-type = "absolute";
qcom,scale-function = <0>;
qcom,hw-settle-time = <0>;
@@ -866,6 +1009,67 @@
qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
};
+
+ chan@13 {
+ label = "pa_therm0";
+ reg = <0x13>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
};
+&pm8110_adc_tm {
+ /* Channel Node */
+ chan@30 {
+ label = "batt_therm";
+ reg = <0x30>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <1>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <3>;
+ qcom,btm-channel-number = <0x48>;
+ };
+ chan@8 {
+ label = "die_temp";
+ reg = <8>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <3>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <3>;
+ qcom,btm-channel-number = <0x68>;
+ };
+
+ chan@6 {
+ label = "vbat_sns";
+ reg = <6>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <3>;
+ qcom,btm-channel-number = <0x70>;
+ };
+
+ chan@13 {
+ label = "pa_therm0";
+ reg = <0x13>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x78>;
+ qcom,thermal-node;
+ };
+};
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/msm8926-cdp.dts
similarity index 69%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/msm8926-cdp.dts
index e410344..7a91d40 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/msm8926-cdp.dts
@@ -10,20 +10,13 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
-/include/ "msmzinc.dtsi"
+/dts-v1/;
+/include/ "msm8926.dtsi"
+/include/ "msm8226-cdp.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
-
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
+ model = "Qualcomm MSM 8926 CDP";
+ compatible = "qcom,msm8926-cdp", "qcom,msm8926", "qcom,cdp";
+ qcom,msm-id = <200 1 0>;
};
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/msm8926-mtp.dts
similarity index 69%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/msm8926-mtp.dts
index e410344..fea925d 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/msm8926-mtp.dts
@@ -10,20 +10,13 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
-/include/ "msmzinc.dtsi"
+/dts-v1/;
+/include/ "msm8926.dtsi"
+/include/ "msm8226-mtp.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
-
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
+ model = "Qualcomm MSM 8926 MTP";
+ compatible = "qcom,msm8926-mtp", "qcom,msm8926", "qcom,mtp";
+ qcom,msm-id = <200 8 0>;
};
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/msm8926-qrd.dts
similarity index 69%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/msm8926-qrd.dts
index e410344..e056b7e 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/msm8926-qrd.dts
@@ -11,19 +11,11 @@
*/
/dts-v1/;
-
-/include/ "msmzinc.dtsi"
+/include/ "msm8926.dtsi"
+/include/ "msm8226-qrd.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
-
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
+ model = "Qualcomm MSM 8926 QRD";
+ compatible = "qcom,msm8926-qrd", "qcom,msm8926", "qcom,qrd";
+ qcom,msm-id = <200 11 0>;
};
diff --git a/arch/arm/boot/dts/msmzinc-ion.dtsi b/arch/arm/boot/dts/msm8926.dtsi
similarity index 64%
copy from arch/arm/boot/dts/msmzinc-ion.dtsi
copy to arch/arm/boot/dts/msm8926.dtsi
index aac4230..6f3f592 100644
--- a/arch/arm/boot/dts/msmzinc-ion.dtsi
+++ b/arch/arm/boot/dts/msm8926.dtsi
@@ -10,22 +10,21 @@
* GNU General Public License for more details.
*/
+/*
+ * Only 8926-specific property overrides should be placed inside this
+ * file. Device definitions should be placed inside the msm8226.dtsi
+ * file.
+ */
+
+/include/ "msm8226.dtsi"
+
/ {
- qcom,ion {
- compatible = "qcom,msm-ion";
- #address-cells = <1>;
- #size-cells = <0>;
+ model = "Qualcomm MSM 8926";
+ compatible = "qcom,msm8926";
+};
- qcom,ion-heap@30 { /* SYSTEM HEAP */
- reg = <30>;
- };
-
- qcom,ion-heap@21 { /* SYSTEM CONTIG HEAP */
- reg = <21>;
- };
-
- qcom,ion-heap@25 { /* IOMMU HEAP */
- reg = <25>;
- };
+&soc {
+ qcom,mss@fc880000 {
+ vdd_mss-supply = <&pm8226_s5>;
};
};
diff --git a/arch/arm/boot/dts/msm8974-bus.dtsi b/arch/arm/boot/dts/msm8974-bus.dtsi
index 3e0ef04..609a1b3 100644
--- a/arch/arm/boot/dts/msm8974-bus.dtsi
+++ b/arch/arm/boot/dts/msm8974-bus.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
msm-mmss-noc@fc478000 {
compatible = "msm-bus-fabric";
reg = <0xfc478000 0x00004000>;
@@ -1151,7 +1151,7 @@
qcom,fabclk-dual = "mem_clk";
qcom,fabclk-active = "mem_a_clk";
qcom,ntieredslaves = <0>;
- qcom,qos-freq = <4800>;
+ qcom,qos-freq = <19200>;
qcom,hw-sel = "BIMC";
qcom,rpm-en;
@@ -1168,12 +1168,18 @@
qcom,masterp = <0>;
qcom,tier = <2>;
qcom,hw-sel = "BIMC";
- qcom,mode = "Fixed";
+ qcom,mode = "Limiter";
qcom,qport = <0>;
qcom,ws = <10000>;
qcom,mas-hw-id = <0>;
qcom,prio-rd = <0>;
qcom,prio-wr = <0>;
+ qcom,mode-thresh = "Fixed";
+ qcom,thresh = <2000000>;
+ qcom,dual-conf;
+ qcom,bimc,bw = <300000>;
+ qcom,bimc,gp = <5>;
+ qcom,bimc,thmp = <50>;
};
mas-ampss-m1 {
@@ -1182,12 +1188,18 @@
qcom,masterp = <1>;
qcom,tier = <2>;
qcom,hw-sel = "BIMC";
- qcom,mode = "Fixed";
+ qcom,mode = "Limiter";
qcom,qport = <1>;
qcom,ws = <10000>;
qcom,mas-hw-id = <0>;
qcom,prio-rd = <0>;
qcom,prio-wr = <0>;
+ qcom,mode-thresh = "Fixed";
+ qcom,thresh = <2000000>;
+ qcom,dual-conf;
+ qcom,bimc,bw = <300000>;
+ qcom,bimc,gp = <5>;
+ qcom,bimc,thmp = <50>;
};
mas-mss-proc {
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-cdp.dtsi
index df0db7e..4a9820d 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-cdp.dtsi
@@ -36,6 +36,8 @@
qcom,actuator-src = <&actuator0>;
qcom,mount-angle = <90>;
qcom,sensor-name = "s5k3l1yx";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -77,6 +79,8 @@
qcom,mount-angle = <90>;
qcom,sensor-name = "imx135";
qcom,actuator-src = <&actuator1>;
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -89,11 +93,15 @@
qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
- <&msmgpio 90 0>;
+ <&msmgpio 90 0>,
+ <&msmgpio 89 0>;
qcom,gpio-reset = <1>;
- qcom,gpio-req-tbl-num = <0 1>;
- qcom,gpio-req-tbl-flags = <1 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK", "CAM_RESET1";
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1",
+ "CAM_STANDBY";
qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 30000>;
@@ -111,9 +119,11 @@
reg = <0x6c 0x0>;
qcom,slave-id = <0x6c 0x300A 0x2720>;
qcom,csiphy-sd-index = <2>;
- qcom,csid-sd-index = <0>;
+ qcom,csid-sd-index = <2>;
qcom,mount-angle = <90>;
qcom,sensor-name = "ov2720";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -149,6 +159,8 @@
qcom,csid-sd-index = <0>;
qcom,mount-angle = <0>;
qcom,sensor-name = "mt9m114";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-dragonboard.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-dragonboard.dtsi
new file mode 100644
index 0000000..e84a47d
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-dragonboard.dtsi
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2012-2013, 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.
+ */
+
+&cci {
+
+ actuator0: qcom,actuator@18 {
+ cell-index = <0>;
+ reg = <0x18 0x0>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ };
+
+ actuator1: qcom,actuator@36 {
+ cell-index = <1>;
+ reg = <0x36>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ };
+
+ qcom,camera@6e {
+ compatible = "qcom,s5k3l1yx";
+ reg = <0x6e 0x0>;
+ qcom,slave-id = <0x6e 0x0 0x3121>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <0>;
+ qcom,actuator-src = <&actuator0>;
+ qcom,sensor-name = "s5k3l1yx";
+ cam_vdig-supply = <&pm8941_l3>;
+ cam_vana-supply = <&pm8941_l17>;
+ cam_vio-supply = <&pm8941_lvs3>;
+ cam_vaf-supply = <&pm8941_l23>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-type = <0 1 0 0>;
+ qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
+ qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 15 0>,
+ <&msmgpio 90 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <1 1>;
+ qcom,gpio-set-tbl-flags = <0 2>;
+ qcom,gpio-set-tbl-delay = <1000 30000>;
+ qcom,csi-lane-assign = <0x4320>;
+ qcom,csi-lane-mask = <0x1F>;
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ };
+
+ qcom,camera@20 {
+ compatible = "qcom,imx135";
+ reg = <0x20>;
+ qcom,slave-id = <0x20 0x0016 0x0135>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <0>;
+ qcom,sensor-name = "imx135";
+ qcom,actuator-src = <&actuator1>;
+ cam_vdig-supply = <&pm8941_l3>;
+ cam_vana-supply = <&pm8941_l17>;
+ cam_vio-supply = <&pm8941_lvs3>;
+ cam_vaf-supply = <&pm8941_l23>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-type = <0 1 0 0>;
+ qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
+ qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 15 0>,
+ <&msmgpio 90 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <1 1>;
+ qcom,gpio-set-tbl-flags = <0 2>;
+ qcom,gpio-set-tbl-delay = <1000 30000>;
+ qcom,csi-lane-assign = <0x4320>;
+ qcom,csi-lane-mask = <0x1F>;
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ };
+
+ qcom,camera@6c {
+ compatible = "qcom,ov2720";
+ reg = <0x6c 0x0>;
+ qcom,slave-id = <0x6c 0x300A 0x2720>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <180>;
+ qcom,sensor-name = "ov2720";
+ cam_vdig-supply = <&pm8941_l3>;
+ cam_vana-supply = <&pm8941_l17>;
+ cam_vio-supply = <&pm8941_lvs3>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio";
+ qcom,cam-vreg-type = <0 0 1>;
+ qcom,cam-vreg-min-voltage = <1225000 2850000 0>;
+ qcom,cam-vreg-max-voltage = <1225000 2850000 0>;
+ qcom,cam-vreg-op-mode = <105000 80000 0>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 17 0>,
+ <&msmgpio 18 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <1 1>;
+ qcom,gpio-set-tbl-flags = <0 2>;
+ qcom,gpio-set-tbl-delay = <1000 4000>;
+ qcom,csi-lane-assign = <0x4320>;
+ qcom,csi-lane-mask = <0x7>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ };
+
+ qcom,camera@90 {
+ compatible = "qcom,mt9m114";
+ reg = <0x90 0x0>;
+ qcom,slave-id = <0x90 0x0 0x2481>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <0>;
+ qcom,sensor-name = "mt9m114";
+ cam_vdig-supply = <&pm8941_l3>;
+ cam_vana-supply = <&pm8941_l17>;
+ cam_vio-supply = <&pm8941_lvs3>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio";
+ qcom,cam-vreg-type = <0 0 1>;
+ qcom,cam-vreg-min-voltage = <1225000 2850000 0>;
+ qcom,cam-vreg-max-voltage = <1225000 2850000 0>;
+ qcom,cam-vreg-op-mode = <105000 80000 0>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 16 0>,
+ <&msmgpio 94 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <1 1>;
+ qcom,gpio-set-tbl-flags = <0 2>;
+ qcom,gpio-set-tbl-delay = <1000 4000>;
+ qcom,csi-lane-assign = <0x4320>;
+ qcom,csi-lane-mask = <0x3>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <0>;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-fluid.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-fluid.dtsi
index f58c1e2..f61b83a 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-fluid.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-fluid.dtsi
@@ -37,6 +37,8 @@
qcom,led-flash-src = <&led_flash0>;
qcom,mount-angle = <270>;
qcom,sensor-name = "s5k3l1yx";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -78,6 +80,8 @@
qcom,mount-angle = <270>;
qcom,sensor-name = "imx135";
qcom,actuator-src = <&actuator1>;
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -90,11 +94,15 @@
qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
- <&msmgpio 90 0>;
+ <&msmgpio 90 0>,
+ <&msmgpio 89 0>;
qcom,gpio-reset = <1>;
- qcom,gpio-req-tbl-num = <0 1>;
- qcom,gpio-req-tbl-flags = <1 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK", "CAM_RESET1";
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1",
+ "CAM_STANDBY";
qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 30000>;
@@ -112,9 +120,11 @@
reg = <0x6c>;
qcom,slave-id = <0x6c 0x300A 0x2720>;
qcom,csiphy-sd-index = <2>;
- qcom,csid-sd-index = <0>;
+ qcom,csid-sd-index = <2>;
qcom,mount-angle = <90>;
qcom,sensor-name = "ov2720";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -150,6 +160,8 @@
qcom,csid-sd-index = <0>;
qcom,mount-angle = <0>;
qcom,sensor-name = "mt9m114";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
index 5a97a11..cf968d2 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
@@ -35,7 +35,10 @@
qcom,csiphy-sd-index = <0>;
qcom,csid-sd-index = <0>;
qcom,mount-angle = <0>;
+ qcom,actuator-src = <&actuator0>;
qcom,sensor-name = "s5k3l1yx";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs2>;
@@ -48,12 +51,15 @@
qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
- <&msmgpio 90 0>;
+ <&msmgpio 90 0>,
+ <&msmgpio 89 0>;
qcom,gpio-reset = <1>;
- qcom,gpio-req-tbl-num = <0 1>;
- qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1";
+ "CAM_RESET1",
+ "CAM_STANDBY";
qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 30000>;
@@ -73,6 +79,8 @@
qcom,csid-sd-index = <0>;
qcom,mount-angle = <0>;
qcom,sensor-name = "imx135";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
qcom,actuator-src = <&actuator1>;
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
@@ -86,11 +94,15 @@
qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
- <&msmgpio 90 0>;
+ <&msmgpio 90 0>,
+ <&msmgpio 89 0>;
qcom,gpio-reset = <1>;
- qcom,gpio-req-tbl-num = <0 1>;
- qcom,gpio-req-tbl-flags = <1 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK", "CAM_RESET1";
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1",
+ "CAM_STANDBY";
qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 30000>;
@@ -111,6 +123,8 @@
qcom,csid-sd-index = <0>;
qcom,mount-angle = <180>;
qcom,sensor-name = "ov2720";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs2>;
@@ -145,6 +159,8 @@
qcom,csiphy-sd-index = <1>;
qcom,csid-sd-index = <0>;
qcom,mount-angle = <0>;
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
qcom,sensor-name = "mt9m114";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-mtp.dtsi
index 767a705..6ad6213 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-mtp.dtsi
@@ -37,6 +37,8 @@
qcom,led-flash-src = <&led_flash0>;
qcom,mount-angle = <90>;
qcom,sensor-name = "s5k3l1yx";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -77,6 +79,8 @@
qcom,csid-sd-index = <0>;
qcom,mount-angle = <90>;
qcom,sensor-name = "imx135";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
qcom,actuator-src = <&actuator1>;
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
@@ -90,11 +94,15 @@
qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
- <&msmgpio 90 0>;
+ <&msmgpio 90 0>,
+ <&msmgpio 89 0>;
qcom,gpio-reset = <1>;
- qcom,gpio-req-tbl-num = <0 1>;
- qcom,gpio-req-tbl-flags = <1 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK", "CAM_RESET1";
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1",
+ "CAM_STANDBY";
qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 30000>;
@@ -113,9 +121,11 @@
reg = <0x6c>;
qcom,slave-id = <0x6c 0x300A 0x2720>;
qcom,csiphy-sd-index = <2>;
- qcom,csid-sd-index = <0>;
+ qcom,csid-sd-index = <2>;
qcom,mount-angle = <90>;
qcom,sensor-name = "ov2720";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -151,6 +161,8 @@
qcom,csid-sd-index = <0>;
qcom,mount-angle = <0>;
qcom,sensor-name = "mt9m114";
+ qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
diff --git a/arch/arm/boot/dts/msm8974-camera.dtsi b/arch/arm/boot/dts/msm8974-camera.dtsi
index 3a78a15..456b079 100644
--- a/arch/arm/boot/dts/msm8974-camera.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera.dtsi
@@ -11,9 +11,7 @@
* GNU General Public License for more details.
*/
-/include/ "skeleton.dtsi"
-
-/ {
+&soc {
qcom,msm-cam@fd8C0000 {
compatible = "qcom,msm-cam";
reg = <0xfd8C0000 0x10000>;
@@ -23,8 +21,9 @@
qcom,csiphy@fda0ac00 {
cell-index = <0>;
compatible = "qcom,csiphy";
- reg = <0xfda0ac00 0x200>;
- reg-names = "csiphy";
+ reg = <0xfda0ac00 0x200>,
+ <0xfda00030 0x4>;
+ reg-names = "csiphy", "csiphy_clk_mux";
interrupts = <0 78 0>;
interrupt-names = "csiphy";
};
@@ -32,8 +31,9 @@
qcom,csiphy@fda0b000 {
cell-index = <1>;
compatible = "qcom,csiphy";
- reg = <0xfda0b000 0x200>;
- reg-names = "csiphy";
+ reg = <0xfda0b000 0x200>,
+ <0xfda00038 0x4>;
+ reg-names = "csiphy", "csiphy_clk_mux";
interrupts = <0 79 0>;
interrupt-names = "csiphy";
};
@@ -41,8 +41,9 @@
qcom,csiphy@fda0b400 {
cell-index = <2>;
compatible = "qcom,csiphy";
- reg = <0xfda0b400 0x200>;
- reg-names = "csiphy";
+ reg = <0xfda0b400 0x200>,
+ <0xfda00040 0x4>;
+ reg-names = "csiphy", "csiphy_clk_mux";
interrupts = <0 80 0>;
interrupt-names = "csiphy";
};
@@ -94,8 +95,9 @@
qcom,ispif@fda0A000 {
cell-index = <0>;
compatible = "qcom,ispif";
- reg = <0xfda0A000 0x500>;
- reg-names = "ispif";
+ reg = <0xfda0A000 0x500>,
+ <0xfda00020 0x10>;
+ reg-names = "ispif", "csi_clk_mux";
interrupts = <0 55 0>;
interrupt-names = "ispif";
};
diff --git a/arch/arm/boot/dts/msm8974-cdp.dtsi b/arch/arm/boot/dts/msm8974-cdp.dtsi
index 0319128..2a60df4 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8974-cdp.dtsi
@@ -15,7 +15,7 @@
/include/ "msm8974-leds.dtsi"
/include/ "msm8974-camera-sensor-cdp.dtsi"
-/ {
+&soc {
serial@f991e000 {
status = "ok";
};
@@ -195,6 +195,7 @@
qcom,model = "msm8974-taiko-cdp-snd-card";
qcom,hdmi-audio-rx;
qcom,us-euro-gpios = <&pm8941_gpios 20 0>;
+ qcom,cdc-micbias2-headset-only;
};
usb2_otg_sw: regulator-tpd4s214 {
@@ -246,7 +247,7 @@
<85 512 40000 160000>,
<85 512 40000 320000>,
<85 512 40000 480000>,
- <85 512 40000 640000>;
+ <85 512 40000 800000>;
};
};
@@ -344,7 +345,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,nonremovable;
@@ -374,11 +375,31 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
status = "ok";
};
+/* Drive strength recommendations for clock line from hardware team is 10 mA.
+ * But since the driver has been been using the below values from the start
+ * without any problems, continue to use those.
+ */
+&sdcc1 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
+
+&sdcc2 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
+
+&sdhc_1 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
+
+&sdhc_2 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
+
&uart7 {
status = "ok";
qcom,tx-gpio = <&msmgpio 41 0x00>;
@@ -479,7 +500,7 @@
qcom,output-type = <0>;
qcom,pull = <5>;
qcom,vin-sel = <2>;
- qcom,out-strength = <3>;
+ qcom,out-strength = <1>;
qcom,src-sel = <2>;
qcom,master-en = <1>;
};
@@ -578,6 +599,13 @@
};
gpio@e300 { /* GPIO 36 */
+ qcom,mode = <1>; /* QPNP_PIN_MODE_DIG_OUT */
+ qcom,output-type = <0>; /* QPNP_PIN_OUT_BUF_CMOS */
+ qcom,pull = <5>; /* QPNP_PIN_PULL_NO */
+ qcom,vin-sel = <2>; /* QPNP_PIN_VIN2 */
+ qcom,out-strength = <3>; /* QPNP_PIN_OUT_STRENGTH_HIGH */
+ qcom,src-sel = <3>; /* QPNP_PIN_SEL_FUNC_2 */
+ qcom,master-en = <1>;
};
};
@@ -690,5 +718,12 @@
qcom,cdc-micbias1-ext-cap;
qcom,cdc-micbias3-ext-cap;
qcom,cdc-micbias4-ext-cap;
+
+ /* If boot isn't available, vph_pwr_vreg can be used instead */
+ cdc-vdd-spkdrv-supply = <&pm8941_boost>;
+ qcom,cdc-vdd-spkdrv-voltage = <5000000 5000000>;
+ qcom,cdc-vdd-spkdrv-current = <1250000>;
+
+ qcom,cdc-on-demand-supplies = "cdc-vdd-spkdrv";
};
};
diff --git a/arch/arm/boot/dts/msm8974-coresight.dtsi b/arch/arm/boot/dts/msm8974-coresight.dtsi
index c064b59..1610f1f 100644
--- a/arch/arm/boot/dts/msm8974-coresight.dtsi
+++ b/arch/arm/boot/dts/msm8974-coresight.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
tmc_etr: tmc@fc322000 {
compatible = "arm,coresight-tmc";
reg = <0xfc322000 0x1000>,
@@ -364,4 +364,18 @@
coresight-name = "coresight-cti-cpu3";
coresight-nr-inports = <0>;
};
+
+ hwevent: hwevent@fdf30018 {
+ compatible = "qcom,coresight-hwevent";
+ reg = <0xfdf30018 0x80>,
+ <0xf9011080 0x80>,
+ <0xfd4ab160 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+
+ coresight-id = <29>;
+ coresight-name = "coresight-hwevent";
+ coresight-nr-inports = <0>;
+
+ qcom,hwevent-clks = "core_mmss_clk";
+ };
};
diff --git a/arch/arm/boot/dts/msm8974-fluid.dtsi b/arch/arm/boot/dts/msm8974-fluid.dtsi
index 25d0885..a822af5 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dtsi
+++ b/arch/arm/boot/dts/msm8974-fluid.dtsi
@@ -14,7 +14,7 @@
/include/ "msm8974-camera-sensor-fluid.dtsi"
/include/ "msm8974-leds.dtsi"
-/ {
+&soc {
serial@f991e000 {
status = "ok";
};
@@ -202,7 +202,35 @@
sound {
qcom,model = "msm8974-taiko-fluid-snd-card";
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "LDO_H", "MCLK",
+ "AMIC1", "MIC BIAS1 Internal1",
+ "MIC BIAS1 Internal1", "Handset Mic",
+ "AMIC2", "MIC BIAS2 External",
+ "MIC BIAS2 External", "Headset Mic",
+ "AMIC3", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCLeft Headset Mic",
+ "DMIC1", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic1",
+ "DMIC2", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic2",
+ "DMIC3", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic3",
+ "DMIC4", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic4",
+ "DMIC5", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic5",
+ "DMIC6", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic6",
+ "Lineout_1 amp", "LINEOUT1",
+ "Lineout_3 amp", "LINEOUT3";
+
qcom,hdmi-audio-rx;
+ qcom,ext-ult-lo-amp-gpio = <&pm8941_gpios 6 0>;
+ qcom,cdc-micbias2-headset-only;
};
};
@@ -212,6 +240,13 @@
qcom,cdc-micbias2-ext-cap;
qcom,cdc-micbias3-ext-cap;
qcom,cdc-micbias4-ext-cap;
+
+ /* If boot isn't available, vph_pwr_vreg can be used instead */
+ cdc-vdd-spkdrv-supply = <&pm8941_boost>;
+ qcom,cdc-vdd-spkdrv-voltage = <5000000 5000000>;
+ qcom,cdc-vdd-spkdrv-current = <1250000>;
+
+ qcom,cdc-on-demand-supplies = "cdc-vdd-spkdrv";
};
};
@@ -308,7 +343,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,nonremovable;
@@ -338,11 +373,31 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
status = "ok";
};
+/* Drive strength recommendations for clock line from hardware team is 10 mA.
+ * But since the driver has been been using the below values from the start
+ * without any problems, continue to use those.
+ */
+&sdcc1 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
+
+&sdcc2 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
+
+&sdhc_1 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
+
+&sdhc_2 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
+
&usb3 {
qcom,otg-capability;
};
@@ -451,7 +506,7 @@
qcom,output-type = <0>;
qcom,pull = <5>;
qcom,vin-sel = <2>;
- qcom,out-strength = <3>;
+ qcom,out-strength = <1>;
qcom,src-sel = <2>;
qcom,master-en = <1>;
};
@@ -553,12 +608,6 @@
};
mpp@a400 { /* MPP 5 */
- /* SPI_ETH config */
- qcom,mode = <1>; /* DIG_OUT */
- qcom,output-type = <0>; /* CMOS */
- qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
- qcom,src-sel = <0>; /* CONSTANT */
- qcom,master-en = <1>; /* ENABLE MPP */
};
mpp@a500 { /* MPP 6 */
diff --git a/arch/arm/boot/dts/msm8974-gpu.dtsi b/arch/arm/boot/dts/msm8974-gpu.dtsi
index 3779dbd..06b9c18 100644
--- a/arch/arm/boot/dts/msm8974-gpu.dtsi
+++ b/arch/arm/boot/dts/msm8974-gpu.dtsi
@@ -9,7 +9,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-/ {
+&soc {
msm_gpu: qcom,kgsl-3d0@fdb00000 {
label = "kgsl-3d0";
compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
@@ -26,7 +26,6 @@
qcom,step-pwrlevel = <2>;
qcom,idle-timeout = <8>; //<HZ/12>
- qcom,nap-allowed = <1>;
qcom,strtstp-sleepwake;
qcom,clk-map = <0x0000006>; //KGSL_CLK_CORE | KGSL_CLK_IFACE
@@ -51,6 +50,14 @@
/* IOMMU Data */
iommu = <&kgsl_iommu>;
+ /* Trace bus */
+ coresight-id = <67>;
+ coresight-name = "coresight-gfx";
+ coresight-nr-inports = <0>;
+ coresight-outports = <0>;
+ coresight-child-list = <&funnel_mmss>;
+ coresight-child-ports = <7>;
+
qcom,gpu-pwrlevels {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/boot/dts/msm8974-ion.dtsi b/arch/arm/boot/dts/msm8974-ion.dtsi
index 31afd9c..63f6d59 100644
--- a/arch/arm/boot/dts/msm8974-ion.dtsi
+++ b/arch/arm/boot/dts/msm8974-ion.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,ion {
compatible = "qcom,msm-ion";
#address-cells = <1>;
@@ -45,9 +45,7 @@
qcom,ion-heap@27 { /* QSECOM HEAP */
compatible = "qcom,msm-ion-reserve";
reg = <27>;
- qcom,heap-align = <0x1000>;
- qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0x1100000>;
+ linux,contiguous-region = <&qsecom_mem>;
};
qcom,ion-heap@28 { /* AUDIO HEAP */
@@ -55,7 +53,7 @@
reg = <28>;
qcom,heap-align = <0x1000>;
qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0x314000>;
+ qcom,memory-reservation-size = <0x614000>;
};
};
};
diff --git a/arch/arm/boot/dts/msm8974-leds.dtsi b/arch/arm/boot/dts/msm8974-leds.dtsi
index befd206..5e91f45 100644
--- a/arch/arm/boot/dts/msm8974-leds.dtsi
+++ b/arch/arm/boot/dts/msm8974-leds.dtsi
@@ -18,7 +18,7 @@
qcom,rgb_0 {
label = "rgb";
linux,name = "led:rgb_red";
- qcom,mode = <0>;
+ qcom,mode = "pwm";
qcom,pwm-channel = <6>;
qcom,pwm-us = <1000>;
qcom,max-current = <12>;
@@ -31,7 +31,7 @@
qcom,rgb_1 {
label = "rgb";
linux,name = "led:rgb_green";
- qcom,mode = <0>;
+ qcom,mode = "pwm";
qcom,pwm-channel = <5>;
qcom,pwm-us = <1000>;
qcom,max-current = <12>;
@@ -43,7 +43,7 @@
qcom,rgb_2 {
label = "rgb";
linux,name = "led:rgb_blue";
- qcom,mode = <0>;
+ qcom,mode = "pwm";
qcom,pwm-channel = <4>;
qcom,pwm-us = <1000>;
qcom,max-current = <12>;
@@ -65,10 +65,10 @@
pm8941_flash0: qcom,flash_0 {
qcom,max-current = <1000>;
qcom,default-state = "off";
- qcom,headroom = <0>;
+ qcom,headroom = <3>;
qcom,duration = <1280>;
qcom,clamp-curr = <200>;
- qcom,startup-dly = <1>;
+ qcom,startup-dly = <3>;
qcom,safety-timer;
label = "flash";
linux,default-trigger =
@@ -81,10 +81,10 @@
pm8941_flash1: qcom,flash_1 {
qcom,max-current = <1000>;
qcom,default-state = "off";
- qcom,headroom = <0>;
+ qcom,headroom = <3>;
qcom,duration = <1280>;
qcom,clamp-curr = <200>;
- qcom,startup-dly = <1>;
+ qcom,startup-dly = <3>;
qcom,safety-timer;
linux,default-trigger =
"flash1_trigger";
@@ -93,6 +93,20 @@
linux,name = "led:flash_1";
qcom,current = <625>;
};
+
+ pm8941_torch: qcom,flash_torch {
+ qcom,max-current = <200>;
+ qcom,default-state = "off";
+ qcom,headroom = <0>;
+ qcom,startup-dly = <1>;
+ linux,default-trigger =
+ "torch_trigger";
+ label = "flash";
+ qcom,id = <2>;
+ linux,name = "led:flash_torch";
+ qcom,current = <200>;
+ qcom,torch-enable;
+ };
};
qcom,leds@d400 {
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
index be890b1..2dc52b6 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-liquid.dtsi
@@ -13,7 +13,7 @@
/include/ "msm8974-leds.dtsi"
/include/ "msm8974-camera-sensor-liquid.dtsi"
-/ {
+&soc {
serial@f991e000 {
status = "ok";
};
@@ -208,7 +208,7 @@
/* Object 6, Instance = 0 */
00 00 00 00 00 00
/* Object 38, Instance = 0 */
- 19 01 00 0D 02 0D 00 00 00 00
+ 19 03 00 1E 05 0D 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
@@ -221,8 +221,8 @@
41 00 14 14 00 00 00 01 00 00
/* Object 9, Instance = 0 */
8F 00 00 20 34 00 87 4B 02 03
- 00 05 03 40 0A 14 14 0A 80 07
- 38 04 03 03 03 03 08 28 02 3C
+ 00 05 03 41 0A 14 14 0A 80 07
+ 38 04 00 00 03 03 08 28 02 3C
0F 0F 2E 33 01 00
/* Object 15, Instance = 0 */
00 00 00 00 00 00 00 00 00 00
@@ -294,9 +294,19 @@
enable-active-high;
};
+ ath_chip_pwd_l: ath_chip_reset {
+ compatible = "regulator-fixed";
+ regulator-name = "ath_chip_pwd_l";
+ gpio = <&pm8941_gpios 33 0>;
+ enable-active-high;
+ };
+
bt_ar3002 {
compatible = "qca,ar3002";
qca,bt-reset-gpio = <&pm8941_gpios 34 0>;
+ qca,bt-chip-pwd-supply = <&ath_chip_pwd_l>;
+ qca,bt-vdd-io-supply = <&pm8941_s3>;
+ qca,bt-vdd-pa-supply = <&pm8941_l19>;
};
bt_ar3002_sleep {
@@ -318,6 +328,7 @@
"Lineout_3 amp", "LINEOUT3",
"Lineout_2 amp", "LINEOUT2",
"Lineout_4 amp", "LINEOUT4",
+ "SPK_ultrasound amp", "SPK_OUT",
"AMIC1", "MIC BIAS4 External",
"MIC BIAS4 External", "Analog Mic4",
"AMIC2", "MIC BIAS2 External",
@@ -346,7 +357,14 @@
qcom,ext-spk-amp-supply = <&ext_5v>;
qcom,ext-spk-amp-gpio = <&pm8841_mpps 1 0>;
qcom,dock-plug-det-irq = <&pm8841_mpps 2 0>;
+ qcom,ext-ult-spk-amp-gpio = <&pm8941_gpios 6 0>;
qcom,hdmi-audio-rx;
+
+ qcom,prim-auxpcm-gpio-clk = <&msmgpio 74 0>;
+ qcom,prim-auxpcm-gpio-sync = <&msmgpio 75 0>;
+ qcom,prim-auxpcm-gpio-din = <&msmgpio 76 0>;
+ qcom,prim-auxpcm-gpio-dout = <&msmgpio 77 0>;
+ qcom,prim-auxpcm-gpio-set = "prim-gpio-tert";
};
hsic_hub {
@@ -388,6 +406,29 @@
<85 512 40000 160000>;
};
};
+
+ wlan0: qca,wlan {
+ compatible = "qca,ar6004-hsic";
+ qcom,msm-bus,name = "wlan";
+ qca,wifi-chip-pwd-supply = <&ath_chip_pwd_l>;
+ qca,wifi-vddpa-supply = <&pm8941_l19>;
+ qca,wifi-vddio-supply = <&pm8941_l10>;
+ qcom,msm-bus,num-cases = <5>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <85 512 0 0>,
+ <85 512 40000 160000>,
+ <85 512 40000 320000>,
+ <85 512 40000 480000>,
+ <85 512 40000 800000>;
+ };
+
+ wlan_sdio:qca,wlan_sdio {
+ compatible = "qca,ar6004-sdio";
+ qcom,msm-bus,name = "wlan_sdio";
+ qca,wifi-chip-pwd-supply = <&ath_chip_pwd_l>;
+ };
};
&mdss_fb0 {
@@ -406,10 +447,6 @@
qcom,otg-capability;
};
-&pm8941_mvs1 {
- parent-supply = <&ext_5v>;
-};
-
&pm8941_mvs2 {
parent-supply = <&ext_5v>;
};
@@ -443,6 +480,14 @@
};
gpio@c500 { /* GPIO 6 */
+ /* ULTRASOUND_EN_1 PA AB enable */
+ qcom,mode = <1>; /* DIG_OUT */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,pull = <4>; /* PULL_DOWN */
+ qcom,vin-sel = <0>; /* VPH */
+ qcom,out-strength = <2>; /* STRENGTH_MED */
+ qcom,src-sel = <0>; /* CONSTANT */
+ qcom,master-en = <1>;
};
gpio@c600 { /* GPIO 7 */
@@ -495,7 +540,7 @@
qcom,output-type = <0>;
qcom,pull = <5>;
qcom,vin-sel = <2>;
- qcom,out-strength = <3>;
+ qcom,out-strength = <1>;
qcom,src-sel = <2>;
qcom,master-en = <1>;
};
@@ -695,10 +740,26 @@
};
};
+&vph_pwr_vreg {
+ status = "ok";
+};
+
&slim_msm {
taiko_codec {
+ qcom,cdc-micbias1-ext-cap;
qcom,cdc-micbias2-ext-cap;
qcom,cdc-micbias3-ext-cap;
+ qcom,cdc-micbias4-ext-cap;
+
+ /*
+ * Liquid has external spkrdrv supply. Give a dummy supply to
+ * make codec driver's happy.
+ */
+ cdc-vdd-spkdrv-supply = <&vph_pwr_vreg>;
+ qcom,cdc-vdd-spkdrv-voltage = <0 0>;
+ qcom,cdc-vdd-spkdrv-current = <0>;
+
+ qcom,cdc-on-demand-supplies = "cdc-vdd-spkdrv";
};
};
@@ -735,6 +796,7 @@
&pm8941_chg {
status = "ok";
+ otg-parent-supply = <&ext_5v>;
qcom,charging-disabled;
@@ -786,7 +848,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,nonremovable;
@@ -805,7 +867,27 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
status = "ok";
};
+
+/* Drive strength recommendations for clock line from hardware team is 10 mA.
+ * But since the driver has been been using the below values from the start
+ * without any problems, continue to use those.
+ */
+&sdcc1 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
+
+&sdcc2 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
+
+&sdhc_1 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
+
+&sdhc_2 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
diff --git a/arch/arm/boot/dts/msm8974-mdss.dtsi b/arch/arm/boot/dts/msm8974-mdss.dtsi
index 86f8141..6b8d600 100644
--- a/arch/arm/boot/dts/msm8974-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8974-mdss.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
mdss_mdp: qcom,mdss_mdp@fd900000 {
compatible = "qcom,mdss_mdp";
reg = <0xfd900000 0x22100>,
diff --git a/arch/arm/boot/dts/msm8974-mtp.dtsi b/arch/arm/boot/dts/msm8974-mtp.dtsi
index 8b9ef87..e798fc0 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8974-mtp.dtsi
@@ -14,7 +14,7 @@
/include/ "msm8974-camera-sensor-mtp.dtsi"
/include/ "msm8974-leds.dtsi"
-/ {
+&soc {
serial@f991e000 {
status = "ok";
};
@@ -187,6 +187,7 @@
sound {
qcom,model = "msm8974-taiko-mtp-snd-card";
+ qcom,cdc-micbias2-headset-only;
};
};
@@ -283,7 +284,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,nonremovable;
@@ -313,11 +314,31 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
status = "ok";
};
+/* Drive strength recommendations for clock line from hardware team is 10 mA.
+ * But since the driver has been been using the below values from the start
+ * without any problems, continue to use those.
+ */
+&sdcc1 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
+
+&sdcc2 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
+
+&sdhc_1 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
+
+&sdhc_2 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
+
&usb_otg {
qcom,hsusb-otg-otg-control = <2>;
};
@@ -469,7 +490,7 @@
qcom,output-type = <0>;
qcom,pull = <5>;
qcom,vin-sel = <2>;
- qcom,out-strength = <3>;
+ qcom,out-strength = <1>;
qcom,src-sel = <2>;
qcom,master-en = <1>;
};
@@ -616,13 +637,6 @@
};
mpp@a400 { /* MPP 5 */
- /* SPI_ETH config */
- qcom,mode = <1>; /* DIG_OUT */
- qcom,output-type = <0>; /* CMOS */
- qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
- qcom,src-sel = <0>; /* CONSTANT */
- qcom,out-strength = <1>; /* QPNP_PIN_OUT_STRENGTH_LOW */
- qcom,master-en = <1>; /* ENABLE MPP */
};
mpp@a500 { /* MPP 6 */
diff --git a/arch/arm/boot/dts/msm8974-regulator.dtsi b/arch/arm/boot/dts/msm8974-regulator.dtsi
index 0be3756..7c191fc 100644
--- a/arch/arm/boot/dts/msm8974-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8974-regulator.dtsi
@@ -25,7 +25,7 @@
};
pm8941_mvs1: regulator@8300 {
- parent-supply = <&pm8941_boost>;
+ parent-supply = <&pm8941_chg_otg>;
qcom,enable-time = <1000>;
qcom,pull-down-enable = <1>;
interrupts = <0x1 0x83 0x2>;
@@ -380,7 +380,7 @@
status = "okay";
pm8941_l19: regulator-l19 {
regulator-min-microvolt = <2900000>;
- regulator-max-microvolt = <2900000>;
+ regulator-max-microvolt = <3300000>;
qcom,init-voltage = <2900000>;
status = "okay";
};
@@ -458,14 +458,16 @@
};
};
-/ {
+&soc {
krait_pdn: krait-pdn@f9011000 {
- reg = <0xf9011000 0x1000>;
- reg-names = "apcs_gcc";
+ reg = <0xf9011000 0x1000>,
+ <0xfc4b80b0 8>;
+ reg-names = "apcs_gcc", "phase-scaling-efuse";
compatible = "qcom,krait-pdn";
#address-cells = <1>;
#size-cells = <1>;
ranges;
+ qcom,pfm-threshold = <73>;
krait0_vreg: regulator@f9088000 {
compatible = "qcom,krait-regulator";
@@ -550,3 +552,17 @@
regulator-always-on;
};
};
+
+&pm8941_chg {
+ otg-parent-supply = <&pm8941_boost>;
+};
+
+&pm8941_chg_boost {
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-name = "8941_smbb_boost";
+};
+
+&pm8941_chg_otg {
+ regulator-name = "8941_smbb_otg";
+};
diff --git a/arch/arm/boot/dts/msm8974-rumi.dtsi b/arch/arm/boot/dts/msm8974-rumi.dtsi
index c569e58..c01a4e5 100644
--- a/arch/arm/boot/dts/msm8974-rumi.dtsi
+++ b/arch/arm/boot/dts/msm8974-rumi.dtsi
@@ -13,7 +13,7 @@
/include/ "msm8974-leds.dtsi"
/include/ "msm8974-camera-sensor-cdp.dtsi"
-/ {
+&soc {
timer {
clock-frequency = <5000000>;
};
diff --git a/arch/arm/boot/dts/msm8974-sim.dtsi b/arch/arm/boot/dts/msm8974-sim.dtsi
index 786c50c..24b8d18 100644
--- a/arch/arm/boot/dts/msm8974-sim.dtsi
+++ b/arch/arm/boot/dts/msm8974-sim.dtsi
@@ -14,7 +14,7 @@
/include/ "msm8974-leds.dtsi"
/include/ "msm8974-camera-sensor-cdp.dtsi"
-/ {
+&soc {
qcom,mdss_dsi@fd922800 {
qcom,mdss_dsi_sim_video {
status = "ok";
diff --git a/arch/arm/boot/dts/msm8974-smp2p.dtsi b/arch/arm/boot/dts/msm8974-smp2p.dtsi
index 079e4ca..3921a68 100644
--- a/arch/arm/boot/dts/msm8974-smp2p.dtsi
+++ b/arch/arm/boot/dts/msm8974-smp2p.dtsi
@@ -9,7 +9,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,smp2p-modem {
compatible = "qcom,smp2p";
reg = <0xf9011008 0x4>;
diff --git a/arch/arm/boot/dts/msm8974-v1-cdp.dts b/arch/arm/boot/dts/msm8974-v1-cdp.dts
index cb58026..c3fd98d 100644
--- a/arch/arm/boot/dts/msm8974-v1-cdp.dts
+++ b/arch/arm/boot/dts/msm8974-v1-cdp.dts
@@ -18,7 +18,9 @@
/ {
model = "Qualcomm MSM 8974 CDP";
compatible = "qcom,msm8974-cdp", "qcom,msm8974", "qcom,cdp";
- qcom,msm-id = <126 1 0>;
+ qcom,msm-id = <126 1 0>,
+ <185 1 0>,
+ <186 1 0>;
};
&ehci {
diff --git a/arch/arm/boot/dts/msm8974-v1-fluid.dts b/arch/arm/boot/dts/msm8974-v1-fluid.dts
index 8ab24df..2b96ecb 100644
--- a/arch/arm/boot/dts/msm8974-v1-fluid.dts
+++ b/arch/arm/boot/dts/msm8974-v1-fluid.dts
@@ -18,7 +18,9 @@
/ {
model = "Qualcomm MSM 8974 FLUID";
compatible = "qcom,msm8974-fluid", "qcom,msm8974", "qcom,fluid";
- qcom,msm-id = <126 3 0>;
+ qcom,msm-id = <126 3 0>,
+ <185 3 0>,
+ <186 3 0>;
};
diff --git a/arch/arm/boot/dts/msm8974-v1-iommu-domains.dtsi b/arch/arm/boot/dts/msm8974-v1-iommu-domains.dtsi
index 6ea5b9e..25fca2a 100644
--- a/arch/arm/boot/dts/msm8974-v1-iommu-domains.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1-iommu-domains.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,iommu-domains {
compatible = "qcom,iommu-domains";
diff --git a/arch/arm/boot/dts/msm8974-v1-iommu.dtsi b/arch/arm/boot/dts/msm8974-v1-iommu.dtsi
index c6693e1..56369dc 100644
--- a/arch/arm/boot/dts/msm8974-v1-iommu.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1-iommu.dtsi
@@ -14,20 +14,27 @@
&jpeg_iommu {
status = "ok";
+ vdd-supply = <&gdsc_jpeg>;
};
&mdp_iommu {
status = "ok";
+ vdd-supply = <&gdsc_mdss>;
};
&venus_iommu {
status = "ok";
+ vdd-supply = <&gdsc_venus>;
};
&kgsl_iommu {
status = "ok";
+ qcom,needs-alt-core-clk;
+ vdd-supply = <&gdsc_oxili_cx>;
+ qcom,alt-vdd-supply = <&gdsc_oxili_gx>;
};
&vfe_iommu {
status = "ok";
+ vdd-supply = <&gdsc_vfe>;
};
diff --git a/arch/arm/boot/dts/msm8974-v1-liquid.dts b/arch/arm/boot/dts/msm8974-v1-liquid.dts
index ccbd82f..29d6150 100644
--- a/arch/arm/boot/dts/msm8974-v1-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-v1-liquid.dts
@@ -18,5 +18,7 @@
/ {
model = "Qualcomm MSM 8974 LIQUID";
compatible = "qcom,msm8974-liquid", "qcom,msm8974", "qcom,liquid";
- qcom,msm-id = <126 9 0>;
+ qcom,msm-id = <126 9 0>,
+ <185 9 0>,
+ <186 9 0>;
};
diff --git a/arch/arm/boot/dts/msm8974-v1-mtp.dts b/arch/arm/boot/dts/msm8974-v1-mtp.dts
index 09ea84b..8cbcca0 100644
--- a/arch/arm/boot/dts/msm8974-v1-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-v1-mtp.dts
@@ -18,7 +18,9 @@
/ {
model = "Qualcomm MSM 8974 MTP";
compatible = "qcom,msm8974-mtp", "qcom,msm8974", "qcom,mtp";
- qcom,msm-id = <126 8 0>;
+ qcom,msm-id = <126 8 0>,
+ <185 8 0>,
+ <186 8 0>;
};
&pm8941_chg {
diff --git a/arch/arm/boot/dts/msm8974-v1-pm.dtsi b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
index f9c0920..288a703 100644
--- a/arch/arm/boot/dts/msm8974-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
@@ -10,9 +10,7 @@
* GNU General Public License for more details.
*/
-/include/ "skeleton.dtsi"
-
-/ {
+&soc {
qcom,spm@f9089000 {
compatible = "qcom,spm-v2";
#address-cells = <1>;
@@ -340,7 +338,8 @@
qcom,ipc-bit-offset = <1>;
qcom,gic-parent = <&intc>;
- qcom,gic-map = <47 165>, /* usb30_hs_phy_irq */
+ qcom,gic-map = <2 216>, /* tsens_upper_lower_int */
+ <47 165>, /* usb30_hs_phy_irq */
<50 172>, /* usb1_hs_async_wakeup_irq */
<53 104>, /* mdss_irq */
<62 222>, /* ee0_krait_hlos_spmi_periph_irq */
@@ -426,13 +425,13 @@
reg = <0xfe805664 0x40>;
qcom,pc-mode = "tz_l2_int";
qcom,use-sync-timer;
+ };
- qcom,cpu-sleep-status@f9088008 {
- compatible = "qcom,cpu-sleep-status";
- reg = <0xf9088008 0x4>;
- qcom,cpu-alias-addr = <0x10000>;
- qcom,sleep-status-mask= <0x80000>;
- };
+ qcom,cpu-sleep-status@f9088008 {
+ compatible = "qcom,cpu-sleep-status";
+ reg = <0xf9088008 0x4>;
+ qcom,cpu-alias-addr = <0x10000>;
+ qcom,sleep-status-mask= <0x80000>;
};
qcom,rpm-log@fc19dc00 {
diff --git a/arch/arm/boot/dts/msm8974-v1-rumi.dts b/arch/arm/boot/dts/msm8974-v1-rumi.dts
index caf89ee..85aab17 100644
--- a/arch/arm/boot/dts/msm8974-v1-rumi.dts
+++ b/arch/arm/boot/dts/msm8974-v1-rumi.dts
@@ -18,5 +18,7 @@
/ {
model = "Qualcomm MSM 8974 RUMI";
compatible = "qcom,msm8974-rumi", "qcom,msm8974", "qcom,rumi";
- qcom,msm-id = <126 15 0>;
+ qcom,msm-id = <126 15 0>,
+ <185 15 0>,
+ <186 15 0>;
};
diff --git a/arch/arm/boot/dts/msm8974-v1-sim.dts b/arch/arm/boot/dts/msm8974-v1-sim.dts
index c4b29c2..fc9858d 100644
--- a/arch/arm/boot/dts/msm8974-v1-sim.dts
+++ b/arch/arm/boot/dts/msm8974-v1-sim.dts
@@ -18,5 +18,7 @@
/ {
model = "Qualcomm MSM 8974 Simulator";
compatible = "qcom,msm8974-sim", "qcom,msm8974", "qcom,sim";
- qcom,msm-id = <126 16 0>;
+ qcom,msm-id = <126 16 0>,
+ <185 16 0>,
+ <186 16 0>;
};
diff --git a/arch/arm/boot/dts/msm8974-v1.dtsi b/arch/arm/boot/dts/msm8974-v1.dtsi
index 62837a1..caec2dc 100644
--- a/arch/arm/boot/dts/msm8974-v1.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1.dtsi
@@ -21,7 +21,7 @@
/include/ "msm8974-v1-iommu-domains.dtsi"
/include/ "msm8974-v1-pm.dtsi"
-/ {
+&soc {
android_usb@fc42b0c8 {
compatible = "qcom,android-usb";
reg = <0xfc42b0c8 0xc8>;
diff --git a/arch/arm/boot/dts/msm8974-v2-cdp.dts b/arch/arm/boot/dts/msm8974-v2-cdp.dts
index 4fa1f2a..85d478b 100644
--- a/arch/arm/boot/dts/msm8974-v2-cdp.dts
+++ b/arch/arm/boot/dts/msm8974-v2-cdp.dts
@@ -18,7 +18,9 @@
/ {
model = "Qualcomm MSM 8974v2 CDP";
compatible = "qcom,msm8974-cdp", "qcom,msm8974", "qcom,cdp";
- qcom,msm-id = <126 1 0x20000>;
+ qcom,msm-id = <126 1 0x20000>,
+ <185 1 0x20000>,
+ <186 1 0x20000>;
};
&usb3 {
diff --git a/arch/arm/boot/dts/msm8974-v2-fluid.dts b/arch/arm/boot/dts/msm8974-v2-fluid.dts
index c5779b1..d83d130 100644
--- a/arch/arm/boot/dts/msm8974-v2-fluid.dts
+++ b/arch/arm/boot/dts/msm8974-v2-fluid.dts
@@ -18,7 +18,9 @@
/ {
model = "Qualcomm MSM 8974v2 FLUID";
compatible = "qcom,msm8974-fluid", "qcom,msm8974", "qcom,fluid";
- qcom,msm-id = <126 3 0x20000>;
+ qcom,msm-id = <126 3 0x20000>,
+ <185 3 0x20000>,
+ <186 3 0x20000>;
};
&usb3 {
diff --git a/arch/arm/boot/dts/msm8974-v2-iommu-domains.dtsi b/arch/arm/boot/dts/msm8974-v2-iommu-domains.dtsi
index a83815e..01c94d0 100644
--- a/arch/arm/boot/dts/msm8974-v2-iommu-domains.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2-iommu-domains.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,iommu-domains {
compatible = "qcom,iommu-domains";
diff --git a/arch/arm/boot/dts/msm8974-v2-iommu.dtsi b/arch/arm/boot/dts/msm8974-v2-iommu.dtsi
index c974884..03f7e80 100644
--- a/arch/arm/boot/dts/msm8974-v2-iommu.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2-iommu.dtsi
@@ -14,6 +14,7 @@
&venus_iommu {
status = "ok";
+ vdd-supply = <&gdsc_venus>;
qcom,iommu-enable-halt;
qcom,iommu-bfb-regs = <0x204c
@@ -76,16 +77,18 @@
};
venus_sec_pixel: qcom,iommu-ctx@fdc8f000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfdc8f000 0x1000>;
- interrupts = <0 42 0>;
+ interrupts = <0 42 0>, <0 43 0>;
qcom,iommu-ctx-sids = <0x85>;
label = "venus_sec_pixel";
qcom,secure-context;
};
venus_sec_non_pixel: qcom,iommu-ctx@fdc90000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfdc90000 0x1000>;
- interrupts = <0 42 0>;
+ interrupts = <0 42 0>, <0 43 0>;
qcom,iommu-ctx-sids = <0x87 0xA0>;
label = "venus_sec_non_pixel";
qcom,secure-context;
@@ -94,6 +97,7 @@
&jpeg_iommu {
status = "ok";
+ vdd-supply = <&gdsc_jpeg>;
qcom,iommu-enable-halt;
qcom,iommu-bfb-regs = <0x204c
@@ -131,6 +135,7 @@
&mdp_iommu {
status = "ok";
+ vdd-supply = <&gdsc_mdss>;
qcom,iommu-enable-halt;
qcom,iommu-bfb-regs = <0x204c
@@ -174,7 +179,10 @@
&kgsl_iommu {
status = "ok";
+ vdd-supply = <&gdsc_oxili_cx>;
+ qcom,alt-vdd-supply = <&gdsc_oxili_gx>;
qcom,iommu-enable-halt;
+ qcom,needs-alt-core-clk;
qcom,iommu-bfb-regs = <0x204c
0x2050
@@ -205,6 +213,7 @@
&vfe_iommu {
status = "ok";
+ vdd-supply = <&gdsc_vfe>;
qcom,iommu-enable-halt;
qcom,iommu-bfb-regs = <0x204c
diff --git a/arch/arm/boot/dts/msm8974-v2-liquid.dts b/arch/arm/boot/dts/msm8974-v2-liquid.dts
index 7132f43..53983dc 100644
--- a/arch/arm/boot/dts/msm8974-v2-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-v2-liquid.dts
@@ -18,7 +18,9 @@
/ {
model = "Qualcomm MSM 8974v2 LIQUID";
compatible = "qcom,msm8974-liquid", "qcom,msm8974", "qcom,liquid";
- qcom,msm-id = <126 9 0x20000>;
+ qcom,msm-id = <126 9 0x20000>,
+ <185 9 0x20000>,
+ <186 9 0x20000>;
};
&usb3 {
diff --git a/arch/arm/boot/dts/msm8974-v2-mtp.dts b/arch/arm/boot/dts/msm8974-v2-mtp.dts
index d38e663..792a78c 100644
--- a/arch/arm/boot/dts/msm8974-v2-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-v2-mtp.dts
@@ -18,7 +18,9 @@
/ {
model = "Qualcomm MSM 8974v2 MTP";
compatible = "qcom,msm8974-mtp", "qcom,msm8974", "qcom,mtp";
- qcom,msm-id = <126 8 0x20000>;
+ qcom,msm-id = <126 8 0x20000>,
+ <185 8 0x20000>,
+ <186 8 0x20000>;
};
&usb3 {
@@ -32,3 +34,7 @@
qcom,misc-ref = <&pm8941_misc>;
};
+
+&pm8941_chg {
+ qcom,bpd-detection = "bpd_thm";
+};
diff --git a/arch/arm/boot/dts/msm8974-v2-pm.dtsi b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
index 5a1c047..178a1ee 100644
--- a/arch/arm/boot/dts/msm8974-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
@@ -10,9 +10,7 @@
* GNU General Public License for more details.
*/
-/include/ "skeleton.dtsi"
-
-/ {
+&soc {
qcom,spm@f9089000 {
compatible = "qcom,spm-v2";
#address-cells = <1>;
@@ -125,9 +123,9 @@
qcom,vctl-port = <0x0>;
qcom,phase-port = <0x1>;
qcom,pfm-port = <0x2>;
- qcom,saw2-spm-cmd-ret = [1f 00 20 03 22 00 0f];
- qcom,saw2-spm-cmd-gdhs = [00 20 32 42 07 44 22 50 02 32 50 0f];
- qcom,saw2-spm-cmd-pc = [00 10 32 b0 11 42 07 01 b0 12 44
+ qcom,saw2-spm-cmd-ret = [1f 00 03 00 0f];
+ qcom,saw2-spm-cmd-gdhs = [00 32 42 07 44 50 02 32 50 0f];
+ qcom,saw2-spm-cmd-pc = [00 32 b0 11 42 07 01 b0 44
50 02 32 50 0f];
};
@@ -190,9 +188,9 @@
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <1>;
- qcom,ss-power = <784>;
- qcom,energy-overhead = <190000>;
- qcom,time-overhead = <100>;
+ qcom,ss-power = <715>;
+ qcom,energy-overhead = <17700>;
+ qcom,time-overhead = <2>;
};
qcom,lpm-level@1 {
@@ -206,10 +204,10 @@
qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,irqs-detectable;
qcom,gpio-detectable;
- qcom,latency-us = <75>;
- qcom,ss-power = <735>;
- qcom,energy-overhead = <77341>;
- qcom,time-overhead = <105>;
+ qcom,latency-us = <35>;
+ qcom,ss-power = <542>;
+ qcom,energy-overhead = <34920>;
+ qcom,time-overhead = <40>;
};
@@ -224,10 +222,10 @@
qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,irqs-detectable;
qcom,gpio-detectable;
- qcom,latency-us = <95>;
- qcom,ss-power = <725>;
- qcom,energy-overhead = <99500>;
- qcom,time-overhead = <130>;
+ qcom,latency-us = <300>;
+ qcom,ss-power = <476>;
+ qcom,energy-overhead = <225300>;
+ qcom,time-overhead = <350>;
};
qcom,lpm-level@3 {
@@ -241,10 +239,10 @@
qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,irqs-detectable;
qcom,gpio-detectable;
- qcom,latency-us = <2000>;
- qcom,ss-power = <138>;
- qcom,energy-overhead = <1208400>;
- qcom,time-overhead = <3200>;
+ qcom,latency-us = <2817>;
+ qcom,ss-power = <163>;
+ qcom,energy-overhead = <1577736>;
+ qcom,time-overhead = <5067>;
};
qcom,lpm-level@4 {
@@ -258,44 +256,29 @@
qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
qcom,irqs-detectable;
qcom,gpio-detectable;
- qcom,latency-us = <3000>;
- qcom,ss-power = <110>;
- qcom,energy-overhead = <1250300>;
- qcom,time-overhead = <3500>;
+ qcom,latency-us = <3922>;
+ qcom,ss-power = <83>;
+ qcom,energy-overhead = <2274420>;
+ qcom,time-overhead = <6605>;
};
qcom,lpm-level@5 {
reg = <0x5>;
qcom,mode = "pc";
qcom,xo = "xo_off";
- qcom,l2 = "l2_cache_gdhs";
- qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
- qcom,latency-us = <3000>;
- qcom,ss-power = <68>;
- qcom,energy-overhead = <1350200>;
- qcom,time-overhead = <4000>;
- };
-
- qcom,lpm-level@6 {
- reg = <0x6>;
- qcom,mode = "pc";
- qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
- qcom,latency-us = <10300>;
- qcom,ss-power = <63>;
- qcom,energy-overhead = <2128000>;
- qcom,time-overhead = <18200>;
+ qcom,latency-us = <4922>;
+ qcom,ss-power = <68>;
+ qcom,energy-overhead = <2568180>;
+ qcom,time-overhead = <8812>;
};
- qcom,lpm-level@7 {
- reg = <0x7>;
+ qcom,lpm-level@6 {
+ reg = <0x6>;
qcom,mode= "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
@@ -303,14 +286,14 @@
qcom,vdd-mem-lower-bound = <950000>; /* SVS SOC */
qcom,vdd-dig-upper-bound = <4>; /* NORMAL */
qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
- qcom,latency-us = <18000>;
- qcom,ss-power = <10>;
- qcom,energy-overhead = <3202600>;
- qcom,time-overhead = <27000>;
+ qcom,latency-us = <5890>;
+ qcom,ss-power = <60>;
+ qcom,energy-overhead = <2675900>;
+ qcom,time-overhead = <10140>;
};
- qcom,lpm-level@8 {
- reg = <0x8>;
+ qcom,lpm-level@7 {
+ reg = <0x7>;
qcom,mode= "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
@@ -318,10 +301,10 @@
qcom,vdd-mem-lower-bound = <675000>; /* RETENTION */
qcom,vdd-dig-upper-bound = <3>; /* SVS SOC */
qcom,vdd-dig-lower-bound = <1>; /* RETENTION */
- qcom,latency-us = <20000>;
- qcom,ss-power = <2>;
- qcom,energy-overhead = <4252000>;
- qcom,time-overhead = <32000>;
+ qcom,latency-us = <8500>;
+ qcom,ss-power = <18>;
+ qcom,energy-overhead = <3286600>;
+ qcom,time-overhead = <15760>;
};
};
@@ -340,7 +323,8 @@
qcom,ipc-bit-offset = <1>;
qcom,gic-parent = <&intc>;
- qcom,gic-map = <47 165>, /* usb30_hs_phy_irq */
+ qcom,gic-map = <2 216>, /* tsens_upper_lower_int */
+ <47 165>, /* usb30_hs_phy_irq */
<50 172>, /* usb1_hs_async_wakeup_irq */
<53 104>, /* mdss_irq */
<62 222>, /* ee0_krait_hlos_spmi_periph_irq */
@@ -427,14 +411,25 @@
qcom,pc-mode = "tz_l2_int";
qcom,use-sync-timer;
- qcom,cpu-sleep-status@f9088008{
- compatible = "qcom,cpu-sleep-status";
- reg = <0xf9088008 0x100>;
- qcom,cpu-alias-addr = <0x10000>;
- qcom,sleep-status-mask= <0x80000>;
+ qcom,pm-snoc-client {
+ compatible = "qcom,pm-snoc-client";
+ qcom,msm-bus,name = "ocimem_snoc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,vectors-KBps =
+ <54 585 0 0>,
+ <54 585 0 800000>;
};
};
+ qcom,cpu-sleep-status@f9088008{
+ compatible = "qcom,cpu-sleep-status";
+ reg = <0xf9088008 0x100>;
+ qcom,cpu-alias-addr = <0x10000>;
+ qcom,sleep-status-mask= <0x80000>;
+ };
+
qcom,rpm-log@fc19dc00 {
compatible = "qcom,rpm-log";
reg = <0xfc19dc00 0x4000>;
diff --git a/arch/arm/boot/dts/msm8974-v2.dtsi b/arch/arm/boot/dts/msm8974-v2.dtsi
index 494b12c..96e78ac 100644
--- a/arch/arm/boot/dts/msm8974-v2.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2.dtsi
@@ -21,7 +21,7 @@
/include/ "msm8974-v2-iommu-domains.dtsi"
/include/ "msm8974-v2-pm.dtsi"
-/ {
+&soc {
android_usb@fe8050c8 {
compatible = "qcom,android-usb";
reg = <0xfe8050c8 0xc8>;
@@ -65,6 +65,7 @@
qcom,mdss-pingpong-off = <0x00012D00 0x00012E00 0x00012F00>;
qcom,mdss-has-bwc;
qcom,mdss-has-decimation;
+ qcom,mdss-ad-off = <0x0013100 0x00013300>;
};
&mdss_hdmi_tx {
@@ -80,7 +81,8 @@
<783360 465000000>,
<489600 266670000>,
<244800 133330000>;
- qcom,reg-presets = <0x80070 0x11FFF>,
+ qcom,reg-presets = <0x80004 0x1>,
+ <0x80070 0x11FFF>,
<0x80074 0xA4>,
<0x800A8 0x1FFF>,
<0x80124 0x3>,
@@ -104,21 +106,21 @@
<3456000 2076000>,
<3662000 2198000>;
qcom,enc-ddr-ab-ib = <0 0>,
- <60000 302000>,
- <182000 302000>,
- <402000 302000>,
- <804000 604000>,
- <1288000 967000>,
- <2340000 1404000>,
- <24940000 1496000>;
+ <120000 302000>,
+ <364000 302000>,
+ <804000 302000>,
+ <1608000 604000>,
+ <2576000 967000>,
+ <4680000 1404000>,
+ <49880000 1496000>;
qcom,dec-ddr-ab-ib = <0 0>,
- <104000 303000>,
- <268000 303000>,
- <506000 303000>,
- <1012000 606000>,
- <1620000 970000>,
- <2024000 1212000>,
- <2132000 1279000>;
+ <208000 303000>,
+ <536000 303000>,
+ <1012000 303000>,
+ <2024000 606000>,
+ <3240000 970000>,
+ <4048000 1212000>,
+ <4264000 1279000>;
qcom,iommu-groups = <&venus_domain_ns &venus_domain_sec_bitstream
&venus_domain_sec_pixel &venus_domain_sec_non_pixel>;
qcom,iommu-group-buffer-types = <0xfff 0x91 0x42 0x120>;
@@ -130,3 +132,7 @@
&krait_pdn {
qcom,use-phase-switching;
};
+
+&tspp {
+ vdd_cx-supply = <&pm8841_s2_corner>;
+};
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index a9685cc..fc0636e 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -11,14 +11,6 @@
*/
/include/ "skeleton.dtsi"
-/include/ "msm8974-camera.dtsi"
-/include/ "msm8974-coresight.dtsi"
-/include/ "msm-gdsc.dtsi"
-/include/ "msm8974-ion.dtsi"
-/include/ "msm8974-gpu.dtsi"
-/include/ "msm8974-mdss.dtsi"
-/include/ "msm8974-smp2p.dtsi"
-/include/ "msm8974-bus.dtsi"
/ {
model = "Qualcomm MSM 8974";
@@ -34,21 +26,73 @@
sdhc4 = &sdhc_4; /* SDC4 SDIO slot */
};
- memory {
+ cpus {
+ #size-cells = <0>;
+ #address-cells = <1>;
+ CPU0: cpu@0 {
+ device_type = "cpu";
+ compatible = "qcom,krait";
+ reg = <0x0>;
+ };
+
+ CPU1: cpu@1 {
+ device_type = "cpu";
+ compatible = "qcom,krait";
+ reg = <0x1>;
+ };
+
+ CPU2: cpu@2 {
+ device_type = "cpu";
+ compatible = "qcom,krait";
+ reg = <0x2>;
+ };
+
+ CPU3: cpu@3 {
+ device_type = "cpu";
+ compatible = "qcom,krait";
+ reg = <0x3>;
+ };
+ };
+
+ memory {
secure_mem: secure_region {
linux,contiguous-region;
- reg = <0 0x7800000>;
+ reg = <0 0xFC00000>;
label = "secure_mem";
};
adsp_mem: adsp_region {
linux,contiguous-region;
- reg = <0 0x2000000>;
+ reg = <0 0x2F00000>;
label = "adsp_mem";
};
+
+ qsecom_mem: qsecom_region {
+ linux,contiguous-region;
+ reg = <0 0x1100000>;
+ label = "qseecom_mem";
+ };
+
};
+ soc: soc { };
+};
+
+/include/ "msm8974-camera.dtsi"
+/include/ "msm8974-coresight.dtsi"
+/include/ "msm-gdsc.dtsi"
+/include/ "msm8974-ion.dtsi"
+/include/ "msm8974-gpu.dtsi"
+/include/ "msm8974-mdss.dtsi"
+/include/ "msm8974-smp2p.dtsi"
+/include/ "msm8974-bus.dtsi"
+
+&soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
intc: interrupt-controller@F9000000 {
compatible = "qcom,msm-qgic2";
interrupt-controller;
@@ -193,8 +237,8 @@
qcom,msm-bus,num-cases = <2>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps =
- <84 512 0 0>,
- <84 512 500 800>;
+ <86 512 0 0>,
+ <86 512 500 800>;
};
usb_otg: usb@f9a55000 {
@@ -246,7 +290,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,clk-rates = <400000 20000000 25000000 50000000 100000000 200000000>;
@@ -291,7 +335,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
- qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,clk-rates = <400000 20000000 25000000 50000000 100000000 200000000>;
@@ -545,6 +589,7 @@
<&msmgpio 54 0>, /* MISO */
<&msmgpio 53 0>; /* MOSI */
cs-gpios = <&msmgpio 55 0>;
+ qcom,master-id = <84>;
};
tspp: msm_tspp@f99d8000 {
@@ -585,6 +630,14 @@
"tsif_data",
"tsif_sync";
qcom,gpios-func = <1>;
+
+ qcom,msm-bus,name = "tsif";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <82 512 0 0>, /* No vote */
+ <82 512 12288 24576>; /* Max. bandwidth, 2xTSIF, each max of 96Mbps */
};
slim_msm: slim@fe12f000 {
@@ -601,7 +654,9 @@
elemental-addr = [00 01 A0 00 17 02];
interrupt-parent = <&wcd9xxx_intc>;
- interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28>;
+ interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+ 17 18 19 20 21 22 23 24 25 26 27 28 29
+ 30>;
qcom,cdc-reset-gpio = <&msmgpio 63 0>;
@@ -691,6 +746,7 @@
qcom,prim-auxpcm-gpio-sync = <&msmgpio 66 0>;
qcom,prim-auxpcm-gpio-din = <&msmgpio 67 0>;
qcom,prim-auxpcm-gpio-dout = <&msmgpio 68 0>;
+ qcom,prim-auxpcm-gpio-set = "prim-gpio-prim";
qcom,sec-auxpcm-gpio-clk = <&msmgpio 79 0>;
qcom,sec-auxpcm-gpio-sync = <&msmgpio 80 0>;
qcom,sec-auxpcm-gpio-din = <&msmgpio 81 0>;
@@ -722,6 +778,24 @@
interrupt-names = "qup_err_intr";
qcom,i2c-bus-freq = <100000>;
qcom,i2c-src-freq = <50000000>;
+ qcom,master-id = <84>;
+ };
+
+ i2c_1: i2c@f9923000 {
+ cell-index = <1>;
+ compatible = "qcom,i2c-qup";
+ reg = <0xf9923000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ interrupts = <0 95 0>;
+ interrupt-names = "qup_err_intr";
+ qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-src-freq = <19200000>;
+ qcom,scl-gpio = <&msmgpio 3 0>;
+ qcom,sda-gpio = <&msmgpio 2 0>;
+ qcom,master-id = <86>;
+ status = "disabled";
};
i2c_2: i2c@f9924000 {
@@ -735,6 +809,7 @@
interrupt-names = "qup_err_intr";
qcom,i2c-bus-freq = <100000>;
qcom,i2c-src-freq = <50000000>;
+ qcom,master-id = <86>;
};
spi_0: spi@f9923000 {
@@ -748,6 +823,7 @@
<&msmgpio 1 0>, /* MISO */
<&msmgpio 0 0>; /* MOSI */
cs-gpios = <&msmgpio 9 0>;
+ qcom,master-id = <86>;
};
qcom,acpuclk@f9000000 {
@@ -834,8 +910,10 @@
qcom,firmware-name = "adsp";
- /* GPIO input from lpass */
+ /* GPIO inputs from lpass */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_2_in 0 0>;
+ qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_2_in 2 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_2_in 1 0>;
/* GPIO output to lpass */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>;
@@ -1094,13 +1172,14 @@
vdd_mx-supply = <&pm8841_s1>;
vdd_pll-supply = <&pm8941_l12>;
qcom,vdd_pll = <1800000>;
- qcom,is-loadable;
qcom,firmware-name = "mba";
qcom,pil-self-auth;
/* GPIO inputs from mss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_1_in 1 0>;
qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_1_in 2 0>;
+ qcom,gpio-stop-ack = <&smp2pgpio_ssr_smp2p_1_in 3 0>;
/* GPIO output to mss */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
@@ -1119,6 +1198,7 @@
/* GPIO inputs from wcnss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_4_in 0 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_4_in 1 0>;
qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_4_in 2 0>;
/* GPIO output to wcnss */
@@ -1146,8 +1226,8 @@
qcom,iris-vdddig-supply = <&pm8941_l3>;
gpios = <&msmgpio 36 0>, <&msmgpio 37 0>, <&msmgpio 38 0>, <&msmgpio 39 0>, <&msmgpio 40 0>;
- qcom,has_48mhz_xo;
- qcom,has_pronto_hw;
+ qcom,has-48mhz-xo;
+ qcom,has-pronto-hw;
};
qcom,ocmem@fdd00000 {
@@ -1160,7 +1240,7 @@
interrupts = <0 76 0 0 77 0>;
interrupt-names = "ocmem_irq", "dm_irq";
qcom,ocmem-num-regions = <0x3>;
- qcom,ocmem-num-macros = <0x8>;
+ qcom,ocmem-num-macros = <0x18>;
qcom,resource-type = <0x706d636f>;
#address-cells = <1>;
#size-cells = <1>;
@@ -1197,7 +1277,7 @@
reg = <0xf9bff000 0x200>;
};
- qcom,qseecom@fe806000 {
+ qseecom: qcom,qseecom@7f00000 {
compatible = "qcom,qseecom";
reg = <0x7f00000 0x500000>;
reg-names = "secapp-region";
@@ -1238,7 +1318,27 @@
qcom,firmware-name = "venus";
};
- qcom,cache_erp {
+ qcom,cache_erp@f9012000 {
+ reg = <0xf9012000 0x80>,
+ <0xf9089000 0x80>,
+ <0xf9099000 0x80>,
+ <0xf90a9000 0x80>,
+ <0xf90b9000 0x80>,
+ <0xf9088000 0x40>,
+ <0xf9098000 0x40>,
+ <0xf90a8000 0x40>,
+ <0xf90b8000 0x40>;
+
+ reg-names = "l2_saw",
+ "krait0_saw",
+ "krait1_saw",
+ "krait2_saw",
+ "krait3_saw",
+ "krait0_acs",
+ "krait1_acs",
+ "krait2_acs",
+ "krait3_acs";
+
compatible = "qcom,cache_erp";
interrupts = <1 9 0>, <0 2 0>;
interrupt-names = "l1_irq", "l2_irq";
@@ -1364,13 +1464,14 @@
qcom,limit-temp = <60>;
qcom,temp-hysteresis = <10>;
qcom,freq-step = <2>;
+ qcom,freq-control-mask = <0xf>;
qcom,core-limit-temp = <80>;
qcom,core-temp-hysteresis = <10>;
qcom,core-control-mask = <0xe>;
qcom,vdd-restriction-temp = <5>;
qcom,vdd-restriction-temp-hysteresis = <10>;
- qcom,pmic-sw-mode-temp = <90>;
- qcom,pmic-sw-mode-temp-hysteresis = <70>;
+ qcom,pmic-sw-mode-temp = <85>;
+ qcom,pmic-sw-mode-temp-hysteresis = <75>;
qcom,pmic-sw-mode-regs = "vdd_dig";
vdd_dig-supply = <&pm8841_s2_floor_corner>;
vdd_gfx-supply = <&pm8841_s4_floor_corner>;
@@ -1387,6 +1488,11 @@
qcom,min-level = <1>; /* No Request */
};
+ qcom,vdd-apps-rstr{
+ qcom,vdd-rstr-reg = "vdd_apps";
+ qcom,levels = <1881600 1958400 2265600>;
+ qcom,freq-req;
+ };
};
qcom,bam_dmux@fc834000 {
@@ -1396,7 +1502,7 @@
qcom,rx-ring-size = <64>;
};
- qcom,msm-mem-hole {
+ memory_hole: qcom,msm-mem-hole {
compatible = "qcom,msm-mem-hole";
qcom,memblock-remove = <0x7f00000 0x8000000>; /* Address and Size of Hole */
};
@@ -1522,23 +1628,32 @@
};
&gdsc_venus {
+ qcom,clock-names = "core_clk";
+ qcom,skip-logic-collapse;
status = "ok";
};
&gdsc_mdss {
+ qcom,clock-names = "core_clk", "lut_clk";
+ qcom,retain-periph;
status = "ok";
};
&gdsc_jpeg {
+ qcom,clock-names = "core0_clk", "core1_clk", "core2_clk";
status = "ok";
};
&gdsc_vfe {
+ qcom,clock-names = "core0_clk", "core1_clk", "csi0_clk", "csi1_clk",
+ "cpp_clk";
status = "ok";
};
&gdsc_oxili_gx {
- qcom,retain-mems;
+ qcom,clock-names = "core_clk";
+ qcom,retain-mem;
+ qcom,retain-periph;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm9625-cdp.dtsi b/arch/arm/boot/dts/msm9625-cdp.dtsi
index 1f9cbb0..6ddb50b 100644
--- a/arch/arm/boot/dts/msm9625-cdp.dtsi
+++ b/arch/arm/boot/dts/msm9625-cdp.dtsi
@@ -13,7 +13,7 @@
/include/ "msm9625-display.dtsi"
/include/ "qpic-panel-ili-qvga.dtsi"
-/ {
+&soc {
i2c@f9925000 {
charger@57 {
compatible = "summit,smb137c";
diff --git a/arch/arm/boot/dts/msm9625-coresight.dtsi b/arch/arm/boot/dts/msm9625-coresight.dtsi
index 69a1d7b..bde734e 100644
--- a/arch/arm/boot/dts/msm9625-coresight.dtsi
+++ b/arch/arm/boot/dts/msm9625-coresight.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
tmc_etr: tmc@fc322000 {
compatible = "arm,coresight-tmc";
reg = <0xfc322000 0x1000>,
@@ -34,6 +34,8 @@
coresight-id = <1>;
coresight-name = "coresight-tpiu";
coresight-nr-inports = <1>;
+
+ vdd-supply = <&ext_2p95v>;
};
replicator: replicator@fc31c000 {
@@ -243,4 +245,15 @@
coresight-name = "coresight-cti-cpu";
coresight-nr-inports = <0>;
};
+
+ hwevent: hwevent@f9011038 {
+ compatible = "qcom,coresight-hwevent";
+ reg = <0xf9011038 0x8>,
+ <0xfd4ab160 0x80>;
+ reg-names = "apcs-mux", "ppss-mux";
+
+ coresight-id = <20>;
+ coresight-name = "coresight-hwevent";
+ coresight-nr-inports = <0>;
+ };
};
diff --git a/arch/arm/boot/dts/msm9625-display.dtsi b/arch/arm/boot/dts/msm9625-display.dtsi
index a160bae..287a63a 100644
--- a/arch/arm/boot/dts/msm9625-display.dtsi
+++ b/arch/arm/boot/dts/msm9625-display.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,msm_qpic@f9ac0000 {
compatible = "qcom,mdss_qpic";
reg = <0xf9ac0000 0x24000>;
diff --git a/arch/arm/boot/dts/msm9625-ion.dtsi b/arch/arm/boot/dts/msm9625-ion.dtsi
index 8183264..2a3e4b5 100644
--- a/arch/arm/boot/dts/msm9625-ion.dtsi
+++ b/arch/arm/boot/dts/msm9625-ion.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,ion {
compatible = "qcom,msm-ion";
#address-cells = <1>;
diff --git a/arch/arm/boot/dts/msm9625-mtp.dtsi b/arch/arm/boot/dts/msm9625-mtp.dtsi
index cc0bf5e..79c873f 100644
--- a/arch/arm/boot/dts/msm9625-mtp.dtsi
+++ b/arch/arm/boot/dts/msm9625-mtp.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
i2c@f9925000 {
charger@57 {
compatible = "summit,smb137c";
diff --git a/arch/arm/boot/dts/msm9625-pm.dtsi b/arch/arm/boot/dts/msm9625-pm.dtsi
index 3e421a8..8eb1119 100644
--- a/arch/arm/boot/dts/msm9625-pm.dtsi
+++ b/arch/arm/boot/dts/msm9625-pm.dtsi
@@ -10,9 +10,7 @@
* GNU General Public License for more details.
*/
-/include/ "skeleton.dtsi"
-
-/ {
+&soc {
qcom,spm@f9009000 {
compatible = "qcom,spm-v2";
#address-cells = <1>;
@@ -201,7 +199,8 @@
qcom,ipc-bit-offset = <1>;
qcom,gic-parent = <&intc>;
- qcom,gic-map = <47 172>, /* usb2_hsic_async_wakeup_irq */
+ qcom,gic-map = <2 216>, /* tsens_upper_lower_int */
+ <47 172>, /* usb2_hsic_async_wakeup_irq */
<41 180>, /* usb_async_wakeup_irq */
<62 222>, /* ee0_krait_hlos_spmi_periph_irq */
<0xff 57>, /* mss_to_apps_irq(0) */
@@ -236,40 +235,40 @@
<0xff 240>; /* summary_irq_kpss */
qcom,gpio-parent = <&msmgpio>;
- qcom,gpio-map = <4 1>,
- <5 5>,
- <6 9>,
- <7 18>,
- <8 20>,
- <9 24>,
- <10 27>,
- <11 28>,
- <12 34>,
- <13 35>,
- <14 37>,
- <15 42>,
- <16 44>,
- <17 46>,
- <18 50>,
- <19 54>,
- <20 59>,
- <21 61>,
- <22 62>,
- <23 64>,
- <24 65>,
- <25 66>,
- <26 67>,
- <27 68>,
- <28 71>,
- <29 72>,
- <30 73>,
- <31 74>,
- <32 75>,
- <33 77>,
- <34 79>,
- <35 80>,
- <36 82>,
- <37 86>;
+ qcom,gpio-map = <4 0>,
+ <5 1>,
+ <6 2>,
+ <7 3>,
+ <8 4>,
+ <9 5>,
+ <10 6>,
+ <11 7>,
+ <12 8>,
+ <13 9>,
+ <14 10>,
+ <15 11>,
+ <16 12>,
+ <17 13>,
+ <18 14>,
+ <19 15>,
+ <20 16>,
+ <21 17>,
+ <22 18>,
+ <23 19>,
+ <24 20>,
+ <25 21>,
+ <26 24>,
+ <27 25>,
+ <28 51>,
+ <29 61>,
+ <30 62>,
+ <31 63>,
+ <32 64>,
+ <33 65>,
+ <34 66>,
+ <35 67>,
+ <36 69>,
+ <37 71>;
};
qcom,pm-8x60 {
diff --git a/arch/arm/boot/dts/msm9625-regulator.dtsi b/arch/arm/boot/dts/msm9625-regulator.dtsi
index 24f616d..ee48b7f 100644
--- a/arch/arm/boot/dts/msm9625-regulator.dtsi
+++ b/arch/arm/boot/dts/msm9625-regulator.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -260,7 +260,7 @@
};
};
-/ {
+&soc {
ext_2p95v: regulator-isl80101 {
compatible = "regulator-fixed";
regulator-name = "ext_2p95v";
diff --git a/arch/arm/boot/dts/msm9625-smp2p.dtsi b/arch/arm/boot/dts/msm9625-smp2p.dtsi
index 46af1b2..f8ad351 100644
--- a/arch/arm/boot/dts/msm9625-smp2p.dtsi
+++ b/arch/arm/boot/dts/msm9625-smp2p.dtsi
@@ -9,7 +9,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,smp2p-modem {
compatible = "qcom,smp2p";
reg = <0xf9011008 0x4>;
diff --git a/arch/arm/boot/dts/msm9625-v1.dtsi b/arch/arm/boot/dts/msm9625-v1.dtsi
index ad95601..b238ba5 100644
--- a/arch/arm/boot/dts/msm9625-v1.dtsi
+++ b/arch/arm/boot/dts/msm9625-v1.dtsi
@@ -18,7 +18,7 @@
/include/ "msm9625.dtsi"
-/ {
+&soc {
qcom,msm-imem@fc42a800 {
compatible = "qcom,msm-imem";
reg = <0xfc42a800 0x1000>; /* Address and size of IMEM */
@@ -37,6 +37,10 @@
};
};
+&hsic_host {
+ qcom,disable-park-mode;
+};
+
&ipa_hw {
qcom,ipa-hw-ver = <1>; /* IPA h-w revision */
};
diff --git a/arch/arm/boot/dts/msm9625-v2-mtp.dts b/arch/arm/boot/dts/msm9625-v2-mtp.dts
index 5324e2c..27d0066 100644
--- a/arch/arm/boot/dts/msm9625-v2-mtp.dts
+++ b/arch/arm/boot/dts/msm9625-v2-mtp.dts
@@ -21,7 +21,9 @@
qcom,msm-id = <134 7 0x20000>, <152 7 0x20000>, <149 7 0x20000>,
<150 7 0x20000>, <151 7 0x20000>, <148 7 0x20000>,
<173 7 0x20000>, <174 7 0x20000>, <175 7 0x20000>;
+};
+&soc {
i2c@f9925000 {
charger@57 {
compatible = "summit,smb137c";
diff --git a/arch/arm/boot/dts/msm9625-v2.1.dtsi b/arch/arm/boot/dts/msm9625-v2.1.dtsi
index c3c2c49..65ff96a 100644
--- a/arch/arm/boot/dts/msm9625-v2.1.dtsi
+++ b/arch/arm/boot/dts/msm9625-v2.1.dtsi
@@ -18,7 +18,7 @@
/include/ "msm9625.dtsi"
-/ {
+&soc {
qcom,msm-imem@fe807800 {
compatible = "qcom,msm-imem";
reg = <0xfe807800 0x1000>; /* Address and size of IMEM */
diff --git a/arch/arm/boot/dts/msm9625-v2.dtsi b/arch/arm/boot/dts/msm9625-v2.dtsi
index 3ce6844..b078309 100644
--- a/arch/arm/boot/dts/msm9625-v2.dtsi
+++ b/arch/arm/boot/dts/msm9625-v2.dtsi
@@ -18,7 +18,7 @@
/include/ "msm9625.dtsi"
-/ {
+&soc {
qcom,msm-imem@fe807800 {
compatible = "qcom,msm-imem";
reg = <0xfe807800 0x1000>; /* Address and size of IMEM */
@@ -35,6 +35,10 @@
qcom,ipa-hw-ver = <2>; /* IPA h-w revision */
};
+&hsic_host {
+ qcom,disable-park-mode;
+};
+
&sfpb_spinlock {
status = "disable";
};
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index faec7af..f184a00 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -11,10 +11,6 @@
*/
/include/ "skeleton.dtsi"
-/include/ "msm9625-ion.dtsi"
-/include/ "msm9625-pm.dtsi"
-/include/ "msm9625-coresight.dtsi"
-/include/ "msm9625-smp2p.dtsi"
/ {
model = "Qualcomm MSM 9625";
@@ -25,6 +21,19 @@
spi0 = &spi_0;
};
+ soc: soc { };
+};
+
+/include/ "msm9625-ion.dtsi"
+/include/ "msm9625-pm.dtsi"
+/include/ "msm9625-coresight.dtsi"
+/include/ "msm9625-smp2p.dtsi"
+
+&soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
intc: interrupt-controller@F9000000 {
compatible = "qcom,msm-qgic2";
interrupt-controller;
@@ -155,6 +164,7 @@
qcom,hsusb-otg-disable-reset;
qcom,hsusb-otg-lpm-on-dev-suspend;
qcom,hsusb-otg-clk-always-on-workaround;
+ qcom,hsusb-otg-delay-lpm-hndshk-on-disconnect;
qcom,hsusb-otg-delay-lpm;
qcom,msm-bus,name = "usb2";
@@ -165,6 +175,11 @@
<87 512 40000 640000>;
};
+ msm_hsusb: qcom,ci13xxx_msm {
+ compatible = "qcom,ci13xxx_msm";
+ qcom,itc-level = <4>;
+ };
+
hsic_host: hsic@f9a15000 {
compatible = "qcom,hsic-host";
reg = <0xf9a15000 0x400>;
@@ -181,6 +196,8 @@
<85 512 40000 640000>;
qcom,pool-64-bit-align;
qcom,enable-hbm;
+ hsic,consider-ipa-handshake;
+ qcom,ahb-async-bridge-bypass;
};
qcom,usbbam@f9a44000 {
@@ -205,6 +222,7 @@
qcom,src-bam-pipe-index = <1>;
qcom,data-fifo-size = <0x8000>;
qcom,descriptor-fifo-size = <0x2000>;
+ qcom,reset-bam-on-connect;
};
qcom,pipe1 {
label = "hsusb-ipa-in-0";
@@ -217,6 +235,7 @@
qcom,dst-bam-pipe-index = <0>;
qcom,data-fifo-size = <0x8000>;
qcom,descriptor-fifo-size = <0x2000>;
+ qcom,reset-bam-on-connect;
};
qcom,pipe2 {
label = "hsusb-qdss-in-0";
@@ -230,9 +249,9 @@
qcom,dst-bam-physical-address = <0xf9a44000>;
qcom,dst-bam-pipe-index = <2>;
qcom,data-fifo-offset = <0x4100>;
- qcom,data-fifo-size = <0x400>;
+ qcom,data-fifo-size = <0x700>;
qcom,descriptor-fifo-offset = <0x4000>;
- qcom,descriptor-fifo-size = <0x400>;
+ qcom,descriptor-fifo-size = <0x100>;
};
qcom,pipe3 {
label = "hsic-ipa-in-0";
@@ -243,8 +262,8 @@
qcom,peer-bam = <2>;
qcom,dst-bam-physical-address = <0xf9a04000>;
qcom,dst-bam-pipe-index = <3>;
- qcom,data-fifo-size = <0xD480>;
- qcom,descriptor-fifo-size = <0x1A80>;
+ qcom,data-fifo-size = <0xF800>;
+ qcom,descriptor-fifo-size = <0x3A58>;
qcom,reset-bam-on-connect;
};
qcom,pipe4 {
@@ -256,8 +275,8 @@
qcom,usb-bam-mem-type = <2>;
qcom,dst-bam-physical-address = <0xf9a04000>;
qcom,dst-bam-pipe-index = <4>;
- qcom,data-fifo-size = <0xD480>;
- qcom,descriptor-fifo-size = <0x1A80>;
+ qcom,data-fifo-size = <0xF800>;
+ qcom,descriptor-fifo-size = <0x3A58>;
qcom,reset-bam-on-connect;
};
qcom,pipe5 {
@@ -269,8 +288,8 @@
qcom,peer-bam = <2>;
qcom,dst-bam-physical-address = <0xf9a04000>;
qcom,dst-bam-pipe-index = <5>;
- qcom,data-fifo-size = <0xD480>;
- qcom,descriptor-fifo-size = <0x1A80>;
+ qcom,data-fifo-size = <0xF800>;
+ qcom,descriptor-fifo-size = <0x3A58>;
qcom,reset-bam-on-connect;
};
qcom,pipe6 {
@@ -282,8 +301,8 @@
qcom,peer-bam = <2>;
qcom,dst-bam-physical-address = <0xf9a04000>;
qcom,dst-bam-pipe-index = <6>;
- qcom,data-fifo-size = <0xD480>;
- qcom,descriptor-fifo-size = <0x1A80>;
+ qcom,data-fifo-size = <0xF800>;
+ qcom,descriptor-fifo-size = <0x3A58>;
qcom,reset-bam-on-connect;
};
qcom,pipe7 {
@@ -295,8 +314,8 @@
qcom,peer-bam = <2>;
qcom,src-bam-physical-address = <0xf9a04000>;
qcom,src-bam-pipe-index = <7>;
- qcom,data-fifo-size = <0xD480>;
- qcom,descriptor-fifo-size = <0x1A80>;
+ qcom,data-fifo-size = <0xDFE>;
+ qcom,descriptor-fifo-size = <0xB30>;
qcom,reset-bam-on-connect;
};
};
@@ -392,7 +411,7 @@
qcom,pad-pull-on = <0x0 0x3 0x3>;
qcom,pad-pull-off = <0x0 0x3 0x3>;
- qcom,pad-drv-on = <0x7 0x4 0x4>;
+ qcom,pad-drv-on = <0x4 0x4 0x4>;
qcom,pad-drv-off = <0x0 0x0 0x0>;
qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
@@ -502,6 +521,25 @@
qcom,calib-mode = "fuse_map1";
};
+ qcom,msm-thermal {
+ compatible = "qcom,msm-thermal";
+ qcom,sensor-id = <0>;
+ qcom,poll-ms = <250>;
+ qcom,limit-temp = <60>;
+ qcom,temp-hysteresis = <10>;
+ qcom,freq-step = <2>;
+ qcom,freq-control-mask = <0x0>;
+ qcom,vdd-restriction-temp = <5>;
+ qcom,vdd-restriction-temp-hysteresis = <10>;
+ vdd_dig-supply = <&pm8019_l10_corner>;
+
+ qcom,vdd-dig-rstr{
+ qcom,vdd-rstr-reg = "vdd_dig";
+ qcom,levels = <5 7 7>; /* Nominal, Super Turbo, Super Turbo */
+ qcom,min-level = <1>; /* No Request */
+ };
+ };
+
qcom,msm-rng@f9bff000 {
compatible = "qcom,msm-rng";
reg = <0xf9bff000 0x200>;
@@ -752,10 +790,13 @@
qcom,mss {
compatible = "qcom,pil-q6v5-mss";
interrupts = <0 24 1>;
+ qcom,is-not-loadable;
/* GPIO inputs from mss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_1_in 1 0>;
qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_1_in 2 0>;
+ qcom,gpio-stop-ack = <&smp2pgpio_ssr_smp2p_1_in 3 0>;
/* GPIO output to mss */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
@@ -955,3 +996,32 @@
qcom,fast-avg-setup = <0>;
};
};
+
+&pm8019_adc_tm {
+ /* Channel Node */
+ chan@33 {
+ label = "pa_therm0";
+ reg = <0x33>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x48>;
+ qcom,thermal-node;
+ };
+
+ chan@34 {
+ label = "pa_therm1";
+ reg = <0x34>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x68>;
+ qcom,thermal-node;
+ };
+};
diff --git a/arch/arm/boot/dts/msmkrypton-sim.dts b/arch/arm/boot/dts/msmkrypton-sim.dts
index 1872a36..29f0df4 100644
--- a/arch/arm/boot/dts/msmkrypton-sim.dts
+++ b/arch/arm/boot/dts/msmkrypton-sim.dts
@@ -23,3 +23,13 @@
&uartdm3{
status = "ok";
};
+
+&spi_6 {
+ ethernet-switch@0 {
+ compatible = "simtec,ks8851";
+ reg = <0>;
+ interrupt-parent = <&msmgpio>;
+ spi-max-frequency = <19200000>;
+ interrupts = <75 0>;
+ };
+};
diff --git a/arch/arm/boot/dts/msmkrypton.dtsi b/arch/arm/boot/dts/msmkrypton.dtsi
index db61dab..7bbd528 100644
--- a/arch/arm/boot/dts/msmkrypton.dtsi
+++ b/arch/arm/boot/dts/msmkrypton.dtsi
@@ -17,6 +17,14 @@
compatible = "qcom,msmkrypton";
interrupt-parent = <&intc>;
+ soc: soc { };
+};
+
+&soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
intc: interrupt-controller@f9000000 {
compatible = "qcom,msm-qgic2";
interrupt-controller;
@@ -37,12 +45,70 @@
qcom,direct-connect-irqs = <8>;
};
- timer: msm-qtimer@f9021000 {
- compatible = "arm,armv7-timer";
- reg = <0xf9021000 0x1000>;
- interrupts = <0 7 0>;
- irq-is-not-percpu;
+ timer@f9020000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ compatible = "arm,armv7-timer-mem";
+ reg = <0xf9020000 0x1000>;
clock-frequency = <19200000>;
+
+ frame@f9021000 {
+ frame-number = <0>;
+ interrupts = <0 7 0x4>,
+ <0 6 0x4>;
+ reg = <0xf9021000 0x1000>,
+ <0xf9022000 0x1000>;
+ };
+
+ frame@f9023000 {
+ frame-number = <1>;
+ interrupts = <0 8 0x4>;
+ reg = <0xf9023000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@f9024000 {
+ frame-number = <2>;
+ interrupts = <0 9 0x4>;
+ reg = <0xf9024000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@f9025000 {
+ frame-number = <3>;
+ interrupts = <0 10 0x4>;
+ reg = <0xf9025000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@f9026000 {
+ frame-number = <4>;
+ interrupts = <0 11 0x4>;
+ reg = <0xf9026000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@f9027000 {
+ frame-number = <5>;
+ interrupts = <0 12 0x4>;
+ reg = <0xf9027000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@f9028000 {
+ frame-number = <6>;
+ interrupts = <0 13 0x4>;
+ reg = <0xf9028000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@f9029000 {
+ frame-number = <7>;
+ interrupts = <0 14 0x4>;
+ reg = <0xf9029000 0x1000>;
+ status = "disabled";
+ };
};
uartdm3: serial@f991f000 {
@@ -51,4 +117,19 @@
interrupts = <0 109 0>;
status = "disabled";
};
+
+ spi_6: spi@f9928000 { /* BLSP1 QUP6 */
+ cell-index = <0>;
+ compatible = "qcom,spi-qup-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0xf9928000 0x1000>;
+ interrupts = <0 100 0>;
+ spi-max-frequency = <19200000>;
+
+ gpios = <&msmgpio 23 0>, /* CLK */
+ <&msmgpio 21 0>, /* MISO */
+ <&msmgpio 20 0>; /* MOSI */
+ cs-gpios = <&msmgpio 22 0>;
+ };
};
diff --git a/arch/arm/boot/dts/msmzinc-ion.dtsi b/arch/arm/boot/dts/msmsamarium-ion.dtsi
similarity index 98%
copy from arch/arm/boot/dts/msmzinc-ion.dtsi
copy to arch/arm/boot/dts/msmsamarium-ion.dtsi
index aac4230..ea954b8 100644
--- a/arch/arm/boot/dts/msmzinc-ion.dtsi
+++ b/arch/arm/boot/dts/msmsamarium-ion.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,ion {
compatible = "qcom,msm-ion";
#address-cells = <1>;
diff --git a/arch/arm/boot/dts/msmzinc-sim.dts b/arch/arm/boot/dts/msmsamarium-rumi.dts
similarity index 69%
copy from arch/arm/boot/dts/msmzinc-sim.dts
copy to arch/arm/boot/dts/msmsamarium-rumi.dts
index e410344..9a679a4 100644
--- a/arch/arm/boot/dts/msmzinc-sim.dts
+++ b/arch/arm/boot/dts/msmsamarium-rumi.dts
@@ -12,18 +12,14 @@
/dts-v1/;
-/include/ "msmzinc.dtsi"
+/include/ "msmsamarium.dtsi"
/ {
- model = "Qualcomm MSM ZINC Simulator";
- compatible = "qcom,msmzinc-sim", "qcom,msmzinc", "qcom,sim";
- qcom,msm-id = <178 0 0>;
+ model = "Qualcomm MSM SAMARIUM RUMI";
+ compatible = "qcom,msmsamarium-rumi", "qcom,msmsamarium", "qcom,rumi";
+ qcom,msm-id = <195 0 0>;
+};
- aliases {
- serial0 = &uart0;
- };
-
- uart0: serial@f991f000 {
- status = "ok";
- };
+&uartblsp0dm2{
+ status = "ok";
};
diff --git a/arch/arm/boot/dts/msmsamarium-sim.dts b/arch/arm/boot/dts/msmsamarium-sim.dts
new file mode 100644
index 0000000..4acffae
--- /dev/null
+++ b/arch/arm/boot/dts/msmsamarium-sim.dts
@@ -0,0 +1,55 @@
+/* Copyright (c) 2013, 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.
+ */
+
+/dts-v1/;
+
+/include/ "msmsamarium.dtsi"
+
+/ {
+ model = "Qualcomm MSM SAMARIUM SIM";
+ compatible = "qcom,msmsamarium-sim", "qcom,msmsamarium", "qcom,sim";
+ qcom,msm-id = <195 0 0>;
+};
+
+&uartblsp0dm2{
+ status = "ok";
+};
+
+&sdcc1 {
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ qcom,nonremovable;
+ status = "ok";
+};
+
+&sdcc2 {
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sup-voltages = <2950 2950>;
+
+ qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
+ qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,xpc;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+ qcom,current-limit = <800>;
+
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/msmsamarium.dtsi b/arch/arm/boot/dts/msmsamarium.dtsi
new file mode 100644
index 0000000..81699b6
--- /dev/null
+++ b/arch/arm/boot/dts/msmsamarium.dtsi
@@ -0,0 +1,90 @@
+/* Copyright (c) 2013, 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/ "skeleton.dtsi"
+
+/ {
+ model = "Qualcomm MSM SAMARIUM";
+ compatible = "qcom,msmsamarium";
+ interrupt-parent = <&intc>;
+ soc: soc { };
+};
+
+/include/ "msmsamarium-ion.dtsi"
+
+&soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ intc: interrupt-controller@f9000000 {
+ compatible = "qcom,msm-qgic2";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = <0xf9000000 0x1000>,
+ <0xf9002000 0x1000>;
+ };
+
+ msmgpio: gpio@fd510000 {
+ compatible = "qcom,msm-gpio";
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0xfd510000 0x4000>;
+ ngpio = <145>;
+ interrupts = <0 208 0>;
+ qcom,direct-connect-irqs = <8>;
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <1 2 0 1 3 0>;
+ clock-frequency = <19200000>;
+ };
+
+ uartblsp0dm2: serial@f991f000 {
+ compatible = "qcom,msm-lsuart-v14";
+ reg = <0xf991f000 0x1000>;
+ interrupts = <0 109 0>;
+ status = "disabled";
+ };
+
+ qcom,msm-imem@fe805000 {
+ compatible = "qcom,msm-imem";
+ reg = <0xfe805000 0x1000>; /* Address and size of IMEM */
+ };
+
+ sdcc1: qcom,sdcc@f9824000 {
+ cell-index = <1>; /* SDC1 eMMC slot */
+ compatible = "qcom,msm-sdcc";
+ reg = <0xf9824000 0x800>;
+ reg-names = "core_mem";
+ interrupts = <0 123 0>;
+ interrupt-names = "core_irq";
+
+ qcom,bus-width = <8>;
+ status = "disabled";
+ };
+
+ sdcc2: qcom,sdcc@f98a4000 {
+ cell-index = <2>; /* SDC2 SD card slot */
+ compatible = "qcom,msm-sdcc";
+ reg = <0xf98a4000 0x800>;
+ reg-names = "core_mem";
+ interrupts = <0 125 0>;
+ interrupt-names = "core_irq";
+
+ qcom,bus-width = <4>;
+ status = "disabled";
+ };
+};
diff --git a/arch/arm/boot/dts/qpic-panel-ili-qvga.dtsi b/arch/arm/boot/dts/qpic-panel-ili-qvga.dtsi
index a0c906e..089f112 100644
--- a/arch/arm/boot/dts/qpic-panel-ili-qvga.dtsi
+++ b/arch/arm/boot/dts/qpic-panel-ili-qvga.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,mdss_lcdc_ili9341_qvga {
compatible = "qcom,mdss-qpic-panel";
label = "ili qvga lcdc panel";
diff --git a/arch/arm/boot/dts/skeleton64.dtsi b/arch/arm/boot/dts/skeleton64.dtsi
new file mode 100644
index 0000000..5bf6a82
--- /dev/null
+++ b/arch/arm/boot/dts/skeleton64.dtsi
@@ -0,0 +1,18 @@
+/*
+ * Skeleton device tree; the bare minimum needed to boot; just include and
+ * add a compatible value. The bootloader will typically populate the memory
+ * node.
+ */
+
+/ {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ chosen { };
+ aliases { };
+ memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ device_type = "memory";
+ reg = <0 0 0 0>;
+ };
+};
diff --git a/arch/arm/configs/msmzinc_defconfig b/arch/arm/configs/apq8084_defconfig
similarity index 94%
copy from arch/arm/configs/msmzinc_defconfig
copy to arch/arm/configs/apq8084_defconfig
index d0ea87a..33e1923 100644
--- a/arch/arm/configs/msmzinc_defconfig
+++ b/arch/arm/configs/apq8084_defconfig
@@ -22,7 +22,6 @@
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_PANIC_TIMEOUT=5
CONFIG_KALLSYMS_ALL=y
-CONFIG_ASHMEM=y
CONFIG_EMBEDDED=y
CONFIG_PROFILING=y
CONFIG_KPROBES=y
@@ -34,7 +33,7 @@
CONFIG_EFI_PARTITION=y
CONFIG_IOSCHED_TEST=y
CONFIG_ARCH_MSM=y
-CONFIG_ARCH_MSMZINC=y
+CONFIG_ARCH_APQ8084=y
CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
CONFIG_MSM_RPM_SMD=y
# CONFIG_MSM_STACKED_MEMORY is not set
@@ -43,11 +42,13 @@
# CONFIG_MSM_PROC_COMM is not set
CONFIG_MSM_SMD=y
CONFIG_MSM_SMD_PKG4=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_SMP2P_TEST=y
CONFIG_MSM_IPC_LOGGING=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_IPC_ROUTER_SECURITY=y
-# CONFIG_MSM_HW3D is not set
+CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_RPM_REGULATOR_SMD=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
CONFIG_MSM_SYSMON_COMM=y
@@ -74,8 +75,6 @@
CONFIG_MSM_L2_ERP_PORT_PANIC=y
CONFIG_MSM_L2_ERP_1BIT_PANIC=y
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
-CONFIG_MSM_CACHE_DUMP=y
-CONFIG_MSM_CACHE_DUMP_ON_PANIC=y
CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
CONFIG_ARM_LPAE=y
CONFIG_NO_HZ=y
@@ -87,7 +86,6 @@
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
-CONFIG_VMALLOC_RESERVE=0x19000000
CONFIG_CC_STACKPROTECTOR=y
CONFIG_CP_ACCESS=y
CONFIG_USE_OF=y
@@ -100,7 +98,6 @@
CONFIG_VFP=y
CONFIG_NEON=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-CONFIG_WAKELOCK=y
CONFIG_PM_RUNTIME=y
CONFIG_NET=y
CONFIG_PACKET=y
@@ -209,8 +206,6 @@
CONFIG_RFKILL=y
CONFIG_GENLOCK=y
CONFIG_GENLOCK_MISCDEVICE=y
-CONFIG_SYNC=y
-CONFIG_SW_SYNC=y
CONFIG_CMA=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
@@ -248,6 +243,7 @@
CONFIG_INPUT_UINPUT=y
CONFIG_SERIAL_MSM_HSL=y
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM=y
CONFIG_I2C=y
@@ -284,6 +280,7 @@
CONFIG_ION_MSM=y
CONFIG_MSM_KGSL=y
CONFIG_FB=y
+CONFIG_FB_VIRTUAL=y
CONFIG_FB_MSM=y
# CONFIG_FB_MSM_BACKLIGHT is not set
CONFIG_FB_MSM_MDSS=y
@@ -313,6 +310,22 @@
CONFIG_USB_STORAGE_KARMA=y
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_STORAGE_ENE_UB6250=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_DWC3_MSM=y
+CONFIG_USB_G_ANDROID=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_BLOCK_MINORS=32
+# CONFIG_MMC_BLOCK_BOUNCE is not set
+CONFIG_MMC_TEST=m
+CONFIG_MMC_BLOCK_TEST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_MSM=y
+CONFIG_MMC_SDHCI_MSM=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_BACKLIGHT=y
@@ -324,6 +337,7 @@
CONFIG_STAGING=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ASHMEM=y
CONFIG_ANDROID_LOGGER=y
CONFIG_ANDROID_TIMED_GPIO=y
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
@@ -334,8 +348,9 @@
CONFIG_QPNP_PWM=y
CONFIG_QPNP_POWER_ON=y
CONFIG_QPNP_CLKDIV=y
-CONFIG_MSM_IOMMU=y
+CONFIG_MSM_IOMMU_V1=y
CONFIG_IOMMU_PGTABLES_L2=y
+CONFIG_IOMMU_NON_SECURE=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
diff --git a/arch/arm/configs/fsm9900_defconfig b/arch/arm/configs/fsm9900_defconfig
new file mode 100644
index 0000000..94e18ae
--- /dev/null
+++ b/arch/arm/configs/fsm9900_defconfig
@@ -0,0 +1,261 @@
+# CONFIG_ARM_PATCH_PHYS_VIRT is not set
+CONFIG_EXPERIMENTAL=y
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_RD_XZ=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+CONFIG_KPROBES=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_IOSCHED_TEST=y
+CONFIG_ARCH_MSM=y
+CONFIG_ARCH_FSM9900=y
+CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
+CONFIG_MSM_MPM_OF=y
+# CONFIG_MSM_STACKED_MEMORY is not set
+CONFIG_CPU_HAS_L2_PMU=y
+# CONFIG_MSM_FIQ_SUPPORT is not set
+# CONFIG_MSM_PROC_COMM is not set
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_PKG4=y
+CONFIG_MSM_IPC_LOGGING=y
+CONFIG_MSM_IPC_ROUTER=y
+CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
+CONFIG_MSM_IPC_ROUTER_SECURITY=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
+CONFIG_MSM_DIRECT_SCLK_ACCESS=y
+CONFIG_MSM_WATCHDOG_V2=y
+CONFIG_MSM_MEMORY_DUMP=y
+CONFIG_MSM_DLOAD_MODE=y
+CONFIG_MSM_SPM_V2=y
+CONFIG_MSM_L2_SPM=y
+CONFIG_MSM_MULTIMEDIA_USE_ION=y
+CONFIG_MSM_OCMEM=y
+CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
+CONFIG_MSM_OCMEM_DEBUG=y
+CONFIG_MSM_OCMEM_NONSECURE=y
+CONFIG_SENSORS_ADSP=y
+CONFIG_MSM_RTB=y
+CONFIG_MSM_RTB_SEPARATE_CPUS=y
+CONFIG_MSM_CACHE_ERP=y
+CONFIG_MSM_L1_ERR_PANIC=y
+CONFIG_MSM_L1_RECOV_ERR_PANIC=y
+CONFIG_MSM_L1_ERR_LOG=y
+CONFIG_MSM_L2_ERP_PRINT_ACCESS_ERRORS=y
+CONFIG_MSM_L2_ERP_PORT_PANIC=y
+CONFIG_MSM_L2_ERP_1BIT_PANIC=y
+CONFIG_MSM_L2_ERP_2BIT_PANIC=y
+CONFIG_MSM_CACHE_DUMP=y
+CONFIG_MSM_CACHE_DUMP_ON_PANIC=y
+CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+# CONFIG_SMP_ON_UP is not set
+CONFIG_SCHED_MC=y
+CONFIG_ARM_ARCH_TIMER=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_CC_STACKPROTECTOR=y
+CONFIG_CP_ACCESS=y
+CONFIG_USE_OF=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_PM_RUNTIME=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_NET_IPIP=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+CONFIG_IPV6=y
+# CONFIG_NET_ACTIVITY_STATS is not set
+# CONFIG_WIRELESS is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_GENLOCK=y
+CONFIG_GENLOCK_MISCDEVICE=y
+CONFIG_SYNC=y
+CONFIG_SW_SYNC=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_MII=y
+CONFIG_TUN=y
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CHELSIO is not set
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_MSM_RMNET is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_EVBUG=m
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_SERIO is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+CONFIG_SERIAL_MSM_HSL=y
+CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM=y
+CONFIG_DCC_TTY=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_QUP=y
+CONFIG_SPI=y
+CONFIG_SPI_SPIDEV=m
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB=y
+CONFIG_MSM_QPNP_INT=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_QPNP_PIN=y
+CONFIG_GPIO_QPNP_PIN_DEBUG=y
+CONFIG_SENSORS_EPM_ADC=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_SENSORS_QPNP_ADC_CURRENT=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_TSENS8974=y
+CONFIG_THERMAL_MONITOR=y
+CONFIG_THERMAL_QPNP=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_STUB=y
+CONFIG_REGULATOR_QPNP=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_USB=y
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM=y
+CONFIG_USB_EHCI_MSM_HSIC=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_BLOCK_MINORS=32
+# CONFIG_MMC_BLOCK_BOUNCE is not set
+CONFIG_MMC_TEST=m
+CONFIG_MMC_BLOCK_TEST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_MSM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_SWITCH=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_DRV_MSM is not set
+CONFIG_RTC_DRV_QPNP=y
+CONFIG_MSM_SSBI=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_BAMDMA=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_PWM=y
+CONFIG_QPNP_POWER_ON=y
+CONFIG_QPNP_CLKDIV=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT4_FS=y
+CONFIG_FUSE_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_PSTORE=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_LOCKUP_DETECTOR=y
+# CONFIG_DETECT_HUNG_TASK is not set
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_DEBUG_STACK_USAGE=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_DEBUG_LIST=y
+CONFIG_FAULT_INJECTION=y
+CONFIG_FAIL_PAGE_ALLOC=y
+CONFIG_FAULT_INJECTION_DEBUG_FS=y
+CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
+CONFIG_DEBUG_PAGEALLOC=y
+CONFIG_CPU_FREQ_SWITCH_PROFILER=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_USER=y
+CONFIG_DEBUG_LL=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_PID_IN_CONTEXTIDR=y
+CONFIG_KEYS=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_DEV_QCRYPTO=m
+CONFIG_CRYPTO_DEV_QCE=m
+CONFIG_CRYPTO_DEV_QCEDEV=m
+CONFIG_CRC_CCITT=y
+CONFIG_LIBCRC32C=y
diff --git a/arch/arm/configs/fsm9xxx-perf_defconfig b/arch/arm/configs/fsm9xxx-perf_defconfig
index 10414e1..37c9554 100644
--- a/arch/arm/configs/fsm9xxx-perf_defconfig
+++ b/arch/arm/configs/fsm9xxx-perf_defconfig
@@ -152,13 +152,6 @@
CONFIG_RTC_CLASS=y
CONFIG_RTC_DEBUG=y
CONFIG_STAGING=y
-CONFIG_ANDROID=y
-CONFIG_ANDROID_BINDER_IPC=y
-CONFIG_ASHMEM=y
-CONFIG_ANDROID_LOGGER=y
-CONFIG_ANDROID_RAM_CONSOLE=y
-CONFIG_ANDROID_TIMED_GPIO=y
-CONFIG_ANDROID_LOW_MEMORY_KILLER=y
CONFIG_MSM_SSBI=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
diff --git a/arch/arm/configs/fsm9xxx_defconfig b/arch/arm/configs/fsm9xxx_defconfig
index aa3befa..f9295b2 100644
--- a/arch/arm/configs/fsm9xxx_defconfig
+++ b/arch/arm/configs/fsm9xxx_defconfig
@@ -151,13 +151,6 @@
CONFIG_RTC_CLASS=y
CONFIG_RTC_DEBUG=y
CONFIG_STAGING=y
-CONFIG_ANDROID=y
-CONFIG_ANDROID_BINDER_IPC=y
-CONFIG_ASHMEM=y
-CONFIG_ANDROID_LOGGER=y
-CONFIG_ANDROID_RAM_CONSOLE=y
-CONFIG_ANDROID_TIMED_GPIO=y
-CONFIG_ANDROID_LOW_MEMORY_KILLER=y
CONFIG_MSM_SSBI=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
diff --git a/arch/arm/configs/msmzinc_defconfig b/arch/arm/configs/mpq8092_defconfig
similarity index 96%
rename from arch/arm/configs/msmzinc_defconfig
rename to arch/arm/configs/mpq8092_defconfig
index d0ea87a..5d05596 100644
--- a/arch/arm/configs/msmzinc_defconfig
+++ b/arch/arm/configs/mpq8092_defconfig
@@ -22,7 +22,6 @@
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_PANIC_TIMEOUT=5
CONFIG_KALLSYMS_ALL=y
-CONFIG_ASHMEM=y
CONFIG_EMBEDDED=y
CONFIG_PROFILING=y
CONFIG_KPROBES=y
@@ -34,9 +33,9 @@
CONFIG_EFI_PARTITION=y
CONFIG_IOSCHED_TEST=y
CONFIG_ARCH_MSM=y
-CONFIG_ARCH_MSMZINC=y
+CONFIG_ARCH_MPQ8092=y
CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
-CONFIG_MSM_RPM_SMD=y
+CONFIG_MSM_MPM_OF=y
# CONFIG_MSM_STACKED_MEMORY is not set
CONFIG_CPU_HAS_L2_PMU=y
# CONFIG_MSM_FIQ_SUPPORT is not set
@@ -47,8 +46,6 @@
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_IPC_ROUTER_SECURITY=y
-# CONFIG_MSM_HW3D is not set
-CONFIG_MSM_RPM_REGULATOR_SMD=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
@@ -77,7 +74,6 @@
CONFIG_MSM_CACHE_DUMP=y
CONFIG_MSM_CACHE_DUMP_ON_PANIC=y
CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
-CONFIG_ARM_LPAE=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
@@ -87,7 +83,6 @@
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
-CONFIG_VMALLOC_RESERVE=0x19000000
CONFIG_CC_STACKPROTECTOR=y
CONFIG_CP_ACCESS=y
CONFIG_USE_OF=y
@@ -100,7 +95,6 @@
CONFIG_VFP=y
CONFIG_NEON=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-CONFIG_WAKELOCK=y
CONFIG_PM_RUNTIME=y
CONFIG_NET=y
CONFIG_PACKET=y
@@ -209,9 +203,6 @@
CONFIG_RFKILL=y
CONFIG_GENLOCK=y
CONFIG_GENLOCK_MISCDEVICE=y
-CONFIG_SYNC=y
-CONFIG_SW_SYNC=y
-CONFIG_CMA=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_HAPTIC_ISA1200=y
@@ -271,10 +262,9 @@
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_SENSORS_QPNP_ADC_CURRENT=y
CONFIG_THERMAL=y
-CONFIG_THERMAL_TSENS8974=y
-CONFIG_THERMAL_MONITOR=y
CONFIG_THERMAL_QPNP=y
CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_STUB=y
CONFIG_REGULATOR_QPNP=y
@@ -313,17 +303,25 @@
CONFIG_USB_STORAGE_KARMA=y
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_STORAGE_ENE_UB6250=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_BLOCK_MINORS=32
+# CONFIG_MMC_BLOCK_BOUNCE is not set
+CONFIG_MMC_TEST=m
+CONFIG_MMC_BLOCK_TEST=m
+CONFIG_MMC_MSM=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_BACKLIGHT=y
CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
CONFIG_SWITCH=y
-CONFIG_RTC_CLASS=y
-# CONFIG_RTC_DRV_MSM is not set
-CONFIG_RTC_DRV_QPNP=y
CONFIG_STAGING=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ASHMEM=y
CONFIG_ANDROID_LOGGER=y
CONFIG_ANDROID_TIMED_GPIO=y
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
@@ -334,7 +332,7 @@
CONFIG_QPNP_PWM=y
CONFIG_QPNP_POWER_ON=y
CONFIG_QPNP_CLKDIV=y
-CONFIG_MSM_IOMMU=y
+CONFIG_MSM_IOMMU_V1=y
CONFIG_IOMMU_PGTABLES_L2=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
diff --git a/arch/arm/configs/msm7627a-perf_defconfig b/arch/arm/configs/msm7627a-perf_defconfig
index aea092e..431ac58 100644
--- a/arch/arm/configs/msm7627a-perf_defconfig
+++ b/arch/arm/configs/msm7627a-perf_defconfig
@@ -278,7 +278,28 @@
CONFIG_MSM_CAMERA_SENSOR=y
CONFIG_MSM_ACTUATOR=y
CONFIG_OV7692=y
-# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2063 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_TUNER_XC4000 is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_MEDIA_TUNER_TDA18212 is not set
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_RADIO_TAVARUA=y
@@ -370,6 +391,7 @@
CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_SHIRQ=y
+CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
CONFIG_DEBUG_STACK_USAGE=y
CONFIG_DEBUG_INFO=y
diff --git a/arch/arm/configs/msm7627a_defconfig b/arch/arm/configs/msm7627a_defconfig
index b903a0b..c635e4b 100644
--- a/arch/arm/configs/msm7627a_defconfig
+++ b/arch/arm/configs/msm7627a_defconfig
@@ -280,7 +280,28 @@
CONFIG_MSM_CAMERA_SENSOR=y
CONFIG_MSM_ACTUATOR=y
CONFIG_OV7692=y
-# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2063 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_TUNER_XC4000 is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_MEDIA_TUNER_TDA18212 is not set
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_RADIO_TAVARUA=y
diff --git a/arch/arm/configs/msmzinc_defconfig b/arch/arm/configs/msm8226-perf_defconfig
similarity index 72%
copy from arch/arm/configs/msmzinc_defconfig
copy to arch/arm/configs/msm8226-perf_defconfig
index d0ea87a..71249c9 100644
--- a/arch/arm/configs/msmzinc_defconfig
+++ b/arch/arm/configs/msm8226-perf_defconfig
@@ -1,5 +1,6 @@
# CONFIG_ARM_PATCH_PHYS_VIRT is not set
CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_SYSVIPC=y
CONFIG_RCU_FAST_NO_HZ=y
CONFIG_IKCONFIG=y
@@ -14,6 +15,7 @@
CONFIG_NAMESPACES=y
# CONFIG_UTS_NS is not set
# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
# CONFIG_PID_NS is not set
CONFIG_RELAY=y
CONFIG_BLK_DEV_INITRD=y
@@ -22,85 +24,72 @@
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_PANIC_TIMEOUT=5
CONFIG_KALLSYMS_ALL=y
-CONFIG_ASHMEM=y
CONFIG_EMBEDDED=y
CONFIG_PROFILING=y
-CONFIG_KPROBES=y
+CONFIG_OPROFILE=m
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODVERSIONS=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_EFI_PARTITION=y
-CONFIG_IOSCHED_TEST=y
CONFIG_ARCH_MSM=y
-CONFIG_ARCH_MSMZINC=y
-CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
-CONFIG_MSM_RPM_SMD=y
+CONFIG_ARCH_MSM8226=y
# CONFIG_MSM_STACKED_MEMORY is not set
CONFIG_CPU_HAS_L2_PMU=y
# CONFIG_MSM_FIQ_SUPPORT is not set
# CONFIG_MSM_PROC_COMM is not set
CONFIG_MSM_SMD=y
CONFIG_MSM_SMD_PKG4=y
+CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_SMP2P_TEST=y
CONFIG_MSM_IPC_LOGGING=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
-CONFIG_MSM_IPC_ROUTER_SECURITY=y
-# CONFIG_MSM_HW3D is not set
-CONFIG_MSM_RPM_REGULATOR_SMD=y
+CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
CONFIG_MSM_SYSMON_COMM=y
+CONFIG_MSM_PIL_LPASS_QDSP6V5=y
+CONFIG_MSM_PIL_MSS_QDSP6V5=y
+CONFIG_MSM_PIL_VENUS=y
+CONFIG_MSM_PIL_PRONTO=y
+CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_WATCHDOG_V2=y
CONFIG_MSM_MEMORY_DUMP=y
CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_RUN_QUEUE_STATS=y
-CONFIG_MSM_SPM_V2=y
-CONFIG_MSM_L2_SPM=y
-CONFIG_MSM_MULTIMEDIA_USE_ION=y
+CONFIG_MSM_ADSP_LOADER=m
CONFIG_MSM_OCMEM=y
CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
CONFIG_MSM_OCMEM_DEBUG=y
CONFIG_MSM_OCMEM_NONSECURE=y
+CONFIG_MSM_OCMEM_POWER_DISABLE=y
CONFIG_SENSORS_ADSP=y
CONFIG_MSM_RTB=y
CONFIG_MSM_RTB_SEPARATE_CPUS=y
-CONFIG_MSM_CACHE_ERP=y
-CONFIG_MSM_L1_ERR_PANIC=y
-CONFIG_MSM_L1_RECOV_ERR_PANIC=y
-CONFIG_MSM_L1_ERR_LOG=y
-CONFIG_MSM_L2_ERP_PRINT_ACCESS_ERRORS=y
-CONFIG_MSM_L2_ERP_PORT_PANIC=y
-CONFIG_MSM_L2_ERP_1BIT_PANIC=y
-CONFIG_MSM_L2_ERP_2BIT_PANIC=y
-CONFIG_MSM_CACHE_DUMP=y
-CONFIG_MSM_CACHE_DUMP_ON_PANIC=y
CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
-CONFIG_ARM_LPAE=y
+CONFIG_MSM_BOOT_STATS=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
-# CONFIG_SMP_ON_UP is not set
CONFIG_SCHED_MC=y
CONFIG_ARM_ARCH_TIMER=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
-CONFIG_VMALLOC_RESERVE=0x19000000
-CONFIG_CC_STACKPROTECTOR=y
-CONFIG_CP_ACCESS=y
CONFIG_USE_OF=y
-CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
-CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
CONFIG_CPU_IDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-CONFIG_WAKELOCK=y
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
CONFIG_PM_RUNTIME=y
CONFIG_NET=y
CONFIG_PACKET=y
@@ -180,6 +169,10 @@
CONFIG_IP_NF_MATCH_TTL=y
CONFIG_IP_NF_FILTER=y
CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_NF_MANGLE=y
CONFIG_IP_NF_RAW=y
CONFIG_IP_NF_ARPTABLES=y
@@ -191,32 +184,27 @@
CONFIG_IP6_NF_TARGET_REJECT=y
CONFIG_IP6_NF_MANGLE=y
CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE_NF_EBTABLES=y
+CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_BRIDGE=y
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_HTB=y
CONFIG_NET_SCH_PRIO=y
CONFIG_NET_CLS_FW=y
-CONFIG_NET_CLS_U32=y
-CONFIG_CLS_U32_MARK=y
-CONFIG_NET_CLS_FLOW=y
-CONFIG_NET_EMATCH=y
-CONFIG_NET_EMATCH_CMP=y
-CONFIG_NET_EMATCH_NBYTE=y
-CONFIG_NET_EMATCH_U32=y
-CONFIG_NET_EMATCH_META=y
-CONFIG_NET_EMATCH_TEXT=y
-CONFIG_NET_CLS_ACT=y
+CONFIG_BT=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=y
+CONFIG_BT_HCISMD=y
CONFIG_CFG80211=y
-CONFIG_RFKILL=y
-CONFIG_GENLOCK=y
-CONFIG_GENLOCK_MISCDEVICE=y
-CONFIG_SYNC=y
-CONFIG_SW_SYNC=y
+CONFIG_NL80211_TESTMODE=y
CONFIG_CMA=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
-CONFIG_HAPTIC_ISA1200=y
-CONFIG_USB_HSIC_SMSC_HUB=y
-CONFIG_TI_DRV2667=y
+CONFIG_QSEECOM=y
CONFIG_SCSI=y
CONFIG_SCSI_TGT=y
CONFIG_BLK_DEV_SD=y
@@ -231,55 +219,86 @@
CONFIG_DM_CRYPT=y
CONFIG_NETDEVICES=y
CONFIG_DUMMY=y
-CONFIG_TUN=y
-CONFIG_KS8851=m
# CONFIG_MSM_RMNET is not set
-CONFIG_SLIP=y
-CONFIG_SLIP_COMPRESSED=y
-CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_MSM_RMNET_BAM=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPPOE=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
CONFIG_USB_USBNET=y
+CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_CORE_PRONTO=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_EVBUG=m
CONFIG_KEYBOARD_GPIO=y
CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_XPAD=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=m
CONFIG_SERIAL_MSM_HSL=y
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_QUP=y
+CONFIG_MSM_BUSPM_DEV=m
CONFIG_SPI=y
+CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=m
CONFIG_SPMI=y
CONFIG_SPMI_MSM_PMIC_ARB=y
CONFIG_MSM_QPNP_INT=y
+CONFIG_SLIMBUS_MSM_NGD=y
CONFIG_DEBUG_GPIO=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
-CONFIG_GPIO_QPNP_PIN_DEBUG=y
CONFIG_POWER_SUPPLY=y
-CONFIG_SMB350_CHARGER=y
-CONFIG_BATTERY_BQ28400=y
CONFIG_QPNP_CHARGER=y
-CONFIG_BATTERY_BCL=y
-CONFIG_SENSORS_EPM_ADC=y
+CONFIG_QPNP_BMS=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_SENSORS_QPNP_ADC_CURRENT=y
CONFIG_THERMAL=y
CONFIG_THERMAL_TSENS8974=y
CONFIG_THERMAL_MONITOR=y
-CONFIG_THERMAL_QPNP=y
CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_WCD9306_CODEC=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_STUB=y
CONFIG_REGULATOR_QPNP=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+# CONFIG_MSM_CAMERA is not set
+CONFIG_OV8825=y
+CONFIG_MSM_CAMERA_SENSOR=y
+CONFIG_MSM_CPP=y
+CONFIG_MSM_CCI=y
+CONFIG_MSM_CSI30_HEADER=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
+CONFIG_MSM_ISPIF=y
+CONFIG_MSMB_CAMERA=y
+CONFIG_OV9724=y
+CONFIG_MSMB_JPEG=y
+CONFIG_MSM_VIDC_V4L2=y
+CONFIG_MSM_WFD=y
+CONFIG_VIDEOBUF2_MSM_MEM=y
+CONFIG_USB_VIDEO_CLASS=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_RADIO_IRIS=y
+CONFIG_RADIO_IRIS_TRANSPORT=m
CONFIG_ION=y
CONFIG_ION_MSM=y
CONFIG_MSM_KGSL=y
@@ -288,35 +307,49 @@
# CONFIG_FB_MSM_BACKLIGHT is not set
CONFIG_FB_MSM_MDSS=y
CONFIG_FB_MSM_MDSS_WRITEBACK=y
-CONFIG_FB_MSM_MDSS_HDMI_PANEL=y
-CONFIG_FB_MSM_MDSS_HDMI_MHL_SII8334=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
# CONFIG_LCD_CLASS_DEVICE is not set
CONFIG_BACKLIGHT_CLASS_DEVICE=y
# CONFIG_BACKLIGHT_GENERIC is not set
-CONFIG_USB=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_USB_AUDIO=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_MSM8226=y
+CONFIG_UHID=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_SUSPEND=y
+CONFIG_USB_MON=y
CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_EHSET=y
CONFIG_USB_EHCI_MSM=y
-CONFIG_USB_EHCI_MSM_HSIC=y
CONFIG_USB_ACM=y
-CONFIG_USB_STORAGE=y
-CONFIG_USB_STORAGE_DATAFAB=y
-CONFIG_USB_STORAGE_FREECOM=y
-CONFIG_USB_STORAGE_ISD200=y
-CONFIG_USB_STORAGE_USBAT=y
-CONFIG_USB_STORAGE_SDDR09=y
-CONFIG_USB_STORAGE_SDDR55=y
-CONFIG_USB_STORAGE_JUMPSHOT=y
-CONFIG_USB_STORAGE_ALAUDA=y
-CONFIG_USB_STORAGE_ONETOUCH=y
-CONFIG_USB_STORAGE_KARMA=y
-CONFIG_USB_STORAGE_CYPRESS_ATACB=y
-CONFIG_USB_STORAGE_ENE_UB6250=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_CSVT=y
+CONFIG_USB_EHSET_TEST_FIXTURE=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_G_ANDROID=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=m
+CONFIG_MMC_BLOCK_TEST=m
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_MSM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_MMC_MSM_SPS_SUPPORT=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_TRIGGERS=y
-CONFIG_LEDS_TRIGGER_BACKLIGHT=y
-CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
CONFIG_SWITCH=y
CONFIG_RTC_CLASS=y
# CONFIG_RTC_DRV_MSM is not set
@@ -324,18 +357,19 @@
CONFIG_STAGING=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ASHMEM=y
CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
CONFIG_ANDROID_TIMED_GPIO=y
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
-CONFIG_MSM_SSBI=y
CONFIG_SPS=y
-CONFIG_SPS_SUPPORT_BAMDMA=y
+CONFIG_USB_BAM=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_QPNP_PWM=y
CONFIG_QPNP_POWER_ON=y
-CONFIG_QPNP_CLKDIV=y
-CONFIG_MSM_IOMMU=y
-CONFIG_IOMMU_PGTABLES_L2=y
+CONFIG_QPNP_VIBRATOR=y
+CONFIG_QPNP_REVID=y
+CONFIG_MSM_IOMMU_V1=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
@@ -344,14 +378,11 @@
CONFIG_FUSE_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
-CONFIG_PSTORE=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ASCII=y
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
-CONFIG_LOCKUP_DETECTOR=y
-# CONFIG_DETECT_HUNG_TASK is not set
CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
CONFIG_DEBUG_KMEMLEAK=y
@@ -368,18 +399,13 @@
CONFIG_FAULT_INJECTION_DEBUG_FS=y
CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
CONFIG_DEBUG_PAGEALLOC=y
-CONFIG_CPU_FREQ_SWITCH_PROFILER=y
+CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
-CONFIG_DEBUG_LL=y
-CONFIG_EARLY_PRINTK=y
-CONFIG_PID_IN_CONTEXTIDR=y
CONFIG_KEYS=y
CONFIG_CRYPTO_MD4=y
-CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_ARC4=y
CONFIG_CRYPTO_TWOFISH=y
CONFIG_CRYPTO_DEV_QCRYPTO=m
-CONFIG_CRYPTO_DEV_QCE=m
+CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=m
-CONFIG_CRC_CCITT=y
diff --git a/arch/arm/configs/msmzinc_defconfig b/arch/arm/configs/msm8226_defconfig
similarity index 65%
copy from arch/arm/configs/msmzinc_defconfig
copy to arch/arm/configs/msm8226_defconfig
index d0ea87a..6c604ff 100644
--- a/arch/arm/configs/msmzinc_defconfig
+++ b/arch/arm/configs/msm8226_defconfig
@@ -1,5 +1,6 @@
# CONFIG_ARM_PATCH_PHYS_VIRT is not set
CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_SYSVIPC=y
CONFIG_RCU_FAST_NO_HZ=y
CONFIG_IKCONFIG=y
@@ -14,6 +15,7 @@
CONFIG_NAMESPACES=y
# CONFIG_UTS_NS is not set
# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
# CONFIG_PID_NS is not set
CONFIG_RELAY=y
CONFIG_BLK_DEV_INITRD=y
@@ -22,85 +24,72 @@
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_PANIC_TIMEOUT=5
CONFIG_KALLSYMS_ALL=y
-CONFIG_ASHMEM=y
CONFIG_EMBEDDED=y
CONFIG_PROFILING=y
-CONFIG_KPROBES=y
+CONFIG_OPROFILE=m
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODVERSIONS=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_EFI_PARTITION=y
-CONFIG_IOSCHED_TEST=y
CONFIG_ARCH_MSM=y
-CONFIG_ARCH_MSMZINC=y
-CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
-CONFIG_MSM_RPM_SMD=y
+CONFIG_ARCH_MSM8226=y
# CONFIG_MSM_STACKED_MEMORY is not set
CONFIG_CPU_HAS_L2_PMU=y
# CONFIG_MSM_FIQ_SUPPORT is not set
# CONFIG_MSM_PROC_COMM is not set
CONFIG_MSM_SMD=y
CONFIG_MSM_SMD_PKG4=y
+CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_SMP2P_TEST=y
CONFIG_MSM_IPC_LOGGING=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
-CONFIG_MSM_IPC_ROUTER_SECURITY=y
-# CONFIG_MSM_HW3D is not set
-CONFIG_MSM_RPM_REGULATOR_SMD=y
+CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
CONFIG_MSM_SYSMON_COMM=y
+CONFIG_MSM_PIL_LPASS_QDSP6V5=y
+CONFIG_MSM_PIL_MSS_QDSP6V5=y
+CONFIG_MSM_PIL_VENUS=y
+CONFIG_MSM_PIL_PRONTO=y
+CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_WATCHDOG_V2=y
CONFIG_MSM_MEMORY_DUMP=y
CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_RUN_QUEUE_STATS=y
-CONFIG_MSM_SPM_V2=y
-CONFIG_MSM_L2_SPM=y
-CONFIG_MSM_MULTIMEDIA_USE_ION=y
+CONFIG_MSM_ADSP_LOADER=m
CONFIG_MSM_OCMEM=y
CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
CONFIG_MSM_OCMEM_DEBUG=y
CONFIG_MSM_OCMEM_NONSECURE=y
+CONFIG_MSM_OCMEM_POWER_DISABLE=y
CONFIG_SENSORS_ADSP=y
CONFIG_MSM_RTB=y
CONFIG_MSM_RTB_SEPARATE_CPUS=y
-CONFIG_MSM_CACHE_ERP=y
-CONFIG_MSM_L1_ERR_PANIC=y
-CONFIG_MSM_L1_RECOV_ERR_PANIC=y
-CONFIG_MSM_L1_ERR_LOG=y
-CONFIG_MSM_L2_ERP_PRINT_ACCESS_ERRORS=y
-CONFIG_MSM_L2_ERP_PORT_PANIC=y
-CONFIG_MSM_L2_ERP_1BIT_PANIC=y
-CONFIG_MSM_L2_ERP_2BIT_PANIC=y
-CONFIG_MSM_CACHE_DUMP=y
-CONFIG_MSM_CACHE_DUMP_ON_PANIC=y
CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
-CONFIG_ARM_LPAE=y
+CONFIG_MSM_BOOT_STATS=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
-# CONFIG_SMP_ON_UP is not set
CONFIG_SCHED_MC=y
CONFIG_ARM_ARCH_TIMER=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
-CONFIG_VMALLOC_RESERVE=0x19000000
-CONFIG_CC_STACKPROTECTOR=y
-CONFIG_CP_ACCESS=y
CONFIG_USE_OF=y
-CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
-CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
CONFIG_CPU_IDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-CONFIG_WAKELOCK=y
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
CONFIG_PM_RUNTIME=y
CONFIG_NET=y
CONFIG_PACKET=y
@@ -180,6 +169,10 @@
CONFIG_IP_NF_MATCH_TTL=y
CONFIG_IP_NF_FILTER=y
CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_NF_MANGLE=y
CONFIG_IP_NF_RAW=y
CONFIG_IP_NF_ARPTABLES=y
@@ -191,32 +184,27 @@
CONFIG_IP6_NF_TARGET_REJECT=y
CONFIG_IP6_NF_MANGLE=y
CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE_NF_EBTABLES=y
+CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_BRIDGE=y
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_HTB=y
CONFIG_NET_SCH_PRIO=y
CONFIG_NET_CLS_FW=y
-CONFIG_NET_CLS_U32=y
-CONFIG_CLS_U32_MARK=y
-CONFIG_NET_CLS_FLOW=y
-CONFIG_NET_EMATCH=y
-CONFIG_NET_EMATCH_CMP=y
-CONFIG_NET_EMATCH_NBYTE=y
-CONFIG_NET_EMATCH_U32=y
-CONFIG_NET_EMATCH_META=y
-CONFIG_NET_EMATCH_TEXT=y
-CONFIG_NET_CLS_ACT=y
+CONFIG_BT=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=y
+CONFIG_BT_HCISMD=y
CONFIG_CFG80211=y
-CONFIG_RFKILL=y
-CONFIG_GENLOCK=y
-CONFIG_GENLOCK_MISCDEVICE=y
-CONFIG_SYNC=y
-CONFIG_SW_SYNC=y
+CONFIG_NL80211_TESTMODE=y
CONFIG_CMA=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
-CONFIG_HAPTIC_ISA1200=y
-CONFIG_USB_HSIC_SMSC_HUB=y
-CONFIG_TI_DRV2667=y
+CONFIG_QSEECOM=y
CONFIG_SCSI=y
CONFIG_SCSI_TGT=y
CONFIG_BLK_DEV_SD=y
@@ -232,42 +220,54 @@
CONFIG_NETDEVICES=y
CONFIG_DUMMY=y
CONFIG_TUN=y
-CONFIG_KS8851=m
# CONFIG_MSM_RMNET is not set
-CONFIG_SLIP=y
-CONFIG_SLIP_COMPRESSED=y
-CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_MSM_RMNET_BAM=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPPOE=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
CONFIG_USB_USBNET=y
+CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_CORE_PRONTO=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_EVBUG=m
CONFIG_KEYBOARD_GPIO=y
CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_XPAD=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=m
CONFIG_SERIAL_MSM_HSL=y
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_QUP=y
+CONFIG_MSM_BUSPM_DEV=m
CONFIG_SPI=y
+CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=m
CONFIG_SPMI=y
CONFIG_SPMI_MSM_PMIC_ARB=y
CONFIG_MSM_QPNP_INT=y
+CONFIG_SLIMBUS_MSM_NGD=y
CONFIG_DEBUG_GPIO=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
-CONFIG_GPIO_QPNP_PIN_DEBUG=y
CONFIG_POWER_SUPPLY=y
-CONFIG_SMB350_CHARGER=y
-CONFIG_BATTERY_BQ28400=y
CONFIG_QPNP_CHARGER=y
CONFIG_BATTERY_BCL=y
-CONFIG_SENSORS_EPM_ADC=y
+CONFIG_QPNP_BMS=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_SENSORS_QPNP_ADC_CURRENT=y
CONFIG_THERMAL=y
@@ -275,11 +275,55 @@
CONFIG_THERMAL_MONITOR=y
CONFIG_THERMAL_QPNP=y
CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_WCD9306_CODEC=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_STUB=y
CONFIG_REGULATOR_QPNP=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+# CONFIG_MSM_CAMERA is not set
+CONFIG_OV8825=y
+CONFIG_MSM_CAMERA_SENSOR=y
+CONFIG_MSM_CPP=y
+CONFIG_MSM_CCI=y
+CONFIG_MSM_CSI30_HEADER=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
+CONFIG_MSM_ISPIF=y
+CONFIG_MSMB_CAMERA=y
+CONFIG_OV9724=y
+CONFIG_MSMB_JPEG=y
+CONFIG_MSM_VIDC_V4L2=y
+CONFIG_MSM_WFD=y
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2063 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_TUNER_XC4000 is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_MEDIA_TUNER_TDA18212 is not set
+CONFIG_VIDEOBUF2_MSM_MEM=y
+CONFIG_USB_VIDEO_CLASS=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_RADIO_IRIS=y
+CONFIG_RADIO_IRIS_TRANSPORT=m
CONFIG_ION=y
CONFIG_ION_MSM=y
CONFIG_MSM_KGSL=y
@@ -288,35 +332,49 @@
# CONFIG_FB_MSM_BACKLIGHT is not set
CONFIG_FB_MSM_MDSS=y
CONFIG_FB_MSM_MDSS_WRITEBACK=y
-CONFIG_FB_MSM_MDSS_HDMI_PANEL=y
-CONFIG_FB_MSM_MDSS_HDMI_MHL_SII8334=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
# CONFIG_LCD_CLASS_DEVICE is not set
CONFIG_BACKLIGHT_CLASS_DEVICE=y
# CONFIG_BACKLIGHT_GENERIC is not set
-CONFIG_USB=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_USB_AUDIO=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_MSM8226=y
+CONFIG_UHID=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_SUSPEND=y
+CONFIG_USB_MON=y
CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_EHSET=y
CONFIG_USB_EHCI_MSM=y
-CONFIG_USB_EHCI_MSM_HSIC=y
CONFIG_USB_ACM=y
-CONFIG_USB_STORAGE=y
-CONFIG_USB_STORAGE_DATAFAB=y
-CONFIG_USB_STORAGE_FREECOM=y
-CONFIG_USB_STORAGE_ISD200=y
-CONFIG_USB_STORAGE_USBAT=y
-CONFIG_USB_STORAGE_SDDR09=y
-CONFIG_USB_STORAGE_SDDR55=y
-CONFIG_USB_STORAGE_JUMPSHOT=y
-CONFIG_USB_STORAGE_ALAUDA=y
-CONFIG_USB_STORAGE_ONETOUCH=y
-CONFIG_USB_STORAGE_KARMA=y
-CONFIG_USB_STORAGE_CYPRESS_ATACB=y
-CONFIG_USB_STORAGE_ENE_UB6250=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_CSVT=y
+CONFIG_USB_EHSET_TEST_FIXTURE=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_G_ANDROID=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=m
+CONFIG_MMC_BLOCK_TEST=m
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_MSM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_MMC_MSM_SPS_SUPPORT=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_TRIGGERS=y
-CONFIG_LEDS_TRIGGER_BACKLIGHT=y
-CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
CONFIG_SWITCH=y
CONFIG_RTC_CLASS=y
# CONFIG_RTC_DRV_MSM is not set
@@ -324,18 +382,28 @@
CONFIG_STAGING=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ASHMEM=y
CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
CONFIG_ANDROID_TIMED_GPIO=y
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
-CONFIG_MSM_SSBI=y
CONFIG_SPS=y
-CONFIG_SPS_SUPPORT_BAMDMA=y
+CONFIG_USB_BAM=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_QPNP_PWM=y
CONFIG_QPNP_POWER_ON=y
-CONFIG_QPNP_CLKDIV=y
-CONFIG_MSM_IOMMU=y
-CONFIG_IOMMU_PGTABLES_L2=y
+CONFIG_QPNP_VIBRATOR=y
+CONFIG_QPNP_REVID=y
+CONFIG_MSM_IOMMU_V1=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_TMC=y
+CONFIG_CORESIGHT_TPIU=y
+CONFIG_CORESIGHT_FUNNEL=y
+CONFIG_CORESIGHT_REPLICATOR=y
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_CORESIGHT_ETM=y
+CONFIG_CORESIGHT_EVENT=m
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
@@ -344,14 +412,11 @@
CONFIG_FUSE_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
-CONFIG_PSTORE=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ASCII=y
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
-CONFIG_LOCKUP_DETECTOR=y
-# CONFIG_DETECT_HUNG_TASK is not set
CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
CONFIG_DEBUG_KMEMLEAK=y
@@ -368,18 +433,13 @@
CONFIG_FAULT_INJECTION_DEBUG_FS=y
CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
CONFIG_DEBUG_PAGEALLOC=y
-CONFIG_CPU_FREQ_SWITCH_PROFILER=y
+CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
-CONFIG_DEBUG_LL=y
-CONFIG_EARLY_PRINTK=y
-CONFIG_PID_IN_CONTEXTIDR=y
CONFIG_KEYS=y
CONFIG_CRYPTO_MD4=y
-CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_ARC4=y
CONFIG_CRYPTO_TWOFISH=y
CONFIG_CRYPTO_DEV_QCRYPTO=m
-CONFIG_CRYPTO_DEV_QCE=m
+CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=m
-CONFIG_CRC_CCITT=y
diff --git a/arch/arm/configs/msmzinc_defconfig b/arch/arm/configs/msm8610-perf_defconfig
similarity index 68%
copy from arch/arm/configs/msmzinc_defconfig
copy to arch/arm/configs/msm8610-perf_defconfig
index d0ea87a..d5e230d 100644
--- a/arch/arm/configs/msmzinc_defconfig
+++ b/arch/arm/configs/msm8610-perf_defconfig
@@ -1,5 +1,6 @@
# CONFIG_ARM_PATCH_PHYS_VIRT is not set
CONFIG_EXPERIMENTAL=y
+CONFIG_LOCALVERSION="-perf"
CONFIG_SYSVIPC=y
CONFIG_RCU_FAST_NO_HZ=y
CONFIG_IKCONFIG=y
@@ -14,6 +15,7 @@
CONFIG_NAMESPACES=y
# CONFIG_UTS_NS is not set
# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
# CONFIG_PID_NS is not set
CONFIG_RELAY=y
CONFIG_BLK_DEV_INITRD=y
@@ -22,10 +24,10 @@
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_PANIC_TIMEOUT=5
CONFIG_KALLSYMS_ALL=y
-CONFIG_ASHMEM=y
CONFIG_EMBEDDED=y
+# CONFIG_SLUB_DEBUG is not set
CONFIG_PROFILING=y
-CONFIG_KPROBES=y
+CONFIG_OPROFILE=m
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
@@ -34,73 +36,63 @@
CONFIG_EFI_PARTITION=y
CONFIG_IOSCHED_TEST=y
CONFIG_ARCH_MSM=y
-CONFIG_ARCH_MSMZINC=y
-CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
-CONFIG_MSM_RPM_SMD=y
+CONFIG_ARCH_MSM8610=y
+CONFIG_ARCH_MSM8226=y
# CONFIG_MSM_STACKED_MEMORY is not set
CONFIG_CPU_HAS_L2_PMU=y
# CONFIG_MSM_FIQ_SUPPORT is not set
# CONFIG_MSM_PROC_COMM is not set
CONFIG_MSM_SMD=y
CONFIG_MSM_SMD_PKG4=y
+CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_SMP2P_TEST=y
CONFIG_MSM_IPC_LOGGING=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
-CONFIG_MSM_IPC_ROUTER_SECURITY=y
-# CONFIG_MSM_HW3D is not set
-CONFIG_MSM_RPM_REGULATOR_SMD=y
+CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
CONFIG_MSM_SYSMON_COMM=y
+CONFIG_MSM_PIL_LPASS_QDSP6V5=y
+CONFIG_MSM_PIL_MSS_QDSP6V5=y
+CONFIG_MSM_PIL_VENUS=y
+CONFIG_MSM_PIL_PRONTO=y
+CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_WATCHDOG_V2=y
CONFIG_MSM_MEMORY_DUMP=y
CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_RUN_QUEUE_STATS=y
-CONFIG_MSM_SPM_V2=y
-CONFIG_MSM_L2_SPM=y
-CONFIG_MSM_MULTIMEDIA_USE_ION=y
+CONFIG_MSM_ADSP_LOADER=m
CONFIG_MSM_OCMEM=y
CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
CONFIG_MSM_OCMEM_DEBUG=y
CONFIG_MSM_OCMEM_NONSECURE=y
+CONFIG_MSM_OCMEM_POWER_DISABLE=y
CONFIG_SENSORS_ADSP=y
CONFIG_MSM_RTB=y
CONFIG_MSM_RTB_SEPARATE_CPUS=y
-CONFIG_MSM_CACHE_ERP=y
-CONFIG_MSM_L1_ERR_PANIC=y
-CONFIG_MSM_L1_RECOV_ERR_PANIC=y
-CONFIG_MSM_L1_ERR_LOG=y
-CONFIG_MSM_L2_ERP_PRINT_ACCESS_ERRORS=y
-CONFIG_MSM_L2_ERP_PORT_PANIC=y
-CONFIG_MSM_L2_ERP_1BIT_PANIC=y
-CONFIG_MSM_L2_ERP_2BIT_PANIC=y
-CONFIG_MSM_CACHE_DUMP=y
-CONFIG_MSM_CACHE_DUMP_ON_PANIC=y
CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
-CONFIG_ARM_LPAE=y
+CONFIG_MSM_BOOT_STATS=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
-# CONFIG_SMP_ON_UP is not set
CONFIG_SCHED_MC=y
CONFIG_ARM_ARCH_TIMER=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
-CONFIG_VMALLOC_RESERVE=0x19000000
-CONFIG_CC_STACKPROTECTOR=y
-CONFIG_CP_ACCESS=y
CONFIG_USE_OF=y
-CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
-CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
CONFIG_CPU_IDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-CONFIG_WAKELOCK=y
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
CONFIG_PM_RUNTIME=y
CONFIG_NET=y
CONFIG_PACKET=y
@@ -180,6 +172,10 @@
CONFIG_IP_NF_MATCH_TTL=y
CONFIG_IP_NF_FILTER=y
CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_NF_MANGLE=y
CONFIG_IP_NF_RAW=y
CONFIG_IP_NF_ARPTABLES=y
@@ -191,83 +187,74 @@
CONFIG_IP6_NF_TARGET_REJECT=y
CONFIG_IP6_NF_MANGLE=y
CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE_NF_EBTABLES=y
+CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_BRIDGE=y
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_HTB=y
CONFIG_NET_SCH_PRIO=y
CONFIG_NET_CLS_FW=y
-CONFIG_NET_CLS_U32=y
-CONFIG_CLS_U32_MARK=y
-CONFIG_NET_CLS_FLOW=y
-CONFIG_NET_EMATCH=y
-CONFIG_NET_EMATCH_CMP=y
-CONFIG_NET_EMATCH_NBYTE=y
-CONFIG_NET_EMATCH_U32=y
-CONFIG_NET_EMATCH_META=y
-CONFIG_NET_EMATCH_TEXT=y
-CONFIG_NET_CLS_ACT=y
+CONFIG_BT=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=y
+CONFIG_BT_HCISMD=y
CONFIG_CFG80211=y
-CONFIG_RFKILL=y
-CONFIG_GENLOCK=y
-CONFIG_GENLOCK_MISCDEVICE=y
-CONFIG_SYNC=y
-CONFIG_SW_SYNC=y
+CONFIG_NL80211_TESTMODE=y
CONFIG_CMA=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
-CONFIG_HAPTIC_ISA1200=y
-CONFIG_USB_HSIC_SMSC_HUB=y
-CONFIG_TI_DRV2667=y
-CONFIG_SCSI=y
-CONFIG_SCSI_TGT=y
-CONFIG_BLK_DEV_SD=y
-CONFIG_CHR_DEV_SG=y
-CONFIG_CHR_DEV_SCH=y
-CONFIG_SCSI_MULTI_LUN=y
-CONFIG_SCSI_CONSTANTS=y
-CONFIG_SCSI_LOGGING=y
-CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_QSEECOM=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
CONFIG_NETDEVICES=y
CONFIG_DUMMY=y
CONFIG_TUN=y
-CONFIG_KS8851=m
# CONFIG_MSM_RMNET is not set
-CONFIG_SLIP=y
-CONFIG_SLIP_COMPRESSED=y
-CONFIG_SLIP_MODE_SLIP6=y
-CONFIG_USB_USBNET=y
+CONFIG_MSM_RMNET_BAM=y
+CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_CORE_PRONTO=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_EVBUG=m
CONFIG_KEYBOARD_GPIO=y
-CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_TOUCHSCREEN_FT5X06=y
+CONFIG_TOUCHSCREEN_GEN_VKEYS=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=m
CONFIG_SERIAL_MSM_HSL=y
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_QUP=y
+CONFIG_MSM_BUSPM_DEV=m
CONFIG_SPI=y
+CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=m
CONFIG_SPMI=y
CONFIG_SPMI_MSM_PMIC_ARB=y
CONFIG_MSM_QPNP_INT=y
+CONFIG_SLIMBUS_MSM_NGD=y
CONFIG_DEBUG_GPIO=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
-CONFIG_GPIO_QPNP_PIN_DEBUG=y
CONFIG_POWER_SUPPLY=y
-CONFIG_SMB350_CHARGER=y
-CONFIG_BATTERY_BQ28400=y
CONFIG_QPNP_CHARGER=y
CONFIG_BATTERY_BCL=y
-CONFIG_SENSORS_EPM_ADC=y
+CONFIG_QPNP_BMS=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_SENSORS_QPNP_ADC_CURRENT=y
CONFIG_THERMAL=y
@@ -275,48 +262,75 @@
CONFIG_THERMAL_MONITOR=y
CONFIG_THERMAL_QPNP=y
CONFIG_THERMAL_QPNP_ADC_TM=y
-CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_WCD9306_CODEC=y
CONFIG_REGULATOR_STUB=y
CONFIG_REGULATOR_QPNP=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+# CONFIG_MSM_CAMERA is not set
+CONFIG_OV8825=y
+CONFIG_MSM_CAMERA_SENSOR=y
+CONFIG_MSM_CPP=y
+CONFIG_MSM_CCI=y
+CONFIG_MSM_CSI30_HEADER=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
+CONFIG_MSM_ISPIF=y
+CONFIG_MSMB_CAMERA=y
+CONFIG_OV9724=y
+CONFIG_SP1628=y
+CONFIG_MSMB_JPEG=y
+CONFIG_MSM_VIDC_V4L2=y
+CONFIG_MSM_WFD=y
+CONFIG_VIDEOBUF2_MSM_MEM=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_RADIO_IRIS=y
+CONFIG_RADIO_IRIS_TRANSPORT=m
CONFIG_ION=y
CONFIG_ION_MSM=y
CONFIG_MSM_KGSL=y
+CONFIG_KGSL_PER_PROCESS_PAGE_TABLE=y
CONFIG_FB=y
+CONFIG_FB_VIRTUAL=y
CONFIG_FB_MSM=y
# CONFIG_FB_MSM_BACKLIGHT is not set
CONFIG_FB_MSM_MDSS=y
CONFIG_FB_MSM_MDSS_WRITEBACK=y
-CONFIG_FB_MSM_MDSS_HDMI_PANEL=y
-CONFIG_FB_MSM_MDSS_HDMI_MHL_SII8334=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
# CONFIG_LCD_CLASS_DEVICE is not set
CONFIG_BACKLIGHT_CLASS_DEVICE=y
# CONFIG_BACKLIGHT_GENERIC is not set
-CONFIG_USB=y
-CONFIG_USB_SUSPEND=y
-CONFIG_USB_EHCI_HCD=y
-CONFIG_USB_EHCI_MSM=y
-CONFIG_USB_EHCI_MSM_HSIC=y
-CONFIG_USB_ACM=y
-CONFIG_USB_STORAGE=y
-CONFIG_USB_STORAGE_DATAFAB=y
-CONFIG_USB_STORAGE_FREECOM=y
-CONFIG_USB_STORAGE_ISD200=y
-CONFIG_USB_STORAGE_USBAT=y
-CONFIG_USB_STORAGE_SDDR09=y
-CONFIG_USB_STORAGE_SDDR55=y
-CONFIG_USB_STORAGE_JUMPSHOT=y
-CONFIG_USB_STORAGE_ALAUDA=y
-CONFIG_USB_STORAGE_ONETOUCH=y
-CONFIG_USB_STORAGE_KARMA=y
-CONFIG_USB_STORAGE_CYPRESS_ATACB=y
-CONFIG_USB_STORAGE_ENE_UB6250=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_MSM8226=y
+CONFIG_SND_SOC_MSM8X10=y
+CONFIG_UHID=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_G_ANDROID=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=m
+CONFIG_MMC_BLOCK_TEST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_MSM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_MMC_MSM_SPS_SUPPORT=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_TRIGGERS=y
-CONFIG_LEDS_TRIGGER_BACKLIGHT=y
-CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
CONFIG_SWITCH=y
CONFIG_RTC_CLASS=y
# CONFIG_RTC_DRV_MSM is not set
@@ -324,18 +338,19 @@
CONFIG_STAGING=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ASHMEM=y
CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
CONFIG_ANDROID_TIMED_GPIO=y
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
-CONFIG_MSM_SSBI=y
CONFIG_SPS=y
-CONFIG_SPS_SUPPORT_BAMDMA=y
+CONFIG_USB_BAM=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_QPNP_PWM=y
CONFIG_QPNP_POWER_ON=y
-CONFIG_QPNP_CLKDIV=y
-CONFIG_MSM_IOMMU=y
-CONFIG_IOMMU_PGTABLES_L2=y
+CONFIG_QPNP_VIBRATOR=y
+CONFIG_QPNP_REVID=y
+CONFIG_MSM_IOMMU_V0=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
@@ -344,42 +359,20 @@
CONFIG_FUSE_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
-CONFIG_PSTORE=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ASCII=y
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
-CONFIG_LOCKUP_DETECTOR=y
-# CONFIG_DETECT_HUNG_TASK is not set
CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
-CONFIG_DEBUG_KMEMLEAK=y
-CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
-CONFIG_DEBUG_SPINLOCK=y
-CONFIG_DEBUG_MUTEXES=y
-CONFIG_DEBUG_ATOMIC_SLEEP=y
-CONFIG_DEBUG_STACK_USAGE=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_MEMORY_INIT=y
-CONFIG_DEBUG_LIST=y
-CONFIG_FAULT_INJECTION=y
-CONFIG_FAIL_PAGE_ALLOC=y
-CONFIG_FAULT_INJECTION_DEBUG_FS=y
-CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
-CONFIG_DEBUG_PAGEALLOC=y
-CONFIG_CPU_FREQ_SWITCH_PROFILER=y
-CONFIG_DYNAMIC_DEBUG=y
+CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_DEBUG_USER=y
-CONFIG_DEBUG_LL=y
-CONFIG_EARLY_PRINTK=y
-CONFIG_PID_IN_CONTEXTIDR=y
CONFIG_KEYS=y
CONFIG_CRYPTO_MD4=y
-CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_ARC4=y
CONFIG_CRYPTO_TWOFISH=y
-CONFIG_CRYPTO_DEV_QCRYPTO=m
-CONFIG_CRYPTO_DEV_QCE=m
-CONFIG_CRYPTO_DEV_QCEDEV=m
+# CONFIG_CRYPTO_HW is not set
CONFIG_CRC_CCITT=y
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index 3291919..b435924 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -32,6 +32,7 @@
CONFIG_MODVERSIONS=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_EFI_PARTITION=y
+CONFIG_IOSCHED_TEST=y
CONFIG_ARCH_MSM=y
CONFIG_ARCH_MSM8610=y
CONFIG_ARCH_MSM8226=y
@@ -59,7 +60,6 @@
CONFIG_MSM_WATCHDOG_V2=y
CONFIG_MSM_MEMORY_DUMP=y
CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
CONFIG_MSM_ADSP_LOADER=m
CONFIG_MSM_OCMEM=y
CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
@@ -69,6 +69,8 @@
CONFIG_SENSORS_ADSP=y
CONFIG_MSM_RTB=y
CONFIG_MSM_RTB_SEPARATE_CPUS=y
+CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
+CONFIG_MSM_BOOT_STATS=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
@@ -78,6 +80,9 @@
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
CONFIG_USE_OF=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
CONFIG_CPU_IDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
@@ -200,11 +205,13 @@
CONFIG_CMA=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
+CONFIG_QSEECOM=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
CONFIG_NETDEVICES=y
CONFIG_DUMMY=y
+CONFIG_TUN=y
# CONFIG_MSM_RMNET is not set
CONFIG_MSM_RMNET_BAM=y
CONFIG_WCNSS_CORE=y
@@ -214,6 +221,9 @@
CONFIG_INPUT_EVBUG=m
CONFIG_KEYBOARD_GPIO=y
CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_TOUCHSCREEN_FT5X06=y
+CONFIG_TOUCHSCREEN_GEN_VKEYS=y
CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y
@@ -228,11 +238,11 @@
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_QUP=y
+CONFIG_MSM_BUSPM_DEV=m
CONFIG_SPI=y
CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=m
CONFIG_SPMI=y
-CONFIG_MSM_BUS_SCALING=y
CONFIG_SPMI_MSM_PMIC_ARB=y
CONFIG_MSM_QPNP_INT=y
CONFIG_SLIMBUS_MSM_NGD=y
@@ -241,12 +251,14 @@
CONFIG_GPIO_QPNP_PIN=y
CONFIG_POWER_SUPPLY=y
CONFIG_QPNP_CHARGER=y
+CONFIG_BATTERY_BCL=y
CONFIG_QPNP_BMS=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_SENSORS_QPNP_ADC_CURRENT=y
CONFIG_THERMAL=y
CONFIG_THERMAL_TSENS8974=y
CONFIG_THERMAL_MONITOR=y
+CONFIG_THERMAL_QPNP=y
CONFIG_THERMAL_QPNP_ADC_TM=y
CONFIG_WCD9306_CODEC=y
CONFIG_REGULATOR_STUB=y
@@ -258,18 +270,40 @@
# CONFIG_MSM_CAMERA is not set
CONFIG_OV8825=y
CONFIG_MSM_CAMERA_SENSOR=y
-CONFIG_MSM_CPP=y
CONFIG_MSM_CCI=y
-CONFIG_MSM_CSI30_HEADER=y
+CONFIG_MSM_CSI20_HEADER=y
CONFIG_MSM_CSIPHY=y
CONFIG_MSM_CSID=y
CONFIG_MSM_ISPIF=y
CONFIG_MSMB_CAMERA=y
+CONFIG_MSM_CSI22_HEADER=y
CONFIG_OV9724=y
+CONFIG_SP1628=y
CONFIG_MSMB_JPEG=y
-CONFIG_SWITCH=y
-CONFIG_MSM_WFD=y
CONFIG_MSM_VIDC_V4L2=y
+CONFIG_MSM_WFD=y
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2063 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_TUNER_XC4000 is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_MEDIA_TUNER_TDA18212 is not set
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_RADIO_IRIS=y
@@ -277,8 +311,8 @@
CONFIG_ION=y
CONFIG_ION_MSM=y
CONFIG_MSM_KGSL=y
+CONFIG_KGSL_PER_PROCESS_PAGE_TABLE=y
CONFIG_FB=y
-CONFIG_FB_VIRTUAL=y
CONFIG_FB_MSM=y
# CONFIG_FB_MSM_BACKLIGHT is not set
CONFIG_FB_MSM_MDSS=y
@@ -292,6 +326,9 @@
CONFIG_SND_SOC=y
CONFIG_SND_SOC_MSM8226=y
CONFIG_SND_SOC_MSM8X10=y
+CONFIG_UHID=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_MAGICMOUSE=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
@@ -305,6 +342,7 @@
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_TEST=m
+CONFIG_MMC_BLOCK_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_MSM=y
@@ -312,6 +350,7 @@
CONFIG_MMC_MSM_SPS_SUPPORT=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_TRIGGERS=y
+CONFIG_SWITCH=y
CONFIG_RTC_CLASS=y
# CONFIG_RTC_DRV_MSM is not set
CONFIG_RTC_DRV_QPNP=y
@@ -328,13 +367,16 @@
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_QPNP_PWM=y
CONFIG_QPNP_POWER_ON=y
-CONFIG_MSM_IOMMU=y
+CONFIG_QPNP_VIBRATOR=y
+CONFIG_QPNP_REVID=y
+CONFIG_MSM_IOMMU_V0=y
CONFIG_CORESIGHT=y
CONFIG_CORESIGHT_TMC=y
CONFIG_CORESIGHT_TPIU=y
CONFIG_CORESIGHT_FUNNEL=y
CONFIG_CORESIGHT_REPLICATOR=y
CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_HWEVENT=y
CONFIG_CORESIGHT_ETM=y
CONFIG_CORESIGHT_EVENT=m
CONFIG_EXT2_FS=y
@@ -373,7 +415,7 @@
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_ARC4=y
CONFIG_CRYPTO_TWOFISH=y
-# CONFIG_CRYPTO_HW is not set
+CONFIG_CRYPTO_DEV_QCRYPTO=m
+CONFIG_CRYPTO_DEV_QCE=y
+CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
-CONFIG_QPNP_VIBRATOR=y
-CONFIG_QSEECOM=y
diff --git a/arch/arm/configs/msm8660-perf_defconfig b/arch/arm/configs/msm8660-perf_defconfig
index dda9bd3..da4de08 100644
--- a/arch/arm/configs/msm8660-perf_defconfig
+++ b/arch/arm/configs/msm8660-perf_defconfig
@@ -278,6 +278,7 @@
# CONFIG_I2C_MSM is not set
CONFIG_I2C_QUP=y
CONFIG_I2C_SSBI=y
+CONFIG_MSM_BUSPM_DEV=m
CONFIG_SPI=y
CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=m
@@ -314,7 +315,28 @@
CONFIG_MSM_ACTUATOR=y
CONFIG_MSM_GEMINI=y
CONFIG_OV7692=y
-# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2063 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_TUNER_XC4000 is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_MEDIA_TUNER_TDA18212 is not set
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_USB_VIDEO_CLASS=y
CONFIG_V4L_PLATFORM_DRIVERS=y
@@ -428,6 +450,7 @@
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
+CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
# CONFIG_DEBUG_PREEMPT is not set
CONFIG_DEBUG_INFO=y
diff --git a/arch/arm/configs/msm8660_defconfig b/arch/arm/configs/msm8660_defconfig
index abc7460..e30c7d8 100644
--- a/arch/arm/configs/msm8660_defconfig
+++ b/arch/arm/configs/msm8660_defconfig
@@ -278,6 +278,7 @@
# CONFIG_I2C_MSM is not set
CONFIG_I2C_QUP=y
CONFIG_I2C_SSBI=y
+CONFIG_MSM_BUSPM_DEV=m
CONFIG_SPI=y
CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=m
@@ -314,7 +315,28 @@
CONFIG_MSM_ACTUATOR=y
CONFIG_MSM_GEMINI=y
CONFIG_OV7692=y
-# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2063 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_TUNER_XC4000 is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_MEDIA_TUNER_TDA18212 is not set
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_USB_VIDEO_CLASS=y
CONFIG_V4L_PLATFORM_DRIVERS=y
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index f90e5f3..4610597 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -95,6 +95,8 @@
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
CONFIG_MSM_DCVS=y
CONFIG_MSM_HSIC_SYSMON=y
+CONFIG_WALL_CLK=m
+CONFIG_WALL_CLK_SYSFS=m
CONFIG_STRICT_MEMORY_RWX=y
CONFIG_PCI=y
CONFIG_PCI_MSI=y
@@ -317,6 +319,7 @@
CONFIG_I2C_CHARDEV=y
# CONFIG_I2C_MSM is not set
CONFIG_I2C_QUP=y
+CONFIG_MSM_BUSPM_DEV=m
CONFIG_SPI=y
CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=m
@@ -378,7 +381,28 @@
CONFIG_DVB_MPQ=m
CONFIG_DVB_MPQ_DEMUX=m
CONFIG_DVB_MPQ_VIDEO=m
-# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2063 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_TUNER_XC4000 is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_MEDIA_TUNER_TDA18212 is not set
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
CONFIG_USB_VIDEO_CLASS=y
@@ -482,7 +506,7 @@
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_BAMDMA=y
CONFIG_MSM_AVTIMER=y
-CONFIG_MSM_IOMMU=y
+CONFIG_MSM_IOMMU_V0=y
CONFIG_IOMMU_PGTABLES_L2=y
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
@@ -510,6 +534,7 @@
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
+CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
# CONFIG_DEBUG_PREEMPT is not set
CONFIG_DEBUG_INFO=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index f699dee..9b55c3b 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -83,6 +83,7 @@
CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_BUS_SCALING=y
+CONFIG_MSM_BUSPM_DEV=m
CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y
CONFIG_MSM_WATCHDOG=y
CONFIG_MSM_DLOAD_MODE=y
@@ -382,7 +383,28 @@
CONFIG_DVB_MPQ=m
CONFIG_DVB_MPQ_DEMUX=m
CONFIG_DVB_MPQ_VIDEO=m
-# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2063 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_TUNER_XC4000 is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_MEDIA_TUNER_TDA18212 is not set
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
CONFIG_USB_VIDEO_CLASS=y
@@ -484,7 +506,7 @@
CONFIG_MSM_SSBI=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_BAMDMA=y
-CONFIG_MSM_IOMMU=y
+CONFIG_MSM_IOMMU_V0=y
CONFIG_IOMMU_PGTABLES_L2=y
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index aa2c028..4d124f0 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -25,6 +25,7 @@
CONFIG_PANIC_TIMEOUT=5
CONFIG_KALLSYMS_ALL=y
CONFIG_EMBEDDED=y
+# CONFIG_SLUB_DEBUG is not set
CONFIG_PROFILING=y
CONFIG_OPROFILE=y
CONFIG_KPROBES=y
@@ -239,6 +240,7 @@
CONFIG_CMA=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
+CONFIG_TSPP=m
CONFIG_HAPTIC_ISA1200=y
CONFIG_QSEECOM=y
CONFIG_QPNP_MISC=y
@@ -288,6 +290,7 @@
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_QUP=y
+CONFIG_MSM_BUSPM_DEV=m
CONFIG_SPI=y
CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=m
@@ -321,23 +324,26 @@
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_DEV=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
+CONFIG_DVB_CORE=m
# CONFIG_MSM_CAMERA is not set
CONFIG_MT9M114=y
CONFIG_OV2720=y
CONFIG_IMX135=y
CONFIG_MSM_CAMERA_SENSOR=y
+CONFIG_MSM_EEPROM=y
CONFIG_MSM_CPP=y
CONFIG_MSM_CCI=y
CONFIG_MSM_CSI30_HEADER=y
CONFIG_MSM_CSIPHY=y
CONFIG_MSM_CSID=y
-CONFIG_MSM_EEPROM=y
CONFIG_MSM_ISPIF=y
CONFIG_S5K3L1YX=y
CONFIG_MSMB_CAMERA=y
CONFIG_MSMB_JPEG=y
CONFIG_MSM_VIDC_V4L2=y
CONFIG_MSM_WFD=y
+CONFIG_DVB_MPQ=m
+CONFIG_DVB_MPQ_DEMUX=m
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_USB_VIDEO_CLASS=y
CONFIG_V4L_PLATFORM_DRIVERS=y
@@ -346,6 +352,7 @@
CONFIG_ION=y
CONFIG_ION_MSM=y
CONFIG_MSM_KGSL=y
+CONFIG_KGSL_PER_PROCESS_PAGE_TABLE=y
CONFIG_FB=y
CONFIG_FB_MSM=y
# CONFIG_FB_MSM_BACKLIGHT is not set
@@ -362,10 +369,12 @@
CONFIG_SND_USB_AUDIO=y
CONFIG_SND_SOC=y
CONFIG_SND_SOC_MSM8974=y
+CONFIG_SND_SOC_APQ8074=y
CONFIG_UHID=y
CONFIG_HID_APPLE=y
CONFIG_HID_MAGICMOUSE=y
CONFIG_HID_MICROSOFT=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_SUSPEND=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
@@ -387,6 +396,8 @@
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_STORAGE_ENE_UB6250=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
+CONFIG_USB_QCOM_DIAG_BRIDGE=y
+CONFIG_USB_QCOM_KS_BRIDGE=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_CI13XXX_MSM=y
@@ -431,19 +442,10 @@
CONFIG_QPNP_CLKDIV=y
CONFIG_QPNP_REVID=y
CONFIG_QPNP_COINCELL=y
-CONFIG_MSM_IOMMU=y
+CONFIG_MSM_IOMMU_V1=y
CONFIG_IOMMU_PGTABLES_L2=y
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
-CONFIG_CORESIGHT=y
-CONFIG_CORESIGHT_TMC=y
-CONFIG_CORESIGHT_TPIU=y
-CONFIG_CORESIGHT_FUNNEL=y
-CONFIG_CORESIGHT_REPLICATOR=y
-CONFIG_CORESIGHT_STM=y
-CONFIG_CORESIGHT_ETM=y
-CONFIG_CORESIGHT_ETM_PCSAVE_DEFAULT_ENABLE=y
-CONFIG_CORESIGHT_EVENT=m
CONFIG_BIF=y
CONFIG_BIF_QPNP=y
CONFIG_EXT2_FS=y
@@ -459,6 +461,7 @@
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
+CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
# CONFIG_DEBUG_PREEMPT is not set
CONFIG_DEBUG_INFO=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index bbfcad9..1ffa7d2 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -37,6 +37,7 @@
CONFIG_DEFAULT_ROW=y
CONFIG_ARCH_MSM=y
CONFIG_ARCH_MSM8974=y
+CONFIG_ARCH_MSMSAMARIUM=y
CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
# CONFIG_MSM_STACKED_MEMORY is not set
CONFIG_CPU_HAS_L2_PMU=y
@@ -62,6 +63,7 @@
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_BUS_SCALING=y
+CONFIG_MSM_BUSPM_DEV=m
CONFIG_MSM_WATCHDOG_V2=y
CONFIG_MSM_MEMORY_DUMP=y
CONFIG_MSM_DLOAD_MODE=y
@@ -334,12 +336,12 @@
CONFIG_OV2720=y
CONFIG_IMX135=y
CONFIG_MSM_CAMERA_SENSOR=y
+CONFIG_MSM_EEPROM=y
CONFIG_MSM_CPP=y
CONFIG_MSM_CCI=y
CONFIG_MSM_CSI30_HEADER=y
CONFIG_MSM_CSIPHY=y
CONFIG_MSM_CSID=y
-CONFIG_MSM_EEPROM=y
CONFIG_MSM_ISPIF=y
CONFIG_S5K3L1YX=y
CONFIG_MSMB_CAMERA=y
@@ -356,6 +358,7 @@
CONFIG_ION=y
CONFIG_ION_MSM=y
CONFIG_MSM_KGSL=y
+CONFIG_KGSL_PER_PROCESS_PAGE_TABLE=y
CONFIG_FB=y
CONFIG_FB_MSM=y
# CONFIG_FB_MSM_BACKLIGHT is not set
@@ -372,10 +375,12 @@
CONFIG_SND_USB_AUDIO=y
CONFIG_SND_SOC=y
CONFIG_SND_SOC_MSM8974=y
+CONFIG_SND_SOC_APQ8074=y
CONFIG_UHID=y
CONFIG_HID_APPLE=y
CONFIG_HID_MAGICMOUSE=y
CONFIG_HID_MICROSOFT=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_SUSPEND=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
@@ -397,6 +402,8 @@
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_STORAGE_ENE_UB6250=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
+CONFIG_USB_QCOM_DIAG_BRIDGE=y
+CONFIG_USB_QCOM_KS_BRIDGE=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_CI13XXX_MSM=y
@@ -441,9 +448,9 @@
CONFIG_QPNP_CLKDIV=y
CONFIG_QPNP_REVID=y
CONFIG_QPNP_COINCELL=y
-CONFIG_MSM_IOMMU=y
-CONFIG_IOMMU_PGTABLES_L2=y
+CONFIG_MSM_IOMMU_V1=y
CONFIG_MSM_IOMMU_PMON=y
+CONFIG_IOMMU_PGTABLES_L2=y
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
CONFIG_CORESIGHT=y
@@ -452,6 +459,7 @@
CONFIG_CORESIGHT_FUNNEL=y
CONFIG_CORESIGHT_REPLICATOR=y
CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_HWEVENT=y
CONFIG_CORESIGHT_ETM=y
CONFIG_CORESIGHT_ETM_PCSAVE_DEFAULT_ENABLE=y
CONFIG_CORESIGHT_EVENT=m
diff --git a/arch/arm/configs/msm9615_defconfig b/arch/arm/configs/msm9615_defconfig
index 0fb8538..9e8e6ac 100644
--- a/arch/arm/configs/msm9615_defconfig
+++ b/arch/arm/configs/msm9615_defconfig
@@ -53,6 +53,7 @@
CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_BUS_SCALING=y
+CONFIG_MSM_BUSPM_DEV=m
CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y
CONFIG_MSM_WATCHDOG=y
CONFIG_MSM_DLOAD_MODE=y
@@ -159,7 +160,6 @@
CONFIG_MTD_BLOCK=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
-# CONFIG_ANDROID_PMEM is not set
CONFIG_SCSI=y
CONFIG_SCSI_TGT=y
CONFIG_BLK_DEV_SD=y
diff --git a/arch/arm/configs/msm9625-perf_defconfig b/arch/arm/configs/msm9625-perf_defconfig
index ae73bad..d02f5da 100644
--- a/arch/arm/configs/msm9625-perf_defconfig
+++ b/arch/arm/configs/msm9625-perf_defconfig
@@ -25,6 +25,7 @@
CONFIG_PANIC_TIMEOUT=5
CONFIG_KALLSYMS_ALL=y
CONFIG_EMBEDDED=y
+# CONFIG_SLUB_DEBUG is not set
CONFIG_PROFILING=y
CONFIG_OPROFILE=m
CONFIG_MODULES=y
@@ -53,9 +54,8 @@
CONFIG_MSM_WATCHDOG_V2=y
CONFIG_MSM_MEMORY_DUMP=y
CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
CONFIG_MSM_ADSP_LOADER=m
-CONFIG_MSM_RTB=y
+CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
CONFIG_MSM_UARTDM_Core_v14=y
CONFIG_MSM_BOOT_STATS=y
CONFIG_NO_HZ=y
@@ -162,12 +162,12 @@
CONFIG_MTD=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_OF_PARTS=y
+CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
# CONFIG_MTD_MSM_NAND is not set
CONFIG_MTD_MSM_QPIC_NAND=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
-# CONFIG_ANDROID_PMEM is not set
CONFIG_SCSI=y
CONFIG_SCSI_TGT=y
CONFIG_BLK_DEV_SD=y
@@ -209,6 +209,7 @@
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_QUP=y
+CONFIG_MSM_BUSPM_DEV=m
CONFIG_SPI=y
CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=m
@@ -224,6 +225,8 @@
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
CONFIG_THERMAL_TSENS8974=y
+CONFIG_THERMAL_MONITOR=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
CONFIG_WCD9320_CODEC=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -256,6 +259,7 @@
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_G_ANDROID=y
CONFIG_MMC=y
@@ -277,14 +281,6 @@
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_QPNP_POWER_ON=y
CONFIG_IPA=y
-CONFIG_CORESIGHT=y
-CONFIG_CORESIGHT_TMC=y
-CONFIG_CORESIGHT_TPIU=y
-CONFIG_CORESIGHT_FUNNEL=y
-CONFIG_CORESIGHT_REPLICATOR=y
-CONFIG_CORESIGHT_STM=y
-CONFIG_CORESIGHT_ETM=y
-CONFIG_CORESIGHT_EVENT=m
CONFIG_EXT3_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index f7c3bff..64c8535 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -50,12 +50,13 @@
CONFIG_MSM_PIL_MSS_QDSP6V5=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_BUS_SCALING=y
+CONFIG_MSM_BUSPM_DEV=m
CONFIG_MSM_WATCHDOG_V2=y
CONFIG_MSM_MEMORY_DUMP=y
CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
CONFIG_MSM_ADSP_LOADER=m
CONFIG_MSM_RTB=y
+CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
CONFIG_MSM_UARTDM_Core_v14=y
CONFIG_MSM_BOOT_STATS=y
CONFIG_NO_HZ=y
@@ -108,12 +109,15 @@
CONFIG_NF_CT_NETLINK=y
CONFIG_NETFILTER_XT_MARK=y
CONFIG_NETFILTER_XT_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_LOG=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
CONFIG_NETFILTER_XT_MATCH_DSCP=y
CONFIG_NETFILTER_XT_MATCH_ESP=y
CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
CONFIG_IP_SET=y
@@ -162,12 +166,12 @@
CONFIG_MTD=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_OF_PARTS=y
+CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
# CONFIG_MTD_MSM_NAND is not set
CONFIG_MTD_MSM_QPIC_NAND=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
-# CONFIG_ANDROID_PMEM is not set
CONFIG_SCSI=y
CONFIG_SCSI_TGT=y
CONFIG_BLK_DEV_SD=y
@@ -225,6 +229,8 @@
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
CONFIG_THERMAL_TSENS8974=y
+CONFIG_THERMAL_MONITOR=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
CONFIG_WCD9320_CODEC=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -257,6 +263,7 @@
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_G_ANDROID=y
CONFIG_MMC=y
@@ -284,6 +291,7 @@
CONFIG_CORESIGHT_FUNNEL=y
CONFIG_CORESIGHT_REPLICATOR=y
CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_HWEVENT=y
CONFIG_CORESIGHT_ETM=y
CONFIG_CORESIGHT_EVENT=m
CONFIG_EXT3_FS=y
@@ -317,8 +325,5 @@
CONFIG_CRYPTO_DES=y
CONFIG_CRYPTO_TWOFISH=y
CONFIG_CRYPTO_DEFLATE=y
-CONFIG_CRYPTO_DEV_QCRYPTO=m
-CONFIG_CRYPTO_DEV_QCE=m
-CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
CONFIG_LIBCRC32C=y
diff --git a/arch/arm/configs/msmkrypton_defconfig b/arch/arm/configs/msmkrypton_defconfig
index 69bc36e..f073d2b 100644
--- a/arch/arm/configs/msmkrypton_defconfig
+++ b/arch/arm/configs/msmkrypton_defconfig
@@ -65,9 +65,28 @@
CONFIG_MTD_BLOCK=y
# CONFIG_MTD_MSM_NAND is not set
CONFIG_MTD_MSM_QPIC_NAND=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IPV6=y
+# CONFIG_WIRELESS is not set
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
# CONFIG_ANDROID_PMEM is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+CONFIG_KS8851=y
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_MSM_RMNET is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_WLAN is not set
# CONFIG_INPUT_MOUSEDEV is not set
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_KEYBOARD is not set
diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h
index c1295c4..70d82ec 100644
--- a/arch/arm/include/asm/io.h
+++ b/arch/arm/include/asm/io.h
@@ -119,16 +119,16 @@
*/
extern void __iomem *__arm_ioremap_pfn_caller(unsigned long, unsigned long,
size_t, unsigned int, void *);
-extern void __iomem *__arm_ioremap_caller(unsigned long, size_t, unsigned int,
+extern void __iomem *__arm_ioremap_caller(phys_addr_t, size_t, unsigned int,
void *);
extern void __iomem *__arm_ioremap_pfn(unsigned long, unsigned long, size_t, unsigned int);
-extern void __iomem *__arm_ioremap(unsigned long, size_t, unsigned int);
-extern void __iomem *__arm_ioremap_exec(unsigned long, size_t, bool cached);
+extern void __iomem *__arm_ioremap(phys_addr_t, size_t, unsigned int);
+extern void __iomem *__arm_ioremap_exec(phys_addr_t, size_t, bool cached);
extern void __iounmap(volatile void __iomem *addr);
extern void __arm_iounmap(volatile void __iomem *addr);
-extern void __iomem * (*arch_ioremap_caller)(unsigned long, size_t,
+extern void __iomem * (*arch_ioremap_caller)(phys_addr_t, size_t,
unsigned int, void *);
extern void (*arch_iounmap)(volatile void __iomem *);
diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h
index 84560dc..7b6f42a 100644
--- a/arch/arm/include/asm/pgtable.h
+++ b/arch/arm/include/asm/pgtable.h
@@ -313,6 +313,7 @@
* We provide our own arch_get_unmapped_area to cope with VIPT caches.
*/
#define HAVE_ARCH_UNMAPPED_AREA
+#define HAVE_ARCH_UNMAPPED_AREA_TOPDOWN
/*
* remap a physical page `pfn' of size `size' with page protection `prot'
diff --git a/arch/arm/include/asm/processor.h b/arch/arm/include/asm/processor.h
index 8e2bb29..07209d7 100644
--- a/arch/arm/include/asm/processor.h
+++ b/arch/arm/include/asm/processor.h
@@ -126,6 +126,8 @@
#endif
+#define HAVE_ARCH_PICK_MMAP_LAYOUT
+
#endif
#endif /* __ASM_ARM_PROCESSOR_H */
diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h
index 2a46914..07a09b5 100644
--- a/arch/arm/include/asm/setup.h
+++ b/arch/arm/include/asm/setup.h
@@ -196,7 +196,7 @@
struct membank {
phys_addr_t start;
- unsigned long size;
+ phys_addr_t size;
unsigned int highmem;
};
@@ -218,7 +218,7 @@
#define bank_phys_end(bank) ((bank)->start + (bank)->size)
#define bank_phys_size(bank) (bank)->size
-extern int arm_add_memory(phys_addr_t start, unsigned long size);
+extern int arm_add_memory(phys_addr_t start, phys_addr_t size);
extern void early_print(const char *str, ...);
extern void dump_machine_table(void);
diff --git a/arch/arm/include/asm/system_misc.h b/arch/arm/include/asm/system_misc.h
index 5a85f14..71f4827 100644
--- a/arch/arm/include/asm/system_misc.h
+++ b/arch/arm/include/asm/system_misc.h
@@ -23,6 +23,7 @@
extern void disable_hlt(void);
extern void enable_hlt(void);
+extern int get_hlt(void);
#endif /* !__ASSEMBLY__ */
diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c
index bee7f9d..24bc80b 100644
--- a/arch/arm/kernel/devtree.c
+++ b/arch/arm/kernel/devtree.c
@@ -26,6 +26,18 @@
void __init early_init_dt_add_memory_arch(u64 base, u64 size)
{
+#ifndef CONFIG_ARM_LPAE
+ if (base > ((phys_addr_t)~0)) {
+ pr_crit("Ignoring memory at 0x%08llx due to lack of LPAE support\n",
+ base);
+ return;
+ }
+
+ if (size > ((phys_addr_t)~0))
+ size = ((phys_addr_t)~0);
+
+ /* arm_add_memory() already checks for the case of base + size > 4GB */
+#endif
arm_add_memory(base, size);
}
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index c440f47..6e94b32 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -742,6 +742,7 @@
static struct of_device_id armpmu_of_device_ids[] = {
{.compatible = "arm,cortex-a9-pmu"},
{.compatible = "arm,cortex-a8-pmu"},
+ {.compatible = "arm,cortex-a7-pmu"},
{.compatible = "arm,cortex-a5-pmu"},
{.compatible = "arm,arm1136-pmu"},
{.compatible = "arm,arm1176-pmu"},
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index e7a9237..fe97ff2 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -86,6 +86,12 @@
EXPORT_SYMBOL(enable_hlt);
+int get_hlt(void)
+{
+ return hlt_counter;
+}
+EXPORT_SYMBOL(get_hlt);
+
static int __init nohlt_setup(char *__unused)
{
hlt_counter = 1;
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 4aabf0e..28b114f 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -510,7 +510,7 @@
/* can't use cpu_relax() here as it may require MMU setup */;
}
-int __init arm_add_memory(phys_addr_t start, unsigned long size)
+int __init arm_add_memory(phys_addr_t start, phys_addr_t size)
{
struct membank *bank = &meminfo.bank[meminfo.nr_banks];
@@ -540,7 +540,7 @@
}
#endif
- bank->size = size & PAGE_MASK;
+ bank->size = size & ~(phys_addr_t)(PAGE_SIZE - 1);
/*
* Check whether this memory region has non-zero size or
@@ -560,7 +560,7 @@
static int __init early_mem(char *p)
{
static int usermem __initdata = 0;
- unsigned long size;
+ phys_addr_t size;
phys_addr_t start;
char *endp;
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index cf10056..a70b300 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -573,6 +573,8 @@
local_fiq_disable();
local_irq_disable();
+ flush_cache_all();
+
while (1)
cpu_relax();
}
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index a6e6914..85fa764 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -145,7 +145,7 @@
select CPU_V7
select GPIO_MSM_V2
select MSM_GPIOMUX
- select MSM_SCM if SMP
+ select MSM_SCM
select MSM_DIRECT_SCLK_ACCESS
select REGULATOR
select MSM_RPM_REGULATOR
@@ -187,7 +187,7 @@
select CPU_V7
select GPIO_MSM_V2
select MSM_GPIOMUX
- select MSM_SCM if SMP
+ select MSM_SCM
select MSM_DIRECT_SCLK_ACCESS
select REGULATOR
select MSM_RPM_REGULATOR
@@ -223,7 +223,7 @@
select GPIO_MSM_V2
select ARM_GIC
select CPU_V7
- select MSM_SCM if SMP
+ select MSM_SCM
select MSM_GPIOMUX
select MSM_REMOTE_SPINLOCK_SFPB
select MSM_PIL
@@ -257,7 +257,7 @@
select GPIO_MSM_V3
select ARM_GIC
select CPU_V7
- select MSM_SCM if SMP
+ select MSM_SCM
select MSM_GPIOMUX
select MULTI_IRQ_HANDLER
select MSM_MULTIMEDIA_USE_ION
@@ -286,13 +286,13 @@
select MSM_RPM_LOG
select ARCH_WANT_KMAP_ATOMIC_FLUSH
-config ARCH_MSMZINC
- bool "MSMZINC"
+config ARCH_APQ8084
+ bool "APQ8084"
select ARCH_MSM_KRAITMP
select GPIO_MSM_V3
select ARM_GIC
select CPU_V7
- select MSM_SCM if SMP
+ select MSM_SCM
select MSM_GPIOMUX
select MULTI_IRQ_HANDLER
select MSM_NATIVE_RESTART
@@ -304,6 +304,9 @@
select ARM_HAS_SG_CHAIN
select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
select ARCH_WANT_KMAP_ATOMIC_FLUSH
+ select MEMORY_HOLE_CARVEOUT
+ select DONT_MAP_HOLE_AFTER_MEMBANK0
+ select QMI_ENCDEC
config ARCH_MPQ8092
bool "MPQ8092"
@@ -317,6 +320,25 @@
select SPARSE_IRQ
select MSM_NOPM
+config ARCH_FSM9900
+ bool "FSM9900"
+ select ARCH_MSM_KRAITMP
+ select GPIO_MSM_V3
+ select ARM_GIC
+ select CPU_V7
+ select MSM_SCM
+ select MSM_GPIOMUX
+ select MULTI_IRQ_HANDLER
+ select MSM_PIL
+ select MSM_NATIVE_RESTART
+ select MSM_RESTART_V2
+ select MAY_HAVE_SPARSE_IRQ
+ select SPARSE_IRQ
+ select REGULATOR
+ select ARM_HAS_SG_CHAIN
+ select MSM_RUN_QUEUE_STATS
+ select MSM_NOPM
+
config ARCH_FSM9XXX
bool "FSM9XXX"
select ARCH_MSM_SCORPION
@@ -378,11 +400,11 @@
select MSM_RESTART_V2
select MSM_SPM_V2
select MSM_PM8X60 if PM
- select MSM_SCM if SMP
select MULTI_IRQ_HANDLER
select GPIO_MSM_V3
select MAY_HAVE_SPARSE_IRQ
select SPARSE_IRQ
+ select CPU_FREQ_MSM
select MSM_MULTIMEDIA_USE_ION
select MSM_RPM_STATS_LOG
select MSM_QDSP6_APRV2
@@ -414,10 +436,11 @@
select GIC_SECURE
select ARCH_MSM_CORTEXMP
select CPU_V7
- select MSM_SCM if SMP
+ select MSM_SCM
select MAY_HAVE_SPARSE_IRQ
select SPARSE_IRQ
select MULTI_IRQ_HANDLER
+ select ARM_TICKET_LOCKS
select GPIO_MSM_V3
select MSM_GPIOMUX
select MSM_NATIVE_RESTART
@@ -435,8 +458,6 @@
select MSM_BUS_SCALING
select CPU_FREQ_MSM
select CPU_FREQ
- select CPU_FREQ_GOV_USERSPACE
- select CPU_FREQ_GOV_ONDEMAND
select MSM_PIL
select MSM_RUN_QUEUE_STATS
select ARM_HAS_SG_CHAIN
@@ -454,10 +475,11 @@
select GIC_SECURE
select ARCH_MSM_CORTEXMP
select CPU_V7
- select MSM_SCM if SMP
+ select MSM_SCM
select MAY_HAVE_SPARSE_IRQ
select SPARSE_IRQ
select MULTI_IRQ_HANDLER
+ select ARM_TICKET_LOCKS
select GPIO_MSM_V3
select MSM_GPIOMUX
select MSM_NATIVE_RESTART
@@ -475,8 +497,6 @@
select MSM_BUS_SCALING
select CPU_FREQ_MSM
select CPU_FREQ
- select CPU_FREQ_GOV_USERSPACE
- select CPU_FREQ_GOV_ONDEMAND
select MSM_PIL
select MSM_RUN_QUEUE_STATS
select ARM_HAS_SG_CHAIN
@@ -488,6 +508,26 @@
select MSM_RPM_LOG
select MSM_RPM_STATS_LOG
select ARCH_WANT_KMAP_ATOMIC_FLUSH
+
+config ARCH_MSMSAMARIUM
+ bool "MSMSAMARIUM"
+ select ARCH_MSM_KRAITMP
+ select GPIO_MSM_V3
+ select ARM_GIC
+ select CPU_V7
+ select MSM_SCM
+ select MSM_GPIOMUX
+ select MULTI_IRQ_HANDLER
+ select MSM_SPM_V2
+ select MSM_L2_SPM
+ select MSM_NATIVE_RESTART
+ select MSM_RESTART_V2
+ select MSM_PM8X60 if PM
+ select MAY_HAVE_SPARSE_IRQ
+ select SPARSE_IRQ
+ select ARM_HAS_SG_CHAIN
+ select MSM_RUN_QUEUE_STATS
+ select ARCH_WANT_KMAP_ATOMIC_FLUSH
endmenu
choice
@@ -1086,13 +1126,15 @@
default "0x80200000" if ARCH_MSM8960
default "0x80200000" if ARCH_MSM8930
default "0x00000000" if ARCH_MSM8974
- default "0x00000000" if ARCH_MSMZINC
+ default "0x00000000" if ARCH_APQ8084
default "0x00000000" if ARCH_MPQ8092
default "0x00000000" if ARCH_MSM8226
default "0x00000000" if ARCH_MSM8610
+ default "0x00000000" if ARCH_MSMSAMARIUM
default "0x10000000" if ARCH_FSM9XXX
+ default "0x00000000" if ARCH_FSM9900
default "0x00200000" if ARCH_MSM9625
- default "0x00200000" if ARCH_MSMKRYPTON
+ default "0x00000000" if ARCH_MSMKRYPTON
default "0x00200000" if !MSM_STACKED_MEMORY
default "0x00000000" if ARCH_QSD8X50 && MSM_SOC_REV_A
default "0x20000000" if ARCH_QSD8X50
@@ -1112,14 +1154,14 @@
config KERNEL_PMEM_SMI_REGION
bool "Enable in-kernel PMEM region for SMI"
default y if ARCH_MSM8X60
- depends on ANDROID_PMEM && ((ARCH_QSD8X50 && !PMEM_GPU0) || (ARCH_MSM8X60 && !VCM))
+ depends on (ARCH_QSD8X50 && !PMEM_GPU0) || (ARCH_MSM8X60 && !VCM)
help
Enable the in-kernel PMEM allocator to use SMI memory.
config PMEM_GPU0
bool "Enable PMEM GPU0 region"
default y
- depends on ARCH_QSD8X50 && ANDROID_PMEM
+ depends on ARCH_QSD8X50
help
Enable the PMEM GPU0 device on SMI Memory.
@@ -1239,13 +1281,37 @@
Say Y here if you want the debug print routines to direct
their output to the serial port on MPQ8092 devices.
- config DEBUG_MSMZINC_UART
- bool "Kernel low-level debugging messages via MSMZINC UART"
- depends on ARCH_MSMZINC
+ config DEBUG_APQ8084_UART
+ bool "Kernel low-level debugging messages via APQ8084 UART"
+ depends on ARCH_APQ8084
select MSM_HAS_DEBUG_UART_HS_V14
help
Say Y here if you want the debug print routines to direct
- their output to the serial port on MSMZINC devices.
+ their output to the serial port on APQ8084 devices.
+
+ config DEBUG_MSM9625_UART
+ bool "Kernel low-level debugging messages via MSM9625 UART"
+ depends on ARCH_MSM9625
+ select MSM_HAS_DEBUG_UART_HS_V14
+ help
+ Say Y here if you want the debug print routines to direct
+ their output to the serial port on MSM9625 devices.
+
+ config DEBUG_FSM9900_UART
+ bool "Kernel low-level debugging messages via FSM9900 UART"
+ depends on ARCH_FSM9900
+ select MSM_HAS_DEBUG_UART_HS_V14
+ help
+ Say Y here if you want the debug print routines to direct
+ their output to the serial port on FSM9900 devices.
+
+ config DEBUG_MSMSAMARIUM_UART
+ bool "Kernel low-level debugging messages via MSM SAMARIUM UART"
+ depends on ARCH_MSMSAMARIUM
+ select MSM_HAS_DEBUG_UART_HS_V14
+ help
+ Say Y here if you want the debug print routines to direct
+ their output to the serial port on MSM SAMARIUM devices.
endchoice
choice
@@ -1847,7 +1913,6 @@
config MSM_HW3D
tristate "MSM Hardware 3D Register Driver"
- depends on ANDROID_PMEM
help
Provides access to registers needed by the userspace OpenGL|ES
library.
@@ -1855,7 +1920,6 @@
config MSM_ADSP
depends on (ARCH_MSM7X01A || ARCH_MSM7X25 || ARCH_MSM7X27)
tristate "MSM ADSP driver"
- depends on ANDROID_PMEM
default y
help
Provides access to registers needed by the userspace aDSP library.
@@ -1881,7 +1945,7 @@
config MSM7KV2_AUDIO
bool "MSM7K v2 audio"
- depends on (ARCH_MSM7X30 && ANDROID_PMEM)
+ depends on ARCH_MSM7X30
default y
help
Enables QDSP5V2-based audio drivers for audio playbacks and
@@ -1897,14 +1961,14 @@
config MSM_QDSP6
tristate "QDSP6 support"
- depends on ARCH_QSD8X50 && ANDROID_PMEM
+ depends on ARCH_QSD8X50
default y
help
Enable support for qdsp6. This provides audio and video functionality.
config MSM8X60_AUDIO
tristate "MSM8X60 audio support"
- depends on ARCH_MSM8X60 && ANDROID_PMEM
+ depends on ARCH_MSM8X60
default y
help
Enable support for qdsp6v2. This provides audio functionality.
@@ -1963,7 +2027,7 @@
config QSD_AUDIO
bool "QSD audio"
- depends on ARCH_MSM_SCORPION && MSM_DALRPC && ANDROID_PMEM && !MSM_SMP
+ depends on ARCH_MSM_SCORPION && MSM_DALRPC && !MSM_SMP
default y
help
Provides PCM, MP3, and AAC audio playback.
@@ -2260,8 +2324,8 @@
config MSM_BUSPM_DEV
tristate "MSM Bus Performance Monitor Kernel Module"
- depends on (ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_MSM8974)
- default m
+ depends on MSM_BUS_SCALING
+ default n
help
This kernel module is used to mmap() hardware registers for the
performance monitors, counters, etc. The module can also be used to
@@ -2654,14 +2718,25 @@
related operations of OCMEM. Both local power management
and RPM assisted power management operations are supported.
-config MSM_OCMEM_POWER_DISABLE
- bool "OCMEM Disable Power Control"
+config MSM_OCMEM_DEBUG_ALWAYS_ON
+ bool "Keep OCMEM always turned ON"
depends on MSM_OCMEM_DEBUG
help
+ Always vote for all OCMEM clocks and keep all OCMEM
+ macros turned ON and never allow them to be turned OFF.
+ Both local power management and RPM assisted power modes
+ are supported for individual macro power control operations.
+
+config MSM_OCMEM_POWER_DISABLE
+ bool "OCMEM Disable Power Control"
+ depends on MSM_OCMEM
+ help
Disable all OCMEM power management.
- This keeps all OCMEM macros turned ON at all times thus
- never allowing them to be turned OFF. Both local power
- management and RPM assisted power modes are supported.
+ Skip all OCMEM power operations that turn ON or
+ turn OFF the macros. Both local power management and
+ RPM assisted power management operations are skipped.
+ Enable this configuration if OCMEM is being exclusively
+ used as GMEM or OCIMEM.
config SENSORS_ADSP
bool "Enable Sensors Driver Support for ADSP"
@@ -2945,4 +3020,21 @@
suggestions in efuse as initial settings. It converts corner vote
to voltage value before writing to a voltage regulator API, such as
that provided by spm-regulator driver.
+
+config WALL_CLK
+ tristate "Wall Clock hardware block simulation"
+ depends on ARCH_APQ8064
+ help
+ This driver simulates the wall-clock hardware block on fsm8064_ep
+ femto emulation platform. This block will be used to provide
+ clock information to the LTE Layer 2 module running on the hexagon
+ processor.
+
+config WALL_CLK_SYSFS
+ tristate "Wall Clock SysFS Support"
+ depends on SYSFS && WALL_CLK
+ help
+ Support the wallclk directory in sysfs filesystem to enable the
+ wall clock simulation and read the current SFN.
+
endif
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 7c78395..1c07dc1 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -4,10 +4,9 @@
ifndef CONFIG_ARM_ARCH_TIMER
obj-y += timer.o
endif
-obj-y += clock.o clock-voter.o clock-dummy.o
+obj-y += clock.o clock-voter.o clock-dummy.o clock-generic.o
obj-y += modem_notifier.o
obj-$(CONFIG_USE_OF) += board-dt.o
-obj-$(CONFIG_CPU_FREQ_MSM) += cpufreq.o
obj-$(CONFIG_DEBUG_FS) += nohlt.o clock-debug.o
obj-$(CONFIG_KEXEC) += msm_kexec.o
@@ -76,7 +75,7 @@
$(obj)/smd_rpc_sym.c: $(src)/smd_rpc_sym $(src)/mkrpcsym.pl
$(call if_changed,mkrpcsym)
-obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o remote_spinlock.o smd_private.o
+obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o remote_spinlock.o smd_private.o smem.o
obj-$(CONFIG_MSM_SMP2P) += smp2p.o smp2p_debug.o smp2p_gpio.o
obj-$(CONFIG_MSM_SMP2P_TEST) += smp2p_loopback.o smp2p_test.o smp2p_gpio_test.o smp2p_spinlock_test.o
obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o
@@ -112,27 +111,15 @@
obj-$(CONFIG_MSM_IPC_LOGGING) += ipc_logging_debug.o
endif
obj-y += socinfo.o
-ifndef CONFIG_ARCH_MSM8960
-ifndef CONFIG_ARCH_MSM8X60
-ifndef CONFIG_ARCH_APQ8064
-ifndef CONFIG_ARCH_MSM8974
-ifndef CONFIG_ARCH_MSM8226
-ifndef CONFIG_ARCH_MSM9625
-ifndef CONFIG_ARCH_MPQ8092
-ifndef CONFIG_ARCH_MSM8610
-ifndef CONFIG_ARCH_MSMZINC
-ifndef CONFIG_ARCH_MSMKRYPTON
- obj-y += nand_partitions.o
-endif
-endif
-endif
-endif
-endif
-endif
-endif
-endif
-endif
-endif
+obj-$(CONFIG_ARCH_MSM7X01A) += nand_partitions.o
+obj-$(CONFIG_ARCH_MSM7X25) += nand_partitions.o
+obj-$(CONFIG_ARCH_MSM7X27) += nand_partitions.o
+obj-$(CONFIG_ARCH_MSM7X30) += nand_partitions.o
+obj-$(CONFIG_ARCH_QSD8X50) += nand_partitions.o
+obj-$(CONFIG_ARCH_FSM9XXX) += nand_partitions.o
+obj-$(CONFIG_ARCH_MSM9615) += nand_partitions.o
+obj-$(CONFIG_ARCH_MSM8625) += nand_partitions.o
+obj-$(CONFIG_ARCH_MSM7X27A) += nand_partitions.o
obj-$(CONFIG_MSM_SDIO_TTY) += sdio_tty.o
obj-$(CONFIG_MSM_SMD_TTY) += smd_tty.o
obj-$(CONFIG_MSM_SMD_QMI) += smd_qmi.o
@@ -231,6 +218,7 @@
obj-$(CONFIG_ARCH_FSM9XXX) += clock-fsm9xxx.o clock-local.o acpuclock-fsm9xxx.o
obj-$(CONFIG_ARCH_FSM9XXX) += dfe-fsm9xxx.o rfic-fsm9xxx.o
obj-$(CONFIG_ARCH_FSM9XXX) += restart-fsm9xxx.o xo-fsm9xxx.o
+obj-$(CONFIG_ARCH_FSM9900) += board-fsm9900.o board-fsm9900-gpiomux.o
obj-$(CONFIG_MSM_WATCHDOG) += msm_watchdog.o
obj-$(CONFIG_MSM_WATCHDOG) += msm_watchdog_asm.o
@@ -289,7 +277,7 @@
obj-$(CONFIG_MACH_MSM8930_FLUID) += board-8930-all.o board-8930-regulator-pm8038.o board-8930-regulator-pm8917.o
obj-$(CONFIG_PM8921_BMS) += bms-batterydata.o bms-batterydata-desay.o batterydata-lib.o
obj-$(CONFIG_QPNP_BMS) += bms-batterydata.o bms-batterydata-desay.o batterydata-lib.o
-obj-$(CONFIG_QPNP_BMS) += bms-batterydata-oem.o
+obj-$(CONFIG_QPNP_BMS) += bms-batterydata-oem.o bms-batterydata-qrd-4v35-2000mah.o bms-batterydata-qrd-4v2-1300mah.o
obj-$(CONFIG_MACH_APQ8064_CDP) += board-8064-all.o board-8064-regulator.o
obj-$(CONFIG_MACH_APQ8064_MTP) += board-8064-all.o board-8064-regulator.o
obj-$(CONFIG_MACH_APQ8064_LIQUID) += board-8064-all.o board-8064-regulator.o
@@ -297,7 +285,8 @@
obj-$(CONFIG_MACH_MPQ8064_DTV) += board-8064-all.o board-8064-regulator.o
obj-$(CONFIG_ARCH_MSM9615) += board-9615.o devices-9615.o board-9615-regulator.o board-9615-gpiomux.o board-9615-storage.o board-9615-display.o
obj-$(CONFIG_ARCH_MSM9615) += clock-local.o clock-9615.o acpuclock-9615.o clock-rpm.o clock-pll.o
-obj-$(CONFIG_ARCH_MSMZINC) += board-zinc.o board-zinc-gpiomux.o
+obj-$(CONFIG_ARCH_APQ8084) += board-8084.o board-8084-gpiomux.o
+obj-$(CONFIG_ARCH_APQ8084) += clock-8084.o
obj-$(CONFIG_ARCH_MSM8974) += board-8974.o board-8974-gpiomux.o
obj-$(CONFIG_ARCH_MSM8974) += acpuclock-8974.o
obj-$(CONFIG_ARCH_MSM8974) += clock-local2.o clock-pll.o clock-8974.o clock-rpm.o clock-voter.o clock-mdss-8974.o
@@ -305,17 +294,21 @@
obj-$(CONFIG_ARCH_MSM9625) += gdsc.o
obj-$(CONFIG_ARCH_MSM8226) += gdsc.o
obj-$(CONFIG_ARCH_MSM8610) += gdsc.o
+obj-$(CONFIG_ARCH_MPQ8092) += gdsc.o
obj-$(CONFIG_ARCH_MSM8974) += krait-regulator.o
obj-$(CONFIG_ARCH_MSMKRYPTON) += board-krypton.o board-krypton-gpiomux.o
+obj-$(CONFIG_ARCH_MSMSAMARIUM) += board-samarium.o board-samarium-gpiomux.o
obj-$(CONFIG_ARCH_MSM9625) += board-9625.o board-9625-gpiomux.o
obj-$(CONFIG_ARCH_MSM9625) += clock-local2.o clock-pll.o clock-9625.o clock-rpm.o clock-voter.o acpuclock-9625.o acpuclock-cortex.o
obj-$(CONFIG_ARCH_MSM8930) += acpuclock-8930.o acpuclock-8627.o acpuclock-8930aa.o acpuclock-8930ab.o
obj-$(CONFIG_ARCH_MPQ8092) += board-8092.o board-8092-gpiomux.o
+obj-$(CONFIG_ARCH_MPQ8092) += clock-8092.o
obj-$(CONFIG_ARCH_MSM8226) += board-8226.o board-8226-gpiomux.o
-obj-$(CONFIG_ARCH_MSM8226) += clock-local2.o clock-pll.o clock-8226.o clock-rpm.o clock-voter.o clock-mdss-8226.o
+obj-$(CONFIG_ARCH_MSM8226) += clock-local2.o clock-pll.o clock-8226.o clock-rpm.o clock-voter.o clock-mdss-8974.o
obj-$(CONFIG_ARCH_MSM8226) += acpuclock-8226.o acpuclock-cortex.o
obj-$(CONFIG_ARCH_MSM8610) += board-8610.o board-8610-gpiomux.o
obj-$(CONFIG_ARCH_MSM8610) += clock-local2.o clock-pll.o clock-8610.o clock-rpm.o clock-voter.o
+obj-$(CONFIG_ARCH_MSM8610) += clock-dsi-8610.o
obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o
obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o
@@ -372,10 +365,12 @@
obj-$(CONFIG_ARCH_MSM8974) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_ARCH_MSM9625) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_ARCH_MSMKRYPTON) += gpiomux-v2.o gpiomux.o
+obj-$(CONFIG_ARCH_MSMSAMARIUM) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_ARCH_MPQ8092) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_ARCH_MSM8226) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_ARCH_MSM8610) += gpiomux-v2.o gpiomux.o
-obj-$(CONFIG_ARCH_MSMZINC) += gpiomux-v2.o gpiomux.o
+obj-$(CONFIG_ARCH_APQ8084) += gpiomux-v2.o gpiomux.o
+obj-$(CONFIG_ARCH_FSM9900) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_MSM_SLEEP_STATS_DEVICE) += idle_stats_device.o
obj-$(CONFIG_MSM_DCVS) += msm_dcvs_scm.o msm_dcvs.o msm_mpdecision.o
@@ -428,3 +423,7 @@
obj-$(CONFIG_ARCH_MSM8974) += msm_mpmctr.o
obj-$(CONFIG_MSM_CPR_REGULATOR) += cpr-regulator.o
+obj-$(CONFIG_CPU_FREQ_MSM) += cpufreq.o
+
+obj-$(CONFIG_WALL_CLK) += wallclk.o
+obj-$(CONFIG_WALL_CLK_SYSFS) += wallclk_sysfs.o
diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot
index 8c366da..3469c66 100644
--- a/arch/arm/mach-msm/Makefile.boot
+++ b/arch/arm/mach-msm/Makefile.boot
@@ -57,13 +57,15 @@
dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v2-fluid.dtb
dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v2-liquid.dtb
dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v2-mtp.dtb
+ dtb-$(CONFIG_ARCH_MSM8974) += apq8074-v2-liquid.dtb
+ dtb-$(CONFIG_ARCH_MSM8974) += apq8074-v2-dragonboard.dtb
-# MSMZINC
- zreladdr-$(CONFIG_ARCH_MSMZINC) := 0x00008000
- dtb-$(CONFIG_ARCH_MSMZINC) += msmzinc-sim.dtb
+# APQ8084
+ zreladdr-$(CONFIG_ARCH_APQ8084) := 0x00008000
+ dtb-$(CONFIG_ARCH_APQ8084) += apq8084-sim.dtb
# MSMKRYPTON
- zreladdr-$(CONFIG_ARCH_MSMKRYPTON) := 0x00208000
+ zreladdr-$(CONFIG_ARCH_MSMKRYPTON) := 0x00008000
dtb-$(CONFIG_ARCH_MSMKRYPTON) += msmkrypton-sim.dtb
# MSM9615
@@ -82,15 +84,28 @@
# MSM8226
zreladdr-$(CONFIG_ARCH_MSM8226) := 0x00008000
dtb-$(CONFIG_ARCH_MSM8226) += msm8226-sim.dtb
- dtb-$(CONFIG_ARCH_MSM8226) += msm8226-cdp.dtb
- dtb-$(CONFIG_ARCH_MSM8226) += msm8226-mtp.dtb
- dtb-$(CONFIG_ARCH_MSM8226) += msm8226-qrd.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v1-cdp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v1-mtp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v1-qrd.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v2-cdp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v2-mtp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v2-qrd.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-cdp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-mtp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-qrd.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += apq8026-xpm.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += apq8026-mtp.dtb
# FSM9XXX
zreladdr-$(CONFIG_ARCH_FSM9XXX) := 0x10008000
params_phys-$(CONFIG_ARCH_FSM9XXX) := 0x10000100
initrd_phys-$(CONFIG_ARCH_FSM9XXX) := 0x12000000
+# FSM9900
+ zreladdr-$(CONFIG_ARCH_FSM9900) := 0x00008000
+ dtb-$(CONFIG_ARCH_FSM9900) := fsm9900-rumi.dtb
+ dtb-$(CONFIG_ARCH_FSM9900) := fsm9900-sim.dtb
+
# MPQ8092
zreladdr-$(CONFIG_ARCH_MPQ8092) := 0x00008000
@@ -98,3 +113,8 @@
zreladdr-$(CONFIG_ARCH_MSM8610) := 0x00008000
dtb-$(CONFIG_ARCH_MSM8610) += msm8610-rumi.dtb
dtb-$(CONFIG_ARCH_MSM8610) += msm8610-sim.dtb
+
+# MSMSAMARIUM
+ zreladdr-$(CONFIG_ARCH_MSMSAMARIUM) := 0x00008000
+ dtb-$(CONFIG_ARCH_MSMSAMARIUM) += msmsamarium-sim.dtb
+ dtb-$(CONFIG_ARCH_MSMSAMARIUM) += msmsamarium-rumi.dtb
diff --git a/arch/arm/mach-msm/acpuclock-8064.c b/arch/arm/mach-msm/acpuclock-8064.c
index a0727b7..8262946 100644
--- a/arch/arm/mach-msm/acpuclock-8064.c
+++ b/arch/arm/mach-msm/acpuclock-8064.c
@@ -131,7 +131,6 @@
[12] = { { 1026000, HFPLL, 1, 0x26 }, 1150000, 1150000, 5 },
[13] = { { 1080000, HFPLL, 1, 0x28 }, 1150000, 1150000, 5 },
[14] = { { 1134000, HFPLL, 1, 0x2A }, 1150000, 1150000, 5 },
- [15] = { { 1188000, HFPLL, 1, 0x2C }, 1150000, 1150000, 5 },
{ }
};
@@ -149,15 +148,15 @@
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 1100000 },
{ 0, { 972000, HFPLL, 1, 0x24 }, L2(5), 1125000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 1125000 },
- { 0, { 1080000, HFPLL, 1, 0x28 }, L2(15), 1175000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1175000 },
- { 0, { 1188000, HFPLL, 1, 0x2C }, L2(15), 1200000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1200000 },
- { 0, { 1296000, HFPLL, 1, 0x30 }, L2(15), 1225000 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1225000 },
- { 0, { 1404000, HFPLL, 1, 0x34 }, L2(15), 1237500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1237500 },
- { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1250000 },
+ { 0, { 1080000, HFPLL, 1, 0x28 }, L2(14), 1175000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 1175000 },
+ { 0, { 1188000, HFPLL, 1, 0x2C }, L2(14), 1200000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 1200000 },
+ { 0, { 1296000, HFPLL, 1, 0x30 }, L2(14), 1225000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1225000 },
+ { 0, { 1404000, HFPLL, 1, 0x34 }, L2(14), 1237500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1237500 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(14), 1250000 },
{ 0, { 0 } }
};
@@ -175,15 +174,15 @@
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 1050000 },
{ 0, { 972000, HFPLL, 1, 0x24 }, L2(5), 1075000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 1075000 },
- { 0, { 1080000, HFPLL, 1, 0x28 }, L2(15), 1125000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1125000 },
- { 0, { 1188000, HFPLL, 1, 0x2C }, L2(15), 1150000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1150000 },
- { 0, { 1296000, HFPLL, 1, 0x30 }, L2(15), 1175000 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1175000 },
- { 0, { 1404000, HFPLL, 1, 0x34 }, L2(15), 1187500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1187500 },
- { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1200000 },
+ { 0, { 1080000, HFPLL, 1, 0x28 }, L2(14), 1125000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 1125000 },
+ { 0, { 1188000, HFPLL, 1, 0x2C }, L2(14), 1150000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 1150000 },
+ { 0, { 1296000, HFPLL, 1, 0x30 }, L2(14), 1175000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1175000 },
+ { 0, { 1404000, HFPLL, 1, 0x34 }, L2(14), 1187500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1187500 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(14), 1200000 },
{ 0, { 0 } }
};
@@ -201,15 +200,15 @@
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 1000000 },
{ 0, { 972000, HFPLL, 1, 0x24 }, L2(5), 1025000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 1025000 },
- { 0, { 1080000, HFPLL, 1, 0x28 }, L2(15), 1075000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1075000 },
- { 0, { 1188000, HFPLL, 1, 0x2C }, L2(15), 1100000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1100000 },
- { 0, { 1296000, HFPLL, 1, 0x30 }, L2(15), 1125000 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1125000 },
- { 0, { 1404000, HFPLL, 1, 0x34 }, L2(15), 1137500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1137500 },
- { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1150000 },
+ { 0, { 1080000, HFPLL, 1, 0x28 }, L2(14), 1075000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 1075000 },
+ { 0, { 1188000, HFPLL, 1, 0x2C }, L2(14), 1100000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 1100000 },
+ { 0, { 1296000, HFPLL, 1, 0x30 }, L2(14), 1125000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1125000 },
+ { 0, { 1404000, HFPLL, 1, 0x34 }, L2(14), 1137500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1137500 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(14), 1150000 },
{ 0, { 0 } }
};
@@ -227,15 +226,15 @@
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 975000 },
{ 0, { 972000, HFPLL, 1, 0x24 }, L2(5), 1000000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 1000000 },
- { 0, { 1080000, HFPLL, 1, 0x28 }, L2(15), 1050000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1050000 },
- { 0, { 1188000, HFPLL, 1, 0x2C }, L2(15), 1075000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1075000 },
- { 0, { 1296000, HFPLL, 1, 0x30 }, L2(15), 1100000 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1100000 },
- { 0, { 1404000, HFPLL, 1, 0x34 }, L2(15), 1112500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1112500 },
- { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1125000 },
+ { 0, { 1080000, HFPLL, 1, 0x28 }, L2(14), 1050000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 1050000 },
+ { 0, { 1188000, HFPLL, 1, 0x2C }, L2(14), 1075000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 1075000 },
+ { 0, { 1296000, HFPLL, 1, 0x30 }, L2(14), 1100000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1100000 },
+ { 0, { 1404000, HFPLL, 1, 0x34 }, L2(14), 1112500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1112500 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(14), 1125000 },
{ 0, { 0 } }
};
@@ -247,11 +246,11 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 1000000 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 1025000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 1037500 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1075000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1087500 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1125000 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1150000 },
- { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1162500 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 1075000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 1087500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1125000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1150000 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(14), 1162500 },
{ 0, { 0 } }
};
@@ -263,11 +262,11 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 975000 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 1000000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 1012500 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1037500 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1050000 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1087500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1112500 },
- { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1125000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 1037500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 1050000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1087500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1112500 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(14), 1125000 },
{ 0, { 0 } }
};
@@ -279,11 +278,11 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 937500 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 950000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 975000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1000000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1012500 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1037500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1075000 },
- { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1087500 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 1000000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 1012500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1037500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1075000 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(14), 1087500 },
{ 0, { 0 } }
};
@@ -295,11 +294,11 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 900000 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 925000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 950000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 975000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 987500 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1000000 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1037500 },
- { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1050000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 975000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 987500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1000000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1037500 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(14), 1050000 },
{ 0, { 0 } }
};
@@ -311,11 +310,11 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 887500 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 900000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 925000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 950000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 962500 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 975000 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1000000 },
- { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1012500 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 950000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 962500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 975000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1000000 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(14), 1012500 },
{ 0, { 0 } }
};
@@ -327,11 +326,11 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 887500 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 900000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 925000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 937500 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 950000 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 962500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 987500 },
- { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1000000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 937500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 950000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 962500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 987500 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(14), 1000000 },
{ 0, { 0 } }
};
@@ -343,11 +342,11 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 887500 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 900000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 925000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 937500 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 950000 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 962500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 975000 },
- { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 987500 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 937500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 950000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 962500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 975000 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(14), 987500 },
{ 0, { 0 } }
};
@@ -359,13 +358,13 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 1000000 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 1025000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 1037500 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1075000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1087500 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1125000 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1150000 },
- { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1175000 },
- { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1225000 },
- { 1, { 1728000, HFPLL, 1, 0x40 }, L2(15), 1250000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 1075000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 1087500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1125000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1150000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(14), 1175000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(14), 1225000 },
+ { 1, { 1728000, HFPLL, 1, 0x40 }, L2(14), 1250000 },
{ 0, { 0 } }
};
@@ -377,13 +376,13 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 975000 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 1000000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 1012500 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1037500 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1050000 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1087500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1112500 },
- { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1150000 },
- { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1187500 },
- { 1, { 1728000, HFPLL, 1, 0x40 }, L2(15), 1200000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 1037500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 1050000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1087500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1112500 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(14), 1150000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(14), 1187500 },
+ { 1, { 1728000, HFPLL, 1, 0x40 }, L2(14), 1200000 },
{ 0, { 0 } }
};
@@ -395,13 +394,13 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 937500 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 950000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 975000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1000000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1012500 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1037500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1075000 },
- { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1100000 },
- { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1137500 },
- { 1, { 1728000, HFPLL, 1, 0x40 }, L2(15), 1162500 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 1000000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 1012500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1037500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1075000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(14), 1100000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(14), 1137500 },
+ { 1, { 1728000, HFPLL, 1, 0x40 }, L2(14), 1162500 },
{ 0, { 0 } }
};
@@ -413,13 +412,13 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 900000 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 925000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 950000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 975000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 987500 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1000000 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1037500 },
- { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1062500 },
- { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1100000 },
- { 1, { 1728000, HFPLL, 1, 0x40 }, L2(15), 1125000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 975000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 987500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1000000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1037500 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(14), 1062500 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(14), 1100000 },
+ { 1, { 1728000, HFPLL, 1, 0x40 }, L2(14), 1125000 },
{ 0, { 0 } }
};
@@ -431,13 +430,13 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 887500 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 900000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 925000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 950000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 962500 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 975000 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1000000 },
- { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1037500 },
- { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1075000 },
- { 1, { 1728000, HFPLL, 1, 0x40 }, L2(15), 1100000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 950000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 962500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 975000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1000000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(14), 1037500 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(14), 1075000 },
+ { 1, { 1728000, HFPLL, 1, 0x40 }, L2(14), 1100000 },
{ 0, { 0 } }
};
@@ -449,13 +448,13 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 887500 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 900000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 925000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 937500 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 950000 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 962500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 987500 },
- { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1012500 },
- { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1050000 },
- { 1, { 1728000, HFPLL, 1, 0x40 }, L2(15), 1075000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 937500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 950000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 962500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 987500 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(14), 1012500 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(14), 1050000 },
+ { 1, { 1728000, HFPLL, 1, 0x40 }, L2(14), 1075000 },
{ 0, { 0 } }
};
@@ -467,13 +466,13 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 887500 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 900000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 925000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 937500 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 950000 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 962500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 975000 },
- { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1000000 },
- { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1025000 },
- { 1, { 1728000, HFPLL, 1, 0x40 }, L2(15), 1050000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 937500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 950000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 962500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 975000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(14), 1000000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(14), 1025000 },
+ { 1, { 1728000, HFPLL, 1, 0x40 }, L2(14), 1050000 },
{ 0, { 0 } }
};
@@ -485,14 +484,14 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 962500 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 975000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 1000000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1025000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1037500 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1062500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1100000 },
- { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1125000 },
- { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1175000 },
- { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1225000 },
- { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1287500 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 1025000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 1037500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1062500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1100000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(14), 1125000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(14), 1175000 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(14), 1225000 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(14), 1287500 },
{ 0, { 0 } }
};
@@ -504,14 +503,14 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 937500 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 950000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 975000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1000000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1012500 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1037500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1075000 },
- { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1100000 },
- { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1137500 },
- { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1187500 },
- { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1250000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 1000000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 1012500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1037500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1075000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(14), 1100000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(14), 1137500 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(14), 1187500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(14), 1250000 },
{ 0, { 0 } }
};
@@ -523,14 +522,14 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 912500 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 925000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 950000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 975000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 987500 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1012500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1050000 },
- { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1075000 },
- { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1112500 },
- { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1162500 },
- { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1212500 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 975000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 987500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1012500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1050000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(14), 1075000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(14), 1112500 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(14), 1162500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(14), 1212500 },
{ 0, { 0 } }
};
@@ -542,14 +541,14 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 900000 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 912500 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 937500 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 962500 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 975000 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1000000 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1025000 },
- { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1050000 },
- { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1087500 },
- { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1137500 },
- { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1175000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 962500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 975000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 1000000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1025000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(14), 1050000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(14), 1087500 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(14), 1137500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(14), 1175000 },
{ 0, { 0 } }
};
@@ -561,14 +560,14 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 887500 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 900000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 925000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 950000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 962500 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 975000 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1000000 },
- { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1037500 },
- { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1075000 },
- { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1112500 },
- { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1150000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 950000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 962500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 975000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 1000000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(14), 1037500 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(14), 1075000 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(14), 1112500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(14), 1150000 },
{ 0, { 0 } }
};
@@ -580,14 +579,14 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 887500 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 900000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 925000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 937500 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 950000 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 962500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 987500 },
- { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1012500 },
- { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1050000 },
- { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1087500 },
- { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1125000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 937500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 950000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 962500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 987500 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(14), 1012500 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(14), 1050000 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(14), 1087500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(14), 1125000 },
{ 0, { 0 } }
};
@@ -599,14 +598,14 @@
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 887500 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 900000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 925000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 937500 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 950000 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 962500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 975000 },
- { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1000000 },
- { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1025000 },
- { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1062500 },
- { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1100000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(14), 937500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(14), 950000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(14), 962500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(14), 975000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(14), 1000000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(14), 1025000 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(14), 1062500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(14), 1100000 },
{ 0, { 0 } }
};
diff --git a/arch/arm/mach-msm/acpuclock-8226.c b/arch/arm/mach-msm/acpuclock-8226.c
index 25bebd1..0028d6e 100644
--- a/arch/arm/mach-msm/acpuclock-8226.c
+++ b/arch/arm/mach-msm/acpuclock-8226.c
@@ -65,33 +65,30 @@
* 3) Depending on Frodo version, may need minimum of LVL_NOM
*/
static struct clkctl_acpu_speed acpu_freq_tbl_8226[] = {
- { 0, 19200, CXO, 0, 0, CPR_CORNER_SVS, 1150000, 0 },
- { 1, 300000, PLL0, 4, 2, CPR_CORNER_SVS, 1150000, 4 },
- { 1, 384000, ACPUPLL, 5, 0, CPR_CORNER_SVS, 1150000, 4 },
- { 1, 600000, PLL0, 4, 0, CPR_CORNER_NORMAL, 1150000, 6 },
- { 1, 787200, ACPUPLL, 5, 0, CPR_CORNER_NORMAL, 1150000, 7 },
- { 0, 998400, ACPUPLL, 5, 0, CPR_CORNER_TURBO, 1150000, 7 },
- { 0, 1190400, ACPUPLL, 5, 0, CPR_CORNER_TURBO, 1150000, 7 },
+ { 1, 300000, PLL0, 4, 2, CPR_CORNER_SVS, 0, 4 },
+ { 1, 384000, ACPUPLL, 5, 2, CPR_CORNER_SVS, 0, 4 },
+ { 1, 600000, PLL0, 4, 0, CPR_CORNER_NORMAL, 0, 6 },
+ { 1, 787200, ACPUPLL, 5, 0, CPR_CORNER_NORMAL, 0, 7 },
+ { 1, 998400, ACPUPLL, 5, 0, CPR_CORNER_TURBO, 0, 7 },
+ { 1, 1094400, ACPUPLL, 5, 0, CPR_CORNER_TURBO, 0, 7 },
+ { 0, 1190400, ACPUPLL, 5, 0, CPR_CORNER_TURBO, 0, 7 },
{ 0 }
};
static struct clkctl_acpu_speed acpu_freq_tbl_8610[] = {
- { 0, 19200, CXO, 0, 0, CPR_CORNER_SVS, 1150000, 0 },
- { 1, 300000, PLL0, 4, 2, CPR_CORNER_SVS, 1150000, 3 },
- { 1, 384000, ACPUPLL, 5, 0, CPR_CORNER_SVS, 1150000, 3 },
- { 1, 600000, PLL0, 4, 0, CPR_CORNER_NORMAL, 1150000, 4 },
- { 1, 787200, ACPUPLL, 5, 0, CPR_CORNER_NORMAL, 1150000, 4 },
- { 0, 998400, ACPUPLL, 5, 0, CPR_CORNER_TURBO, 1275000, 5 },
- { 0, 1190400, ACPUPLL, 5, 0, CPR_CORNER_TURBO, 1275000, 5 },
+ { 1, 300000, PLL0, 4, 2, CPR_CORNER_SVS, 0, 3 },
+ { 1, 384000, ACPUPLL, 5, 2, CPR_CORNER_SVS, 0, 3 },
+ { 1, 600000, PLL0, 4, 0, CPR_CORNER_NORMAL, 0, 4 },
+ { 1, 787200, ACPUPLL, 5, 0, CPR_CORNER_NORMAL, 0, 4 },
+ { 1, 998400, ACPUPLL, 5, 0, CPR_CORNER_TURBO, 0, 5 },
+ { 1, 1190400, ACPUPLL, 5, 0, CPR_CORNER_TURBO, 0, 5 },
{ 0 }
};
static struct acpuclk_drv_data drv_data = {
.freq_tbl = acpu_freq_tbl_8226,
- .current_speed = &(struct clkctl_acpu_speed){ 0 },
.bus_scale = &bus_client_pdata,
.vdd_max_cpu = CPR_CORNER_TURBO,
- .vdd_max_mem = 1150000,
.src_clocks = {
[PLL0].name = "gpll0",
[ACPUPLL].name = "a7sspll",
@@ -130,12 +127,6 @@
return PTR_ERR(drv_data.vdd_cpu);
}
- drv_data.vdd_mem = devm_regulator_get(&pdev->dev, "a7_mem");
- if (IS_ERR(drv_data.vdd_mem)) {
- dev_err(&pdev->dev, "regulator for %s get failed\n", "a7_mem");
- return PTR_ERR(drv_data.vdd_mem);
- }
-
for (i = 0; i < NUM_SRC; i++) {
if (!drv_data.src_clocks[i].name)
continue;
diff --git a/arch/arm/mach-msm/acpuclock-8974.c b/arch/arm/mach-msm/acpuclock-8974.c
index c60e89a..a61f5ca 100644
--- a/arch/arm/mach-msm/acpuclock-8974.c
+++ b/arch/arm/mach-msm/acpuclock-8974.c
@@ -128,122 +128,122 @@
};
static struct acpu_level acpu_freq_tbl_v1_pvs0[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 825000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(3), 825000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(3), 825000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(6), 825000, 3200000 },
- { 1, { 576000, HFPLL, 1, 30 }, L2(6), 825000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(7), 825000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(7), 825000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(10), 835000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(10), 845000, 3200000 },
- { 0, { 960000, HFPLL, 1, 50 }, L2(10), 860000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 880000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(12), 905000, 3200000 },
- { 0, { 1190400, HFPLL, 1, 62 }, L2(12), 920000, 3200000 },
- { 0, { 1267200, HFPLL, 1, 66 }, L2(12), 940000, 3200000 },
- { 1, { 1344000, HFPLL, 1, 70 }, L2(12), 960000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(16), 980000, 3200000 },
- { 0, { 1497600, HFPLL, 1, 78 }, L2(16), 995000, 3200000 },
- { 0, { 1574400, HFPLL, 1, 82 }, L2(16), 1015000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(16), 1030000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(16), 1050000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 825000, 73 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(3), 825000, 85 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(3), 825000, 104 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(6), 825000, 124 },
+ { 1, { 576000, HFPLL, 1, 30 }, L2(6), 825000, 144 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(7), 825000, 165 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(7), 825000, 186 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(10), 835000, 208 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(10), 845000, 229 },
+ { 0, { 960000, HFPLL, 1, 50 }, L2(10), 860000, 252 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 880000, 275 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(12), 905000, 298 },
+ { 0, { 1190400, HFPLL, 1, 62 }, L2(12), 920000, 321 },
+ { 0, { 1267200, HFPLL, 1, 66 }, L2(12), 940000, 346 },
+ { 1, { 1344000, HFPLL, 1, 70 }, L2(12), 960000, 371 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(16), 980000, 397 },
+ { 0, { 1497600, HFPLL, 1, 78 }, L2(16), 995000, 423 },
+ { 0, { 1574400, HFPLL, 1, 82 }, L2(16), 1015000, 450 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(16), 1030000, 477 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(16), 1050000, 506 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_v1_pvs1[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 825000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(3), 825000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(3), 825000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(6), 825000, 3200000 },
- { 1, { 576000, HFPLL, 1, 30 }, L2(6), 825000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(7), 825000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(7), 825000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(10), 835000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(10), 845000, 3200000 },
- { 0, { 960000, HFPLL, 1, 50 }, L2(10), 860000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 880000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(12), 905000, 3200000 },
- { 0, { 1190400, HFPLL, 1, 62 }, L2(12), 920000, 3200000 },
- { 0, { 1267200, HFPLL, 1, 66 }, L2(12), 940000, 3200000 },
- { 1, { 1344000, HFPLL, 1, 70 }, L2(12), 960000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(16), 980000, 3200000 },
- { 0, { 1497600, HFPLL, 1, 78 }, L2(16), 995000, 3200000 },
- { 0, { 1574400, HFPLL, 1, 82 }, L2(16), 1015000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(16), 1030000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(16), 1050000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 825000, 73 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(3), 825000, 85 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(3), 825000, 104 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(6), 825000, 124 },
+ { 1, { 576000, HFPLL, 1, 30 }, L2(6), 825000, 144 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(7), 825000, 165 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(7), 825000, 186 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(10), 835000, 208 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(10), 845000, 229 },
+ { 0, { 960000, HFPLL, 1, 50 }, L2(10), 860000, 252 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 880000, 275 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(12), 905000, 298 },
+ { 0, { 1190400, HFPLL, 1, 62 }, L2(12), 920000, 321 },
+ { 0, { 1267200, HFPLL, 1, 66 }, L2(12), 940000, 346 },
+ { 1, { 1344000, HFPLL, 1, 70 }, L2(12), 960000, 371 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(16), 980000, 397 },
+ { 0, { 1497600, HFPLL, 1, 78 }, L2(16), 995000, 423 },
+ { 0, { 1574400, HFPLL, 1, 82 }, L2(16), 1015000, 450 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(16), 1030000, 477 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(16), 1050000, 506 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_v1_pvs2[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 825000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(3), 825000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(3), 825000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(6), 825000, 3200000 },
- { 1, { 576000, HFPLL, 1, 30 }, L2(6), 825000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(7), 825000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(7), 825000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(10), 825000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(10), 825000, 3200000 },
- { 0, { 960000, HFPLL, 1, 50 }, L2(10), 835000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 855000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(12), 875000, 3200000 },
- { 0, { 1190400, HFPLL, 1, 62 }, L2(12), 895000, 3200000 },
- { 0, { 1267200, HFPLL, 1, 66 }, L2(12), 915000, 3200000 },
- { 1, { 1344000, HFPLL, 1, 70 }, L2(12), 930000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(16), 945000, 3200000 },
- { 0, { 1497600, HFPLL, 1, 78 }, L2(16), 960000, 3200000 },
- { 0, { 1574400, HFPLL, 1, 82 }, L2(16), 975000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(16), 990000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(16), 1000000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 825000, 73 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(3), 825000, 85 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(3), 825000, 104 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(6), 825000, 124 },
+ { 1, { 576000, HFPLL, 1, 30 }, L2(6), 825000, 144 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(7), 825000, 165 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(7), 825000, 186 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(10), 825000, 208 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(10), 825000, 229 },
+ { 0, { 960000, HFPLL, 1, 50 }, L2(10), 835000, 252 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 855000, 275 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(12), 875000, 298 },
+ { 0, { 1190400, HFPLL, 1, 62 }, L2(12), 895000, 321 },
+ { 0, { 1267200, HFPLL, 1, 66 }, L2(12), 915000, 346 },
+ { 1, { 1344000, HFPLL, 1, 70 }, L2(12), 930000, 371 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(16), 945000, 397 },
+ { 0, { 1497600, HFPLL, 1, 78 }, L2(16), 960000, 423 },
+ { 0, { 1574400, HFPLL, 1, 82 }, L2(16), 975000, 450 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(16), 990000, 477 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(16), 1000000, 506 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_v1_pvs3[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 825000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(3), 825000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(3), 825000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(6), 825000, 3200000 },
- { 1, { 576000, HFPLL, 1, 30 }, L2(6), 825000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(7), 825000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(7), 825000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(10), 825000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(10), 825000, 3200000 },
- { 0, { 960000, HFPLL, 1, 50 }, L2(10), 835000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 855000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(12), 875000, 3200000 },
- { 0, { 1190400, HFPLL, 1, 62 }, L2(12), 895000, 3200000 },
- { 0, { 1267200, HFPLL, 1, 66 }, L2(12), 915000, 3200000 },
- { 1, { 1344000, HFPLL, 1, 70 }, L2(12), 930000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(16), 945000, 3200000 },
- { 0, { 1497600, HFPLL, 1, 78 }, L2(16), 960000, 3200000 },
- { 0, { 1574400, HFPLL, 1, 82 }, L2(16), 975000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(16), 990000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(16), 1000000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 825000, 73 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(3), 825000, 85 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(3), 825000, 104 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(6), 825000, 124 },
+ { 1, { 576000, HFPLL, 1, 30 }, L2(6), 825000, 144 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(7), 825000, 165 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(7), 825000, 186 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(10), 825000, 208 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(10), 825000, 229 },
+ { 0, { 960000, HFPLL, 1, 50 }, L2(10), 835000, 252 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 855000, 275 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(12), 875000, 298 },
+ { 0, { 1190400, HFPLL, 1, 62 }, L2(12), 895000, 321 },
+ { 0, { 1267200, HFPLL, 1, 66 }, L2(12), 915000, 346 },
+ { 1, { 1344000, HFPLL, 1, 70 }, L2(12), 930000, 371 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(16), 945000, 397 },
+ { 0, { 1497600, HFPLL, 1, 78 }, L2(16), 960000, 423 },
+ { 0, { 1574400, HFPLL, 1, 82 }, L2(16), 975000, 450 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(16), 990000, 477 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(16), 1000000, 506 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_v1_pvs4[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 825000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(3), 825000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(3), 825000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(6), 825000, 3200000 },
- { 1, { 576000, HFPLL, 1, 30 }, L2(6), 825000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(7), 825000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(7), 825000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(10), 825000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(10), 825000, 3200000 },
- { 0, { 960000, HFPLL, 1, 50 }, L2(10), 825000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 825000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(12), 835000, 3200000 },
- { 0, { 1190400, HFPLL, 1, 62 }, L2(12), 855000, 3200000 },
- { 0, { 1267200, HFPLL, 1, 66 }, L2(12), 870000, 3200000 },
- { 1, { 1344000, HFPLL, 1, 70 }, L2(12), 885000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(16), 900000, 3200000 },
- { 0, { 1497600, HFPLL, 1, 78 }, L2(16), 910000, 3200000 },
- { 0, { 1574400, HFPLL, 1, 82 }, L2(16), 925000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(16), 940000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(16), 950000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 825000, 73 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(3), 825000, 85 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(3), 825000, 104 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(6), 825000, 124 },
+ { 1, { 576000, HFPLL, 1, 30 }, L2(6), 825000, 144 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(7), 825000, 165 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(7), 825000, 186 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(10), 825000, 208 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(10), 825000, 229 },
+ { 0, { 960000, HFPLL, 1, 50 }, L2(10), 825000, 252 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 825000, 275 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(12), 835000, 298 },
+ { 0, { 1190400, HFPLL, 1, 62 }, L2(12), 855000, 321 },
+ { 0, { 1267200, HFPLL, 1, 66 }, L2(12), 870000, 346 },
+ { 1, { 1344000, HFPLL, 1, 70 }, L2(12), 885000, 371 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(16), 900000, 397 },
+ { 0, { 1497600, HFPLL, 1, 78 }, L2(16), 910000, 423 },
+ { 0, { 1574400, HFPLL, 1, 82 }, L2(16), 925000, 450 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(16), 940000, 477 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(16), 950000, 506 },
{ 0, { 0 } }
};
@@ -284,618 +284,618 @@
};
static struct acpu_level acpu_freq_tbl_2g_pvs0[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 815000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 825000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 835000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 845000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 855000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 865000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 875000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 890000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 900000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 915000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 925000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 940000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 950000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 965000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 980000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 995000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 1010000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 1025000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 1040000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 1055000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 1070000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1085000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1100000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 815000, 73 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 825000, 85 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 835000, 104 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 845000, 124 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 855000, 144 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 865000, 165 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 875000, 186 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 890000, 208 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 900000, 229 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 915000, 252 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 925000, 275 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 940000, 298 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 950000, 321 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 965000, 346 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 980000, 371 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 995000, 397 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 1010000, 423 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 1025000, 450 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 1040000, 477 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 1055000, 506 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 1070000, 536 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1085000, 567 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1100000, 598 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2g_pvs1[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 800000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 810000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 820000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 830000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 840000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 850000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 860000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 875000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 885000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 895000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 910000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 920000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 930000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 945000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 960000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 975000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 990000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 1005000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 1020000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 1030000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 1045000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1060000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1075000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 800000, 73 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 810000, 85 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 820000, 104 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 830000, 124 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 840000, 144 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 850000, 165 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 860000, 186 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 875000, 208 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 885000, 229 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 895000, 252 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 910000, 275 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 920000, 298 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 930000, 321 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 945000, 346 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 960000, 371 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 975000, 397 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 990000, 423 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 1005000, 450 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 1020000, 477 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 1030000, 506 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 1045000, 536 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1060000, 567 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1075000, 598 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2g_pvs2[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 785000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 795000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 805000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 815000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 825000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 835000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 845000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 855000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 865000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 875000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 890000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 900000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 910000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 925000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 940000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 955000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 970000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 980000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 995000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 1005000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 1020000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1035000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1050000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 785000, 73 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 795000, 85 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 805000, 104 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 815000, 124 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 825000, 144 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 835000, 165 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 845000, 186 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 855000, 208 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 865000, 229 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 875000, 252 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 890000, 275 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 900000, 298 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 910000, 321 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 925000, 346 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 940000, 371 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 955000, 397 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 970000, 423 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 980000, 450 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 995000, 477 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 1005000, 506 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 1020000, 536 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1035000, 567 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1050000, 598 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2g_pvs3[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 780000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 790000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 800000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 810000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 820000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 830000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 840000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 850000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 860000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 875000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 885000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 895000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 910000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 925000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 935000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 950000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 960000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 970000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 985000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 995000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1010000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1025000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 73 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 780000, 85 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 790000, 104 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 800000, 124 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 810000, 144 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 820000, 165 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 830000, 186 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 840000, 208 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 850000, 229 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 860000, 252 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 875000, 275 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 885000, 298 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 895000, 321 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 910000, 346 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 925000, 371 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 935000, 397 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 950000, 423 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 960000, 450 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 970000, 477 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 985000, 506 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 995000, 536 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1010000, 567 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1025000, 598 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2g_pvs4[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 775000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 780000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 790000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 800000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 810000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 820000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 830000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 840000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 850000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 860000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 870000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 880000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 895000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 910000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 920000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 930000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 940000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 950000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 960000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 975000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 985000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1000000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 73 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 775000, 85 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 780000, 104 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 790000, 124 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 800000, 144 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 810000, 165 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 820000, 186 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 830000, 208 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 840000, 229 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 850000, 252 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 860000, 275 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 870000, 298 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 880000, 321 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 895000, 346 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 910000, 371 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 920000, 397 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 930000, 423 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 940000, 450 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 950000, 477 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 960000, 506 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 975000, 536 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 985000, 567 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1000000, 598 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2g_pvs5[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 750000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 760000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 770000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 780000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 790000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 800000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 810000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 820000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 830000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 840000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 850000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 860000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 870000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 880000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 890000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 900000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 910000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 920000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 930000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 940000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 955000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 965000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 975000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 750000, 73 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 760000, 85 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 770000, 104 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 780000, 124 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 790000, 144 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 800000, 165 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 810000, 186 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 820000, 208 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 830000, 229 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 840000, 252 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 850000, 275 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 860000, 298 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 870000, 321 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 880000, 346 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 890000, 371 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 900000, 397 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 910000, 423 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 920000, 450 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 930000, 477 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 940000, 506 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 955000, 536 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 965000, 567 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 975000, 598 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2g_pvs6[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 750000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 750000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 760000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 770000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 780000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 790000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 800000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 810000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 820000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 830000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 840000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 850000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 860000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 870000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 875000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 885000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 895000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 905000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 915000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 920000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 930000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 940000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 950000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 750000, 73 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 750000, 85 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 760000, 104 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 770000, 124 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 780000, 144 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 790000, 165 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 800000, 186 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 810000, 208 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 820000, 229 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 830000, 252 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 840000, 275 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 850000, 298 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 860000, 321 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 870000, 346 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 875000, 371 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 885000, 397 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 895000, 423 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 905000, 450 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 915000, 477 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 920000, 506 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 930000, 536 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 940000, 567 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 950000, 598 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2p2g_pvs0[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 800000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 800000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 805000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 815000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 825000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 835000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 845000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 855000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 865000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 875000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 890000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 900000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 915000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 925000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 940000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 950000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 965000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 980000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 995000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 1010000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 1025000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1040000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1055000, 3200000 },
- { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 1070000, 3200000 },
- { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 1085000, 3200000 },
- { 1, { 2150400, HFPLL, 1, 112 }, L2(19), 1100000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 800000, 72 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 800000, 83 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 805000, 102 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 815000, 121 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 825000, 141 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 835000, 161 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 845000, 181 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 855000, 202 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 865000, 223 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 875000, 245 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 890000, 267 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 900000, 289 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 915000, 313 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 925000, 336 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 940000, 360 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 950000, 383 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 965000, 409 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 980000, 435 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 995000, 461 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 1010000, 488 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 1025000, 516 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1040000, 543 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1055000, 573 },
+ { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 1070000, 604 },
+ { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 1085000, 636 },
+ { 1, { 2150400, HFPLL, 1, 112 }, L2(19), 1100000, 656 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2p2g_pvs1[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 800000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 800000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 800000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 800000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 810000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 820000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 830000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 840000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 850000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 860000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 875000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 885000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 895000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 910000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 920000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 930000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 945000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 960000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 975000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 990000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 1005000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1020000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1030000, 3200000 },
- { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 1045000, 3200000 },
- { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 1060000, 3200000 },
- { 1, { 2150400, HFPLL, 1, 112 }, L2(19), 1075000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 800000, 72 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 800000, 83 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 800000, 102 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 800000, 121 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 810000, 141 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 820000, 161 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 830000, 181 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 840000, 202 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 850000, 223 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 860000, 245 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 875000, 267 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 885000, 289 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 895000, 313 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 910000, 336 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 920000, 360 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 930000, 383 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 945000, 409 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 960000, 435 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 975000, 461 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 990000, 488 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 1005000, 516 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1020000, 543 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1030000, 573 },
+ { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 1045000, 604 },
+ { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 1060000, 636 },
+ { 1, { 2150400, HFPLL, 1, 112 }, L2(19), 1075000, 656 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2p2g_pvs2[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 775000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 775000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 785000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 795000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 805000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 815000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 825000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 835000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 845000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 855000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 865000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 875000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 890000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 900000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 910000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 925000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 940000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 955000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 970000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 980000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 995000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1005000, 3200000 },
- { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 1020000, 3200000 },
- { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 1035000, 3200000 },
- { 1, { 2150400, HFPLL, 1, 112 }, L2(19), 1050000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 72 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 775000, 83 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 775000, 102 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 785000, 121 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 795000, 141 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 805000, 161 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 815000, 181 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 825000, 202 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 835000, 223 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 845000, 245 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 855000, 267 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 865000, 289 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 875000, 313 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 890000, 336 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 900000, 360 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 910000, 383 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 925000, 409 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 940000, 435 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 955000, 461 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 970000, 488 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 980000, 516 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 995000, 543 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1005000, 573 },
+ { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 1020000, 604 },
+ { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 1035000, 636 },
+ { 1, { 2150400, HFPLL, 1, 112 }, L2(19), 1050000, 656 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2p2g_pvs3[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 775000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 775000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 775000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 780000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 790000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 800000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 810000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 820000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 830000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 840000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 850000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 860000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 875000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 885000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 895000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 910000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 925000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 935000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 950000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 960000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 970000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 985000, 3200000 },
- { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 995000, 3200000 },
- { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 1010000, 3200000 },
- { 1, { 2150400, HFPLL, 1, 112 }, L2(19), 1025000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 72 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 775000, 83 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 775000, 102 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 775000, 121 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 780000, 141 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 790000, 161 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 800000, 181 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 810000, 202 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 820000, 223 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 830000, 245 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 840000, 267 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 850000, 289 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 860000, 313 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 875000, 336 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 885000, 360 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 895000, 383 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 910000, 409 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 925000, 435 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 935000, 461 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 950000, 488 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 960000, 516 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 970000, 543 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 985000, 573 },
+ { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 995000, 604 },
+ { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 1010000, 636 },
+ { 1, { 2150400, HFPLL, 1, 112 }, L2(19), 1025000, 656 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2p2g_pvs4[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 775000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 775000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 775000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 775000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 780000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 790000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 800000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 810000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 820000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 830000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 840000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 850000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 860000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 870000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 880000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 895000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 910000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 920000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 930000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 940000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 950000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 960000, 3200000 },
- { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 975000, 3200000 },
- { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 985000, 3200000 },
- { 1, { 2150400, HFPLL, 1, 112 }, L2(19), 1000000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 72 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 775000, 83 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 775000, 102 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 775000, 121 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 775000, 141 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 780000, 161 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 790000, 181 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 800000, 202 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 810000, 223 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 820000, 245 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 830000, 267 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 840000, 289 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 850000, 313 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 860000, 336 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 870000, 360 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 880000, 383 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 895000, 409 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 910000, 435 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 920000, 461 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 930000, 488 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 940000, 516 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 950000, 543 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 960000, 573 },
+ { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 975000, 604 },
+ { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 985000, 636 },
+ { 1, { 2150400, HFPLL, 1, 112 }, L2(19), 1000000, 656 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2p2g_pvs5[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 750000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 750000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 750000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 750000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 760000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 770000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 780000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 790000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 800000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 810000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 820000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 830000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 840000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 850000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 860000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 870000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 880000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 890000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 900000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 910000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 920000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 930000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 940000, 3200000 },
- { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 955000, 3200000 },
- { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 965000, 3200000 },
- { 1, { 2150400, HFPLL, 1, 112 }, L2(19), 975000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 750000, 72 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 750000, 83 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 750000, 102 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 750000, 121 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 760000, 141 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 770000, 161 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 780000, 181 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 790000, 202 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 800000, 223 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 810000, 245 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 820000, 267 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 830000, 289 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 840000, 313 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 850000, 336 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 860000, 360 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 870000, 383 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 880000, 409 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 890000, 435 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 900000, 461 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 910000, 488 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 920000, 516 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 930000, 543 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 940000, 573 },
+ { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 955000, 604 },
+ { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 965000, 636 },
+ { 1, { 2150400, HFPLL, 1, 112 }, L2(19), 975000, 656 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2p2g_pvs6[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 750000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 750000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 750000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 750000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 750000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 760000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 770000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 780000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 790000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 800000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 810000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 820000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 830000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 840000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 850000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 860000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 870000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 875000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 885000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 895000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 905000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 915000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 920000, 3200000 },
- { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 930000, 3200000 },
- { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 940000, 3200000 },
- { 1, { 2150400, HFPLL, 1, 112 }, L2(19), 950000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 750000, 72 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 750000, 83 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 750000, 102 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 750000, 121 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 750000, 141 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 760000, 161 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 770000, 181 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 780000, 202 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 790000, 223 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 800000, 245 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 810000, 267 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 820000, 289 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 830000, 313 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 840000, 336 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 850000, 360 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 860000, 383 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 870000, 409 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 875000, 435 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 885000, 461 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 895000, 488 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 905000, 516 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 915000, 543 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 920000, 573 },
+ { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 930000, 604 },
+ { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 940000, 636 },
+ { 1, { 2150400, HFPLL, 1, 112 }, L2(19), 950000, 656 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2p3g_pvs0[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 800000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 800000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 800000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 805000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 815000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 825000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 835000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 845000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 855000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 865000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 875000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 890000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 900000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 915000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 925000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 940000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 950000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 965000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 980000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 995000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 1010000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1025000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1040000, 3200000 },
- { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 1055000, 3200000 },
- { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 1070000, 3200000 },
- { 0, { 2188800, HFPLL, 1, 114 }, L2(19), 1085000, 3200000 },
- { 1, { 2265600, HFPLL, 1, 118 }, L2(19), 1100000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 800000, 72 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 800000, 83 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 800000, 101 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 805000, 120 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 815000, 139 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 825000, 159 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 835000, 180 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 845000, 200 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 855000, 221 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 865000, 242 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 875000, 264 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 890000, 287 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 900000, 308 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 915000, 333 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 925000, 356 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 940000, 380 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 950000, 404 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 965000, 430 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 980000, 456 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 995000, 482 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 1010000, 510 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1025000, 538 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1040000, 565 },
+ { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 1055000, 596 },
+ { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 1070000, 627 },
+ { 0, { 2188800, HFPLL, 1, 114 }, L2(19), 1085000, 659 },
+ { 1, { 2265600, HFPLL, 1, 118 }, L2(19), 1100000, 691 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2p3g_pvs1[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 800000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 800000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 800000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 800000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 800000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 810000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 820000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 830000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 840000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 850000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 860000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 875000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 885000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 895000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 910000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 920000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 930000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 945000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 960000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 975000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 990000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1005000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1020000, 3200000 },
- { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 1030000, 3200000 },
- { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 1045000, 3200000 },
- { 0, { 2188800, HFPLL, 1, 114 }, L2(19), 1060000, 3200000 },
- { 1, { 2265600, HFPLL, 1, 118 }, L2(19), 1075000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 800000, 72 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 800000, 83 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 800000, 101 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 800000, 120 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 800000, 139 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 810000, 159 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 820000, 180 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 830000, 200 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 840000, 221 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 850000, 242 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 860000, 264 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 875000, 287 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 885000, 308 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 895000, 333 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 910000, 356 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 920000, 380 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 930000, 404 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 945000, 430 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 960000, 456 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 975000, 482 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 990000, 510 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 1005000, 538 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 1020000, 565 },
+ { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 1030000, 596 },
+ { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 1045000, 627 },
+ { 0, { 2188800, HFPLL, 1, 114 }, L2(19), 1060000, 659 },
+ { 1, { 2265600, HFPLL, 1, 118 }, L2(19), 1075000, 691 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2p3g_pvs2[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 775000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 775000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 775000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 785000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 795000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 805000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 815000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 825000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 835000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 845000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 855000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 865000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 875000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 890000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 900000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 910000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 925000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 940000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 955000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 970000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 980000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 995000, 3200000 },
- { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 1005000, 3200000 },
- { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 1020000, 3200000 },
- { 0, { 2188800, HFPLL, 1, 114 }, L2(19), 1035000, 3200000 },
- { 1, { 2265600, HFPLL, 1, 118 }, L2(19), 1050000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 72 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 775000, 83 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 775000, 101 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 775000, 120 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 785000, 139 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 795000, 159 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 805000, 180 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 815000, 200 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 825000, 221 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 835000, 242 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 845000, 264 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 855000, 287 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 865000, 308 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 875000, 333 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 890000, 356 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 900000, 380 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 910000, 404 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 925000, 430 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 940000, 456 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 955000, 482 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 970000, 510 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 980000, 538 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 995000, 565 },
+ { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 1005000, 596 },
+ { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 1020000, 627 },
+ { 0, { 2188800, HFPLL, 1, 114 }, L2(19), 1035000, 659 },
+ { 1, { 2265600, HFPLL, 1, 118 }, L2(19), 1050000, 691 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2p3g_pvs3[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 775000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 775000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 775000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 775000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 780000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 790000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 800000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 810000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 820000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 830000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 840000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 850000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 860000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 875000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 885000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 895000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 910000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 925000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 935000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 950000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 960000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 970000, 3200000 },
- { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 985000, 3200000 },
- { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 995000, 3200000 },
- { 0, { 2188800, HFPLL, 1, 114 }, L2(19), 1010000, 3200000 },
- { 1, { 2265600, HFPLL, 1, 118 }, L2(19), 1025000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 72 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 775000, 83 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 775000, 101 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 775000, 120 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 775000, 139 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 780000, 159 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 790000, 180 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 800000, 200 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 810000, 221 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 820000, 242 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 830000, 264 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 840000, 287 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 850000, 308 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 860000, 333 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 875000, 356 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 885000, 380 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 895000, 404 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 910000, 430 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 925000, 456 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 935000, 482 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 950000, 510 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 960000, 538 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 970000, 565 },
+ { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 985000, 596 },
+ { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 995000, 627 },
+ { 0, { 2188800, HFPLL, 1, 114 }, L2(19), 1010000, 659 },
+ { 1, { 2265600, HFPLL, 1, 118 }, L2(19), 1025000, 691 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2p3g_pvs4[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 775000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 775000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 775000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 775000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 775000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 780000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 790000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 800000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 810000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 820000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 830000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 840000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 850000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 860000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 870000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 880000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 895000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 910000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 920000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 930000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 940000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 950000, 3200000 },
- { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 960000, 3200000 },
- { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 975000, 3200000 },
- { 0, { 2188800, HFPLL, 1, 114 }, L2(19), 985000, 3200000 },
- { 1, { 2265600, HFPLL, 1, 118 }, L2(19), 1000000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 775000, 72 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 775000, 83 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 775000, 101 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 775000, 120 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 775000, 139 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 775000, 159 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 780000, 180 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 790000, 200 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 800000, 221 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 810000, 242 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 820000, 264 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 830000, 287 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 840000, 308 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 850000, 333 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 860000, 356 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 870000, 380 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 880000, 404 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 895000, 430 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 910000, 456 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 920000, 482 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 930000, 510 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 940000, 538 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 950000, 565 },
+ { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 960000, 596 },
+ { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 975000, 627 },
+ { 0, { 2188800, HFPLL, 1, 114 }, L2(19), 985000, 659 },
+ { 1, { 2265600, HFPLL, 1, 118 }, L2(19), 1000000, 691 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2p3g_pvs5[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 750000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 750000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 750000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 750000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 750000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 760000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 770000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 780000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 790000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 800000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 810000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 820000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 830000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 840000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 850000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 860000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 870000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 880000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 890000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 900000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 910000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 920000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 930000, 3200000 },
- { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 940000, 3200000 },
- { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 955000, 3200000 },
- { 0, { 2188800, HFPLL, 1, 114 }, L2(19), 965000, 3200000 },
- { 1, { 2265600, HFPLL, 1, 118 }, L2(19), 975000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 750000, 72 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 750000, 83 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 750000, 101 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 750000, 120 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 750000, 139 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 760000, 159 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 770000, 180 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 780000, 200 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 790000, 221 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 800000, 242 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 810000, 264 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 820000, 287 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 830000, 308 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 840000, 333 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 850000, 356 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 860000, 380 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 870000, 404 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 880000, 430 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 890000, 456 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 900000, 482 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 910000, 510 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 920000, 538 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 930000, 565 },
+ { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 940000, 596 },
+ { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 955000, 627 },
+ { 0, { 2188800, HFPLL, 1, 114 }, L2(19), 965000, 659 },
+ { 1, { 2265600, HFPLL, 1, 118 }, L2(19), 975000, 691 },
{ 0, { 0 } }
};
static struct acpu_level acpu_freq_tbl_2p3g_pvs6[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 750000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(1), 750000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(2), 750000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(2), 750000, 3200000 },
- { 0, { 576000, HFPLL, 1, 30 }, L2(3), 750000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(3), 750000, 3200000 },
- { 1, { 729600, HFPLL, 1, 38 }, L2(4), 760000, 3200000 },
- { 0, { 806400, HFPLL, 1, 42 }, L2(4), 770000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(4), 780000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(9), 790000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 800000, 3200000 },
- { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 810000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 820000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 830000, 3200000 },
- { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 840000, 3200000 },
- { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 850000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 860000, 3200000 },
- { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 870000, 3200000 },
- { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 875000, 3200000 },
- { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 885000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 895000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 905000, 3200000 },
- { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 915000, 3200000 },
- { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 920000, 3200000 },
- { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 930000, 3200000 },
- { 0, { 2188800, HFPLL, 1, 114 }, L2(19), 940000, 3200000 },
- { 1, { 2265600, HFPLL, 1, 118 }, L2(19), 950000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 750000, 72 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(1), 750000, 83 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(2), 750000, 101 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(2), 750000, 120 },
+ { 0, { 576000, HFPLL, 1, 30 }, L2(3), 750000, 139 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(3), 750000, 159 },
+ { 1, { 729600, HFPLL, 1, 38 }, L2(4), 760000, 180 },
+ { 0, { 806400, HFPLL, 1, 42 }, L2(4), 770000, 200 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(4), 780000, 221 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(9), 790000, 242 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(10), 800000, 264 },
+ { 0, { 1113600, HFPLL, 1, 58 }, L2(10), 810000, 287 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(10), 820000, 308 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(13), 830000, 333 },
+ { 0, { 1344000, HFPLL, 1, 70 }, L2(14), 840000, 356 },
+ { 0, { 1420800, HFPLL, 1, 74 }, L2(15), 850000, 380 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 860000, 404 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(17), 870000, 430 },
+ { 0, { 1651200, HFPLL, 1, 86 }, L2(17), 875000, 456 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(18), 885000, 482 },
+ { 0, { 1804800, HFPLL, 1, 94 }, L2(18), 895000, 510 },
+ { 0, { 1881600, HFPLL, 1, 98 }, L2(18), 905000, 538 },
+ { 1, { 1958400, HFPLL, 1, 102 }, L2(19), 915000, 565 },
+ { 0, { 2035200, HFPLL, 1, 106 }, L2(19), 920000, 596 },
+ { 0, { 2112000, HFPLL, 1, 110 }, L2(19), 930000, 627 },
+ { 0, { 2188800, HFPLL, 1, 114 }, L2(19), 940000, 659 },
+ { 1, { 2265600, HFPLL, 1, 118 }, L2(19), 950000, 691 },
{ 0, { 0 } }
};
diff --git a/arch/arm/mach-msm/acpuclock-9625.c b/arch/arm/mach-msm/acpuclock-9625.c
index 42659f9..a4a5b2a 100644
--- a/arch/arm/mach-msm/acpuclock-9625.c
+++ b/arch/arm/mach-msm/acpuclock-9625.c
@@ -65,7 +65,6 @@
static struct acpuclk_drv_data drv_data = {
.freq_tbl = acpu_freq_tbl,
- .current_speed = &(struct clkctl_acpu_speed){ 0 },
.bus_scale = &bus_client_pdata,
.vdd_max_cpu = LVL_HIGH,
.vdd_max_mem = 1050000,
diff --git a/arch/arm/mach-msm/acpuclock-cortex.c b/arch/arm/mach-msm/acpuclock-cortex.c
index 74ca145..2c3f97b 100644
--- a/arch/arm/mach-msm/acpuclock-cortex.c
+++ b/arch/arm/mach-msm/acpuclock-cortex.c
@@ -66,11 +66,17 @@
{
int rc = 0;
- /* Increase vdd_mem before vdd_cpu. vdd_mem should be >= vdd_cpu. */
- rc = regulator_set_voltage(priv->vdd_mem, vdd_mem, priv->vdd_max_mem);
- if (rc) {
- pr_err("vdd_mem increase failed (%d)\n", rc);
- return rc;
+ if (priv->vdd_mem) {
+ /*
+ * Increase vdd_mem before vdd_cpu. vdd_mem should
+ * be >= vdd_cpu.
+ */
+ rc = regulator_set_voltage(priv->vdd_mem, vdd_mem,
+ priv->vdd_max_mem);
+ if (rc) {
+ pr_err("vdd_mem increase failed (%d)\n", rc);
+ return rc;
+ }
}
rc = regulator_set_voltage(priv->vdd_cpu, vdd_cpu, priv->vdd_max_cpu);
@@ -92,6 +98,9 @@
return;
}
+ if (!priv->vdd_mem)
+ return;
+
/* Decrease vdd_mem after vdd_cpu. vdd_mem should be >= vdd_cpu. */
ret = regulator_set_voltage(priv->vdd_mem, vdd_mem, priv->vdd_max_mem);
if (ret)
@@ -129,15 +138,73 @@
pr_warn("acpu rcg didn't update its configuration\n");
}
-/*
- * This function can be called in both atomic and nonatomic context.
- * Since regulator APIS can sleep, we cannot always use the clk prepare
- * unprepare API.
- */
-static int set_speed(struct clkctl_acpu_speed *tgt_s, bool atomic)
+static struct clkctl_acpu_speed *__init find_cur_cpu_level(void)
+{
+ struct clkctl_acpu_speed *f, *max = priv->freq_tbl;
+ void __iomem *apcs_rcg_config = priv->apcs_rcg_config;
+ struct acpuclk_reg_data *r = &priv->reg_data;
+ u32 regval, div, src;
+ unsigned long rate;
+ struct clk *parent;
+
+ regval = readl_relaxed(apcs_rcg_config);
+ src = regval & r->cfg_src_mask;
+ src >>= r->cfg_src_shift;
+
+ div = regval & r->cfg_div_mask;
+ div >>= r->cfg_div_shift;
+ /* No support for half-integer dividers */
+ div = div > 1 ? (div + 1) / 2 : 0;
+
+ for (f = priv->freq_tbl; f->khz; f++) {
+ if (f->use_for_scaling)
+ max = f;
+
+ if (f->src_sel != src || f->src_div != div)
+ continue;
+
+ parent = priv->src_clocks[f->src].clk;
+ rate = parent->rate / (div ? div : 1);
+ if (f->khz * 1000 == rate)
+ break;
+ }
+
+ if (f->khz)
+ return f;
+
+ pr_err("CPUs are running at an unknown rate. Defaulting to %u KHz.\n",
+ max->khz);
+
+ /* Change to a safe frequency */
+ select_clk_source_div(priv, priv->freq_tbl);
+ /* Default to largest frequency */
+ return max;
+}
+
+static int set_speed_atomic(struct clkctl_acpu_speed *tgt_s)
+{
+ struct clkctl_acpu_speed *strt_s = priv->current_speed;
+ struct clk *strt = priv->src_clocks[strt_s->src].clk;
+ struct clk *tgt = priv->src_clocks[tgt_s->src].clk;
+ int rc = 0;
+
+ WARN(strt_s->src == ACPUPLL && tgt_s->src == ACPUPLL,
+ "can't reprogram ACPUPLL during atomic context\n");
+ rc = clk_enable(tgt);
+ if (rc)
+ return rc;
+
+ select_clk_source_div(priv, tgt_s);
+ clk_disable(strt);
+
+ return rc;
+}
+
+static int set_speed(struct clkctl_acpu_speed *tgt_s)
{
int rc = 0;
- unsigned int tgt_freq_hz = tgt_s->khz * 1000;
+ unsigned int div = tgt_s->src_div ? tgt_s->src_div : 1;
+ unsigned int tgt_freq_hz = tgt_s->khz * 1000 * div;
struct clkctl_acpu_speed *strt_s = priv->current_speed;
struct clkctl_acpu_speed *cxo_s = &priv->freq_tbl[0];
struct clk *strt = priv->src_clocks[strt_s->src].clk;
@@ -148,19 +215,13 @@
select_clk_source_div(priv, cxo_s);
/* Re-program acpu pll */
- if (atomic)
- clk_disable(tgt);
- else
- clk_disable_unprepare(tgt);
+ clk_disable_unprepare(tgt);
rc = clk_set_rate(tgt, tgt_freq_hz);
if (rc)
pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
- if (atomic)
- BUG_ON(clk_enable(tgt));
- else
- BUG_ON(clk_prepare_enable(tgt));
+ BUG_ON(clk_prepare_enable(tgt));
/* Switch back to acpu pll */
select_clk_source_div(priv, tgt_s);
@@ -172,10 +233,7 @@
return rc;
}
- if (atomic)
- rc = clk_enable(tgt);
- else
- rc = clk_prepare_enable(tgt);
+ rc = clk_prepare_enable(tgt);
if (rc) {
pr_err("ACPU PLL enable failed\n");
@@ -184,16 +242,10 @@
select_clk_source_div(priv, tgt_s);
- if (atomic)
- clk_disable(strt);
- else
- clk_disable_unprepare(strt);
+ clk_disable_unprepare(strt);
} else {
- if (atomic)
- rc = clk_enable(tgt);
- else
- rc = clk_prepare_enable(tgt);
+ rc = clk_prepare_enable(tgt);
if (rc) {
pr_err("%s enable failed\n",
@@ -203,10 +255,7 @@
select_clk_source_div(priv, tgt_s);
- if (atomic)
- clk_disable(strt);
- else
- clk_disable_unprepare(strt);
+ clk_disable_unprepare(strt);
}
@@ -225,7 +274,7 @@
strt_s = priv->current_speed;
/* Return early if rate didn't change */
- if (rate == strt_s->khz)
+ if (rate == strt_s->khz && reason != SETRATE_INIT)
goto out;
/* Find target frequency */
@@ -238,7 +287,7 @@
}
/* Increase VDD levels if needed */
- if ((reason == SETRATE_CPUFREQ || reason == SETRATE_INIT)
+ if ((reason == SETRATE_CPUFREQ)
&& (tgt_s->khz > strt_s->khz)) {
rc = increase_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
if (rc)
@@ -250,9 +299,9 @@
/* Switch CPU speed. Flag indicates atomic context */
if (reason == SETRATE_CPUFREQ || reason == SETRATE_INIT)
- rc = set_speed(tgt_s, false);
+ rc = set_speed(tgt_s);
else
- rc = set_speed(tgt_s, true);
+ rc = set_speed_atomic(tgt_s);
if (rc)
goto out;
@@ -268,7 +317,7 @@
set_bus_bw(tgt_s->bw_level);
/* Drop VDD levels if we can. */
- if (tgt_s->khz < strt_s->khz)
+ if (tgt_s->khz < strt_s->khz || reason == SETRATE_INIT)
decrease_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
out:
@@ -322,8 +371,8 @@
int __init acpuclk_cortex_init(struct platform_device *pdev,
struct acpuclk_drv_data *data)
{
- unsigned long max_cpu_khz = 0;
- int i, rc;
+ int rc;
+ int parent;
priv = data;
mutex_init(&priv->lock);
@@ -337,43 +386,41 @@
BUG();
}
- /* Improve boot time by ramping up CPU immediately */
- for (i = 0; priv->freq_tbl[i].khz != 0; i++)
- if (priv->freq_tbl[i].use_for_scaling)
- max_cpu_khz = priv->freq_tbl[i].khz;
-
/* Initialize regulators */
rc = increase_vdd(priv->vdd_max_cpu, priv->vdd_max_mem);
if (rc)
- goto err_vdd;
+ return rc;
- rc = regulator_enable(priv->vdd_mem);
- if (rc) {
- dev_err(&pdev->dev, "regulator_enable for mem failed\n");
- goto err_vdd;
+ if (priv->vdd_mem) {
+ rc = regulator_enable(priv->vdd_mem);
+ if (rc) {
+ dev_err(&pdev->dev, "regulator_enable for mem failed\n");
+ return rc;
+ }
}
rc = regulator_enable(priv->vdd_cpu);
if (rc) {
dev_err(&pdev->dev, "regulator_enable for cpu failed\n");
- goto err_vdd_cpu;
+ return rc;
}
- /*
- * Select a state which is always a valid transition to align SW with
- * the HW configuration set by the bootloaders.
- */
- acpuclk_cortex_set_rate(0, acpuclk_cortex_data.power_collapse_khz,
- SETRATE_INIT);
- acpuclk_cortex_set_rate(0, max_cpu_khz, SETRATE_INIT);
+ priv->current_speed = find_cur_cpu_level();
+ parent = priv->current_speed->src;
+ rc = clk_prepare_enable(priv->src_clocks[parent].clk);
+ if (rc) {
+ dev_err(&pdev->dev, "handoff: prepare_enable failed\n");
+ return rc;
+ }
+
+ rc = acpuclk_cortex_set_rate(0, priv->current_speed->khz, SETRATE_INIT);
+ if (rc) {
+ dev_err(&pdev->dev, "handoff: set rate failed\n");
+ return rc;
+ }
acpuclk_register(&acpuclk_cortex_data);
cpufreq_table_init();
return 0;
-
-err_vdd_cpu:
- regulator_disable(priv->vdd_mem);
-err_vdd:
- return rc;
}
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index bed794b..b5a7552 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -1576,8 +1576,14 @@
static int ssrestart_check(void)
{
- DMUX_LOG_KERR("%s: modem timeout: BAM DMUX disabled\n", __func__);
+ int ret = 0;
+
+ DMUX_LOG_KERR("%s: modem timeout: BAM DMUX disabled for SSR\n",
+ __func__);
in_global_reset = 1;
+ ret = subsystem_restart("modem");
+ if (ret == -ENODEV)
+ panic("modem subsystem restart failed\n");
return 1;
}
diff --git a/arch/arm/mach-msm/bms-batterydata-qrd-4v2-1300mah.c b/arch/arm/mach-msm/bms-batterydata-qrd-4v2-1300mah.c
new file mode 100644
index 0000000..a2c6391
--- /dev/null
+++ b/arch/arm/mach-msm/bms-batterydata-qrd-4v2-1300mah.c
@@ -0,0 +1,112 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mfd/pm8xxx/batterydata-lib.h>
+
+static struct single_row_lut fcc_temp = {
+ .x = {-20, 0, 25, 40, 60},
+ .y = {1343, 1353, 1408, 1345, 1342},
+ .cols = 5
+};
+
+static struct single_row_lut fcc_sf = {
+ .x = {0},
+ .y = {100},
+ .cols = 1
+};
+
+static struct sf_lut rbatt_sf = {
+ .rows = 29,
+ .cols = 5,
+ .row_entries = {-20, 0, 25, 40, 60},
+ .percent = {100, 95, 90, 85, 80, 75, 70, 65, 60, 55, 50, 45, 40, 35,
+ 30, 25, 20, 15, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
+ .sf = {
+ {604, 192, 100, 79, 71},
+ {605, 192, 100, 79, 71},
+ {641, 205, 103, 81, 72},
+ {641, 221, 108, 84, 75},
+ {622, 238, 115, 87, 77},
+ {612, 254, 123, 92, 79},
+ {605, 252, 137, 96, 83},
+ {607, 219, 154, 104, 87},
+ {613, 202, 135, 109, 89},
+ {626, 200, 106, 90, 77},
+ {656, 201, 101, 82, 75},
+ {684, 204, 100, 84, 77},
+ {710, 211, 100, 85, 79},
+ {747, 224, 106, 89, 82},
+ {806, 241, 116, 90, 80},
+ {905, 260, 119, 87, 77},
+ {1046, 291, 113, 87, 77},
+ {1309, 329, 116, 90, 79},
+ {1476, 300, 126, 97, 83},
+ {1598, 311, 127, 98, 84},
+ {1771, 323, 130, 99, 85},
+ {1984, 342, 136, 101, 86},
+ {2438, 368, 140, 101, 86},
+ {3381, 388, 137, 100, 84},
+ {4913, 414, 141, 99, 86},
+ {6979, 468, 155, 104, 90},
+ {9968, 565, 192, 113, 98},
+ {16163, 833, 350, 140, 120},
+ {36511, 6483, 4872, 472, 1095}
+ }
+};
+
+static struct pc_temp_ocv_lut pc_temp_ocv = {
+ .rows = 29,
+ .cols = 5,
+ .temp = {-20, 0, 25, 40, 60},
+ .percent = {100, 95, 90, 85, 80, 75, 70, 65, 60, 55, 50, 45, 40, 35,
+ 30, 25, 20, 15, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
+ .ocv = {
+ {4177, 4174, 4199, 4167, 4162},
+ {4107, 4112, 4141, 4109, 4106},
+ {4058, 4064, 4091, 4061, 4059},
+ {3996, 4015, 4044, 4017, 4015},
+ {3947, 3975, 4001, 3978, 3976},
+ {3909, 3939, 3962, 3943, 3940},
+ {3874, 3901, 3926, 3911, 3907},
+ {3845, 3858, 3892, 3882, 3878},
+ {3821, 3826, 3851, 3849, 3846},
+ {3801, 3804, 3815, 3810, 3808},
+ {3788, 3789, 3793, 3789, 3787},
+ {3778, 3780, 3778, 3776, 3773},
+ {3769, 3776, 3770, 3767, 3764},
+ {3757, 3772, 3766, 3762, 3757},
+ {3740, 3765, 3762, 3754, 3744},
+ {3714, 3747, 3750, 3739, 3724},
+ {3668, 3706, 3717, 3710, 3697},
+ {3602, 3644, 3662, 3662, 3654},
+ {3533, 3571, 3601, 3607, 3605},
+ {3518, 3557, 3583, 3592, 3590},
+ {3500, 3543, 3565, 3576, 3574},
+ {3478, 3528, 3546, 3559, 3557},
+ {3451, 3506, 3521, 3538, 3534},
+ {3417, 3473, 3481, 3505, 3496},
+ {3377, 3423, 3424, 3454, 3444},
+ {3327, 3361, 3351, 3391, 3380},
+ {3261, 3279, 3258, 3310, 3297},
+ {3165, 3165, 3138, 3198, 3182},
+ {3000, 3000, 3000, 3000, 3000}
+ }
+};
+
+struct bms_battery_data qrd_4v2_1300mah_data = {
+ .fcc = 1300,
+ .fcc_temp_lut = &fcc_temp,
+ .fcc_sf_lut = &fcc_sf,
+ .pc_temp_ocv_lut = &pc_temp_ocv,
+ .rbatt_sf_lut = &rbatt_sf,
+ .default_rbatt_mohm = 172
+};
diff --git a/arch/arm/mach-msm/bms-batterydata-qrd-4v35-2000mah.c b/arch/arm/mach-msm/bms-batterydata-qrd-4v35-2000mah.c
new file mode 100644
index 0000000..8adf8ca
--- /dev/null
+++ b/arch/arm/mach-msm/bms-batterydata-qrd-4v35-2000mah.c
@@ -0,0 +1,117 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/mfd/pm8xxx/batterydata-lib.h>
+
+static struct single_row_lut fcc_temp = {
+ .x = {-20, 0, 25, 40, 60},
+ .y = {2096, 2124, 2121, 2118, 2103},
+ .cols = 5
+};
+
+static struct single_row_lut fcc_sf = {
+ .x = {0},
+ .y = {100},
+ .cols = 1
+};
+
+static struct sf_lut rbatt_sf = {
+ .rows = 30,
+ .cols = 5,
+ .row_entries = {-20, 0, 25, 40, 60},
+ .percent = {100, 95, 90, 85, 80, 75, 70, 65, 60,
+ 55, 50, 45, 40, 35, 30, 25, 20, 16, 13,
+ 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
+ .sf = {
+ {2422, 324, 100, 79, 72},
+ {2417, 325, 100, 79, 71},
+ {2344, 327, 100, 80, 72},
+ {2416, 336, 102, 81, 73},
+ {2072, 354, 107, 82, 73},
+ {1961, 372, 113, 84, 75},
+ {1929, 341, 118, 87, 77},
+ {1929, 321, 130, 93, 80},
+ {2041, 306, 140, 104, 85},
+ {2202, 292, 119, 96, 83},
+ {2374, 290, 98, 80, 73},
+ {2550, 292, 98, 79, 72},
+ {2727, 294, 99, 81, 73},
+ {2904, 303, 100, 82, 75},
+ {3091, 323, 100, 81, 73},
+ {3278, 348, 100, 80, 73},
+ {3470, 376, 99, 79, 72},
+ {3627, 386, 100, 79, 72},
+ {3672, 398, 100, 80, 71},
+ {3812, 424, 100, 80, 73},
+ {3895, 443, 101, 80, 73},
+ {3985, 465, 102, 82, 75},
+ {4094, 497, 105, 83, 76},
+ {4211, 533, 109, 85, 79},
+ {4335, 579, 113, 87, 80},
+ {4505, 612, 113, 85, 76},
+ {4693, 643, 113, 86, 77},
+ {4930, 712, 120, 90, 81},
+ {5283, 835, 145, 111, 107},
+ {10293, 15765, 5566, 6904, 2547},
+ }
+};
+
+static struct pc_temp_ocv_lut pc_temp_ocv = {
+ .rows = 31,
+ .cols = 5,
+ .temp = {-20, 0, 25, 40, 60},
+ .percent = {100, 95, 90, 85, 80, 75, 70, 65, 60,
+ 55, 50, 45, 40, 35, 30, 25, 20, 16, 13,
+ 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
+ .ocv = {
+ {4340, 4340, 4335, 4330, 4323},
+ {4217, 4260, 4265, 4263, 4258},
+ {4135, 4203, 4207, 4205, 4201},
+ {4084, 4150, 4152, 4150, 4146},
+ {3992, 4101, 4101, 4097, 4093},
+ {3934, 4049, 4051, 4046, 4044},
+ {3889, 3974, 3995, 3998, 3999},
+ {3852, 3926, 3958, 3961, 3959},
+ {3832, 3892, 3921, 3923, 3921},
+ {3819, 3859, 3874, 3877, 3877},
+ {3807, 3831, 3838, 3838, 3838},
+ {3796, 3809, 3815, 3815, 3814},
+ {3784, 3792, 3797, 3797, 3796},
+ {3770, 3780, 3783, 3782, 3781},
+ {3754, 3770, 3772, 3769, 3764},
+ {3737, 3758, 3763, 3754, 3742},
+ {3717, 3737, 3744, 3735, 3720},
+ {3700, 3713, 3718, 3710, 3696},
+ {3687, 3701, 3692, 3683, 3671},
+ {3674, 3695, 3689, 3681, 3669},
+ {3667, 3692, 3688, 3680, 3669},
+ {3659, 3690, 3687, 3680, 3668},
+ {3649, 3687, 3685, 3678, 3667},
+ {3636, 3683, 3683, 3676, 3664},
+ {3618, 3674, 3679, 3671, 3658},
+ {3596, 3652, 3663, 3652, 3632},
+ {3566, 3611, 3620, 3606, 3584},
+ {3522, 3547, 3555, 3540, 3517},
+ {3460, 3449, 3461, 3446, 3424},
+ {3356, 3282, 3312, 3299, 3273},
+ {3000, 3000, 3000, 3000, 3000}
+ }
+};
+
+
+struct bms_battery_data QRD_4v35_2000mAh_data = {
+ .fcc = 2000,
+ .fcc_temp_lut = &fcc_temp,
+ .fcc_sf_lut = &fcc_sf,
+ .pc_temp_ocv_lut = &pc_temp_ocv,
+ .rbatt_sf_lut = &rbatt_sf,
+ .default_rbatt_mohm = 172
+};
diff --git a/arch/arm/mach-msm/board-8064-gpiomux.c b/arch/arm/mach-msm/board-8064-gpiomux.c
index 0f88287..ede53bc 100644
--- a/arch/arm/mach-msm/board-8064-gpiomux.c
+++ b/arch/arm/mach-msm/board-8064-gpiomux.c
@@ -968,7 +968,6 @@
.pull = GPIOMUX_PULL_DOWN,
};
-
static struct gpiomux_setting ap2mdm_soft_reset_cfg = {
.func = GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_4MA,
diff --git a/arch/arm/mach-msm/board-8064-gpu.c b/arch/arm/mach-msm/board-8064-gpu.c
index 6b15883..bb0a6cc 100644
--- a/arch/arm/mach-msm/board-8064-gpu.c
+++ b/arch/arm/mach-msm/board-8064-gpu.c
@@ -255,7 +255,6 @@
.num_levels = 5,
.set_grp_async = NULL,
.idle_timeout = HZ/10,
- .nap_allowed = true,
.strtstp_sleepwake = true,
.clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c
index 5ab4a53..b2dcd7a 100644
--- a/arch/arm/mach-msm/board-8064-pmic.c
+++ b/arch/arm/mach-msm/board-8064-pmic.c
@@ -478,6 +478,10 @@
.high_ocv_correction_limit_uv = 50,
.low_ocv_correction_limit_uv = 100,
.hold_soc_est = 3,
+ .enable_fcc_learning = 1,
+ .min_fcc_learning_soc = 20,
+ .min_fcc_ocv_pc = 30,
+ .max_fcc_learning_samples = 5,
};
static struct pm8921_platform_data
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index f969e31..f5a9070 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -85,11 +85,11 @@
#include "pm.h"
#include "pm-boot.h"
#include "devices-msm8x60.h"
-#include "smd_private.h"
#include "platsmp.h"
#define MHL_GPIO_INT 30
#define MHL_GPIO_RESET 35
+#include "sysmon.h"
#define MSM_PMEM_ADSP_SIZE 0x7800000
#define MSM_PMEM_AUDIO_SIZE 0x4CF000
@@ -1902,6 +1902,8 @@
.peripheral_platform_device = &apq8064_device_hsic_host,
.ramdump_timeout_ms = 120000,
.mdm2ap_status_gpio_run_cfg = &mdm2ap_status_gpio_run_cfg,
+ .sysmon_subsys_id_valid = 1,
+ .sysmon_subsys_id = SYSMON_SS_EXT_MODEM,
};
static struct tsens_platform_data apq_tsens_pdata = {
diff --git a/arch/arm/mach-msm/board-8084-gpiomux.c b/arch/arm/mach-msm/board-8084-gpiomux.c
new file mode 100644
index 0000000..27f2e0d
--- /dev/null
+++ b/arch/arm/mach-msm/board-8084-gpiomux.c
@@ -0,0 +1,52 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <mach/board.h>
+#include <mach/gpiomux.h>
+
+static struct gpiomux_setting gpio_i2c_config = {
+ .func = GPIOMUX_FUNC_3,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct msm_gpiomux_config msm_blsp_configs[] __initdata = {
+ {
+ .gpio = 10, /* BLSP1 QUP3 I2C_SDA */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
+ {
+ .gpio = 11, /* BLSP1 QUP3 I2C_SCL */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
+};
+
+void __init apq8084_init_gpiomux(void)
+{
+ int rc;
+
+ rc = msm_gpiomux_init_dt();
+ if (rc) {
+ pr_err("%s failed %d\n", __func__, rc);
+ return;
+ }
+
+ msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
+}
diff --git a/arch/arm/mach-msm/board-8084.c b/arch/arm/mach-msm/board-8084.c
new file mode 100644
index 0000000..67c05ba
--- /dev/null
+++ b/arch/arm/mach-msm/board-8084.c
@@ -0,0 +1,129 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/memory.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
+#include <mach/board.h>
+#include <mach/gpiomux.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_memtypes.h>
+#include <mach/msm_smd.h>
+#include <mach/restart.h>
+#include <mach/socinfo.h>
+#include <mach/clk-provider.h>
+#include "board-dt.h"
+#include "clock.h"
+#include "devices.h"
+#include "platsmp.h"
+#include "modem_notifier.h"
+
+static struct memtype_reserve apq8084_reserve_table[] __initdata = {
+ [MEMTYPE_SMI] = {
+ },
+ [MEMTYPE_EBI0] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+ [MEMTYPE_EBI1] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+};
+
+static int apq8084_paddr_to_memtype(phys_addr_t paddr)
+{
+ return MEMTYPE_EBI1;
+}
+
+static struct reserve_info apq8084_reserve_info __initdata = {
+ .memtype_reserve_table = apq8084_reserve_table,
+ .paddr_to_memtype = apq8084_paddr_to_memtype,
+};
+
+static struct of_dev_auxdata apq8084_auxdata_lookup[] __initdata = {
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9824000, \
+ "msm_sdcc.1", NULL),
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
+ "msm_sdcc.2", NULL),
+ {}
+};
+
+void __init apq8084_reserve(void)
+{
+ reserve_info = &apq8084_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_reserve, apq8084_reserve_table);
+ msm_reserve();
+}
+
+static void __init apq8084_early_memory(void)
+{
+ reserve_info = &apq8084_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_hole, apq8084_reserve_table);
+}
+
+/*
+ * Used to satisfy dependencies for devices that need to be
+ * run early or in a particular order. Most likely your device doesn't fall
+ * into this category, and thus the driver should not be added here. The
+ * EPROBE_DEFER can satisfy most dependency problems.
+ */
+void __init apq8084_add_drivers(void)
+{
+ msm_init_modem_notifier_list();
+ msm_smd_init();
+ msm_clock_init(&msm8084_clock_init_data);
+}
+
+static void __init apq8084_map_io(void)
+{
+ msm_map_8084_io();
+}
+
+void __init apq8084_init(void)
+{
+ struct of_dev_auxdata *adata = apq8084_auxdata_lookup;
+
+ if (socinfo_init() < 0)
+ pr_err("%s: socinfo_init() failed\n", __func__);
+
+ apq8084_init_gpiomux();
+ board_dt_populate(adata);
+ apq8084_add_drivers();
+}
+
+void __init apq8084_init_very_early(void)
+{
+ apq8084_early_memory();
+}
+
+static const char *apq8084_dt_match[] __initconst = {
+ "qcom,apq8084",
+ NULL
+};
+
+DT_MACHINE_START(APQ8084_DT, "Qualcomm APQ 8084 (Flattened Device Tree)")
+ .map_io = apq8084_map_io,
+ .init_irq = msm_dt_init_irq,
+ .init_machine = apq8084_init,
+ .handle_irq = gic_handle_irq,
+ .timer = &msm_dt_timer,
+ .dt_compat = apq8084_dt_match,
+ .reserve = apq8084_reserve,
+ .init_very_early = apq8084_init_very_early,
+ .restart = msm_restart,
+ .smp = &msm8974_smp_ops,
+MACHINE_END
diff --git a/arch/arm/mach-msm/board-8092.c b/arch/arm/mach-msm/board-8092.c
index b4c63f9..56826c7 100644
--- a/arch/arm/mach-msm/board-8092.c
+++ b/arch/arm/mach-msm/board-8092.c
@@ -29,21 +29,12 @@
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
+#include <mach/clk-provider.h>
#include "board-dt.h"
#include "clock.h"
#include "platsmp.h"
-static struct clk_lookup msm_clocks_dummy[] = {
- CLK_DUMMY("core_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
- CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
-};
-
-struct clock_init_data mpq8092_clock_init_data __initdata = {
- .table = msm_clocks_dummy,
- .size = ARRAY_SIZE(msm_clocks_dummy),
-};
-
static struct memtype_reserve mpq8092_reserve_table[] __initdata = {
[MEMTYPE_SMI] = {
},
@@ -84,8 +75,8 @@
static struct of_dev_auxdata mpq8092_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF991F000, \
"msm_serial_hsl.0", NULL),
- OF_DEV_AUXDATA("qcom,spmi-pmic-arb", 0xFC4C0000, \
- "spmi-pmic-arb.0", NULL),
+ OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF9922000, \
+ "msm_serial_hsl.1", NULL),
OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9824000, \
"msm_sdcc.1", NULL),
OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
@@ -102,11 +93,13 @@
mpq8092_init_gpiomux();
msm_clock_init(&mpq8092_clock_init_data);
- of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
+ board_dt_populate(adata);
}
static const char *mpq8092_dt_match[] __initconst = {
"qcom,mpq8092-sim",
+ "qcom,mpq8092-rumi",
+ "qcom,mpq8092",
NULL
};
diff --git a/arch/arm/mach-msm/board-8226-gpiomux.c b/arch/arm/mach-msm/board-8226-gpiomux.c
index 819ca56..378edc8 100644
--- a/arch/arm/mach-msm/board-8226-gpiomux.c
+++ b/arch/arm/mach-msm/board-8226-gpiomux.c
@@ -16,6 +16,7 @@
#include <mach/board.h>
#include <mach/gpio.h>
#include <mach/gpiomux.h>
+#include <mach/socinfo.h>
#define KS8851_IRQ_GPIO 115
@@ -397,6 +398,64 @@
};
+static struct gpiomux_setting auxpcm_act_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting auxpcm_sus_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct msm_gpiomux_config msm_auxpcm_configs[] __initdata = {
+ {
+ .gpio = 63,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &auxpcm_sus_cfg,
+ [GPIOMUX_ACTIVE] = &auxpcm_act_cfg,
+ },
+ },
+ {
+ .gpio = 64,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &auxpcm_sus_cfg,
+ [GPIOMUX_ACTIVE] = &auxpcm_act_cfg,
+ },
+ },
+ {
+ .gpio = 65,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &auxpcm_sus_cfg,
+ [GPIOMUX_ACTIVE] = &auxpcm_act_cfg,
+ },
+ },
+ {
+ .gpio = 66,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &auxpcm_sus_cfg,
+ [GPIOMUX_ACTIVE] = &auxpcm_act_cfg,
+ },
+ },
+};
+
+static struct gpiomux_setting usb_otg_sw_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .dir = GPIOMUX_OUT_LOW,
+};
+
+static struct msm_gpiomux_config usb_otg_sw_configs[] __initdata = {
+ {
+ .gpio = 67,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &usb_otg_sw_cfg,
+ },
+ },
+};
+
void __init msm8226_init_gpiomux(void)
{
int rc;
@@ -423,4 +482,10 @@
msm_gpiomux_install_nowrite(msm_lcd_configs,
ARRAY_SIZE(msm_lcd_configs));
msm_gpiomux_install(msm_sensor_configs, ARRAY_SIZE(msm_sensor_configs));
+ msm_gpiomux_install(msm_auxpcm_configs,
+ ARRAY_SIZE(msm_auxpcm_configs));
+
+ if (of_board_is_cdp() || of_board_is_mtp() || of_board_is_xpm())
+ msm_gpiomux_install(usb_otg_sw_configs,
+ ARRAY_SIZE(usb_otg_sw_configs));
}
diff --git a/arch/arm/mach-msm/board-8226.c b/arch/arm/mach-msm/board-8226.c
index a892e32..39efcd8 100644
--- a/arch/arm/mach-msm/board-8226.c
+++ b/arch/arm/mach-msm/board-8226.c
@@ -49,6 +49,7 @@
#include "clock.h"
#include "platsmp.h"
#include "spm.h"
+#include "pm.h"
#include "lpm_resources.h"
#include "modem_notifier.h"
@@ -111,6 +112,7 @@
msm_rpm_driver_init();
msm_lpmrs_module_init();
msm_spm_device_init();
+ msm_pm_sleep_status_init();
rpm_regulator_smd_driver_init();
qpnp_regulator_init();
if (of_board_is_rumi())
@@ -129,12 +131,14 @@
pr_err("%s: socinfo_init() failed\n", __func__);
msm8226_init_gpiomux();
- of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
+ board_dt_populate(adata);
msm8226_add_drivers();
}
static const char *msm8226_dt_match[] __initconst = {
"qcom,msm8226",
+ "qcom,msm8926",
+ "qcom,apq8026",
NULL
};
diff --git a/arch/arm/mach-msm/board-8610-gpiomux.c b/arch/arm/mach-msm/board-8610-gpiomux.c
index 4b435de..b261ce4 100644
--- a/arch/arm/mach-msm/board-8610-gpiomux.c
+++ b/arch/arm/mach-msm/board-8610-gpiomux.c
@@ -17,20 +17,44 @@
#include <mach/gpio.h>
#include <mach/gpiomux.h>
-static struct gpiomux_setting gpio_i2c_config = {
- .func = GPIOMUX_FUNC_3,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_NONE,
-};
-
static struct gpiomux_setting gpio_spi_config = {
.func = GPIOMUX_FUNC_1,
.drv = GPIOMUX_DRV_6MA,
.pull = GPIOMUX_PULL_NONE,
};
-static struct gpiomux_setting gpio_spi_cs_config = {
+static struct gpiomux_setting gpio_i2c_config = {
+ .func = GPIOMUX_FUNC_3,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting gpio_cam_i2c_config = {
.func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting atmel_int_act_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting atmel_int_sus_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting atmel_reset_act_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_6MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting atmel_reset_sus_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_6MA,
.pull = GPIOMUX_PULL_DOWN,
};
@@ -60,6 +84,32 @@
.pull = GPIOMUX_PULL_DOWN,
};
+static struct gpiomux_setting lcd_te_act_config = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ .dir = GPIOMUX_IN,
+};
+
+static struct gpiomux_setting lcd_te_sus_config = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ .dir = GPIOMUX_IN,
+};
+
+static struct gpiomux_setting gpio_keys_active = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting gpio_keys_suspend = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
static struct msm_gpiomux_config msm_lcd_configs[] __initdata = {
{
.gpio = 41,
@@ -75,10 +125,29 @@
[GPIOMUX_SUSPENDED] = &lcd_en_sus_cfg,
},
},
+ {
+ .gpio = 12,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &lcd_te_act_config,
+ [GPIOMUX_SUSPENDED] = &lcd_te_sus_config,
+ },
+ },
};
static struct msm_gpiomux_config msm_blsp_configs[] __initdata = {
{
+ .gpio = 2, /* BLSP1 QUP1 I2C_SDA */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
+ {
+ .gpio = 3, /* BLSP1 QUP1 I2C_SCL */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
+ {
.gpio = 10, /* BLSP1 QUP3 I2C_SDA */
.settings = {
[GPIOMUX_SUSPENDED] = &gpio_i2c_config,
@@ -91,27 +160,56 @@
},
},
{
- .gpio = 0, /* BLSP1 QUP1 SPI_DATA_MOSI */
+ .gpio = 16, /* BLSP1 QUP6 I2C_SDA */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_cam_i2c_config,
+ },
+ },
+ {
+ .gpio = 17, /* BLSP1 QUP6 I2C_SCL */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_cam_i2c_config,
+ },
+ },
+};
+
+static struct msm_gpiomux_config msm_atmel_configs[] __initdata = {
+ {
+ .gpio = 0, /* TOUCH RESET */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &atmel_reset_act_cfg,
+ [GPIOMUX_SUSPENDED] = &atmel_reset_sus_cfg,
+ },
+ },
+ {
+ .gpio = 1, /* TOUCH INT */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &atmel_int_act_cfg,
+ [GPIOMUX_SUSPENDED] = &atmel_int_sus_cfg,
+ },
+ },
+ {
+ .gpio = 86, /* BLSP1 QUP4 SPI_DATA_MOSI */
.settings = {
[GPIOMUX_SUSPENDED] = &gpio_spi_config,
},
},
{
- .gpio = 1, /* BLSP1 QUP1 SPI_DATA_MISO */
+ .gpio = 87, /* BLSP1 QUP4 SPI_DATA_MISO */
.settings = {
[GPIOMUX_SUSPENDED] = &gpio_spi_config,
},
},
{
- .gpio = 3, /* BLSP1 QUP1 SPI_CLK */
+ .gpio = 89, /* BLSP1 QUP4 SPI_CLK */
.settings = {
[GPIOMUX_SUSPENDED] = &gpio_spi_config,
},
},
{
- .gpio = 2, /* BLSP1 QUP1 SPI_CS1 */
+ .gpio = 88, /* BLSP1 QUP4 SPI_CS */
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_spi_cs_config,
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
},
},
};
@@ -154,6 +252,173 @@
},
};
+static struct gpiomux_setting gpio_suspend_config[] = {
+ {
+ .func = GPIOMUX_FUNC_GPIO, /* IN-NP */
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ },
+ {
+ .func = GPIOMUX_FUNC_GPIO, /* O-LOW */
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_LOW,
+ },
+};
+
+static struct gpiomux_setting cam_settings[] = {
+ {
+ .func = GPIOMUX_FUNC_1, /*active 1*/ /* 0 */
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ },
+
+ {
+ .func = GPIOMUX_FUNC_1, /*suspend*/ /* 1 */
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+
+ {
+ .func = GPIOMUX_FUNC_1, /*i2c suspend*/ /* 2 */
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_KEEPER,
+ },
+
+ {
+ .func = GPIOMUX_FUNC_GPIO, /*active 0*/ /* 3 */
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ },
+
+ {
+ .func = GPIOMUX_FUNC_GPIO, /*suspend 0*/ /* 4 */
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+};
+
+static struct msm_gpiomux_config msm_sensor_configs[] __initdata = {
+ {
+ .gpio = 13, /* CAM_MCLK0 */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &cam_settings[1],
+ },
+ },
+ {
+ .gpio = 14, /* CAM_MCLK1 */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &cam_settings[1],
+ },
+ },
+ {
+ .gpio = 16, /* CCI_I2C_SDA */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[0],
+ },
+ },
+ {
+ .gpio = 17, /* CCI_I2C_SCL */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[0],
+ },
+ },
+ {
+ .gpio = 18, /* FLASH_LED_EN */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
+ },
+ },
+ {
+ .gpio = 19, /* FLASH_LED_NOW */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[0],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
+ },
+ },
+ {
+ .gpio = 8, /* CAM1_STANDBY_N */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[3],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
+ },
+ },
+ {
+ .gpio = 15, /* CAM1_RST_N */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[3],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
+ },
+ },
+ {
+ .gpio = 20, /* WEBCAM1_STANDBY */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[3],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
+ },
+ },
+ {
+ .gpio = 21, /* WEBCAM2_RESET_N */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &cam_settings[3],
+ [GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
+ },
+ },
+};
+
+static struct msm_gpiomux_config msm_keypad_configs[] __initdata = {
+ {
+ .gpio = 72,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &gpio_keys_active,
+ [GPIOMUX_SUSPENDED] = &gpio_keys_suspend,
+ },
+ },
+ {
+ .gpio = 73,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &gpio_keys_active,
+ [GPIOMUX_SUSPENDED] = &gpio_keys_suspend,
+ },
+ },
+ {
+ .gpio = 74,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &gpio_keys_active,
+ [GPIOMUX_SUSPENDED] = &gpio_keys_suspend,
+ },
+ },
+};
+
+static struct gpiomux_setting sd_card_det_active_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_IN,
+};
+
+static struct gpiomux_setting sd_card_det_suspend_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+ .dir = GPIOMUX_IN,
+};
+
+static struct msm_gpiomux_config sd_card_det[] __initdata = {
+ {
+ .gpio = 42,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sd_card_det_active_config,
+ [GPIOMUX_SUSPENDED] = &sd_card_det_suspend_config,
+ },
+ },
+};
+
void __init msm8610_init_gpiomux(void)
{
int rc;
@@ -165,7 +430,13 @@
}
msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
+ msm_gpiomux_install(msm_atmel_configs,
+ ARRAY_SIZE(msm_atmel_configs));
msm_gpiomux_install(wcnss_5wire_interface,
ARRAY_SIZE(wcnss_5wire_interface));
msm_gpiomux_install(msm_lcd_configs, ARRAY_SIZE(msm_lcd_configs));
+ msm_gpiomux_install(msm_keypad_configs,
+ ARRAY_SIZE(msm_keypad_configs));
+ msm_gpiomux_install(sd_card_det, ARRAY_SIZE(sd_card_det));
+ msm_gpiomux_install(msm_sensor_configs, ARRAY_SIZE(msm_sensor_configs));
}
diff --git a/arch/arm/mach-msm/board-8610.c b/arch/arm/mach-msm/board-8610.c
index 2cd7134..b4d77f1 100644
--- a/arch/arm/mach-msm/board-8610.c
+++ b/arch/arm/mach-msm/board-8610.c
@@ -50,6 +50,7 @@
#include "clock.h"
#include "platsmp.h"
#include "spm.h"
+#include "pm.h"
#include "lpm_resources.h"
#include "modem_notifier.h"
@@ -106,6 +107,7 @@
msm_rpm_driver_init();
msm_lpmrs_module_init();
msm_spm_device_init();
+ msm_pm_sleep_status_init();
rpm_regulator_smd_driver_init();
qpnp_regulator_init();
tsens_tm_init_driver();
@@ -125,7 +127,7 @@
pr_err("%s: socinfo_init() failed\n", __func__);
msm8610_init_gpiomux();
- of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
+ board_dt_populate(adata);
msm8610_add_drivers();
}
diff --git a/arch/arm/mach-msm/board-8930-gpu.c b/arch/arm/mach-msm/board-8930-gpu.c
index 0c6a271..12a6ab1 100644
--- a/arch/arm/mach-msm/board-8930-gpu.c
+++ b/arch/arm/mach-msm/board-8930-gpu.c
@@ -145,7 +145,6 @@
.num_levels = 4,
.set_grp_async = NULL,
.idle_timeout = HZ/12,
- .nap_allowed = true,
.strtstp_sleepwake = false,
.clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
diff --git a/arch/arm/mach-msm/board-8930-pmic.c b/arch/arm/mach-msm/board-8930-pmic.c
index ef65613..d7e678e 100644
--- a/arch/arm/mach-msm/board-8930-pmic.c
+++ b/arch/arm/mach-msm/board-8930-pmic.c
@@ -481,6 +481,10 @@
.high_ocv_correction_limit_uv = 50,
.low_ocv_correction_limit_uv = 100,
.hold_soc_est = 3,
+ .enable_fcc_learning = 1,
+ .min_fcc_learning_soc = 20,
+ .min_fcc_ocv_pc = 30,
+ .max_fcc_learning_samples = 5,
};
static struct pm8038_platform_data pm8038_platform_data __devinitdata = {
diff --git a/arch/arm/mach-msm/board-8930-regulator-pm8038.c b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
index c34394e..8ed93ea 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8038.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013, 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
@@ -95,7 +95,7 @@
REGULATOR_SUPPLY("VDDIO_CDC", "sitar1p1-slim"),
REGULATOR_SUPPLY("CDC_VDDA_TX", "sitar1p1-slim"),
REGULATOR_SUPPLY("CDC_VDDA_RX", "sitar1p1-slim"),
- REGULATOR_SUPPLY("vddp", "0-0048"),
+ REGULATOR_SUPPLY("vcc_i2c", "0-0048"),
REGULATOR_SUPPLY("mhl_iovcc18", "0-0039"),
REGULATOR_SUPPLY("vdd-io", "spi0.0"),
REGULATOR_SUPPLY("vdd-phy", "spi0.0"),
@@ -209,7 +209,6 @@
REGULATOR_SUPPLY("8038_lvs2", NULL),
REGULATOR_SUPPLY("vcc_i2c", "3-004a"),
REGULATOR_SUPPLY("vcc_i2c", "3-0024"),
- REGULATOR_SUPPLY("vcc_i2c", "0-0048"),
REGULATOR_SUPPLY("vddio", "12-0018"),
REGULATOR_SUPPLY("vlogic", "12-0068"),
};
diff --git a/arch/arm/mach-msm/board-8930-regulator-pm8917.c b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
index 8f853a4..cdc419f 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8917.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, 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
@@ -194,7 +194,7 @@
REGULATOR_SUPPLY("VDDIO_CDC", "sitar1p1-slim"),
REGULATOR_SUPPLY("CDC_VDDA_TX", "sitar1p1-slim"),
REGULATOR_SUPPLY("CDC_VDDA_RX", "sitar1p1-slim"),
- REGULATOR_SUPPLY("vddp", "0-0048"),
+ REGULATOR_SUPPLY("vcc_i2c", "0-0048"),
REGULATOR_SUPPLY("mhl_iovcc18", "0-0039"),
REGULATOR_SUPPLY("CDC_VDD_CP", "sitar-slim"),
REGULATOR_SUPPLY("CDC_VDD_CP", "sitar1p1-slim"),
@@ -233,7 +233,6 @@
REGULATOR_SUPPLY("8917_lvs4", NULL),
REGULATOR_SUPPLY("vcc_i2c", "3-004a"),
REGULATOR_SUPPLY("vcc_i2c", "3-0024"),
- REGULATOR_SUPPLY("vcc_i2c", "0-0048"),
REGULATOR_SUPPLY("vddio", "12-0018"),
REGULATOR_SUPPLY("vlogic", "12-0068"),
};
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index 6ccaba6..e097faf 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -96,7 +96,6 @@
#include "rpm_resources.h"
#include <mach/mpm.h>
#include "clock.h"
-#include "smd_private.h"
#include "pm-boot.h"
#include "msm_watchdog.h"
#include "board-8930.h"
@@ -1718,12 +1717,6 @@
static struct isa1200_regulator isa1200_reg_data[] = {
{
- .name = "vddp",
- .min_uV = ISA_I2C_VTG_MIN_UV,
- .max_uV = ISA_I2C_VTG_MAX_UV,
- .load_uA = ISA_I2C_CURR_UA,
- },
- {
.name = "vcc_i2c",
.min_uV = ISA_I2C_VTG_MIN_UV,
.max_uV = ISA_I2C_VTG_MAX_UV,
@@ -2356,6 +2349,7 @@
&msm_pcm_hostless,
&msm_multi_ch_pcm,
&msm_lowlatency_pcm,
+ &msm_fm_loopback,
};
static void __init msm8930_i2c_init(void)
diff --git a/arch/arm/mach-msm/board-8960-pmic.c b/arch/arm/mach-msm/board-8960-pmic.c
index c87d966..8e758bf 100644
--- a/arch/arm/mach-msm/board-8960-pmic.c
+++ b/arch/arm/mach-msm/board-8960-pmic.c
@@ -438,6 +438,10 @@
.high_ocv_correction_limit_uv = 50,
.low_ocv_correction_limit_uv = 100,
.hold_soc_est = 3,
+ .enable_fcc_learning = 1,
+ .min_fcc_learning_soc = 20,
+ .min_fcc_ocv_pc = 30,
+ .max_fcc_learning_samples = 5,
};
#define PM8921_LC_LED_MAX_CURRENT 4 /* I = 4mA */
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 5d96389..b45e690 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -98,7 +98,6 @@
#include "rpm_resources.h"
#include <mach/mpm.h>
#include "clock.h"
-#include "smd_private.h"
#include "pm-boot.h"
#include "msm_watchdog.h"
#include "platsmp.h"
@@ -1308,6 +1307,7 @@
.gpios = tsif_gpios,
.tsif_pclk = "tsif_pclk",
.tsif_ref_clk = "tsif_ref_clk",
+ .tsif_vreg_present = 0,
};
static struct platform_device msm_device_tspp = {
diff --git a/arch/arm/mach-msm/board-8974-gpiomux.c b/arch/arm/mach-msm/board-8974-gpiomux.c
index 705275c..c8a88d7 100644
--- a/arch/arm/mach-msm/board-8974-gpiomux.c
+++ b/arch/arm/mach-msm/board-8974-gpiomux.c
@@ -20,6 +20,98 @@
#define KS8851_IRQ_GPIO 94
+static struct gpiomux_setting ap2mdm_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting mdm2ap_status_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ .dir = GPIOMUX_IN,
+};
+
+static struct gpiomux_setting mdm2ap_errfatal_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ .dir = GPIOMUX_IN,
+};
+
+static struct gpiomux_setting mdm2ap_pblrdy = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_IN,
+};
+
+
+static struct gpiomux_setting ap2mdm_soft_reset_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting ap2mdm_wakeup = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct msm_gpiomux_config mdm_configs[] __initdata = {
+ /* AP2MDM_STATUS */
+ {
+ .gpio = 105,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &ap2mdm_cfg,
+ }
+ },
+ /* MDM2AP_STATUS */
+ {
+ .gpio = 46,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mdm2ap_status_cfg,
+ }
+ },
+ /* MDM2AP_ERRFATAL */
+ {
+ .gpio = 82,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mdm2ap_errfatal_cfg,
+ }
+ },
+ /* AP2MDM_ERRFATAL */
+ {
+ .gpio = 106,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &ap2mdm_cfg,
+ }
+ },
+ /* AP2MDM_SOFT_RESET, aka AP2MDM_PON_RESET_N */
+ {
+ .gpio = 24,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &ap2mdm_soft_reset_cfg,
+ }
+ },
+ /* AP2MDM_WAKEUP */
+ {
+ .gpio = 104,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &ap2mdm_wakeup,
+ }
+ },
+ /* MDM2AP_PBL_READY*/
+ {
+ .gpio = 80,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mdm2ap_pblrdy,
+ }
+ },
+};
+
static struct gpiomux_setting gpio_uart_config = {
.func = GPIOMUX_FUNC_2,
.drv = GPIOMUX_DRV_16MA,
@@ -909,7 +1001,6 @@
},
};
-#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT
static struct gpiomux_setting sdc3_clk_actv_cfg = {
.func = GPIOMUX_FUNC_2,
.drv = GPIOMUX_DRV_8MA,
@@ -990,9 +1081,6 @@
msm_gpiomux_install(msm8974_sdc3_configs,
ARRAY_SIZE(msm8974_sdc3_configs));
}
-#else
-static void msm_gpiomux_sdc3_install(void) {}
-#endif /* CONFIG_MMC_MSM_SDC3_SUPPORT */
#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT
static struct gpiomux_setting sdc4_clk_actv_cfg = {
@@ -1079,6 +1167,23 @@
static void msm_gpiomux_sdc4_install(void) {}
#endif /* CONFIG_MMC_MSM_SDC4_SUPPORT */
+static struct msm_gpiomux_config apq8074_dragonboard_ts_config[] __initdata = {
+ {
+ /* BLSP1 QUP I2C_DATA */
+ .gpio = 2,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
+ {
+ /* BLSP1 QUP I2C_CLK */
+ .gpio = 3,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
+};
+
void __init msm_8974_init_gpiomux(void)
{
int rc;
@@ -1110,7 +1215,11 @@
msm_gpiomux_install(msm_sensor_configs, ARRAY_SIZE(msm_sensor_configs));
msm_gpiomux_install(&sd_card_det, 1);
- msm_gpiomux_sdc3_install();
+
+ if (machine_is_apq8074() && (of_board_is_liquid() || \
+ of_board_is_dragonboard()))
+ msm_gpiomux_sdc3_install();
+
msm_gpiomux_sdc4_install();
msm_gpiomux_install(msm_taiko_config, ARRAY_SIZE(msm_taiko_config));
@@ -1140,4 +1249,12 @@
if (of_board_is_rumi())
msm_gpiomux_install(msm_rumi_blsp_configs,
ARRAY_SIZE(msm_rumi_blsp_configs));
+
+ if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_MDM)
+ msm_gpiomux_install(mdm_configs,
+ ARRAY_SIZE(mdm_configs));
+
+ if (of_board_is_dragonboard() && machine_is_apq8074())
+ msm_gpiomux_install(apq8074_dragonboard_ts_config,
+ ARRAY_SIZE(apq8074_dragonboard_ts_config));
}
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index cfa1628..771359c 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -95,6 +95,7 @@
msm_smd_init();
msm_rpm_driver_init();
msm_lpmrs_module_init();
+ msm_pm_sleep_status_init();
rpm_regulator_smd_driver_init();
msm_spm_device_init();
krait_power_init();
@@ -163,7 +164,7 @@
msm_8974_init_gpiomux();
regulator_has_full_constraints();
- of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
+ board_dt_populate(adata);
msm8974_add_drivers();
}
@@ -174,6 +175,7 @@
static const char *msm8974_dt_match[] __initconst = {
"qcom,msm8974",
+ "qcom,apq8074",
NULL
};
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
index 3bb00bb..56246dd 100644
--- a/arch/arm/mach-msm/board-9625.c
+++ b/arch/arm/mach-msm/board-9625.c
@@ -22,6 +22,7 @@
#include <linux/of_irq.h>
#include <linux/memory.h>
#include <linux/msm_tsens.h>
+#include <linux/msm_thermal.h>
#include <asm/mach/map.h>
#include <asm/hardware/gic.h>
#include <asm/mach/arch.h>
@@ -239,6 +240,7 @@
msm_clock_init(&msm9625_clock_init_data);
msm9625_init_buses();
tsens_tm_init_driver();
+ msm_thermal_device_init();
}
void __init msm9625_init(void)
@@ -247,8 +249,7 @@
pr_err("%s: socinfo_init() failed\n", __func__);
msm9625_init_gpiomux();
- of_platform_populate(NULL, of_default_bus_match_table,
- msm9625_auxdata_lookup, NULL);
+ board_dt_populate(msm9625_auxdata_lookup);
msm9625_add_drivers();
}
diff --git a/arch/arm/mach-msm/board-dt.c b/arch/arm/mach-msm/board-dt.c
index 5d2fdf9..1f77b4c 100644
--- a/arch/arm/mach-msm/board-dt.c
+++ b/arch/arm/mach-msm/board-dt.c
@@ -115,3 +115,14 @@
return 1;
}
+
+void __init board_dt_populate(struct of_dev_auxdata *adata)
+{
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+
+ /* Explicitly parent the /soc devices to the root node to preserve
+ * the kernel ABI (sysfs structure, etc) until userspace is updated
+ */
+ of_platform_populate(of_find_node_by_path("/soc"),
+ of_default_bus_match_table, adata, NULL);
+}
diff --git a/arch/arm/mach-msm/board-dt.h b/arch/arm/mach-msm/board-dt.h
index 03ffa0b..d79e414 100644
--- a/arch/arm/mach-msm/board-dt.h
+++ b/arch/arm/mach-msm/board-dt.h
@@ -10,9 +10,12 @@
* GNU General Public License for more details.
*/
+#include <linux/of_platform.h>
+
extern struct sys_timer msm_dt_timer;
void __init msm_dt_init_irq(void);
void __init msm_dt_init_irq_nompm(void);
void __init msm_dt_init_irq_l2x0(void);
int __init msm_scan_dt_map_imem(unsigned long node, const char *uname,
int depth, void *data);
+void __init board_dt_populate(struct of_dev_auxdata *adata);
diff --git a/arch/arm/mach-msm/board-zinc-gpiomux.c b/arch/arm/mach-msm/board-fsm9900-gpiomux.c
similarity index 94%
rename from arch/arm/mach-msm/board-zinc-gpiomux.c
rename to arch/arm/mach-msm/board-fsm9900-gpiomux.c
index ac4daa8..dede706 100644
--- a/arch/arm/mach-msm/board-zinc-gpiomux.c
+++ b/arch/arm/mach-msm/board-fsm9900-gpiomux.c
@@ -17,7 +17,7 @@
#include <mach/board.h>
#include <mach/gpiomux.h>
-void __init msmzinc_init_gpiomux(void)
+void __init fsm9900_init_gpiomux(void)
{
int rc;
diff --git a/arch/arm/mach-msm/board-fsm9900.c b/arch/arm/mach-msm/board-fsm9900.c
new file mode 100644
index 0000000..6e85ece
--- /dev/null
+++ b/arch/arm/mach-msm/board-fsm9900.c
@@ -0,0 +1,118 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/memory.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
+#include <mach/board.h>
+#include <mach/gpiomux.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_smd.h>
+#include <mach/restart.h>
+#include <mach/socinfo.h>
+#include <mach/clk-provider.h>
+#include "board-dt.h"
+#include "clock.h"
+#include "devices.h"
+#include "platsmp.h"
+
+void __init fsm9900_reserve(void)
+{
+}
+
+static void __init fsm9900_early_memory(void)
+{
+}
+
+static struct clk_lookup msm_clocks_dummy[] = {
+ CLK_DUMMY("core_clk", BLSP2_UART_CLK, "f9960000.serial", OFF),
+ CLK_DUMMY("iface_clk", BLSP2_UART_CLK, "f9960000.serial", OFF),
+ CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("core_clk", BLSP2_I2C_CLK, "f9966000.i2c", OFF),
+ CLK_DUMMY("iface_clk", BLSP2_I2C_CLK, "f9966000.i2c", OFF),
+ CLK_DUMMY("core_clk", BLSP1_I2C_CLK, "f9924000.i2c", OFF),
+ CLK_DUMMY("iface_clk", BLSP1_I2C_CLK, "f9924000.i2c", OFF),
+ CLK_DUMMY("core_clk", NULL, "f9a55000.usb", OFF),
+ CLK_DUMMY("iface_clk", NULL, "f9a55000.usb", OFF),
+ CLK_DUMMY("phy_clk", NULL, "f9a55000.usb", OFF),
+ CLK_DUMMY("xo", NULL, "f9a55000.usb", OFF),
+ CLK_DUMMY("core_clk", NULL, "msm_ehci_host", OFF),
+ CLK_DUMMY("iface_clk", NULL, "msm_ehci_host", OFF),
+ CLK_DUMMY("sleep_clk", NULL, "msm_ehci_host", OFF),
+ CLK_DUMMY("xo", NULL, "msm_ehci_host", OFF),
+ CLK_DUMMY("core_clk", NULL, "f9824900.sdhci_msm", OFF),
+ CLK_DUMMY("iface_clk", NULL, "f9824900.sdhci_msm", OFF),
+ CLK_DUMMY("core_clk", NULL, "f98a4900.sdhci_msm", OFF),
+ CLK_DUMMY("iface_clk", NULL, "f98a4900.sdhci_msm", OFF),
+};
+
+static struct clock_init_data msm_dummy_clock_init_data __initdata = {
+ .table = msm_clocks_dummy,
+ .size = ARRAY_SIZE(msm_clocks_dummy),
+};
+
+/*
+ * Used to satisfy dependencies for devices that need to be
+ * run early or in a particular order. Most likely your device doesn't fall
+ * into this category, and thus the driver should not be added here. The
+ * EPROBE_DEFER can satisfy most dependency problems.
+ */
+void __init fsm9900_add_drivers(void)
+{
+ msm_smd_init();
+ msm_clock_init(&msm_dummy_clock_init_data);
+}
+
+static void __init fsm9900_map_io(void)
+{
+ msm_map_fsm9900_io();
+}
+
+void __init fsm9900_init(void)
+{
+ if (socinfo_init() < 0)
+ pr_err("%s: socinfo_init() failed\n", __func__);
+
+ fsm9900_init_gpiomux();
+ board_dt_populate(NULL);
+ fsm9900_add_drivers();
+}
+
+void __init fsm9900_init_very_early(void)
+{
+ fsm9900_early_memory();
+}
+
+static const char *fsm9900_dt_match[] __initconst = {
+ "qcom,fsm9900",
+ NULL
+};
+
+DT_MACHINE_START(FSM9900_DT, "Qualcomm FSM 9900 (Flattened Device Tree)")
+ .map_io = fsm9900_map_io,
+ .init_irq = msm_dt_init_irq,
+ .init_machine = fsm9900_init,
+ .handle_irq = gic_handle_irq,
+ .timer = &msm_dt_timer,
+ .dt_compat = fsm9900_dt_match,
+ .reserve = fsm9900_reserve,
+ .init_very_early = fsm9900_init_very_early,
+ .restart = msm_restart,
+ .smp = &msm8974_smp_ops,
+MACHINE_END
diff --git a/arch/arm/mach-msm/board-krypton-gpiomux.c b/arch/arm/mach-msm/board-krypton-gpiomux.c
index 3d86ba7..ee38e5f 100644
--- a/arch/arm/mach-msm/board-krypton-gpiomux.c
+++ b/arch/arm/mach-msm/board-krypton-gpiomux.c
@@ -17,12 +17,37 @@
#include <mach/gpio.h>
#include <mach/gpiomux.h>
+#define KS8851_IRQ_GPIO 75
+
+#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
+static struct gpiomux_setting gpio_eth_config = {
+ .pull = GPIOMUX_PULL_UP,
+ .drv = GPIOMUX_DRV_2MA,
+ .func = GPIOMUX_FUNC_GPIO,
+};
+
+static struct msm_gpiomux_config msm_eth_config[] = {
+ {
+ .gpio = KS8851_IRQ_GPIO,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_eth_config,
+ }
+ },
+};
+#endif
+
static struct gpiomux_setting gpio_uart_config = {
.func = GPIOMUX_FUNC_1,
.drv = GPIOMUX_DRV_8MA,
.pull = GPIOMUX_PULL_NONE,
};
+static struct gpiomux_setting gpio_spi_config = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_6MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
static struct msm_gpiomux_config msm_blsp_configs[] __initdata = {
{
.gpio = 8, /* BLSP1 UART TX */
@@ -36,6 +61,30 @@
[GPIOMUX_SUSPENDED] = &gpio_uart_config,
},
},
+ {
+ .gpio = 20, /* BLSP1 QUP6 SPI_DATA_MOSI */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+ {
+ .gpio = 21, /* BLSP1 QUP6 SPI_DATA_MISO */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+ {
+ .gpio = 23, /* BLSP1 QUP6 SPI_CLK */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+ {
+ .gpio = 22, /* BLSP1 QUP6 SPI_CS */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
};
void __init msmkrypton_init_gpiomux(void)
@@ -49,4 +98,7 @@
}
msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
+#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
+ msm_gpiomux_install(msm_eth_config, ARRAY_SIZE(msm_eth_config));
+#endif
}
diff --git a/arch/arm/mach-msm/board-krypton.c b/arch/arm/mach-msm/board-krypton.c
index aada3b0..b40fcfa 100644
--- a/arch/arm/mach-msm/board-krypton.c
+++ b/arch/arm/mach-msm/board-krypton.c
@@ -34,6 +34,9 @@
static struct clk_lookup msm_clocks_dummy[] = {
CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("core_clk", SPI_CLK, "f9928000.spi", OFF),
+ CLK_DUMMY("iface_clk", SPI_P_CLK, "f9928000.spi", OFF),
+
};
static struct clock_init_data msm_dummy_clock_init_data __initdata = {
@@ -41,6 +44,10 @@
.size = ARRAY_SIZE(msm_clocks_dummy),
};
+static struct of_dev_auxdata msmkrypton_auxdata_lookup[] __initdata = {
+ {}
+};
+
/*
* Used to satisfy dependencies for devices that need to be
* run early or in a particular order. Most likely your device doesn't fall
@@ -64,7 +71,7 @@
pr_err("%s: socinfo_init() failed\n", __func__);
msmkrypton_init_gpiomux();
- of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+ board_dt_populate(msmkrypton_auxdata_lookup);
msmkrypton_add_drivers();
}
diff --git a/arch/arm/mach-msm/board-samarium-gpiomux.c b/arch/arm/mach-msm/board-samarium-gpiomux.c
new file mode 100644
index 0000000..645cb6f
--- /dev/null
+++ b/arch/arm/mach-msm/board-samarium-gpiomux.c
@@ -0,0 +1,53 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <mach/gpiomux.h>
+
+static struct gpiomux_setting gpio_uart_config = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_16MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_HIGH,
+};
+
+static struct msm_gpiomux_config msm_blsp_configs[] __initdata = {
+ {
+ .gpio = 4, /* BLSP2 UART TX */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_uart_config,
+ },
+ },
+ {
+ .gpio = 5, /* BLSP2 UART RX */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_uart_config,
+ },
+ },
+};
+
+void __init msmsamarium_init_gpiomux(void)
+{
+ int rc;
+
+ rc = msm_gpiomux_init_dt();
+ if (rc) {
+ pr_err("%s failed %d\n", __func__, rc);
+ return;
+ }
+
+ msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
+}
diff --git a/arch/arm/mach-msm/board-samarium.c b/arch/arm/mach-msm/board-samarium.c
new file mode 100644
index 0000000..a656cee
--- /dev/null
+++ b/arch/arm/mach-msm/board-samarium.c
@@ -0,0 +1,98 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/memory.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
+#include <mach/board.h>
+#include <mach/gpiomux.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_memtypes.h>
+#include <mach/restart.h>
+#include <mach/socinfo.h>
+#include <mach/clk-provider.h>
+#include "board-dt.h"
+#include "clock.h"
+#include "devices.h"
+#include "platsmp.h"
+
+static struct clk_lookup msm_clocks_dummy[] = {
+ CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("core_clk", SDC1_CLK, "msm_sdcc.1", OFF),
+ CLK_DUMMY("iface_clk", SDC1_P_CLK, "msm_sdcc.1", OFF),
+ CLK_DUMMY("core_clk", SDC2_CLK, "msm_sdcc.2", OFF),
+ CLK_DUMMY("iface_clk", SDC2_P_CLK, "msm_sdcc.2", OFF),
+};
+
+static struct clock_init_data msm_dummy_clock_init_data __initdata = {
+ .table = msm_clocks_dummy,
+ .size = ARRAY_SIZE(msm_clocks_dummy),
+};
+
+static struct of_dev_auxdata msmsamarium_auxdata_lookup[] __initdata = {
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9824000, \
+ "msm_sdcc.1", NULL),
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
+ "msm_sdcc.2", NULL),
+ {},
+};
+
+/*
+ * Used to satisfy dependencies for devices that need to be
+ * run early or in a particular order. Most likely your device doesn't fall
+ * into this category, and thus the driver should not be added here. The
+ * EPROBE_DEFER can satisfy most dependency problems.
+ */
+void __init msmsamarium_add_drivers(void)
+{
+ msm_clock_init(&msm_dummy_clock_init_data);
+}
+
+static void __init msmsamarium_map_io(void)
+{
+ msm_map_msmsamarium_io();
+}
+
+void __init msmsamarium_init(void)
+{
+ struct of_dev_auxdata *adata = msmsamarium_auxdata_lookup;
+
+ if (socinfo_init() < 0)
+ pr_err("%s: socinfo_init() failed\n", __func__);
+
+ msmsamarium_init_gpiomux();
+ board_dt_populate(adata);
+ msmsamarium_add_drivers();
+}
+
+static const char *msmsamarium_dt_match[] __initconst = {
+ "qcom,msmsamarium",
+ NULL
+};
+
+DT_MACHINE_START(MSMSAMARIUM_DT, "Qualcomm MSM Samarium(Flattened Device Tree)")
+ .map_io = msmsamarium_map_io,
+ .init_irq = msm_dt_init_irq,
+ .init_machine = msmsamarium_init,
+ .handle_irq = gic_handle_irq,
+ .timer = &msm_dt_timer,
+ .dt_compat = msmsamarium_dt_match,
+ .restart = msm_restart,
+ .smp = &msm8974_smp_ops,
+MACHINE_END
diff --git a/arch/arm/mach-msm/board-zinc.c b/arch/arm/mach-msm/board-zinc.c
deleted file mode 100644
index 444444f..0000000
--- a/arch/arm/mach-msm/board-zinc.c
+++ /dev/null
@@ -1,127 +0,0 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/err.h>
-#include <linux/kernel.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
-#include <linux/memory.h>
-#include <asm/hardware/gic.h>
-#include <asm/mach/map.h>
-#include <asm/mach/arch.h>
-#include <mach/board.h>
-#include <mach/gpiomux.h>
-#include <mach/msm_iomap.h>
-#include <mach/msm_memtypes.h>
-#include <mach/msm_smd.h>
-#include <mach/restart.h>
-#include <mach/socinfo.h>
-#include <mach/clk-provider.h>
-#include "board-dt.h"
-#include "clock.h"
-#include "devices.h"
-#include "platsmp.h"
-
-static struct memtype_reserve msmzinc_reserve_table[] __initdata = {
- [MEMTYPE_SMI] = {
- },
- [MEMTYPE_EBI0] = {
- .flags = MEMTYPE_FLAGS_1M_ALIGN,
- },
- [MEMTYPE_EBI1] = {
- .flags = MEMTYPE_FLAGS_1M_ALIGN,
- },
-};
-
-static int msmzinc_paddr_to_memtype(phys_addr_t paddr)
-{
- return MEMTYPE_EBI1;
-}
-
-static struct reserve_info msmzinc_reserve_info __initdata = {
- .memtype_reserve_table = msmzinc_reserve_table,
- .paddr_to_memtype = msmzinc_paddr_to_memtype,
-};
-
-void __init msmzinc_reserve(void)
-{
- reserve_info = &msmzinc_reserve_info;
- of_scan_flat_dt(dt_scan_for_memory_reserve, msmzinc_reserve_table);
- msm_reserve();
-}
-
-static void __init msmzinc_early_memory(void)
-{
- reserve_info = &msmzinc_reserve_info;
- of_scan_flat_dt(dt_scan_for_memory_hole, msmzinc_reserve_table);
-}
-
-static struct clk_lookup msm_clocks_dummy[] = {
- CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
- CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
-};
-
-static struct clock_init_data msm_dummy_clock_init_data __initdata = {
- .table = msm_clocks_dummy,
- .size = ARRAY_SIZE(msm_clocks_dummy),
-};
-
-/*
- * Used to satisfy dependencies for devices that need to be
- * run early or in a particular order. Most likely your device doesn't fall
- * into this category, and thus the driver should not be added here. The
- * EPROBE_DEFER can satisfy most dependency problems.
- */
-void __init msmzinc_add_drivers(void)
-{
- msm_smd_init();
- msm_clock_init(&msm_dummy_clock_init_data);
-}
-
-static void __init msmzinc_map_io(void)
-{
- msm_map_zinc_io();
-}
-
-void __init msmzinc_init(void)
-{
- if (socinfo_init() < 0)
- pr_err("%s: socinfo_init() failed\n", __func__);
-
- msmzinc_init_gpiomux();
- of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
- msmzinc_add_drivers();
-}
-
-void __init msmzinc_init_very_early(void)
-{
- msmzinc_early_memory();
-}
-
-static const char *msmzinc_dt_match[] __initconst = {
- "qcom,msmzinc",
- NULL
-};
-
-DT_MACHINE_START(MSMZINC_DT, "Qualcomm MSM ZINC (Flattened Device Tree)")
- .map_io = msmzinc_map_io,
- .init_irq = msm_dt_init_irq,
- .init_machine = msmzinc_init,
- .handle_irq = gic_handle_irq,
- .timer = &msm_dt_timer,
- .dt_compat = msmzinc_dt_match,
- .reserve = msmzinc_reserve,
- .init_very_early = msmzinc_init_very_early,
- .restart = msm_restart,
- .smp = &msm8974_smp_ops,
-MACHINE_END
diff --git a/arch/arm/mach-msm/cache_erp.c b/arch/arm/mach-msm/cache_erp.c
index ddea91c..f52bc28 100644
--- a/arch/arm/mach-msm/cache_erp.c
+++ b/arch/arm/mach-msm/cache_erp.c
@@ -123,11 +123,18 @@
unsigned int mplxrexnok;
};
+struct msm_erp_dump_region {
+ struct resource *res;
+ void __iomem *va;
+};
+
static DEFINE_PER_CPU(struct msm_l1_err_stats, msm_l1_erp_stats);
static struct msm_l2_err_stats msm_l2_erp_stats;
static int l1_erp_irq, l2_erp_irq;
static struct proc_dir_entry *procfs_entry;
+static int num_dump_regions;
+static struct msm_erp_dump_region *dump_regions;
#ifdef CONFIG_MSM_L1_ERR_LOG
static struct proc_dir_entry *procfs_log_entry;
@@ -211,6 +218,22 @@
return len;
}
+static int msm_erp_dump_regions(void)
+{
+ int i = 0;
+ struct msm_erp_dump_region *r;
+
+ for (i = 0; i < num_dump_regions; i++) {
+ r = &dump_regions[i];
+
+ pr_alert("%s %pR:\n", r->res->name, r->res);
+ print_hex_dump(KERN_ALERT, "", DUMP_PREFIX_OFFSET, 32, 4, r->va,
+ resource_size(r->res), 0);
+ }
+
+ return 0;
+}
+
#ifdef CONFIG_MSM_L1_ERR_LOG
static int proc_read_log(char *page, char **start, off_t off, int count,
int *eof, void *data)
@@ -267,6 +290,7 @@
pr_alert("\tCESR = 0x%08x\n", cesr);
pr_alert("\tCPU speed = %lu\n", acpuclk_get_rate(cpu));
pr_alert("\tMIDR = 0x%08x\n", read_cpuid_id());
+ msm_erp_dump_regions();
}
if (cesr & CESR_DCTPE) {
@@ -425,6 +449,9 @@
if (port_error && print_alert)
ERP_PORT_ERR("L2 master port error detected");
+ if (soft_error && print_alert)
+ msm_erp_dump_regions();
+
if (soft_error && !unrecoverable)
ERP_1BIT_ERR("L2 single-bit error detected");
@@ -464,6 +491,37 @@
.notifier_call = cache_erp_cpu_callback,
};
+static int msm_erp_read_dump_regions(struct platform_device *pdev)
+{
+ int i;
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res;
+
+ num_dump_regions = of_property_count_strings(np, "reg-names");
+
+ if (num_dump_regions <= 0) {
+ num_dump_regions = 0;
+ return 0; /* Not an error - this is an optional property */
+ }
+
+ dump_regions = devm_kzalloc(&pdev->dev,
+ sizeof(*dump_regions) * num_dump_regions,
+ GFP_KERNEL);
+ if (!dump_regions)
+ return -ENOMEM;
+
+ for (i = 0; i < num_dump_regions; i++) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ dump_regions[i].res = res;
+ dump_regions[i].va = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!dump_regions[i].va)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
static int msm_cache_erp_probe(struct platform_device *pdev)
{
struct resource *r;
@@ -511,6 +569,11 @@
goto fail_l2;
}
+ ret = msm_erp_read_dump_regions(pdev);
+
+ if (ret)
+ goto fail_l2;
+
get_online_cpus();
register_hotcpu_notifier(&cache_erp_cpu_notifier);
for_each_cpu(cpu, cpu_online_mask)
diff --git a/arch/arm/mach-msm/clock-8084.c b/arch/arm/mach-msm/clock-8084.c
new file mode 100644
index 0000000..bec9f1b4
--- /dev/null
+++ b/arch/arm/mach-msm/clock-8084.c
@@ -0,0 +1,377 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/rpm-regulator-smd.h>
+#include <mach/socinfo.h>
+#include <mach/rpm-smd.h>
+
+#include "clock-local2.h"
+#include "clock-pll.h"
+#include "clock-rpm.h"
+#include "clock-voter.h"
+#include "clock.h"
+
+/*
+ * TODO: Drivers need to fill in the clock names and device names for the clocks
+ * they need to control.
+ */
+static struct clk_lookup msm_clocks_8084[] = {
+ CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("core_clk", SDC1_CLK, "msm_sdcc.1", OFF),
+ CLK_DUMMY("iface_clk", SDC1_P_CLK, "msm_sdcc.1", OFF),
+ CLK_DUMMY("core_clk", SDC2_CLK, "msm_sdcc.2", OFF),
+ CLK_DUMMY("iface_clk", SDC2_P_CLK, "msm_sdcc.2", OFF),
+ CLK_DUMMY("xo", NULL, "f9200000.qcom,ssusb", OFF),
+ CLK_DUMMY("core_clk", NULL, "f9200000.qcom,ssusb", OFF),
+ CLK_DUMMY("iface_clk", NULL, "f9200000.qcom,ssusb", OFF),
+ CLK_DUMMY("sleep_clk", NULL, "f9200000.qcom,ssusb", OFF),
+ CLK_DUMMY("sleep_a_clk", NULL, "f9200000.qcom,ssusb", OFF),
+ CLK_DUMMY("utmi_clk", NULL, "f9200000.qcom,ssusb", OFF),
+ CLK_DUMMY("ref_clk", NULL, "f9200000.qcom,ssusb", OFF),
+ CLK_DUMMY("dfab_clk", DFAB_CLK, "msm_sps", OFF),
+ CLK_DUMMY("dma_bam_pclk", DMA_BAM_P_CLK, "msm_sps", OFF),
+ CLK_DUMMY("", ufs_axi_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb30_master_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb30_sec_master_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hsic_ahb_clk_src.c, "", OFF),
+ CLK_DUMMY("", sata_asic0_clk_src.c, "", OFF),
+ CLK_DUMMY("", sata_pmalive_clk_src.c, "", OFF),
+ CLK_DUMMY("", sata_rx_clk_src.c, "", OFF),
+ CLK_DUMMY("", sata_rx_oob_clk_src.c, "", OFF),
+ CLK_DUMMY("", sdcc1_apps_clk_src.c, "", OFF),
+ CLK_DUMMY("", sdcc2_apps_clk_src.c, "", OFF),
+ CLK_DUMMY("", sdcc3_apps_clk_src.c, "", OFF),
+ CLK_DUMMY("", sdcc4_apps_clk_src.c, "", OFF),
+ CLK_DUMMY("", tsif_ref_clk_src.c, "", OFF),
+ CLK_DUMMY("", ufs_rx_cfg_postdiv_clk_src.c, "", OFF),
+ CLK_DUMMY("", ufs_tx_cfg_postdiv_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb30_mock_utmi_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb30_sec_mock_utmi_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hs_system_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hsic_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hsic_io_cal_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hsic_mock_utmi_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hsic_system_clk_src.c, "", OFF),
+ CLK_DUMMY("", gcc_bam_dma_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_bam_dma_inactivity_timers_clk.c, "", OFF),
+ CLK_DUMMY("iface_clk", gcc_blsp1_ahb_clk.c, "f9925000.i2c", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup1_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup1_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup2_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup2_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup3_i2c_apps_clk.c, "f9925000.i2c",
+ OFF),
+ CLK_DUMMY("", gcc_blsp1_qup3_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup4_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup4_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup5_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup5_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup6_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup6_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart1_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart2_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart3_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart4_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart5_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart6_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup1_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup1_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup2_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup2_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup3_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup3_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup4_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup4_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup5_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup5_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup6_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup6_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart1_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart2_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart3_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart4_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart5_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart6_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_boot_rom_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce1_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce1_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce1_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce2_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce2_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce2_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce3_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce3_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce3_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_copss_smmu_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_copss_smmu_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_dcd_xo_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_bimc_gfx_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_xo_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_xo_div4_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gp1_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gp2_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gp3_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_lpass_mport_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_lpass_q6_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_lpass_sway_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_mmss_bimc_gfx_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_mmss_vpu_maple_sys_noc_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ocmem_noc_cfg_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_msg_ram_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_pdm2_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_pdm_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_pdm_xo4_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_periph_noc_usb_hsic_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_prng_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_asic0_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_cfg_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_pmalive_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_rx_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_rx_oob_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc1_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc1_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc1_cdccal_ff_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc1_cdccal_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc2_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc2_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc2_inactivity_timers_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc3_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc3_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc3_inactivity_timers_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc4_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc4_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc4_inactivity_timers_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_spss_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sys_noc_ufs_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sys_noc_usb3_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sys_noc_usb3_sec_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_tsif_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_tsif_inactivity_timers_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_tsif_ref_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_rx_cfg_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_rx_symbol_0_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_rx_symbol_1_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_tx_cfg_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_tx_symbol_0_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_tx_symbol_1_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb2a_phy_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb2b_phy_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb30_master_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb30_mock_utmi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb30_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb30_sec_master_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb30_sec_mock_utmi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb30_sec_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hs_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hs_inactivity_timers_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hs_system_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_io_cal_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_io_cal_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_mock_utmi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_system_clk.c, "", OFF),
+
+ CLK_DUMMY("", axi_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmpll0_pll_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmpll1_pll_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmpll2_pll_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmpll3_pll_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmpll4_pll_clk_src.c, "", OFF),
+ CLK_DUMMY("", csi0_clk_src.c, "", OFF),
+ CLK_DUMMY("", csi1_clk_src.c, "", OFF),
+ CLK_DUMMY("", csi2_clk_src.c, "", OFF),
+ CLK_DUMMY("", csi3_clk_src.c, "", OFF),
+ CLK_DUMMY("", vcodec0_clk_src.c, "", OFF),
+ CLK_DUMMY("", vfe0_clk_src.c, "", OFF),
+ CLK_DUMMY("", vfe1_clk_src.c, "", OFF),
+ CLK_DUMMY("", edppixel_clk_src.c, "", OFF),
+ CLK_DUMMY("", extpclk_clk_src.c, "", OFF),
+ CLK_DUMMY("", mdp_clk_src.c, "", OFF),
+ CLK_DUMMY("", pclk0_clk_src.c, "", OFF),
+ CLK_DUMMY("", pclk1_clk_src.c, "", OFF),
+ CLK_DUMMY("", ocmemnoc_clk_src.c, "", OFF),
+ CLK_DUMMY("", gfx3d_clk_src.c, "", OFF),
+ CLK_DUMMY("", vp_clk_src.c, "", OFF),
+ CLK_DUMMY("", cci_clk_src.c, "", OFF),
+ CLK_DUMMY("", gp0_clk_src.c, "", OFF),
+ CLK_DUMMY("", gp1_clk_src.c, "", OFF),
+ CLK_DUMMY("", jpeg0_clk_src.c, "", OFF),
+ CLK_DUMMY("", jpeg1_clk_src.c, "", OFF),
+ CLK_DUMMY("", jpeg2_clk_src.c, "", OFF),
+ CLK_DUMMY("", mclk0_clk_src.c, "", OFF),
+ CLK_DUMMY("", mclk1_clk_src.c, "", OFF),
+ CLK_DUMMY("", mclk2_clk_src.c, "", OFF),
+ CLK_DUMMY("", mclk3_clk_src.c, "", OFF),
+ CLK_DUMMY("", csi0phytimer_clk_src.c, "", OFF),
+ CLK_DUMMY("", csi1phytimer_clk_src.c, "", OFF),
+ CLK_DUMMY("", csi2phytimer_clk_src.c, "", OFF),
+ CLK_DUMMY("", cpp_clk_src.c, "", OFF),
+ CLK_DUMMY("", byte0_clk_src.c, "", OFF),
+ CLK_DUMMY("", byte1_clk_src.c, "", OFF),
+ CLK_DUMMY("", edpaux_clk_src.c, "", OFF),
+ CLK_DUMMY("", edplink_clk_src.c, "", OFF),
+ CLK_DUMMY("", esc0_clk_src.c, "", OFF),
+ CLK_DUMMY("", esc1_clk_src.c, "", OFF),
+ CLK_DUMMY("", hdmi_clk_src.c, "", OFF),
+ CLK_DUMMY("", vsync_clk_src.c, "", OFF),
+ CLK_DUMMY("", rbbmtimer_clk_src.c, "", OFF),
+ CLK_DUMMY("", maple_clk_src.c, "", OFF),
+ CLK_DUMMY("", vdp_clk_src.c, "", OFF),
+ CLK_DUMMY("", vpu_bus_clk_src.c, "", OFF),
+ CLK_DUMMY("", dsi0_phy_pll_out_byteclk.c, "", OFF),
+ CLK_DUMMY("", dsi0_phy_pll_out_dsiclk.c, "", OFF),
+ CLK_DUMMY("", dsi1_phy_pll_out_byteclk.c, "", OFF),
+ CLK_DUMMY("", dsi1_phy_pll_out_dsiclk.c, "", OFF),
+ CLK_DUMMY("", edpphy_cc_link_clk.c, "", OFF),
+ CLK_DUMMY("", edpphy_cc_vco_div_clk.c, "", OFF),
+ CLK_DUMMY("", hdmi_phy_pll_out.c, "", OFF),
+ CLK_DUMMY("", csiphy_bist_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_edppixel_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_extpclk_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_pclk0_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_pclk1_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_vp_clk.c, "", OFF),
+ CLK_DUMMY("", camss_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_cci_cci_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_cci_cci_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi0_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi0_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi0phy_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi0pix_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi0rdi_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi1_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi1_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi1phy_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi1pix_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi1rdi_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi2_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi2_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi2phy_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi2pix_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi2rdi_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi3_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi3_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi3phy_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi3pix_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi3rdi_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi_vfe0_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi_vfe1_clk.c, "", OFF),
+ CLK_DUMMY("", camss_gp0_clk.c, "", OFF),
+ CLK_DUMMY("", camss_gp1_clk.c, "", OFF),
+ CLK_DUMMY("", camss_ispif_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_jpeg_jpeg0_clk.c, "", OFF),
+ CLK_DUMMY("", camss_jpeg_jpeg1_clk.c, "", OFF),
+ CLK_DUMMY("", camss_jpeg_jpeg2_clk.c, "", OFF),
+ CLK_DUMMY("", camss_jpeg_jpeg_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_jpeg_jpeg_axi_clk.c, "", OFF),
+ CLK_DUMMY("", camss_mclk0_clk.c, "", OFF),
+ CLK_DUMMY("", camss_mclk1_clk.c, "", OFF),
+ CLK_DUMMY("", camss_mclk2_clk.c, "", OFF),
+ CLK_DUMMY("", camss_mclk3_clk.c, "", OFF),
+ CLK_DUMMY("", camss_micro_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_phy0_csi0phytimer_clk.c, "", OFF),
+ CLK_DUMMY("", camss_phy1_csi1phytimer_clk.c, "", OFF),
+ CLK_DUMMY("", camss_phy2_csi2phytimer_clk.c, "", OFF),
+ CLK_DUMMY("", camss_top_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_vfe_cpp_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_vfe_cpp_clk.c, "", OFF),
+ CLK_DUMMY("", camss_vfe_vfe0_clk.c, "", OFF),
+ CLK_DUMMY("", camss_vfe_vfe1_clk.c, "", OFF),
+ CLK_DUMMY("", camss_vfe_vfe_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_vfe_vfe_axi_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_axi_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_byte0_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_byte1_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_edpaux_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_edplink_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_edppixel_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_esc0_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_esc1_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_extpclk_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_hdmi_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_hdmi_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_mdp_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_mdp_lut_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_pclk0_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_pclk1_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_vsync_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_misc_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_mmssnoc_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_mmssnoc_axi_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_s0_axi_clk.c, "", OFF),
+ CLK_DUMMY("core_clk", ocmemgx_core_clk.c, "fdd00000.qcom,ocmem", OFF),
+ CLK_DUMMY("iface_clk", ocmemcx_ocmemnoc_clk.c,
+ "fdd00000.qcom,ocmem", OFF),
+ CLK_DUMMY("", oxili_ocmemgx_clk.c, "", OFF),
+ CLK_DUMMY("", oxili_gfx3d_clk.c, "", OFF),
+ CLK_DUMMY("", oxili_rbbmtimer_clk.c, "", OFF),
+ CLK_DUMMY("", oxilicx_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_axi_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_core0_vcodec_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_core1_vcodec_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_ocmemnoc_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_vcodec0_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_axi_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_bus_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_cxo_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_maple_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_vdp_clk.c, "", OFF),
+ CLK_DUMMY("iface_clk", NULL, "fda64000.qcom,iommu", OFF),
+ CLK_DUMMY("core_clk", NULL, "fda64000.qcom,iommu", OFF),
+ CLK_DUMMY("alt_core_clk", NULL, "fda64000.qcom,iommu", OFF),
+ CLK_DUMMY("iface_clk", NULL, "fda44000.qcom,iommu", OFF),
+ CLK_DUMMY("core_clk", NULL, "fda44000.qcom,iommu", OFF),
+ CLK_DUMMY("alt_core_clk", NULL, "fda44000.qcom,iommu", OFF),
+ CLK_DUMMY("iface_clk", NULL, "fd928000.qcom,iommu", OFF),
+ CLK_DUMMY("core_clk", NULL, "fd928000.qcom,iommu", oFF),
+ CLK_DUMMY("core_clk", NULL, "fdb10000.qcom,iommu", OFF),
+ CLK_DUMMY("iface_clk", NULL, "fdb10000.qcom,iommu", OFF),
+ CLK_DUMMY("iface_clk", NULL, "fdc84000.qcom,iommu", OFF),
+ CLK_DUMMY("alt_core_clk", NULL, "fdc84000.qcom,iommu", OFF),
+ CLK_DUMMY("core_clk", NULL, "fdc84000.qcom,iommu", OFF),
+ CLK_DUMMY("iface_clk", NULL, "f9bc4000.qcom,iommu", OFF),
+ CLK_DUMMY("core_clk", NULL, "f9bc4000.qcom,iommu", OFF),
+ CLK_DUMMY("iface_clk", NULL, "fdee4000.qcom,iommu", OFF),
+ CLK_DUMMY("core_clk", NULL, "fdee4000.qcom,iommu", OFF),
+ CLK_DUMMY("iface_clk", NULL, "fe054000.qcom,iommu", OFF),
+ CLK_DUMMY("core_clk", NULL, "fe054000.qcom,iommu", OFF),
+ CLK_DUMMY("iface_clk", NULL, "fe064000.qcom,iommu", OFF),
+ CLK_DUMMY("core_clk", NULL, "fe064000.qcom,iommu", OFF),
+};
+
+struct clock_init_data msm8084_clock_init_data __initdata = {
+ .table = msm_clocks_8084,
+ .size = ARRAY_SIZE(msm_clocks_8084),
+};
diff --git a/arch/arm/mach-msm/clock-8092.c b/arch/arm/mach-msm/clock-8092.c
new file mode 100644
index 0000000..9de97ea
--- /dev/null
+++ b/arch/arm/mach-msm/clock-8092.c
@@ -0,0 +1,327 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/rpm-regulator-smd.h>
+#include <mach/socinfo.h>
+#include <mach/rpm-smd.h>
+
+#include "clock-local2.h"
+#include "clock-pll.h"
+#include "clock-rpm.h"
+#include "clock-voter.h"
+#include "clock.h"
+
+/*
+ * Drivers need to fill in the clock names and device names for the clocks
+ * they need to control.
+ */
+static struct clk_lookup msm_clocks_8092[] = {
+ CLK_DUMMY("core_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
+ CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
+ CLK_DUMMY("core_clk", BLSP1_UART_CLK, "msm_serial_hsl.1", OFF),
+ CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "msm_serial_hsl.1", OFF),
+ CLK_DUMMY("core_clk", SDC1_CLK, "msm_sdcc.1", OFF),
+ CLK_DUMMY("iface_clk", SDC1_P_CLK, "msm_sdcc.1", OFF),
+ CLK_DUMMY("core_clk", SDC2_CLK, "msm_sdcc.2", OFF),
+ CLK_DUMMY("iface_clk", SDC2_P_CLK, "msm_sdcc.2", OFF),
+ CLK_DUMMY("", usb30_master_clk_src.c, "", OFF),
+ CLK_DUMMY("", tsif_ref_clk_src.c, "", OFF),
+ CLK_DUMMY("", ce1_clk_src.c, "", OFF),
+ CLK_DUMMY("", ce2_clk_src.c, "", OFF),
+ CLK_DUMMY("", ce3_clk_src.c, "", OFF),
+ CLK_DUMMY("", geni_ser_clk_src.c, "", OFF),
+ CLK_DUMMY("", gmac_125m_clk_src.c, "", OFF),
+ CLK_DUMMY("", gmac_core_clk_src.c, "", OFF),
+ CLK_DUMMY("", gmac_sys_25m_clk_src.c, "", OFF),
+ CLK_DUMMY("", gp1_clk_src.c, "", OFF),
+ CLK_DUMMY("", gp2_clk_src.c, "", OFF),
+ CLK_DUMMY("", gp3_clk_src.c, "", OFF),
+ CLK_DUMMY("", pcie_aux_clk_src.c, "", OFF),
+ CLK_DUMMY("", pcie_pipe_clk_src.c, "", OFF),
+ CLK_DUMMY("", pdm2_clk_src.c, "", OFF),
+ CLK_DUMMY("", pwm_clk_src.c, "", OFF),
+ CLK_DUMMY("", sata_asic0_clk_src.c, "", OFF),
+ CLK_DUMMY("", sata_pmalive_clk_src.c, "", OFF),
+ CLK_DUMMY("", sata_rx_clk_src.c, "", OFF),
+ CLK_DUMMY("", sata_rx_oob_clk_src.c, "", OFF),
+ CLK_DUMMY("", sdcc1_apps_clk_src.c, "", OFF),
+ CLK_DUMMY("", sdcc2_apps_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb30_mock_utmi_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hs_system_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hs2_system_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hsic_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hsic_io_cal_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hsic_system_clk_src.c, "", OFF),
+ CLK_DUMMY("", gcc_bam_dma_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_bcss_cfg_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_bimc_gfx_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_bimc_kpss_axi_mstr_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_bimc_sysnoc_axi_mstr_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup1_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup1_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup2_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup2_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup3_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup3_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup4_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup4_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup5_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup5_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup6_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup6_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart1_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart2_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart3_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart4_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart5_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart6_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup1_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup1_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup2_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup2_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup3_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup3_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup4_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup4_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup5_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup5_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup6_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup6_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart1_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart2_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart3_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart4_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart5_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart6_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_boot_rom_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce1_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce1_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce1_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce2_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce2_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce2_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce3_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce3_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce3_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_xo_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_xo_div4_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_geni_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_geni_ser_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gmac_125m_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gmac_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gmac_cfg_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gmac_core_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gmac_rx_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gmac_sys_25m_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gmac_sys_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gp1_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gp2_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gp3_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_klm_core_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_klm_s_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_lpass_q6_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sys_noc_lpass_mport_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sys_noc_lpass_sway_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_mmss_a5ss_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_mmss_bimc_gfx_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_pcie_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_pcie_axi_mstr_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_pcie_cfg_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_pcie_pipe_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_pcie_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_pdm2_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_pdm_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_prng_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_pwm_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_pwm_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_asic0_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_cfg_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_pmalive_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_rx_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_rx_oob_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc1_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc1_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc2_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc2_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_spss_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sys_noc_usb3_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb2a_phy_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb2b_phy_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb2c_phy_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb30_master_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb30_mock_utmi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb30_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hs_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hs_system_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hs2_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hs2_system_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_io_cal_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_system_clk.c, "", OFF),
+ /* MMSS Clock Dummy */
+ CLK_DUMMY("", axi_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmpll0_pll_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmpll1_pll_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmpll2_pll_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmpll3_pll_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmpll6_pll_clk_src.c, "", OFF),
+ CLK_DUMMY("", vcodec0_clk_src.c, "", OFF),
+ CLK_DUMMY("", extpclk_clk_src.c, "", OFF),
+ CLK_DUMMY("", lvds_clk_src.c, "", OFF),
+ CLK_DUMMY("", mdp_clk_src.c, "", OFF),
+ CLK_DUMMY("", vbyone_clk_src.c, "", OFF),
+ CLK_DUMMY("", gfx3d_clk_src.c, "", OFF),
+ CLK_DUMMY("", vp_clk_src.c, "", OFF),
+ CLK_DUMMY("", jpeg2_clk_src.c, "", OFF),
+ CLK_DUMMY("", hdmi_clk_src.c, "", OFF),
+ CLK_DUMMY("", vbyone_symbol_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmss_spdm_axi_div_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_spdm_gfx3d_div_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_spdm_jpeg2_div_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_spdm_mdp_div_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_spdm_vcodec0_div_clk.c, "", OFF),
+ CLK_DUMMY("", afe_pixel_clk_src.c, "", OFF),
+ CLK_DUMMY("", cfg_clk_src.c, "", OFF),
+ CLK_DUMMY("", hdmi_bus_clk_src.c, "", OFF),
+ CLK_DUMMY("", hdmi_rx_clk_src.c, "", OFF),
+ CLK_DUMMY("", md_clk_src.c, "", OFF),
+ CLK_DUMMY("", ttl_clk_src.c, "", OFF),
+ CLK_DUMMY("", vafe_ext_clk_src.c, "", OFF),
+ CLK_DUMMY("", vcap_vp_clk_src.c, "", OFF),
+ CLK_DUMMY("", gproc_clk_src.c, "", OFF),
+ CLK_DUMMY("", hdmc_frcf_clk_src.c, "", OFF),
+ CLK_DUMMY("", kproc_clk_src.c, "", OFF),
+ CLK_DUMMY("", maple_clk_src.c, "", OFF),
+ CLK_DUMMY("", preproc_clk_src.c, "", OFF),
+ CLK_DUMMY("", sdmc_frcs_clk_src.c, "", OFF),
+ CLK_DUMMY("", sdme_frcf_clk_src.c, "", OFF),
+ CLK_DUMMY("", sdme_vproc_clk_src.c, "", OFF),
+ CLK_DUMMY("", vdp_clk_src.c, "", OFF),
+ CLK_DUMMY("", vpu_bus_clk_src.c, "", OFF),
+ CLK_DUMMY("", vpu_frc_xin_clk_src.c, "", OFF),
+ CLK_DUMMY("", vpu_vdp_xin_clk_src.c, "", OFF),
+ CLK_DUMMY("", avsync_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_extpclk_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_lvds_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_vbyone_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_vp_clk.c, "", OFF),
+ CLK_DUMMY("", camss_jpeg_jpeg2_clk.c, "", OFF),
+ CLK_DUMMY("", camss_jpeg_jpeg_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_jpeg_jpeg_axi_clk.c, "", OFF),
+ CLK_DUMMY("", camss_micro_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_top_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_axi_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_extpclk_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_hdmi_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_hdmi_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_lvds_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_mdp_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_mdp_lut_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_vbyone_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_vbyone_symbol_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_misc_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_mmssnoc_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_mmssnoc_axi_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_s0_axi_clk.c, "", OFF),
+ CLK_DUMMY("core_clk", ocmemgx_core_clk.c, "fdd00000.qcom,ocmem", OFF),
+ CLK_DUMMY("iface_clk", ocmemcx_ocmemnoc_clk.c, "fdd00000.qcom.ocmem",
+ OFF),
+ CLK_DUMMY("", oxili_ocmemgx_clk.c, "", OFF),
+ CLK_DUMMY("", oxili_gfx3d_clk.c, "", OFF),
+ CLK_DUMMY("", oxilicx_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", bcss_mmss_ifdemod_clk.c, "", OFF),
+ CLK_DUMMY("", vcap_afe_pixel_clk.c, "", OFF),
+ CLK_DUMMY("", vcap_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", vcap_audio_clk.c, "", OFF),
+ CLK_DUMMY("", vcap_axi_clk.c, "", OFF),
+ CLK_DUMMY("", vcap_cfg_clk.c, "", OFF),
+ CLK_DUMMY("", vcap_hdmi_bus_clk.c, "", OFF),
+ CLK_DUMMY("", vcap_hdmi_rx_clk.c, "", OFF),
+ CLK_DUMMY("", vcap_md_clk.c, "", OFF),
+ CLK_DUMMY("", vcap_ttl_clk.c, "", OFF),
+ CLK_DUMMY("", vcap_ttl_debug_clk.c, "", OFF),
+ CLK_DUMMY("", vcap_vafe_ext_clk.c, "", OFF),
+ CLK_DUMMY("", vcap_vp_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_axi_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_core0_vcodec_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_core1_vcodec_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_ocmemnoc_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_vcodec0_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_axi_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_bus_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_cxo_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_frc_xin_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_gproc_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_hdmc_frcf_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_kproc_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_maple_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_preproc_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_sdmc_frcs_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_sdme_frcf_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_sdme_frcs_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_sdme_vproc_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_vdp_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_vdp_xin_clk.c, "", OFF),
+ /* BCSS broadcast */
+ CLK_DUMMY("", bcc_dem_core_b_clk_src.c, "", OFF),
+ CLK_DUMMY("", adc_01_clk_src.c, "", OFF),
+ CLK_DUMMY("", bcc_adc_0_in_clk.c, "", OFF),
+ CLK_DUMMY("", bcc_dem_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", bcc_klm_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", bcc_lnb_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", bcc_tsc_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", bcc_tspp2_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", bcc_vbif_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_bcss_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", bcc_dem_atv_rxfe_clk.c, "", OFF),
+ CLK_DUMMY("", bcc_dem_atv_rxfe_resamp_clk.c, "", OFF),
+ CLK_DUMMY("", bcc_dem_core_clk_src.c, "", OFF),
+ CLK_DUMMY("", bcc_dem_core_div2_clk_src.c, "", OFF),
+ CLK_DUMMY("", bcc_dem_core_x2_b_clk_src.c, "", OFF),
+ CLK_DUMMY("", bcc_dem_core_x2_pre_cgf_clk.c, "", OFF),
+ CLK_DUMMY("", bcc_tsc_ci_clk.c, "", OFF),
+ CLK_DUMMY("", bcc_tsc_cicam_ts_clk_src.c, "", OFF),
+ CLK_DUMMY("", bcc_tsc_par_clk.c, "", OFF),
+ CLK_DUMMY("", bcc_tsc_ser_clk_src.c, "", OFF),
+ CLK_DUMMY("", bcc_tspp2_clk_src.c, "", OFF),
+ CLK_DUMMY("", dig_dem_core_b_div2_clk.c, "", OFF),
+ CLK_DUMMY("", atv_x5_pre_cgc_clk.c, "", OFF),
+ CLK_DUMMY("", bcc_albacore_cvbs_clk.c, "", OFF),
+ CLK_DUMMY("", bcc_atv_x1_clk.c, "", OFF),
+ CLK_DUMMY("", nidaq_out_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_bcss_axi_clk.c, "", OFF),
+ CLK_DUMMY("", bcc_lnb_core_clk.c, "", OFF),
+};
+
+struct clock_init_data mpq8092_clock_init_data __initdata = {
+ .table = msm_clocks_8092,
+ .size = ARRAY_SIZE(msm_clocks_8092),
+};
diff --git a/arch/arm/mach-msm/clock-8226.c b/arch/arm/mach-msm/clock-8226.c
index 18c922d..fd3ba14 100644
--- a/arch/arm/mach-msm/clock-8226.c
+++ b/arch/arm/mach-msm/clock-8226.c
@@ -24,6 +24,7 @@
#include <mach/rpm-regulator-smd.h>
#include <mach/socinfo.h>
#include <mach/rpm-smd.h>
+#include <mach/clock-generic.h>
#include "clock-local2.h"
#include "clock-pll.h"
@@ -169,14 +170,14 @@
VDD_DIG_NUM
};
-static const int *vdd_corner[] = {
- [VDD_DIG_NONE] = VDD_UV(RPM_REGULATOR_CORNER_NONE),
- [VDD_DIG_LOW] = VDD_UV(RPM_REGULATOR_CORNER_SVS_SOC),
- [VDD_DIG_NOMINAL] = VDD_UV(RPM_REGULATOR_CORNER_NORMAL),
- [VDD_DIG_HIGH] = VDD_UV(RPM_REGULATOR_CORNER_SUPER_TURBO),
+static int vdd_corner[] = {
+ RPM_REGULATOR_CORNER_NONE, /* VDD_DIG_NONE */
+ RPM_REGULATOR_CORNER_SVS_SOC, /* VDD_DIG_LOW */
+ RPM_REGULATOR_CORNER_NORMAL, /* VDD_DIG_NOMINAL */
+ RPM_REGULATOR_CORNER_SUPER_TURBO, /* VDD_DIG_HIGH */
};
-static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner);
+static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL);
#define RPM_MISC_CLK_TYPE 0x306b6c63
#define RPM_BUS_CLK_TYPE 0x316b6c63
@@ -1709,13 +1710,19 @@
F_MMSS( 100000000, gpll0, 6, 0, 0),
F_MMSS( 109090000, gpll0, 5.5, 0, 0),
F_MMSS( 133330000, gpll0, 4.5, 0, 0),
+ F_MMSS( 150000000, gpll0, 4, 0, 0),
F_MMSS( 200000000, gpll0, 3, 0, 0),
F_MMSS( 228570000, mmpll0_pll, 3.5, 0, 0),
F_MMSS( 266670000, mmpll0_pll, 3, 0, 0),
F_MMSS( 320000000, mmpll0_pll, 2.5, 0, 0),
+ F_MMSS( 400000000, mmpll0_pll, 2, 0, 0),
F_END
};
+static unsigned long camss_vfe_vfe0_fmax_v2[VDD_DIG_NUM] = {
+ 150000000, 320000000, 400000000,
+};
+
static struct rcg_clk vfe0_clk_src = {
.cmd_rcgr_reg = VFE0_CMD_RCGR,
.set_rate = set_rate_hid,
@@ -1783,27 +1790,73 @@
},
};
-static struct branch_clk mdss_ahb_clk;
-static struct clk dsipll0_byte_clk_src = {
- .depends = &mdss_ahb_clk.c,
- .parent = &xo.c,
- .dbg_name = "dsipll0_byte_clk_src",
- .ops = &clk_ops_dsi_byte_pll,
- CLK_INIT(dsipll0_byte_clk_src),
-};
+struct clk_ops clk_ops_pixel_clock;
-static struct clk dsipll0_pixel_clk_src = {
- .depends = &mdss_ahb_clk.c,
- .parent = &xo.c,
- .dbg_name = "dsipll0_pixel_clk_src",
- .ops = &clk_ops_dsi_pixel_pll,
- CLK_INIT(dsipll0_pixel_clk_src),
-};
+static long round_rate_pixel(struct clk *clk, unsigned long rate)
+{
+ int frac_num[] = {3, 2, 4, 1};
+ int frac_den[] = {8, 9, 9, 1};
+ int delta = 100000;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(frac_num); i++) {
+ unsigned long request = (rate * frac_den[i]) / frac_num[i];
+ unsigned long src_rate;
+
+ src_rate = clk_round_rate(clk->parent, request);
+ if ((src_rate < (request - delta)) ||
+ (src_rate > (request + delta)))
+ continue;
+
+ return (src_rate * frac_num[i]) / frac_den[i];
+ }
+
+ return -EINVAL;
+}
+
+
+static int set_rate_pixel(struct clk *clk, unsigned long rate)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ struct clk_freq_tbl *pixel_freq = rcg->current_freq;
+ int frac_num[] = {3, 2, 4, 1};
+ int frac_den[] = {8, 9, 9, 1};
+ int delta = 100000;
+ int i, rc;
+
+ for (i = 0; i < ARRAY_SIZE(frac_num); i++) {
+ unsigned long request = (rate * frac_den[i]) / frac_num[i];
+ unsigned long src_rate;
+
+ src_rate = clk_round_rate(clk->parent, request);
+ if ((src_rate < (request - delta)) ||
+ (src_rate > (request + delta)))
+ continue;
+
+ rc = clk_set_rate(clk->parent, src_rate);
+ if (rc)
+ return rc;
+
+ pixel_freq->div_src_val &= ~BM(4, 0);
+ if (frac_den[i] == frac_num[i]) {
+ pixel_freq->m_val = 0;
+ pixel_freq->n_val = 0;
+ } else {
+ pixel_freq->m_val = frac_num[i];
+ pixel_freq->n_val = ~(frac_den[i] - frac_num[i]);
+ pixel_freq->d_val = ~frac_den[i];
+ }
+ set_rate_mnd(rcg, pixel_freq);
+ return 0;
+ }
+ return -EINVAL;
+}
static struct clk_freq_tbl pixel_freq_tbl[] = {
{
- .src_clk = &dsipll0_pixel_clk_src,
- .div_src_val = BVAL(10, 8, dsipll0_pixel_mm_source_val),
+ .src_clk = &pixel_clk_src_8226.c,
+ .div_src_val = BVAL(10, 8, dsipll0_pixel_mm_source_val)
+ | BVAL(4, 0, 0),
},
F_END
};
@@ -1813,7 +1866,7 @@
.current_freq = pixel_freq_tbl,
.base = &virt_bases[MMSS_BASE],
.c = {
- .parent = &dsipll0_pixel_clk_src,
+ .parent = &pixel_clk_src_8226.c,
.dbg_name = "pclk0_clk_src",
.ops = &clk_ops_pixel,
VDD_DIG_FMAX_MAP2(LOW, 83330000, NOMINAL, 166670000),
@@ -1838,7 +1891,7 @@
.c = {
.dbg_name = "vcodec0_clk_src",
.ops = &clk_ops_rcg_mnd,
- VDD_DIG_FMAX_MAP3(LOW, 66670000, NOMINAL, 133330000, HIGH,
+ VDD_DIG_FMAX_MAP3(LOW, 66700000, NOMINAL, 133330000, HIGH,
160000000),
CLK_INIT(vcodec0_clk_src.c),
},
@@ -1972,11 +2025,17 @@
static struct clk_freq_tbl ftbl_camss_vfe_cpp_clk[] = {
F_MMSS( 133330000, gpll0, 4.5, 0, 0),
+ F_MMSS( 150000000, gpll0, 4, 0, 0),
F_MMSS( 266670000, mmpll0_pll, 3, 0, 0),
F_MMSS( 320000000, mmpll0_pll, 2.5, 0, 0),
+ F_MMSS( 400000000, mmpll0_pll, 2, 0, 0),
F_END
};
+static unsigned long camss_vfe_cpp_fmax_v2[VDD_DIG_NUM] = {
+ 150000000, 320000000, 400000000,
+};
+
static struct rcg_clk cpp_clk_src = {
.cmd_rcgr_reg = CPP_CMD_RCGR,
.set_rate = set_rate_hid,
@@ -1994,7 +2053,7 @@
static struct clk_freq_tbl byte_freq_tbl[] = {
{
- .src_clk = &dsipll0_byte_clk_src,
+ .src_clk = &byte_clk_src_8226.c,
.div_src_val = BVAL(10, 8, dsipll0_byte_mm_source_val),
},
F_END
@@ -2005,7 +2064,7 @@
.current_freq = byte_freq_tbl,
.base = &virt_bases[MMSS_BASE],
.c = {
- .parent = &dsipll0_byte_clk_src,
+ .parent = &byte_clk_src_8226.c,
.dbg_name = "byte0_clk_src",
.ops = &clk_ops_byte,
VDD_DIG_FMAX_MAP2(LOW, 62500000, NOMINAL, 125000000),
@@ -2697,7 +2756,6 @@
.base = &virt_bases[LPASS_BASE],
.c = {
.dbg_name = "q6ss_xo_clk",
- .parent = &xo.c,
.ops = &clk_ops_branch,
CLK_INIT(q6ss_xo_clk.c),
},
@@ -2742,19 +2800,21 @@
VDD_SR2_PLL_NUM
};
-static const int *vdd_sr2_levels[] = {
- [VDD_SR2_PLL_OFF] = VDD_UV(0, RPM_REGULATOR_CORNER_NONE),
- [VDD_SR2_PLL_SVS] = VDD_UV(1800000, RPM_REGULATOR_CORNER_SVS_SOC),
- [VDD_SR2_PLL_NOM] = VDD_UV(1800000, RPM_REGULATOR_CORNER_NORMAL),
- [VDD_SR2_PLL_TUR] = VDD_UV(1800000, RPM_REGULATOR_CORNER_SUPER_TURBO),
+static int vdd_sr2_levels[] = {
+ 0, RPM_REGULATOR_CORNER_NONE, /* VDD_SR2_PLL_OFF */
+ 1800000, RPM_REGULATOR_CORNER_SVS_SOC, /* VDD_SR2_PLL_SVS */
+ 1800000, RPM_REGULATOR_CORNER_NORMAL, /* VDD_SR2_PLL_NOM */
+ 1800000, RPM_REGULATOR_CORNER_SUPER_TURBO, /* VDD_SR2_PLL_TUR */
};
-static DEFINE_VDD_REGULATORS(vdd_sr2_pll, VDD_SR2_PLL_NUM, 2, vdd_sr2_levels);
+static DEFINE_VDD_REGULATORS(vdd_sr2_pll, VDD_SR2_PLL_NUM, 2,
+ vdd_sr2_levels, NULL);
static struct pll_freq_tbl apcs_pll_freq[] = {
- F_APCS_PLL( 384000000, 20, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 768000000, 40, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL( 787200000, 41, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL( 998400000, 52, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1094400000, 57, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL(1190400000, 62, 0x0, 0x1, 0x0, 0x0, 0x0),
PLL_F_END
};
@@ -2786,11 +2846,6 @@
},
.num_fmax = VDD_SR2_PLL_NUM,
CLK_INIT(a7sspll.c),
- /*
- * Need to skip handoff of the acpu pll to avoid
- * turning off the pll when the cpu is using it
- */
- .flags = CLKFLAG_SKIP_HANDOFF,
},
};
@@ -2811,8 +2866,9 @@
static DEFINE_CLK_VOTER(pnoc_sps_clk, &pnoc_clk.c, LONG_MAX);
-static DEFINE_CLK_VOTER(qseecom_ce1_clk_src, &ce1_clk_src.c, LONG_MAX);
-static DEFINE_CLK_VOTER(scm_ce1_clk_src, &ce1_clk_src.c, LONG_MAX);
+static DEFINE_CLK_VOTER(qseecom_ce1_clk_src, &ce1_clk_src.c, 100000000);
+static DEFINE_CLK_VOTER(scm_ce1_clk_src, &ce1_clk_src.c, 100000000);
+static DEFINE_CLK_VOTER(gud_ce1_clk_src, &ce1_clk_src.c, 100000000);
static DEFINE_CLK_BRANCH_VOTER(cxo_otg_clk, &xo.c);
static DEFINE_CLK_BRANCH_VOTER(cxo_pil_lpass_clk, &xo.c);
@@ -3110,6 +3166,7 @@
CLK_LOOKUP("core_clk", qdss_clk.c, "fc342000.cti"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc343000.cti"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc344000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fd828018.hwevent"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc322000.tmc"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc318000.tpiu"),
@@ -3125,10 +3182,10 @@
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33d000.etm"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33e000.etm"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33f000.etm"),
- CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc33c000.jtagmm"),
- CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc33d000.jtagmm"),
- CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc33e000.jtagmm"),
- CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc33f000.jtagmm"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33c000.jtagmm"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33d000.jtagmm"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33e000.jtagmm"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33f000.jtagmm"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc308000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc309000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30a000.cti"),
@@ -3143,6 +3200,9 @@
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc342000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc343000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc344000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fd828018.hwevent"),
+
+ CLK_LOOKUP("core_mmss_clk", mmss_misc_ahb_clk.c, "fd828018.hwevent"),
/* HSUSB-OTG Clocks */
CLK_LOOKUP("xo", cxo_otg_clk.c, "f9a55000.usb"),
@@ -3177,6 +3237,11 @@
CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, "qseecom"),
CLK_LOOKUP("core_clk_src", qseecom_ce1_clk_src.c, "qseecom"),
+ CLK_LOOKUP("core_clk", gcc_ce1_clk.c, "mcd"),
+ CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, "mcd"),
+ CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, "mcd"),
+ CLK_LOOKUP("core_clk_src", gud_ce1_clk_src.c, "mcd"),
+
CLK_LOOKUP("core_clk", gcc_ce1_clk.c, "scm"),
CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, "scm"),
CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, "scm"),
@@ -3254,7 +3319,8 @@
CLK_LOOKUP("byte_clk", mdss_byte0_clk.c, "fd922800.qcom,mdss_dsi"),
CLK_LOOKUP("core_clk", mdss_esc0_clk.c, "fd922800.qcom,mdss_dsi"),
CLK_LOOKUP("pixel_clk", mdss_pclk0_clk.c, "fd922800.qcom,mdss_dsi"),
- CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "mdss_dsi_clk_ctrl"),
+ CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd922800.qcom,mdss_dsi"),
+ CLK_LOOKUP("bus_clk", mdss_axi_clk.c, "fd922800.qcom,mdss_dsi"),
CLK_LOOKUP("core_clk", mdss_mdp_clk.c, "fd900000.qcom,mdss_mdp"),
CLK_LOOKUP("lut_clk", mdss_mdp_lut_clk.c, "fd900000.qcom,mdss_mdp"),
@@ -3266,6 +3332,15 @@
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd928000.qcom,iommu"),
CLK_LOOKUP("core_clk", mdss_axi_clk.c, "fd928000.qcom,iommu"),
+ CLK_LOOKUP("core_clk", venus0_vcodec0_clk.c, "fd8c1024.qcom,gdsc"),
+ CLK_LOOKUP("core_clk", mdss_mdp_clk.c, "fd8c2304.qcom,gdsc"),
+ CLK_LOOKUP("lut_clk", mdss_mdp_lut_clk.c, "fd8c2304.qcom,gdsc"),
+ CLK_LOOKUP("core_clk", camss_jpeg_jpeg0_clk.c, "fd8c35a4.qcom,gdsc"),
+ CLK_LOOKUP("core_clk", camss_vfe_vfe0_clk.c, "fd8c36a4.qcom,gdsc"),
+ CLK_LOOKUP("csi_clk", camss_csi_vfe0_clk.c, "fd8c36a4.qcom,gdsc"),
+ CLK_LOOKUP("cpp_clk", camss_vfe_cpp_clk.c, "fd8c36a4.qcom,gdsc"),
+ CLK_LOOKUP("core_clk", oxili_gfx3d_clk.c, "fd8c4034.qcom,gdsc"),
+
/* MM sensor clocks */
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6f.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "90.qcom,camera"),
@@ -3301,31 +3376,40 @@
"fda0b000.qcom,csiphy"),
/* CSID clocks */
- CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
- "fda08000.qcom,csid"),
CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
- "fda08000.qcom,csid"),
- CLK_LOOKUP("csi0_ahb_clk", camss_csi0_ahb_clk.c, "fda08000.qcom,csid"),
- CLK_LOOKUP("csi0_src_clk", csi0_clk_src.c, "fda08000.qcom,csid"),
- CLK_LOOKUP("csi0_phy_clk", camss_csi0phy_clk.c, "fda08000.qcom,csid"),
- CLK_LOOKUP("csi0_clk", camss_csi0_clk.c, "fda08000.qcom,csid"),
- CLK_LOOKUP("csi0_pix_clk", camss_csi0pix_clk.c, "fda08000.qcom,csid"),
- CLK_LOOKUP("csi0_rdi_clk", camss_csi0rdi_clk.c, "fda08000.qcom,csid"),
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("csi_ahb_clk", camss_csi0_ahb_clk.c,
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("csi_src_clk", csi0_clk_src.c,
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("csi_phy_clk", camss_csi0phy_clk.c,
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("csi_clk", camss_csi0_clk.c,
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("csi_pix_clk", camss_csi0pix_clk.c,
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("csi_rdi_clk", camss_csi0rdi_clk.c,
+ "fda08000.qcom,csid"),
- CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
- "fda08400.qcom,csid"),
+
CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
- "fda08400.qcom,csid"),
- CLK_LOOKUP("csi0_ahb_clk", camss_csi0_ahb_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi1_ahb_clk", camss_csi1_ahb_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi0_src_clk", csi0_clk_src.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi1_src_clk", csi1_clk_src.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi0_phy_clk", camss_csi0phy_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi1_phy_clk", camss_csi1phy_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi0_pix_clk", camss_csi0pix_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi1_pix_clk", camss_csi1pix_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi0_rdi_clk", camss_csi0rdi_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi1_rdi_clk", camss_csi1rdi_clk.c, "fda08400.qcom,csid"),
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("csi_ahb_clk", camss_csi1_ahb_clk.c,
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("csi_src_clk", csi1_clk_src.c,
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("csi_phy_clk", camss_csi1phy_clk.c,
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("csi_clk", camss_csi1_clk.c,
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("csi_pix_clk", camss_csi1pix_clk.c,
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("csi_rdi_clk", camss_csi1rdi_clk.c,
+ "fda08400.qcom,csid"),
/* ISPIF clocks */
CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
@@ -3417,6 +3501,25 @@
CLK_LOOKUP("osr_clk", div_clk1.c, "msm-dai-q6-dev.16390"),
CLK_LOOKUP("osr_clk", div_clk1.c, "msm-dai-q6-dev.16391"),
+ /* Add QCEDEV clocks */
+ CLK_LOOKUP("core_clk", gcc_ce1_clk.c, "fd400000.qcom,qcedev"),
+ CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, "fd400000.qcom,qcedev"),
+ CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, "fd400000.qcom,qcedev"),
+ CLK_LOOKUP("core_clk_src", ce1_clk_src.c, "fd400000.qcom,qcedev"),
+
+ /* Add QCRYPTO clocks */
+ CLK_LOOKUP("core_clk", gcc_ce1_clk.c, "fd404000.qcom,qcrypto"),
+ CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, "fd404000.qcom,qcrypto"),
+ CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, "fd404000.qcom,qcrypto"),
+ CLK_LOOKUP("core_clk_src", ce1_clk_src.c, "fd404000.qcom,qcrypto"),
+
+ /* DSI PLL clocks */
+ CLK_LOOKUP("", dsi_vco_clk_8226.c, ""),
+ CLK_LOOKUP("", analog_postdiv_clk_8226.c, ""),
+ CLK_LOOKUP("", indirect_path_div2_clk_8226.c, ""),
+ CLK_LOOKUP("", pixel_clk_src_8226.c, ""),
+ CLK_LOOKUP("", byte_mux_8226.c, ""),
+ CLK_LOOKUP("", byte_clk_src_8226.c, ""),
};
static struct clk_lookup msm_clocks_8226_rumi[] = {
@@ -3460,6 +3563,9 @@
*/
clk_prepare_enable(&xo_a_clk.c);
+ /* Set an initial rate (fmax at nominal) on the MMSSNOC AXI clock */
+ clk_set_rate(&axi_clk_src.c, 200000000);
+
/* Set rates for single-rate clocks. */
clk_set_rate(&usb_hs_system_clk_src.c,
usb_hs_system_clk_src.freq_tbl[0].freq_hz);
@@ -3539,13 +3645,20 @@
*/
clk_set_rate(&mmssnoc_ahb_a_clk.c, 40000000);
- /* Set an initial rate (fmax at nominal) on the MMSSNOC AXI clock */
- clk_set_rate(&axi_clk_src.c, 200000000);
-
enable_rpm_scaling();
reg_init();
+ /* v2 specific changes */
+ if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) {
+ cpp_clk_src.c.fmax = camss_vfe_cpp_fmax_v2;
+ vfe0_clk_src.c.fmax = camss_vfe_vfe0_fmax_v2;
+ }
+
+ clk_ops_pixel_clock = clk_ops_pixel;
+ clk_ops_pixel_clock.set_rate = set_rate_pixel;
+ clk_ops_pixel_clock.round_rate = round_rate_pixel;
+
/*
* MDSS needs the ahb clock and needs to init before we register the
* lookup table.
diff --git a/arch/arm/mach-msm/clock-8610.c b/arch/arm/mach-msm/clock-8610.c
index fae09d0..58c5745 100644
--- a/arch/arm/mach-msm/clock-8610.c
+++ b/arch/arm/mach-msm/clock-8610.c
@@ -30,6 +30,7 @@
#include "clock-rpm.h"
#include "clock-voter.h"
#include "clock.h"
+#include "clock-dsi-8610.h"
enum {
GCC_BASE,
@@ -255,6 +256,7 @@
#define MMSS_MMSSNOC_AXI_CBCR 0x506C
#define BIMC_GFX_BCR 0x5090
#define BIMC_GFX_CBCR 0x5094
+#define MMSS_CAMSS_MISC 0x3718
#define AUDIO_CORE_GDSCR 0x7000
#define SPDM_BCR 0x1000
@@ -432,14 +434,14 @@
VDD_DIG_NUM
};
-static const int *vdd_corner[] = {
- [VDD_DIG_NONE] = VDD_UV(RPM_REGULATOR_CORNER_NONE),
- [VDD_DIG_LOW] = VDD_UV(RPM_REGULATOR_CORNER_SVS_SOC),
- [VDD_DIG_NOMINAL] = VDD_UV(RPM_REGULATOR_CORNER_NORMAL),
- [VDD_DIG_HIGH] = VDD_UV(RPM_REGULATOR_CORNER_SUPER_TURBO),
+static int vdd_corner[] = {
+ RPM_REGULATOR_CORNER_NONE, /* VDD_DIG_NONE */
+ RPM_REGULATOR_CORNER_SVS_SOC, /* VDD_DIG_LOW */
+ RPM_REGULATOR_CORNER_NORMAL, /* VDD_DIG_NOMINAL */
+ RPM_REGULATOR_CORNER_SUPER_TURBO, /* VDD_DIG_HIGH */
};
-static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner);
+static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL);
#define RPM_MISC_CLK_TYPE 0x306b6c63
#define RPM_BUS_CLK_TYPE 0x316b6c63
@@ -507,7 +509,6 @@
static DEFINE_CLK_VOTER(pnoc_sps_clk, &pnoc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(pnoc_iommu_clk, &pnoc_clk.c, LONG_MAX);
-static DEFINE_CLK_VOTER(pnoc_qseecom_clk, &pnoc_clk.c, LONG_MAX);
static DEFINE_CLK_MEASURE(apc0_m_clk);
static DEFINE_CLK_MEASURE(apc1_m_clk);
@@ -531,17 +532,18 @@
VDD_SR2_PLL_NUM
};
-static const int *vdd_sr2_levels[] = {
- [VDD_SR2_PLL_OFF] = VDD_UV(0, RPM_REGULATOR_CORNER_NONE),
- [VDD_SR2_PLL_SVS] = VDD_UV(1800000, RPM_REGULATOR_CORNER_SVS_SOC),
- [VDD_SR2_PLL_NOM] = VDD_UV(1800000, RPM_REGULATOR_CORNER_NORMAL),
- [VDD_SR2_PLL_TUR] = VDD_UV(1800000, RPM_REGULATOR_CORNER_SUPER_TURBO),
+static int vdd_sr2_levels[] = {
+ 0, RPM_REGULATOR_CORNER_NONE, /* VDD_SR2_PLL_OFF */
+ 1800000, RPM_REGULATOR_CORNER_SVS_SOC, /* VDD_SR2_PLL_SVS */
+ 1800000, RPM_REGULATOR_CORNER_NORMAL, /* VDD_SR2_PLL_NOM */
+ 1800000, RPM_REGULATOR_CORNER_SUPER_TURBO, /* VDD_SR2_PLL_TUR */
};
-static DEFINE_VDD_REGULATORS(vdd_sr2_pll, VDD_SR2_PLL_NUM, 2, vdd_sr2_levels);
+static DEFINE_VDD_REGULATORS(vdd_sr2_pll, VDD_SR2_PLL_NUM, 2,
+ vdd_sr2_levels, NULL);
static struct pll_freq_tbl apcs_pll_freq[] = {
- F_APCS_PLL( 384000000, 20, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 768000000, 40, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL( 787200000, 41, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL( 998400000, 52, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL(1190400000, 62, 0x0, 0x1, 0x0, 0x0, 0x0),
@@ -575,11 +577,6 @@
},
.num_fmax = VDD_SR2_PLL_NUM,
CLK_INIT(a7sspll.c),
- /*
- * Need to skip handoff of the acpu pll to avoid
- * turning off the pll when the cpu is using it
- */
- .flags = CLKFLAG_SKIP_HANDOFF,
},
};
@@ -1543,6 +1540,7 @@
F_END,
};
+static struct branch_clk mmss_mmssnoc_axi_clk;
static struct rcg_clk axi_clk_src = {
.cmd_rcgr_reg = AXI_CMD_RCGR,
.set_rate = set_rate_hid,
@@ -1554,27 +1552,92 @@
.ops = &clk_ops_rcg,
VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000),
CLK_INIT(axi_clk_src.c),
+ .depends = &mmss_mmssnoc_axi_clk.c
},
};
static DEFINE_CLK_VOTER(mdp_axi_clk_src, &axi_clk_src.c, 200000000);
static DEFINE_CLK_VOTER(mmssnoc_axi_clk_src, &axi_clk_src.c, 200000000);
-static struct clk_freq_tbl ftbl_dsi_pclk_clk[] = {
- F_MDSS( 50000000, dsipll, 10, 0, 0),
- F_MDSS(103330000, dsipll, 9, 0, 0),
- F_END,
+static struct clk_ops dsi_byte_clk_src_ops;
+static struct clk_ops dsi_pixel_clk_src_ops;
+static struct clk_ops dsi_dsi_clk_src_ops;
+
+static struct dsi_pll_vco_clk dsi_vco = {
+ .vco_clk_min = 600000000,
+ .vco_clk_max = 1200000000,
+ .pref_div_ratio = 26,
+ .c = {
+ .parent = &gcc_xo_clk_src.c,
+ .dbg_name = "dsi_vco",
+ .ops = &clk_ops_dsi_vco,
+ CLK_INIT(dsi_vco.c),
+ },
};
+static struct clk dsi_pll_byte = {
+ .parent = &dsi_vco.c,
+ .dbg_name = "dsi_pll_byte",
+ .ops = &clk_ops_dsi_byteclk,
+ CLK_INIT(dsi_pll_byte),
+};
+
+static struct clk dsi_pll_pixel = {
+ .parent = &dsi_vco.c,
+ .dbg_name = "dsi_pll_pixel",
+ .ops = &clk_ops_dsi_dsiclk,
+ CLK_INIT(dsi_pll_pixel),
+};
+
+static struct clk_freq_tbl pixel_freq_tbl[] = {
+ {
+ .src_clk = &dsi_pll_pixel,
+ .div_src_val = BVAL(10, 8, dsipll_mm_source_val),
+ },
+ F_END
+};
+
+#define CFG_RCGR_DIV_MASK BM(4, 0)
+
+static int set_rate_pixel_byte_clk(struct clk *clk, unsigned long rate)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ struct clk *pll = clk->parent;
+ unsigned long source_rate, div;
+ struct clk_freq_tbl *cur_freq = rcg->current_freq;
+ int rc;
+
+ if (rate == 0)
+ return clk_set_rate(pll, 0);
+
+ source_rate = clk_round_rate(pll, rate);
+ if (!source_rate || ((2 * source_rate) % rate))
+ return -EINVAL;
+
+ div = ((2 * source_rate)/rate) - 1;
+ if (div > CFG_RCGR_DIV_MASK)
+ return -EINVAL;
+
+ rc = clk_set_rate(pll, source_rate);
+ if (rc)
+ return rc;
+
+ cur_freq->div_src_val &= ~CFG_RCGR_DIV_MASK;
+ cur_freq->div_src_val |= BVAL(4, 0, div);
+ rcg->set_rate(rcg, cur_freq);
+
+ return 0;
+}
+
static struct rcg_clk dsi_pclk_clk_src = {
.cmd_rcgr_reg = DSI_PCLK_CMD_RCGR,
.set_rate = set_rate_mnd,
- .freq_tbl = ftbl_dsi_pclk_clk,
- .current_freq = &rcg_dummy_freq,
+ .current_freq = pixel_freq_tbl,
.base = &virt_bases[MMSS_BASE],
.c = {
+ .parent = &dsi_pll_pixel,
.dbg_name = "dsi_pclk_clk_src",
- .ops = &clk_ops_rcg_mnd,
+ .ops = &dsi_pixel_clk_src_ops,
VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 103330000),
CLK_INIT(dsi_pclk_clk_src.c),
},
@@ -1686,41 +1749,60 @@
},
};
-static struct clk_freq_tbl ftbl_dsi_clk[] = {
- F_MDSS(155000000, dsipll, 6, 0, 0),
- F_MDSS(310000000, dsipll, 3, 0, 0),
- F_END,
+/*
+ * The DSI clock will always use a divider of 1. However, we still
+ * need to set the right voltage and source.
+ */
+static int set_rate_dsi_clk(struct clk *clk, unsigned long rate)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ struct clk_freq_tbl *cur_freq = rcg->current_freq;
+
+ rcg->set_rate(rcg, cur_freq);
+
+ return 0;
+}
+
+static struct clk_freq_tbl dsi_freq_tbl[] = {
+ {
+ .src_clk = &dsi_pll_pixel,
+ .div_src_val = BVAL(4, 0, 0) |
+ BVAL(10, 8, dsipll_mm_source_val),
+ },
+ F_END
};
static struct rcg_clk dsi_clk_src = {
.cmd_rcgr_reg = DSI_CMD_RCGR,
.set_rate = set_rate_mnd,
- .freq_tbl = ftbl_dsi_clk,
- .current_freq = &rcg_dummy_freq,
+ .current_freq = dsi_freq_tbl,
.base = &virt_bases[MMSS_BASE],
.c = {
+ .parent = &dsi_pll_pixel,
.dbg_name = "dsi_clk_src",
- .ops = &clk_ops_rcg_mnd,
+ .ops = &dsi_dsi_clk_src_ops,
VDD_DIG_FMAX_MAP2(LOW, 155000000, NOMINAL, 310000000),
CLK_INIT(dsi_clk_src.c),
},
};
-static struct clk_freq_tbl ftbl_dsi_byte_clk[] = {
- F_MDSS( 62500000, dsipll, 12, 0, 0),
- F_MDSS(125000000, dsipll, 6, 0, 0),
- F_END,
+static struct clk_freq_tbl byte_freq_tbl[] = {
+ {
+ .src_clk = &dsi_pll_byte,
+ .div_src_val = BVAL(10, 8, dsipll_mm_source_val),
+ },
+ F_END
};
static struct rcg_clk dsi_byte_clk_src = {
.cmd_rcgr_reg = DSI_BYTE_CMD_RCGR,
.set_rate = set_rate_hid,
- .freq_tbl = ftbl_dsi_byte_clk,
- .current_freq = &rcg_dummy_freq,
+ .current_freq = byte_freq_tbl,
.base = &virt_bases[MMSS_BASE],
.c = {
+ .parent = &dsi_pll_byte,
.dbg_name = "dsi_byte_clk_src",
- .ops = &clk_ops_rcg,
+ .ops = &dsi_byte_clk_src_ops,
VDD_DIG_FMAX_MAP2(LOW, 62500000, NOMINAL, 125000000),
CLK_INIT(dsi_byte_clk_src.c),
},
@@ -1746,6 +1828,7 @@
};
static struct clk_freq_tbl ftbl_mclk0_1_clk[] = {
+ F_MM(24000000, gpll0, 5, 1, 5),
F_MM(66670000, gpll0, 9, 0, 0),
F_END,
};
@@ -1932,6 +2015,115 @@
},
};
+static struct cam_mux_clk csi0phy_cam_mux_clk = {
+ .enable_reg = MMSS_CAMSS_MISC,
+ .enable_mask = BIT(11),
+ .select_reg = MMSS_CAMSS_MISC,
+ .select_mask = BIT(9),
+ .sources = (struct mux_source[]) {
+ { &csi0phy_clk.c, 0 },
+ { &csi1phy_clk.c, BIT(9) },
+ { 0 },
+ },
+ .base = &virt_bases[MMSS_BASE],
+ .c = {
+ .dbg_name = "csi0phy_cam_mux_clk",
+ .ops = &clk_ops_cam_mux,
+ CLK_INIT(csi0phy_cam_mux_clk.c),
+ },
+};
+
+static struct cam_mux_clk csi1phy_cam_mux_clk = {
+ .enable_reg = MMSS_CAMSS_MISC,
+ .enable_mask = BIT(10),
+ .select_reg = MMSS_CAMSS_MISC,
+ .select_mask = BIT(8),
+ .sources = (struct mux_source[]) {
+ { &csi0phy_clk.c, 0 },
+ { &csi1phy_clk.c, BIT(8) },
+ { 0 },
+ },
+ .base = &virt_bases[MMSS_BASE],
+ .c = {
+ .dbg_name = "csi1phy_cam_mux_clk",
+ .ops = &clk_ops_cam_mux,
+ CLK_INIT(csi1phy_cam_mux_clk.c),
+ },
+};
+
+static struct cam_mux_clk csi0pix_cam_mux_clk = {
+ .enable_reg = MMSS_CAMSS_MISC,
+ .enable_mask = BIT(7),
+ .select_reg = MMSS_CAMSS_MISC,
+ .select_mask = BIT(3),
+ .sources = (struct mux_source[]) {
+ { &csi0pix_clk.c, 0 },
+ { &csi1pix_clk.c, BIT(3) },
+ { 0 },
+ },
+ .base = &virt_bases[MMSS_BASE],
+ .c = {
+ .dbg_name = "csi0pix_cam_mux_clk",
+ .ops = &clk_ops_cam_mux,
+ CLK_INIT(csi0pix_cam_mux_clk.c),
+ },
+};
+
+
+static struct cam_mux_clk rdi2_cam_mux_clk = {
+ .enable_reg = MMSS_CAMSS_MISC,
+ .enable_mask = BIT(6),
+ .select_reg = MMSS_CAMSS_MISC,
+ .select_mask = BIT(2),
+ .sources = (struct mux_source[]) {
+ { &csi0rdi_clk.c, 0 },
+ { &csi1rdi_clk.c, BIT(2) },
+ { 0 },
+ },
+ .base = &virt_bases[MMSS_BASE],
+ .c = {
+ .dbg_name = "rdi2_cam_mux_clk",
+ .ops = &clk_ops_cam_mux,
+ CLK_INIT(rdi2_cam_mux_clk.c),
+ },
+};
+
+static struct cam_mux_clk rdi1_cam_mux_clk = {
+ .enable_reg = MMSS_CAMSS_MISC,
+ .enable_mask = BIT(5),
+ .select_reg = MMSS_CAMSS_MISC,
+ .select_mask = BIT(1),
+ .sources = (struct mux_source[]) {
+ { &csi0rdi_clk.c, 0 },
+ { &csi1rdi_clk.c, BIT(1) },
+ { 0 },
+ },
+ .base = &virt_bases[MMSS_BASE],
+ .c = {
+ .dbg_name = "rdi1_cam_mux_clk",
+ .ops = &clk_ops_cam_mux,
+ CLK_INIT(rdi1_cam_mux_clk.c),
+ },
+};
+
+static struct cam_mux_clk rdi0_cam_mux_clk = {
+ .enable_reg = MMSS_CAMSS_MISC,
+ .enable_mask = BIT(4),
+ .select_reg = MMSS_CAMSS_MISC,
+ .select_mask = BIT(0),
+ .sources = (struct mux_source[]) {
+ { &csi0rdi_clk.c, 0 },
+ { &csi1rdi_clk.c, BIT(0) },
+ { 0 },
+ },
+ .base = &virt_bases[MMSS_BASE],
+ .c = {
+ .dbg_name = "rdi0_cam_mux_clk",
+ .ops = &clk_ops_cam_mux,
+ CLK_INIT(rdi0_cam_mux_clk.c),
+ },
+};
+
static struct branch_clk csi_ahb_clk = {
.cbcr_reg = CSI_AHB_CBCR,
.has_sibling = 1,
@@ -2004,7 +2196,6 @@
static struct branch_clk dsi_pclk_clk = {
.cbcr_reg = DSI_PCLK_CBCR,
- .has_sibling = 1,
.base = &virt_bases[MMSS_BASE],
.c = {
.parent = &dsi_pclk_clk_src.c,
@@ -2126,7 +2317,6 @@
.has_sibling = 1,
.base = &virt_bases[MMSS_BASE],
.c = {
- .parent = &axi_clk_src.c,
.dbg_name = "mmss_mmssnoc_axi_clk",
.ops = &clk_ops_branch,
CLK_INIT(mmss_mmssnoc_axi_clk.c),
@@ -2142,7 +2332,6 @@
.dbg_name = "mmss_s0_axi_clk",
.ops = &clk_ops_branch,
CLK_INIT(mmss_s0_axi_clk.c),
- .depends = &mmss_mmssnoc_axi_clk.c,
},
};
@@ -2245,7 +2434,6 @@
.bcr_reg = LPASS_Q6SS_BCR,
.base = &virt_bases[LPASS_BASE],
.c = {
- .parent = &gcc_xo_clk_src.c,
.dbg_name = "q6ss_xo_clk",
.ops = &clk_ops_branch,
CLK_INIT(q6ss_xo_clk.c),
@@ -2303,7 +2491,7 @@
{ &gcc_ce1_axi_clk.c, GCC_BASE, 0x0139},
{ &gcc_ce1_ahb_clk.c, GCC_BASE, 0x013a},
{ &gcc_xo_clk_src.c, GCC_BASE, 0x0149},
- { &bimc_clk.c, GCC_BASE, 0x0154},
+ { &bimc_clk.c, GCC_BASE, 0x015d},
{ &gcc_bimc_smmu_clk.c, GCC_BASE, 0x015e},
{ &gcc_lpass_q6_axi_clk.c, GCC_BASE, 0x0160},
@@ -2347,11 +2535,11 @@
{ &q6ss_ahb_lfabif_clk.c, LPASS_BASE, 0x001e},
{ &q6ss_xo_clk.c, LPASS_BASE, 0x002b},
- {&apc0_m_clk, APCS_BASE, 0x10},
- {&apc1_m_clk, APCS_BASE, 0x11},
- {&apc2_m_clk, APCS_BASE, 0x12},
- {&apc3_m_clk, APCS_BASE, 0x13},
- {&l2_m_clk, APCS_BASE, 0x15},
+ {&apc0_m_clk, APCS_BASE, 0x00010},
+ {&apc1_m_clk, APCS_BASE, 0x00114},
+ {&apc2_m_clk, APCS_BASE, 0x00220},
+ {&apc3_m_clk, APCS_BASE, 0x00324},
+ {&l2_m_clk, APCS_BASE, 0x01000},
{&dummy_clk, N_BASES, 0x0000},
};
@@ -2418,6 +2606,8 @@
clk->multiplier = 4;
clk_sel = 0x16A;
regval = measure_mux[i].debug_mux;
+ /* Use a divider value of 4. */
+ regval |= BVAL(31, 30, 0x3);
writel_relaxed(regval, APCS_REG_BASE(GLB_CLK_DIAG));
break;
@@ -2569,7 +2759,6 @@
CLK_LOOKUP("core_clk", gcc_blsp1_uart2_apps_clk.c, "f991e000.serial"),
CLK_LOOKUP("dfab_clk", pnoc_sps_clk.c, "msm_sps"),
- CLK_LOOKUP("bus_clk", pnoc_qseecom_clk.c, "qseecom"),
CLK_LOOKUP("bus_clk", snoc_clk.c, ""),
CLK_LOOKUP("bus_clk", pnoc_clk.c, ""),
@@ -2624,6 +2813,7 @@
CLK_LOOKUP("core_clk", qdss_clk.c, "fc34d000.jtagmm"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc34e000.jtagmm"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc34f000.jtagmm"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fd820018.hwevent"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc326000.tmc"),
@@ -2657,8 +2847,9 @@
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc34d000.jtagmm"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc34e000.jtagmm"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc34f000.jtagmm"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fd820018.hwevent"),
-
+ CLK_LOOKUP("core_mmss_clk", mmss_misc_ahb_clk.c, "fd820018.hwevent"),
CLK_LOOKUP("core_clk_src", blsp1_qup1_spi_apps_clk_src.c, ""),
CLK_LOOKUP("core_clk_src", blsp1_qup2_spi_apps_clk_src.c, ""),
@@ -2680,19 +2871,23 @@
CLK_LOOKUP("core_clk_src", sdcc1_apps_clk_src.c, ""),
CLK_LOOKUP("core_clk_src", sdcc2_apps_clk_src.c, ""),
CLK_LOOKUP("core_clk_src", usb_hs_system_clk_src.c, ""),
+ CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f9923000.i2c"),
CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f9925000.i2c"),
- CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f9923000.spi"),
- CLK_LOOKUP("core_clk", gcc_blsp1_qup1_i2c_apps_clk.c, ""),
- CLK_LOOKUP("core_clk", gcc_blsp1_qup1_spi_apps_clk.c, "f9923000.spi"),
+ CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f9927000.i2c"),
+ CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f9926000.spi"),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup1_i2c_apps_clk.c, "f9923000.i2c"),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup1_spi_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup2_i2c_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup2_spi_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup3_i2c_apps_clk.c, "f9925000.i2c"),
CLK_LOOKUP("core_clk", gcc_blsp1_qup3_spi_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup4_i2c_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup4_spi_apps_clk.c, "f9926000.spi"),
CLK_LOOKUP("core_clk", gcc_blsp1_qup4_spi_apps_clk.c, ""),
- CLK_LOOKUP("core_clk", gcc_blsp1_qup5_i2c_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup5_i2c_apps_clk.c, "f9927000.i2c"),
CLK_LOOKUP("core_clk", gcc_blsp1_qup5_spi_apps_clk.c, ""),
- CLK_LOOKUP("core_clk", gcc_blsp1_qup6_i2c_apps_clk.c, ""),
+ CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f9928000.i2c"),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup6_i2c_apps_clk.c, "f9928000.i2c"),
CLK_LOOKUP("core_clk", gcc_blsp1_qup6_spi_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_uart1_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_uart2_apps_clk.c, ""),
@@ -2774,6 +2969,13 @@
CLK_LOOKUP("core_clk", vfe_ahb_clk.c, ""),
CLK_LOOKUP("core_clk", vfe_axi_clk.c, ""),
+ CLK_LOOKUP("core_clk", csi0pix_cam_mux_clk.c, ""),
+ CLK_LOOKUP("core_clk", csi0phy_cam_mux_clk.c, ""),
+ CLK_LOOKUP("core_clk", csi1phy_cam_mux_clk.c, ""),
+ CLK_LOOKUP("core_clk", rdi2_cam_mux_clk.c, ""),
+ CLK_LOOKUP("core_clk", rdi1_cam_mux_clk.c, ""),
+ CLK_LOOKUP("core_clk", rdi0_cam_mux_clk.c, ""),
+
CLK_LOOKUP("core_clk", oxili_gfx3d_clk.c, "fdc00000.qcom,kgsl-3d0"),
CLK_LOOKUP("iface_clk", oxili_ahb_clk.c, "fdc00000.qcom,kgsl-3d0"),
CLK_LOOKUP("mem_iface_clk", bimc_gfx_clk.c, "fdc00000.qcom,kgsl-3d0"),
@@ -2796,6 +2998,77 @@
"fd010000.qcom,iommu"),
CLK_LOOKUP("core_clk", pnoc_iommu_clk.c, "fd010000.qcom,iommu"),
+ /* MM sensor clocks */
+ CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-006f"),
+ CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-006d"),
+ CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-006f"),
+ CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-006d"),
+
+
+ /* CSIPHY clocks */
+ CLK_LOOKUP("csiphy_timer_src_clk", csi0phytimer_clk_src.c,
+ "fda00c00.qcom,csiphy"),
+ CLK_LOOKUP("csiphy_timer_clk", csi0phytimer_clk.c,
+ "fda00c00.qcom,csiphy"),
+ CLK_LOOKUP("csiphy_timer_src_clk", csi1phytimer_clk_src.c,
+ "fda01000.qcom,csiphy"),
+ CLK_LOOKUP("csiphy_timer_clk", csi1phytimer_clk.c,
+ "fda01000.qcom,csiphy"),
+
+ /* CSID clocks */
+
+ CLK_LOOKUP("csi_ahb_clk", csi_ahb_clk.c, "fda00000.qcom,csid"),
+ CLK_LOOKUP("csi_src_clk", csi0_clk_src.c, "fda00000.qcom,csid"),
+ CLK_LOOKUP("csi_clk", csi0_clk.c, "fda00000.qcom,csid"),
+ CLK_LOOKUP("csi_phy_clk", csi0phy_clk.c, "fda00000.qcom,csid"),
+ CLK_LOOKUP("csi_pix_clk", csi0pix_clk.c, "fda00000.qcom,csid"),
+ CLK_LOOKUP("csi_rdi_clk", csi0rdi_clk.c, "fda00000.qcom,csid"),
+
+ /*CLK_LOOKUP("csi0_phy_mux_sel", csi0phy_mux_clk.c,
+ "fda00000.qcom,csid"),
+ CLK_LOOKUP("csi0_pix_mux_sel", csi0pix_mux_clk.c,
+ "fda00000.qcom,csid"),
+ CLK_LOOKUP("csi0_rdi_mux_sel", rdi0_mux_clk.c,
+ "fda00000.qcom,csid"),*/
+
+ CLK_LOOKUP("csi_ahb_clk", csi_ahb_clk.c, "fda00400.qcom,csid"),
+ CLK_LOOKUP("csi_src_clk", csi1_clk_src.c, "fda00400.qcom,csid"),
+ CLK_LOOKUP("csi_clk", csi1_clk.c, "fda00400.qcom,csid"),
+ CLK_LOOKUP("csi_phy_clk", csi1phy_clk.c, "fda00400.qcom,csid"),
+ CLK_LOOKUP("csi_pix_clk", csi1pix_clk.c, "fda00400.qcom,csid"),
+ CLK_LOOKUP("csi_rdi_clk", csi1rdi_clk.c, "fda00400.qcom,csid"),
+
+ /*CLK_LOOKUP("csi1_phy_mux_sel", csi1phy_mux_clk.c,
+ "fda00400.qcom,csid"),
+ CLK_LOOKUP("csi1_pix_mux_sel", csi0pix_mux_clk.c,
+ "fda00400.qcom,csid"),
+ CLK_LOOKUP("csi1_rdi_mux_sel", rdi1_mux_clk.c,
+ "fda00400.qcom,csid"),*/
+
+ /* ISPIF clocks */
+ CLK_LOOKUP("csi_src_clk", csi0_clk_src.c, "fda00800.qcom,ispif"),
+ CLK_LOOKUP("csi_src_clk", csi1_clk_src.c, "fda00800.qcom,ispif"),
+
+ CLK_LOOKUP("csi_pix_clk", csi0pix_clk.c,
+ "fda00800.qcom,ispif"),
+ CLK_LOOKUP("csi_rdi_clk", csi0rdi_clk.c,
+ "fda00800.qcom,ispif"),
+ CLK_LOOKUP("csi_pix1_clk", csi1pix_clk.c,
+ "fda00800.qcom,ispif"),
+ CLK_LOOKUP("csi_rdi1_clk", csi1rdi_clk.c,
+ "fda00800.qcom,ispif"),
+ CLK_LOOKUP("csi_rdi2_clk", csi1rdi_clk.c,
+ "fda00800.qcom,ispif"),
+
+ CLK_LOOKUP("vfe_clk_src", vfe_clk_src.c, "fde00000.qcom,vfe"),
+ CLK_LOOKUP("vfe_clk", vfe_clk.c, "fde00000.qcom,vfe"),
+
+ CLK_LOOKUP("csi_vfe_clk", csi_vfe_clk.c, "fde00000.qcom,vfe"),
+ CLK_LOOKUP("vfe_ahb_clk", vfe_ahb_clk.c, "fde00000.qcom,vfe"),
+
+ CLK_LOOKUP("bus_clk", vfe_axi_clk.c, "fde00000.qcom,vfe"),
+
+
CLK_LOOKUP("core_clk", q6ss_xo_clk.c, "fe200000.qcom,lpass"),
CLK_LOOKUP("bus_clk", gcc_lpass_q6_axi_clk.c, "fe200000.qcom,lpass"),
CLK_LOOKUP("iface_clk", q6ss_ahb_lfabif_clk.c, "fe200000.qcom,lpass"),
@@ -2835,6 +3108,18 @@
CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, "scm"),
CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, "scm"),
CLK_LOOKUP("core_clk_src", ce1_clk_src.c, "scm"),
+
+ /* Add QCEDEV clocks */
+ CLK_LOOKUP("core_clk", gcc_ce1_clk.c, "fd400000.qcom,qcedev"),
+ CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, "fd400000.qcom,qcedev"),
+ CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, "fd400000.qcom,qcedev"),
+ CLK_LOOKUP("core_clk_src", ce1_clk_src.c, "fd400000.qcom,qcedev"),
+
+ /* Add QCRYPTO clocks */
+ CLK_LOOKUP("core_clk", gcc_ce1_clk.c, "fd404000.qcom,qcrypto"),
+ CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, "fd404000.qcom,qcrypto"),
+ CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, "fd404000.qcom,qcrypto"),
+ CLK_LOOKUP("core_clk_src", ce1_clk_src.c, "fd404000.qcom,qcrypto"),
};
static struct clk_lookup msm_clocks_8610_rumi[] = {
@@ -2857,6 +3142,7 @@
CLK_DUMMY("core_clk", NULL, "fd870000.qcom,iommu", OFF),
CLK_DUMMY("iface_clk", NULL, "fd880000.qcom,iommu", OFF),
CLK_DUMMY("core_clk", NULL, "fd880000.qcom,iommu", OFF),
+ CLK_DUMMY("alt_core_clk", NULL, "fd880000.qcom,iommu", OFF),
CLK_DUMMY("iface_clk", NULL, "fd000000.qcom,iommu", OFF),
CLK_DUMMY("core_clk", NULL, "fd000000.qcom,iommu", OFF),
CLK_DUMMY("iface_clk", NULL, "fd010000.qcom,iommu", OFF),
@@ -2905,23 +3191,6 @@
.main_output_mask = BIT(0),
};
-#define PLL_AUX_OUTPUT_BIT 1
-#define PLL_AUX2_OUTPUT_BIT 2
-
-#define PWR_ON_MASK BIT(31)
-#define EN_REST_WAIT_MASK (0xF << 20)
-#define EN_FEW_WAIT_MASK (0xF << 16)
-#define CLK_DIS_WAIT_MASK (0xF << 12)
-#define SW_OVERRIDE_MASK BIT(2)
-#define HW_CONTROL_MASK BIT(1)
-#define SW_COLLAPSE_MASK BIT(0)
-
-/* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */
-#define EN_REST_WAIT_VAL (0x2 << 20)
-#define EN_FEW_WAIT_VAL (0x2 << 16)
-#define CLK_DIS_WAIT_VAL (0x2 << 12)
-#define GDSC_TIMEOUT_US 50000
-
static void __init reg_init(void)
{
u32 regval;
@@ -2958,6 +3227,26 @@
clk_set_rate(&mclk1_clk_src.c, mclk1_clk_src.freq_tbl[0].freq_hz);
}
+static void dsi_init(void)
+{
+ dsi_byte_clk_src_ops = clk_ops_rcg;
+ dsi_byte_clk_src_ops.set_rate = set_rate_pixel_byte_clk;
+ dsi_byte_clk_src_ops.handoff = byte_rcg_handoff;
+ dsi_byte_clk_src_ops.get_parent = NULL;
+
+ dsi_dsi_clk_src_ops = clk_ops_rcg_mnd;
+ dsi_dsi_clk_src_ops.set_rate = set_rate_dsi_clk;
+ dsi_dsi_clk_src_ops.handoff = pixel_rcg_handoff;
+ dsi_dsi_clk_src_ops.get_parent = NULL;
+
+ dsi_pixel_clk_src_ops = clk_ops_rcg_mnd;
+ dsi_pixel_clk_src_ops.set_rate = set_rate_pixel_byte_clk;
+ dsi_pixel_clk_src_ops.handoff = pixel_rcg_handoff;
+ dsi_pixel_clk_src_ops.get_parent = NULL;
+
+ dsi_clk_ctrl_init(&dsi_ahb_clk.c);
+}
+
#define GCC_CC_PHYS 0xFC400000
#define GCC_CC_SIZE SZ_16K
@@ -3017,13 +3306,11 @@
reg_init();
+ dsi_init();
+
/* Maintain the max nominal frequency on the MMSSNOC AHB bus. */
clk_set_rate(&mmssnoc_ahb_a_clk.c, 40000000);
clk_prepare_enable(&mmssnoc_ahb_a_clk.c);
-
- /* TODO: Remove this once the bus driver is in place */
- clk_set_rate(&axi_clk_src.c, 200000000);
- clk_prepare_enable(&mmss_s0_axi_clk.c);
}
struct clock_init_data msm8610_clock_init_data __initdata = {
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index be6d965..1e91d5b 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -3526,6 +3526,7 @@
/*Shared by 8064, and 8930*/
static struct clk_freq_tbl clk_tbl_gfx3d[] = {
F_GFX3D( 0, gnd, 0, 0),
+ F_GFX3D( 1800000, pxo, 1, 15),
F_GFX3D( 27000000, pxo, 0, 0),
F_GFX3D( 48000000, pll8, 1, 8),
F_GFX3D( 54857000, pll8, 1, 7),
@@ -5436,9 +5437,9 @@
CLK_LOOKUP("npl_clk", npl_tv_clk.c, ""),
CLK_LOOKUP("core_clk", gfx3d_clk.c, "kgsl-3d0.0"),
- CLK_LOOKUP("core_clk", gfx3d_clk.c, "footswitch-8x60.2"),
+ CLK_LOOKUP("core_clk", gfx3d_clk.c, "footswitch-8x60.11"),
CLK_LOOKUP("bus_clk",
- gfx3d_axi_clk.c, "footswitch-8x60.2"),
+ gfx3d_axi_clk.c, "footswitch-8x60.11"),
CLK_LOOKUP("iface_clk", vcap_p_clk.c, ""),
CLK_LOOKUP("iface_clk", vcap_p_clk.c, "msm_vcap.0"),
CLK_LOOKUP("iface_clk", vcap_p_clk.c, "footswitch-8x60.10"),
@@ -5492,7 +5493,7 @@
CLK_LOOKUP("master_iface_clk", dsi2_m_p_clk.c, "mipi_dsi.2"),
CLK_LOOKUP("slave_iface_clk", dsi2_s_p_clk.c, "mipi_dsi.2"),
CLK_LOOKUP("iface_clk", gfx3d_p_clk.c, "kgsl-3d0.0"),
- CLK_LOOKUP("iface_clk", gfx3d_p_clk.c, "footswitch-8x60.2"),
+ CLK_LOOKUP("iface_clk", gfx3d_p_clk.c, "footswitch-8x60.11"),
CLK_LOOKUP("master_iface_clk", hdmi_m_p_clk.c, "hdmi_msm.1"),
CLK_LOOKUP("slave_iface_clk", hdmi_s_p_clk.c, "hdmi_msm.1"),
CLK_LOOKUP("iface_clk", ijpeg_p_clk.c, "msm_gemini.0"),
@@ -6406,79 +6407,14 @@
.main_output_mask = BIT(23),
};
-static void __init reg_init(void)
+
+static void __init init_mm_cc(void)
{
- void __iomem *imem_reg;
-
- /* Deassert MM SW_RESET_ALL signal. */
- writel_relaxed(0, SW_RESET_ALL_REG);
-
/*
- * Some bits are only used on 8960 or 8064 or 8930 and are marked as
- * reserved bits on the other SoCs. Writing to these reserved bits
- * should have no effect.
- */
- /*
- * Initialize MM AHB registers: Enable the FPB clock and disable HW
- * gating on 8627 and 8930ab for all clocks. Also set VFE_AHB's
- * FORCE_CORE_ON bit to prevent its memory from being collapsed when
- * the clock is halted. The sleep and wake-up delays are set to safe
- * values.
- */
- if (cpu_is_msm8627() || cpu_is_msm8930ab()) {
- rmwreg(0x00000003, AHB_EN_REG, 0x6C000103);
- writel_relaxed(0x000007F9, AHB_EN2_REG);
- } else {
- rmwreg(0x40000000, AHB_EN_REG, 0x6C000103);
- writel_relaxed(0x3C7097F9, AHB_EN2_REG);
- }
-
- if (soc_class_is_apq8064())
- rmwreg(0x00000000, AHB_EN3_REG, 0x00000001);
-
- /* Deassert all locally-owned MM AHB resets. */
- rmwreg(0, SW_RESET_AHB_REG, 0xFFF7DFFF);
- rmwreg(0, SW_RESET_AHB2_REG, 0x0000000F);
-
- /* Initialize MM AXI registers: Enable HW gating for all clocks that
- * support it. Also set FORCE_CORE_ON bits, and any sleep and wake-up
- * delays to safe values. */
- if ((cpu_is_msm8960() &&
- SOCINFO_VERSION_MAJOR(socinfo_get_version()) < 3) ||
- cpu_is_msm8627() || cpu_is_msm8930ab()) {
- rmwreg(0x000007F9, MAXI_EN_REG, 0x0803FFFF);
- rmwreg(0x3027FCFF, MAXI_EN2_REG, 0x3A3FFFFF);
- } else {
- rmwreg(0x0003AFF9, MAXI_EN_REG, 0x0803FFFF);
- rmwreg(0x3A27FCFF, MAXI_EN2_REG, 0x3A3FFFFF);
- }
-
- rmwreg(0x0027FCFF, MAXI_EN3_REG, 0x003FFFFF);
- rmwreg(0x0027FCFF, MAXI_EN4_REG, 0x017FFFFF);
-
- if (soc_class_is_apq8064())
- rmwreg(0x019FECFF, MAXI_EN5_REG, 0x01FFEFFF);
- if (cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627() ||
- cpu_is_msm8930ab())
- rmwreg(0x000004FF, MAXI_EN5_REG, 0x00000FFF);
- if (cpu_is_msm8960ab())
- rmwreg(0x009FE000, MAXI_EN5_REG, 0x01FFE000);
-
- if (cpu_is_msm8627() || cpu_is_msm8930ab())
- rmwreg(0x000003C7, SAXI_EN_REG, 0x00003FFF);
- else
- rmwreg(0x00003C38, SAXI_EN_REG, 0x00003FFF);
-
- /* Enable IMEM's clk_on signal */
- imem_reg = ioremap(0x04b00040, 4);
- if (imem_reg) {
- writel_relaxed(0x3, imem_reg);
- iounmap(imem_reg);
- }
-
- /* Initialize MM CC registers: Set MM FORCE_CORE_ON bits so that core
+ * Initialize MM CC registers: Set MM FORCE_CORE_ON bits so that core
* memories retain state even when not clocked. Also, set sleep and
- * wake-up delays to safe values. */
+ * wake-up delays to safe values.
+ */
rmwreg(0x00000000, CSI0_CC_REG, 0x00000410);
rmwreg(0x00000000, CSI1_CC_REG, 0x00000410);
rmwreg(0x80FF0000, DSI1_BYTE_CC_REG, 0xE0FF0010);
@@ -6493,28 +6429,72 @@
rmwreg(0x80FF0000, VFE_CC_REG, 0xE0FF4010);
rmwreg(0x800000FF, VFE_CC2_REG, 0xE00000FF);
rmwreg(0x80FF0000, VPE_CC_REG, 0xE0FF0010);
- if (cpu_is_msm8960ab() || cpu_is_msm8960() || soc_class_is_apq8064()) {
- rmwreg(0x80FF0000, DSI2_BYTE_CC_REG, 0xE0FF0010);
- rmwreg(0x80FF0000, DSI2_PIXEL_CC_REG, 0xE0FF0010);
- rmwreg(0x80FF0000, JPEGD_CC_REG, 0xE0FF0010);
- }
- if (cpu_is_msm8960ab())
- rmwreg(0x00000001, DSI2_PIXEL_CC2_REG, 0x00000001);
+}
- if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
- cpu_is_msm8627() || cpu_is_msm8930ab())
- rmwreg(0x80FF0000, TV_CC_REG, 0xE1FFC010);
- if (cpu_is_msm8960ab())
- rmwreg(0x00000000, TV_CC_REG, 0x00004010);
+static void __init enable_imem_clk(unsigned long phys)
+{
+ void __iomem *imem_reg;
- if (cpu_is_msm8960()) {
- rmwreg(0x80FF0000, GFX2D0_CC_REG, 0xE0FF0010);
- rmwreg(0x80FF0000, GFX2D1_CC_REG, 0xE0FF0010);
+ /* Enable IMEM's clk_on signal */
+ imem_reg = ioremap(phys, SZ_4);
+ if (imem_reg) {
+ writel_relaxed(0x3, imem_reg);
+ iounmap(imem_reg);
}
- if (soc_class_is_apq8064()) {
- rmwreg(0x00000000, TV_CC_REG, 0x00004010);
- rmwreg(0x80FF0000, VCAP_CC_REG, 0xE0FF1010);
+}
+
+static void __init reg_init_8930(void)
+{
+ /* MM-AHB default values */
+ u32 en_reg = 0x40000000, en2_reg = 0x3C7097F9;
+ /* MM-AXI default values */
+ u32 aen_reg = 0x0003AFF9, aen2_reg = 0x3A27FCFF,
+ saen_reg = 0x00003C38;
+
+
+ /* Deassert MM SW_RESET_ALL signal. */
+ writel_relaxed(0, SW_RESET_ALL_REG);
+
+ /*
+ * Initialize MM AHB registers: Enable the FPB clock and disable HW
+ * gating on 8627 and 8930ab for all clocks. Also set VFE_AHB's
+ * FORCE_CORE_ON bit to prevent its memory from being collapsed when
+ * the clock is halted. The sleep and wake-up delays are set to safe
+ * values.
+ */
+ if (cpu_is_msm8627() || cpu_is_msm8930ab()) {
+ en_reg = 0x00000003;
+ en2_reg = 0x000007F9;
}
+ rmwreg(en_reg, AHB_EN_REG, 0x6C000103);
+ writel_relaxed(en2_reg, AHB_EN2_REG);
+
+ /* Deassert all locally-owned MM AHB resets. */
+ rmwreg(0, SW_RESET_AHB_REG, 0xFFF7DFFF);
+ rmwreg(0, SW_RESET_AHB2_REG, 0x0000000F);
+
+ /*
+ * Initialize MM AXI registers: Enable HW gating for all clocks that
+ * support it. Also set FORCE_CORE_ON bits, and any sleep and wake-up
+ * delays to safe values.
+ */
+ if (cpu_is_msm8627() || cpu_is_msm8930ab()) {
+ aen_reg = 0x000007F9;
+ aen2_reg = 0x3027FCFF;
+ }
+ rmwreg(aen_reg, MAXI_EN_REG, 0x0803FFFF);
+ rmwreg(aen2_reg, MAXI_EN2_REG, 0x3A3FFFFF);
+ rmwreg(0x0027FCFF, MAXI_EN3_REG, 0x003FFFFF);
+ rmwreg(0x0027FCFF, MAXI_EN4_REG, 0x017FFFFF);
+ rmwreg(0x000004FF, MAXI_EN5_REG, 0x00000FFF);
+ if (cpu_is_msm8627() || cpu_is_msm8930ab())
+ saen_reg = 0x000003C7;
+ rmwreg(saen_reg, SAXI_EN_REG, 0x00003FFF);
+
+ enable_imem_clk(0x04b00040);
+
+ init_mm_cc();
+ rmwreg(0x80FF0000, TV_CC_REG, 0xE1FFC010);
/*
* Initialize USB_HS_HCLK_FS registers: Set FORCE_C_ON bits so that
@@ -6522,10 +6502,6 @@
* and wake-up value to max.
*/
rmwreg(0x0000004F, USB_HS1_HCLK_FS_REG, 0x0000007F);
- if (soc_class_is_apq8064()) {
- rmwreg(0x0000004F, USB_HS3_HCLK_FS_REG, 0x0000007F);
- rmwreg(0x0000004F, USB_HS4_HCLK_FS_REG, 0x0000007F);
- }
/* De-assert MM AXI resets to all hardware blocks. */
writel_relaxed(0, SW_RESET_AXI_REG);
@@ -6538,88 +6514,231 @@
writel_relaxed(BIT(11), TSSC_CLK_CTL_REG);
writel_relaxed(BIT(15), PDM_CLK_NS_REG);
- /* Source SLIMBus xo src from slimbus reference clock */
- if (cpu_is_msm8960ab() || cpu_is_msm8960())
- writel_relaxed(0x3, SLIMBUS_XO_SRC_CLK_CTL_REG);
-
- /* Source the dsi_byte_clks from the DSI PHY PLLs */
+ /* Source the dsi1_byte_clks/dsi1_esc_clk from the DSI PHY PLLs */
rmwreg(0x1, DSI1_BYTE_NS_REG, 0x7);
- if (cpu_is_msm8960ab() || cpu_is_msm8960() || soc_class_is_apq8064())
- rmwreg(0x2, DSI2_BYTE_NS_REG, 0x7);
-
- /* Source the dsi1_esc_clk from the DSI1 PHY PLLs */
rmwreg(0x1, DSI1_ESC_NS_REG, 0x7);
/*
+ * Change PLL15 configuration based on the SoC we're running on.
+ *
+ * Default pll15 l, m, n values for 8930/8930aa/8627()
+ */
+ pll15_config.l = 0x21 | BVAL(31, 7, 0x600);
+ pll15_config.m = 0x1;
+ pll15_config.n = 0x3;
+
+ if (cpu_is_msm8930ab()) {
+ pll15_config.l = 0x25 | BVAL(31, 7, 0x600);
+ pll15_config.m = 0x25;
+ pll15_config.n = 0x3E7;
+ }
+ configure_sr_pll(&pll15_config, &pll15_regs, 0);
+
+ /* Disable AUX and BIST outputs */
+ writel_relaxed(0, MM_PLL3_TEST_CTL_REG);
+}
+
+static void __init reg_init_8064(void)
+{
+ u32 is_pll_enabled;
+
+ /* Deassert MM SW_RESET_ALL signal. */
+ writel_relaxed(0, SW_RESET_ALL_REG);
+
+ /*
+ * Initialize MM AHB registers:
+ * Also set VFE_AHB's FORCE_CORE_ON bit to prevent its memory
+ * from being collapsed when the clock is halted. The sleep and
+ * wake-up delays are set to safe values.
+ */
+ rmwreg(0x40000000, AHB_EN_REG, 0x6C000103);
+ writel_relaxed(0x3C7097F9, AHB_EN2_REG);
+ rmwreg(0x00000000, AHB_EN3_REG, 0x00000001);
+
+ /* Deassert all locally-owned MM AHB resets. */
+ rmwreg(0, SW_RESET_AHB_REG, 0xFFF7DFFF);
+ rmwreg(0, SW_RESET_AHB2_REG, 0x0000000F);
+
+ /*
+ * Initialize MM AXI registers: Enable HW gating for all clocks that
+ * support it. Also set FORCE_CORE_ON bits, and any sleep and wake-up
+ * delays to safe values.
+ */
+ rmwreg(0x0003AFF9, MAXI_EN_REG, 0x0803FFFF);
+ rmwreg(0x3A27FCFF, MAXI_EN2_REG, 0x3A3FFFFF);
+ rmwreg(0x0027FCFF, MAXI_EN3_REG, 0x003FFFFF);
+ rmwreg(0x0027FCFF, MAXI_EN4_REG, 0x017FFFFF);
+ rmwreg(0x019FECFF, MAXI_EN5_REG, 0x01FFEFFF);
+ rmwreg(0x00003C38, SAXI_EN_REG, 0x00003FFF);
+
+ enable_imem_clk(0x04b00040);
+
+ init_mm_cc();
+ rmwreg(0x80FF0000, DSI2_BYTE_CC_REG, 0xE0FF0010);
+ rmwreg(0x80FF0000, DSI2_PIXEL_CC_REG, 0xE0FF0010);
+ rmwreg(0x80FF0000, JPEGD_CC_REG, 0xE0FF0010);
+ rmwreg(0x00000000, TV_CC_REG, 0x00004010);
+ rmwreg(0x80FF0000, VCAP_CC_REG, 0xE0FF1010);
+
+ /*
+ * Initialize USB_HS_HCLK_FS registers: Set FORCE_C_ON bits so that
+ * core remain active during halt state of the clk. Also, set sleep
+ * and wake-up value to max.
+ */
+ rmwreg(0x0000004F, USB_HS1_HCLK_FS_REG, 0x0000007F);
+ rmwreg(0x0000004F, USB_HS3_HCLK_FS_REG, 0x0000007F);
+ rmwreg(0x0000004F, USB_HS4_HCLK_FS_REG, 0x0000007F);
+
+ /* De-assert MM AXI resets to all hardware blocks. */
+ writel_relaxed(0, SW_RESET_AXI_REG);
+
+ /* Deassert all MM core resets. */
+ writel_relaxed(0, SW_RESET_CORE_REG);
+ writel_relaxed(0, SW_RESET_CORE2_REG);
+
+ /* Enable TSSC and PDM PXO sources. */
+ writel_relaxed(BIT(11), TSSC_CLK_CTL_REG);
+ writel_relaxed(BIT(15), PDM_CLK_NS_REG);
+
+ /* Source the dsi1_byte_clks/dsi1_esc_clk from the DSI PHY PLLs */
+ rmwreg(0x1, DSI1_BYTE_NS_REG, 0x7);
+ rmwreg(0x1, DSI1_ESC_NS_REG, 0x7);
+
+ /* Source the dsi2_byte_clks from the DSI PHY PLLs */
+ rmwreg(0x2, DSI2_BYTE_NS_REG, 0x7);
+
+ /*
* Source the sata_phy_ref_clk from PXO and set predivider of
* sata_pmalive_clk to 1.
*/
- if (soc_class_is_apq8064()) {
- rmwreg(0, SATA_PHY_REF_CLK_CTL_REG, 0x1);
- rmwreg(0, SATA_PMALIVE_CLK_CTL_REG, 0x3);
- }
+ rmwreg(0, SATA_PHY_REF_CLK_CTL_REG, 0x1);
+ rmwreg(0, SATA_PMALIVE_CLK_CTL_REG, 0x3);
/*
* TODO: Programming below PLLs and prng_clk is temporary and
* needs to be removed after bootloaders program them.
*/
- if (soc_class_is_apq8064()) {
- u32 is_pll_enabled;
- /* Program pxo_src_clk to source from PXO */
- rmwreg(0x1, PXO_SRC_CLK_CTL_REG, 0x7);
+ /* Program pxo_src_clk to source from PXO */
+ rmwreg(0x1, PXO_SRC_CLK_CTL_REG, 0x7);
- /* Check if PLL14 is active */
- is_pll_enabled = readl_relaxed(BB_PLL14_STATUS_REG) & BIT(16);
- if (!is_pll_enabled)
- /* Ref clk = 27MHz and program pll14 to 480MHz */
- configure_sr_pll(&pll14_config, &pll14_regs, 1);
+ /* Check if PLL14 is active */
+ is_pll_enabled = readl_relaxed(BB_PLL14_STATUS_REG) & BIT(16);
+ if (!is_pll_enabled)
+ /* Ref clk = 27MHz and program pll14 to 480MHz */
+ configure_sr_pll(&pll14_config, &pll14_regs, 1);
- /* Check if PLL4 is active */
- is_pll_enabled = readl_relaxed(LCC_PLL0_STATUS_REG) & BIT(16);
- if (!is_pll_enabled)
- /* Ref clk = 27MHz and program pll4 to 393.2160MHz */
- configure_sr_pll(&pll4_config_393, &pll4_regs, 1);
+ /* Check if PLL4 is active */
+ is_pll_enabled = readl_relaxed(LCC_PLL0_STATUS_REG) & BIT(16);
+ if (!is_pll_enabled)
+ /* Ref clk = 27MHz and program pll4 to 393.2160MHz */
+ configure_sr_pll(&pll4_config_393, &pll4_regs, 1);
- /* Enable PLL4 source on the LPASS Primary PLL Mux */
- writel_relaxed(0x1, LCC_PRI_PLL_CLK_CTL_REG);
+ /* Enable PLL4 source on the LPASS Primary PLL Mux */
+ writel_relaxed(0x1, LCC_PRI_PLL_CLK_CTL_REG);
- /* Program prng_clk to 64MHz if it isn't configured */
- if (!readl_relaxed(PRNG_CLK_NS_REG))
- writel_relaxed(0x2B, PRNG_CLK_NS_REG);
- }
+ /* Program prng_clk to 64MHz if it isn't configured */
+ if (!readl_relaxed(PRNG_CLK_NS_REG))
+ writel_relaxed(0x2B, PRNG_CLK_NS_REG);
- if (cpu_is_apq8064() || cpu_is_apq8064aa()) {
- /* Program PLL15 to 975MHz with ref clk = 27MHz */
- configure_sr_pll(&pll15_config, &pll15_regs, 0);
- } else if (cpu_is_apq8064ab()) {
+ if (cpu_is_apq8064ab()) {
/* Program PLL15 to 900MHZ */
pll15_config.l = 0x21 | BVAL(31, 7, 0x620);
pll15_config.m = 0x1;
pll15_config.n = 0x3;
- configure_sr_pll(&pll15_config, &pll15_regs, 0);
- } else if (cpu_is_msm8960ab()) {
- pll3_clk.c.rate = 880000000;
- configure_sr_pll(&pll3_config, &pll3_regs, 0);
+ }
+ /*
+ * Default Program PLL15 to 975MHz with ref clk = 27MHz
+ * In case of apq8064ab PLL15 is set to 900MHZ
+ */
+ configure_sr_pll(&pll15_config, &pll15_regs, 0);
+}
+
+static void __init reg_init_8960(void)
+{
+ u32 aen_reg = 0x0003AFF9, aen2_reg = 0x3A27FCFF;
+
+ /* Deassert MM SW_RESET_ALL signal. */
+ writel_relaxed(0, SW_RESET_ALL_REG);
+
+ /*
+ * Initialize MM AHB registers:
+ * Also set VFE_AHB's FORCE_CORE_ON bit to prevent its memory
+ * from being collapsed when the clock is halted. The sleep and
+ * wake-up delays are set to safe values.
+ */
+ rmwreg(0x40000000, AHB_EN_REG, 0x6C000103);
+ writel_relaxed(0x3C7097F9, AHB_EN2_REG);
+
+ /* Deassert all locally-owned MM AHB resets. */
+ rmwreg(0, SW_RESET_AHB_REG, 0xFFF7DFFF);
+ rmwreg(0, SW_RESET_AHB2_REG, 0x0000000F);
+
+ /*
+ * Initialize MM AXI registers: Enable HW gating for all clocks that
+ * support it. Also set FORCE_CORE_ON bits, and any sleep and wake-up
+ * delays to safe values.
+ */
+ if (cpu_is_msm8960() &&
+ SOCINFO_VERSION_MAJOR(socinfo_get_version()) < 3) {
+ aen_reg = 0x000007F9;
+ aen2_reg = 0x3027FCFF;
+ }
+ rmwreg(aen_reg, MAXI_EN_REG, 0x0803FFFF);
+ rmwreg(aen2_reg, MAXI_EN2_REG, 0x3A3FFFFF);
+ rmwreg(0x0027FCFF, MAXI_EN3_REG, 0x003FFFFF);
+ rmwreg(0x0027FCFF, MAXI_EN4_REG, 0x017FFFFF);
+ if (cpu_is_msm8960ab())
+ rmwreg(0x009FE000, MAXI_EN5_REG, 0x01FFE000);
+ rmwreg(0x00003C38, SAXI_EN_REG, 0x00003FFF);
+
+ enable_imem_clk(0x04b00040);
+
+ init_mm_cc();
+ rmwreg(0x80FF0000, DSI2_BYTE_CC_REG, 0xE0FF0010);
+ rmwreg(0x80FF0000, DSI2_PIXEL_CC_REG, 0xE0FF0010);
+ rmwreg(0x80FF0000, JPEGD_CC_REG, 0xE0FF0010);
+ if (cpu_is_msm8960ab()) {
+ rmwreg(0x00000001, DSI2_PIXEL_CC2_REG, 0x00000001);
+ rmwreg(0x00000000, TV_CC_REG, 0x00004010);
+ }
+ if (cpu_is_msm8960()) {
+ rmwreg(0x80FF0000, TV_CC_REG, 0xE1FFC010);
+ rmwreg(0x80FF0000, GFX2D0_CC_REG, 0xE0FF0010);
+ rmwreg(0x80FF0000, GFX2D1_CC_REG, 0xE0FF0010);
}
/*
- * Change PLL15 configuration based on the SoC we're running on.
+ * Initialize USB_HS_HCLK_FS registers: Set FORCE_C_ON bits so that
+ * core remain active during halt state of the clk. Also, set sleep
+ * and wake-up value to max.
*/
- if (cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627()) {
- pll15_config.l = 0x21 | BVAL(31, 7, 0x600);
- pll15_config.m = 0x1;
- pll15_config.n = 0x3;
- configure_sr_pll(&pll15_config, &pll15_regs, 0);
- /* Disable AUX and BIST outputs */
- writel_relaxed(0, MM_PLL3_TEST_CTL_REG);
- } else if (cpu_is_msm8930ab()) {
- pll15_config.l = 0x25 | BVAL(31, 7, 0x600);
- pll15_config.m = 0x25;
- pll15_config.n = 0x3E7;
- configure_sr_pll(&pll15_config, &pll15_regs, 0);
- /* Disable AUX and BIST outputs */
- writel_relaxed(0, MM_PLL3_TEST_CTL_REG);
+ rmwreg(0x0000004F, USB_HS1_HCLK_FS_REG, 0x0000007F);
+
+ /* De-assert MM AXI resets to all hardware blocks. */
+ writel_relaxed(0, SW_RESET_AXI_REG);
+
+ /* Deassert all MM core resets. */
+ writel_relaxed(0, SW_RESET_CORE_REG);
+ writel_relaxed(0, SW_RESET_CORE2_REG);
+
+ /* Enable TSSC and PDM PXO sources. */
+ writel_relaxed(BIT(11), TSSC_CLK_CTL_REG);
+ writel_relaxed(BIT(15), PDM_CLK_NS_REG);
+
+ /* Source the dsi1_byte_clks/dsi1_esc_clk from the DSI PHY PLLs */
+ rmwreg(0x1, DSI1_BYTE_NS_REG, 0x7);
+ rmwreg(0x1, DSI1_ESC_NS_REG, 0x7);
+
+ /* Source SLIMBus xo src from slimbus reference clock */
+ writel_relaxed(0x3, SLIMBUS_XO_SRC_CLK_CTL_REG);
+
+ /* Source the dsi2_byte_clks from the DSI PHY PLLs */
+ rmwreg(0x2, DSI2_BYTE_NS_REG, 0x7);
+
+ if (cpu_is_msm8960ab()) {
+ pll3_clk.c.rate = 880000000;
+ configure_sr_pll(&pll3_config, &pll3_regs, 0);
}
}
@@ -6645,13 +6764,15 @@
}
struct clock_init_data msm8960_clock_init_data __initdata;
+
static void __init msm8960_clock_pre_init(void)
{
- /* Initialize clock registers. */
- reg_init();
+ struct clk_lookup *clk_lkup;
+ size_t clk_size;
+ struct clk_freq_tbl *tbl;
- if (soc_class_is_apq8064())
- vdd_sr2_hdmi_pll.set_vdd = set_vdd_sr2_hdmi_pll_8064;
+ /* Initialize clock registers. */
+ reg_init_8960();
/* Detect PLL4 programmed for alternate 491.52MHz clock plan. */
if (readl_relaxed(LCC_PLL0_L_VAL_REG) == 0x12) {
@@ -6665,73 +6786,120 @@
pcm_clk.freq_tbl = clk_tbl_pcm_492;
}
- if (cpu_is_msm8960() || cpu_is_msm8960ab())
- memcpy(msm_clocks_8960, msm_clocks_8960_common,
- sizeof(msm_clocks_8960_common));
- if (cpu_is_msm8960ab()) {
- gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8960ab;
- mdp_clk.c.fmax = fmax_mdp_8960ab;
-
- gfx3d_clk.c.fmax = select_gfx_fmax_plan(fmax_gfx3d_8960ab,
- ARRAY_SIZE(fmax_gfx3d_8960ab));
-
- memcpy(msm_clocks_8960 + ARRAY_SIZE(msm_clocks_8960_common),
- msm_clocks_8960ab_only, sizeof(msm_clocks_8960ab_only));
- msm8960_clock_init_data.size -=
- ARRAY_SIZE(msm_clocks_8960_only);
-
- gmem_axi_clk.c.depends = &gfx3d_axi_clk.c;
- } else if (cpu_is_msm8960()) {
- gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8960;
- memcpy(msm_clocks_8960 + ARRAY_SIZE(msm_clocks_8960_common),
- msm_clocks_8960_only, sizeof(msm_clocks_8960_only));
+ if (cpu_is_msm8960()) {
+ tbl = clk_tbl_gfx3d_8960;
+ clk_lkup = msm_clocks_8960_only;
+ clk_size = sizeof(msm_clocks_8960_only);
msm8960_clock_init_data.size -=
ARRAY_SIZE(msm_clocks_8960ab_only);
}
- /*
- * Change the freq tables for and voltage requirements for
- * clocks which differ between chips.
- */
- if (cpu_is_apq8064() || cpu_is_apq8064aa())
- gfx3d_clk.c.fmax = fmax_gfx3d_8064;
+
+ if (cpu_is_msm8960ab()) {
+ mdp_clk.c.fmax = fmax_mdp_8960ab;
+ gmem_axi_clk.c.depends = &gfx3d_axi_clk.c;
+ tbl = clk_tbl_gfx3d_8960ab;
+ gfx3d_clk.c.fmax = select_gfx_fmax_plan(fmax_gfx3d_8960ab,
+ ARRAY_SIZE(fmax_gfx3d_8960ab));
+
+ clk_lkup = msm_clocks_8960ab_only;
+ clk_size = sizeof(msm_clocks_8960ab_only);
+ msm8960_clock_init_data.size -=
+ ARRAY_SIZE(msm_clocks_8960_only);
+ }
+
+ gfx3d_clk.freq_tbl = tbl;
+
+ memcpy(msm_clocks_8960, msm_clocks_8960_common,
+ sizeof(msm_clocks_8960_common));
+ memcpy(msm_clocks_8960 + ARRAY_SIZE(msm_clocks_8960_common),
+ clk_lkup, clk_size);
+
+ if ((readl_relaxed(PRNG_CLK_NS_REG) & 0x7F) == 0x2B)
+ prng_clk.freq_tbl = clk_tbl_prng_64;
+
+ clk_ops_local_pll.enable = sr_pll_clk_enable;
+}
+
+static void __init msm8064_clock_pre_init(void)
+{
+ unsigned long *fmax = fmax_gfx3d_8064;
+
+ /* Initialize clock registers. */
+ reg_init_8064();
+
+ vdd_sr2_hdmi_pll.set_vdd = set_vdd_sr2_hdmi_pll_8064;
+
+ /* Detect PLL4 programmed for alternate 491.52MHz clock plan. */
+ if (readl_relaxed(LCC_PLL0_L_VAL_REG) == 0x12) {
+ pll4_clk.c.rate = 491520000;
+ audio_slimbus_clk.freq_tbl = clk_tbl_aif_osr_492;
+ mi2s_osr_clk.freq_tbl = clk_tbl_aif_osr_492;
+ codec_i2s_mic_osr_clk.freq_tbl = clk_tbl_aif_osr_492;
+ spare_i2s_mic_osr_clk.freq_tbl = clk_tbl_aif_osr_492;
+ codec_i2s_spkr_osr_clk.freq_tbl = clk_tbl_aif_osr_492;
+ spare_i2s_spkr_osr_clk.freq_tbl = clk_tbl_aif_osr_492;
+ pcm_clk.freq_tbl = clk_tbl_pcm_492;
+ }
if (cpu_is_apq8064ab())
- gfx3d_clk.c.fmax = fmax_gfx3d_8064ab;
+ fmax = fmax_gfx3d_8064ab;
if ((cpu_is_apq8064() &&
SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) ||
cpu_is_apq8064ab() || cpu_is_apq8064aa()) {
-
vcodec_clk.c.fmax = fmax_vcodec_8064v2;
ce3_src_clk.c.fmax = fmax_ce3_8064v2;
sdc1_clk.c.fmax = fmax_sdc1_8064v2;
}
- if (soc_class_is_apq8064()) {
- ijpeg_clk.c.fmax = fmax_ijpeg_8064;
- mdp_clk.c.fmax = fmax_mdp_8064;
- tv_src_clk.c.fmax = fmax_tv_src_8064;
- vfe_clk.c.fmax = fmax_vfe_8064;
- gmem_axi_clk.c.depends = &gfx3d_axi_clk.c;
+
+ gfx3d_clk.c.fmax = fmax;
+ ijpeg_clk.c.fmax = fmax_ijpeg_8064;
+ mdp_clk.c.fmax = fmax_mdp_8064;
+ tv_src_clk.c.fmax = fmax_tv_src_8064;
+ vfe_clk.c.fmax = fmax_vfe_8064;
+
+ gmem_axi_clk.c.depends = &gfx3d_axi_clk.c;
+
+ if ((readl_relaxed(PRNG_CLK_NS_REG) & 0x7F) == 0x2B)
+ prng_clk.freq_tbl = clk_tbl_prng_64;
+
+ clk_ops_local_pll.enable = sr_pll_clk_enable;
+}
+
+static void __init __msm8930_clock_pre_init(void)
+{
+ unsigned long rate = 900000000;
+ unsigned long *fmax = fmax_gfx3d_8930;
+
+ /* Initialize clock registers. */
+ reg_init_8930();
+
+ /* Detect PLL4 programmed for alternate 491.52MHz clock plan. */
+ if (readl_relaxed(LCC_PLL0_L_VAL_REG) == 0x12) {
+ pll4_clk.c.rate = 491520000;
+ audio_slimbus_clk.freq_tbl = clk_tbl_aif_osr_492;
+ mi2s_osr_clk.freq_tbl = clk_tbl_aif_osr_492;
+ codec_i2s_mic_osr_clk.freq_tbl = clk_tbl_aif_osr_492;
+ spare_i2s_mic_osr_clk.freq_tbl = clk_tbl_aif_osr_492;
+ codec_i2s_spkr_osr_clk.freq_tbl = clk_tbl_aif_osr_492;
+ spare_i2s_spkr_osr_clk.freq_tbl = clk_tbl_aif_osr_492;
+ pcm_clk.freq_tbl = clk_tbl_pcm_492;
}
- /*
- * Change the freq tables and voltage requirements for
- * clocks which differ between 8960 and 8930.
- */
- if (cpu_is_msm8930() || cpu_is_msm8627())
- gfx3d_clk.c.fmax = fmax_gfx3d_8930;
- else if (cpu_is_msm8930aa())
- gfx3d_clk.c.fmax = fmax_gfx3d_8930aa;
- if (cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627()) {
- pll15_clk.c.rate = 900000000;
- gmem_axi_clk.c.depends = &gfx3d_axi_clk_8930.c;
- } else if (cpu_is_msm8930ab()) {
+ if (cpu_is_msm8930aa())
+ fmax = fmax_gfx3d_8930aa;
+
+ if (cpu_is_msm8930ab()) {
+ rate = 1000000000;
+ fmax = fmax_gfx3d_8930ab;
gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8930ab;
- pll15_clk.c.rate = 1000000000;
- gfx3d_clk.c.fmax = fmax_gfx3d_8930ab;
- gmem_axi_clk.c.depends = &gfx3d_axi_clk_8930.c;
vcodec_clk.c.fmax = fmax_vcodec_8930ab;
}
+
+ pll15_clk.c.rate = rate;
+ gfx3d_clk.c.fmax = fmax;
+ gmem_axi_clk.c.depends = &gfx3d_axi_clk_8930.c;
+
if ((readl_relaxed(PRNG_CLK_NS_REG) & 0x7F) == 0x2B)
prng_clk.freq_tbl = clk_tbl_prng_64;
@@ -6746,7 +6914,7 @@
rpm_vreg_dig_8930 = RPM_VREG_ID_PM8917_VDD_DIG_CORNER;
vdd_sr2_hdmi_pll.set_vdd = set_vdd_sr2_hdmi_pll_8930_pm8917;
- msm8960_clock_pre_init();
+ __msm8930_clock_pre_init();
}
static void __init msm8930_clock_pre_init(void)
@@ -6754,10 +6922,10 @@
vdd_dig.set_vdd = set_vdd_dig_8930;
vdd_sr2_hdmi_pll.set_vdd = set_vdd_sr2_hdmi_pll_8930;
- msm8960_clock_pre_init();
+ __msm8930_clock_pre_init();
}
-static void __init msm8960_clock_post_init(void)
+static void __init common_clock_post_init(void)
{
/* Keep PXO on whenever APPS cpu is active */
clk_prepare_enable(&pxo_a_clk.c);
@@ -6777,18 +6945,12 @@
clk_set_rate(&tsif_ref_clk.c, 105000);
clk_set_rate(&tssc_clk.c, 27000000);
clk_set_rate(&usb_hs1_xcvr_clk.c, 60000000);
- if (soc_class_is_apq8064()) {
- clk_set_rate(&usb_hs3_xcvr_clk.c, 60000000);
- clk_set_rate(&usb_hs4_xcvr_clk.c, 60000000);
- }
clk_set_rate(&usb_fs1_src_clk.c, 60000000);
- if (cpu_is_msm8960ab() || cpu_is_msm8960() || cpu_is_msm8930() ||
- cpu_is_msm8930aa() || cpu_is_msm8627() || cpu_is_msm8930ab())
- clk_set_rate(&usb_fs2_src_clk.c, 60000000);
clk_set_rate(&usb_hsic_xcvr_fs_clk.c, 60000000);
clk_set_rate(&usb_hsic_hsic_src_clk.c, 480000000);
clk_set_rate(&usb_hsic_hsio_cal_clk.c, 9000000);
clk_set_rate(&usb_hsic_system_clk.c, 60000000);
+
/*
* Set the CSI rates to a safe default to avoid warnings when
* switching csi pix and rdi clocks.
@@ -6817,6 +6979,19 @@
clk_prepare_enable(&sfab_tmr_a_clk.c);
}
+static void __init msm8960_clock_post_init(void)
+{
+ common_clock_post_init();
+ clk_set_rate(&usb_fs2_src_clk.c, 60000000);
+}
+
+static void __init msm8064_clock_post_init(void)
+{
+ common_clock_post_init();
+ clk_set_rate(&usb_hs3_xcvr_clk.c, 60000000);
+ clk_set_rate(&usb_hs4_xcvr_clk.c, 60000000);
+}
+
static int __init msm8960_clock_late_init(void)
{
int rc;
@@ -6859,8 +7034,8 @@
struct clock_init_data apq8064_clock_init_data __initdata = {
.table = msm_clocks_8064,
.size = ARRAY_SIZE(msm_clocks_8064),
- .pre_init = msm8960_clock_pre_init,
- .post_init = msm8960_clock_post_init,
+ .pre_init = msm8064_clock_pre_init,
+ .post_init = msm8064_clock_post_init,
.late_init = msm8960_clock_late_init,
};
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 2de8799..3aef106 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -24,6 +24,7 @@
#include <mach/rpm-regulator-smd.h>
#include <mach/socinfo.h>
#include <mach/rpm-smd.h>
+#include <mach/clock-generic.h>
#include "clock-local2.h"
#include "clock-pll.h"
@@ -638,14 +639,14 @@
VDD_DIG_NUM
};
-static const int *vdd_corner[] = {
- [VDD_DIG_NONE] = VDD_UV(RPM_REGULATOR_CORNER_NONE),
- [VDD_DIG_LOW] = VDD_UV(RPM_REGULATOR_CORNER_SVS_SOC),
- [VDD_DIG_NOMINAL] = VDD_UV(RPM_REGULATOR_CORNER_NORMAL),
- [VDD_DIG_HIGH] = VDD_UV(RPM_REGULATOR_CORNER_SUPER_TURBO),
+static int vdd_corner[] = {
+ RPM_REGULATOR_CORNER_NONE, /* VDD_DIG_NONE */
+ RPM_REGULATOR_CORNER_SVS_SOC, /* VDD_DIG_LOW */
+ RPM_REGULATOR_CORNER_NORMAL, /* VDD_DIG_NOMINAL */
+ RPM_REGULATOR_CORNER_SUPER_TURBO, /* VDD_DIG_HIGH */
};
-static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner);
+static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL);
#define RPM_MISC_CLK_TYPE 0x306b6c63
#define RPM_BUS_CLK_TYPE 0x316b6c63
@@ -784,6 +785,7 @@
static DEFINE_CLK_VOTER(snoc_msmbus_clk, &snoc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(cnoc_msmbus_clk, &cnoc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(pnoc_msmbus_a_clk, &pnoc_a_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(pnoc_pm_clk, &pnoc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(snoc_msmbus_a_clk, &snoc_a_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(cnoc_msmbus_a_clk, &cnoc_a_clk.c, LONG_MAX);
@@ -920,6 +922,7 @@
};
static struct clk_freq_tbl ftbl_gcc_blsp1_2_qup1_6_i2c_apps_clk[] = {
+ F(19200000, cxo, 1, 0, 0),
F(50000000, gpll0, 12, 0, 0),
F_END
};
@@ -3010,26 +3013,9 @@
},
};
-static struct branch_clk mdss_ahb_clk;
-static struct clk dsipll0_byte_clk_src = {
- .depends = &mdss_ahb_clk.c,
- .parent = &cxo_clk_src.c,
- .dbg_name = "dsipll0_byte_clk_src",
- .ops = &clk_ops_dsi_byte_pll,
- CLK_INIT(dsipll0_byte_clk_src),
-};
-
-static struct clk dsipll0_pixel_clk_src = {
- .depends = &mdss_ahb_clk.c,
- .parent = &cxo_clk_src.c,
- .dbg_name = "dsipll0_pixel_clk_src",
- .ops = &clk_ops_dsi_pixel_pll,
- CLK_INIT(dsipll0_pixel_clk_src),
-};
-
static struct clk_freq_tbl byte_freq_tbl[] = {
{
- .src_clk = &dsipll0_byte_clk_src,
+ .src_clk = &byte_clk_src_8974.c,
.div_src_val = BVAL(10, 8, dsipll0_byte_mm_source_val),
},
F_END
@@ -3040,7 +3026,7 @@
.current_freq = byte_freq_tbl,
.base = &virt_bases[MMSS_BASE],
.c = {
- .parent = &dsipll0_byte_clk_src,
+ .parent = &byte_clk_src_8974.c,
.dbg_name = "byte0_clk_src",
.ops = &clk_ops_byte,
VDD_DIG_FMAX_MAP3(LOW, 93800000, NOMINAL, 187500000,
@@ -3054,7 +3040,7 @@
.current_freq = byte_freq_tbl,
.base = &virt_bases[MMSS_BASE],
.c = {
- .parent = &dsipll0_byte_clk_src,
+ .parent = &byte_clk_src_8974.c,
.dbg_name = "byte1_clk_src",
.ops = &clk_ops_byte,
VDD_DIG_FMAX_MAP3(LOW, 93800000, NOMINAL, 187500000,
@@ -3193,7 +3179,9 @@
F_HDMI( 25200000, hdmipll, 1, 0, 0),
F_HDMI( 27000000, hdmipll, 1, 0, 0),
F_HDMI( 27030000, hdmipll, 1, 0, 0),
+ F_HDMI( 65000000, hdmipll, 1, 0, 0),
F_HDMI( 74250000, hdmipll, 1, 0, 0),
+ F_HDMI(108000000, hdmipll, 1, 0, 0),
F_HDMI(148500000, hdmipll, 1, 0, 0),
F_HDMI(268500000, hdmipll, 1, 0, 0),
F_HDMI(297000000, hdmipll, 1, 0, 0),
@@ -3232,10 +3220,73 @@
},
};
+struct clk_ops clk_ops_pixel_clock;
+
+static long round_rate_pixel(struct clk *clk, unsigned long rate)
+{
+ int frac_num[] = {3, 2, 4, 1};
+ int frac_den[] = {8, 9, 9, 1};
+ int delta = 100000;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(frac_num); i++) {
+ unsigned long request = (rate * frac_den[i]) / frac_num[i];
+ unsigned long src_rate;
+
+ src_rate = clk_round_rate(clk->parent, request);
+ if ((src_rate < (request - delta)) ||
+ (src_rate > (request + delta)))
+ continue;
+
+ return (src_rate * frac_num[i]) / frac_den[i];
+ }
+
+ return -EINVAL;
+}
+
+
+static int set_rate_pixel(struct clk *clk, unsigned long rate)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ struct clk_freq_tbl *pixel_freq = rcg->current_freq;
+ int frac_num[] = {3, 2, 4, 1};
+ int frac_den[] = {8, 9, 9, 1};
+ int delta = 100000;
+ int i, rc;
+
+ for (i = 0; i < ARRAY_SIZE(frac_num); i++) {
+ unsigned long request = (rate * frac_den[i]) / frac_num[i];
+ unsigned long src_rate;
+
+ src_rate = clk_round_rate(clk->parent, request);
+ if ((src_rate < (request - delta)) ||
+ (src_rate > (request + delta)))
+ continue;
+
+ rc = clk_set_rate(clk->parent, src_rate);
+ if (rc)
+ return rc;
+
+ pixel_freq->div_src_val &= ~BM(4, 0);
+ if (frac_den[i] == frac_num[i]) {
+ pixel_freq->m_val = 0;
+ pixel_freq->n_val = 0;
+ } else {
+ pixel_freq->m_val = frac_num[i];
+ pixel_freq->n_val = ~(frac_den[i] - frac_num[i]);
+ pixel_freq->d_val = ~frac_den[i];
+ }
+ set_rate_mnd(rcg, pixel_freq);
+ return 0;
+ }
+ return -EINVAL;
+}
+
static struct clk_freq_tbl pixel_freq_tbl[] = {
{
- .src_clk = &dsipll0_pixel_clk_src,
- .div_src_val = BVAL(10, 8, dsipll0_pixel_mm_source_val),
+ .src_clk = &pixel_clk_src_8974.c,
+ .div_src_val = BVAL(10, 8, dsipll0_pixel_mm_source_val)
+ | BVAL(4, 0, 0),
},
F_END
};
@@ -3245,9 +3296,9 @@
.current_freq = pixel_freq_tbl,
.base = &virt_bases[MMSS_BASE],
.c = {
- .parent = &dsipll0_pixel_clk_src,
+ .parent = &pixel_clk_src_8974.c,
.dbg_name = "pclk0_clk_src",
- .ops = &clk_ops_pixel,
+ .ops = &clk_ops_pixel_clock,
VDD_DIG_FMAX_MAP2(LOW, 125000000, NOMINAL, 250000000),
CLK_INIT(pclk0_clk_src.c),
},
@@ -3258,9 +3309,9 @@
.current_freq = pixel_freq_tbl,
.base = &virt_bases[MMSS_BASE],
.c = {
- .parent = &dsipll0_pixel_clk_src,
+ .parent = &pixel_clk_src_8974.c,
.dbg_name = "pclk1_clk_src",
- .ops = &clk_ops_pixel,
+ .ops = &clk_ops_pixel_clock,
VDD_DIG_FMAX_MAP2(LOW, 125000000, NOMINAL, 250000000),
CLK_INIT(pclk1_clk_src.c),
},
@@ -4209,6 +4260,7 @@
static struct branch_clk venus0_vcodec0_clk = {
.cbcr_reg = VENUS0_VCODEC0_CBCR,
+ .bcr_reg = VENUS0_BCR,
.has_sibling = 0,
.base = &virt_bases[MMSS_BASE],
.c = {
@@ -4888,12 +4940,17 @@
CLK_LOOKUP("byte_clk", mdss_byte1_clk.c, "fd922e00.qcom,mdss_dsi"),
CLK_LOOKUP("core_clk", mdss_esc0_clk.c, "fd922800.qcom,mdss_dsi"),
CLK_LOOKUP("core_clk", mdss_esc1_clk.c, "fd922e00.qcom,mdss_dsi"),
+ CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd922800.qcom,mdss_dsi"),
+ CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd922e00.qcom,mdss_dsi"),
+ CLK_LOOKUP("bus_clk", mdss_axi_clk.c, "fd922800.qcom,mdss_dsi"),
+ CLK_LOOKUP("bus_clk", mdss_axi_clk.c, "fd922e00.qcom,mdss_dsi"),
CLK_LOOKUP("pixel_clk", mdss_pclk0_clk.c, "fd922800.qcom,mdss_dsi"),
CLK_LOOKUP("pixel_clk", mdss_pclk1_clk.c, "fd922e00.qcom,mdss_dsi"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd922100.qcom,hdmi_tx"),
CLK_LOOKUP("alt_iface_clk", mdss_hdmi_ahb_clk.c,
"fd922100.qcom,hdmi_tx"),
CLK_LOOKUP("core_clk", mdss_hdmi_clk.c, "fd922100.qcom,hdmi_tx"),
+ CLK_LOOKUP("mdp_core_clk", mdss_mdp_clk.c, "fd922100.qcom,hdmi_tx"),
CLK_LOOKUP("extp_clk", mdss_extpclk_clk.c, "fd922100.qcom,hdmi_tx"),
CLK_LOOKUP("core_clk", mdss_mdp_clk.c, "mdp.0"),
CLK_LOOKUP("lut_clk", mdss_mdp_lut_clk.c, "mdp.0"),
@@ -4947,68 +5004,75 @@
"fda0b400.qcom,csiphy"),
CLK_LOOKUP("csiphy_timer_clk", camss_phy2_csi2phytimer_clk.c,
"fda0b400.qcom,csiphy"),
+
/* CSID clocks */
- CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
- "fda08000.qcom,csid"),
CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
- "fda08000.qcom,csid"),
- CLK_LOOKUP("csi0_ahb_clk", camss_csi0_ahb_clk.c, "fda08000.qcom,csid"),
- CLK_LOOKUP("csi0_src_clk", csi0_clk_src.c, "fda08000.qcom,csid"),
- CLK_LOOKUP("csi0_phy_clk", camss_csi0phy_clk.c, "fda08000.qcom,csid"),
- CLK_LOOKUP("csi0_clk", camss_csi0_clk.c, "fda08000.qcom,csid"),
- CLK_LOOKUP("csi0_pix_clk", camss_csi0pix_clk.c, "fda08000.qcom,csid"),
- CLK_LOOKUP("csi0_rdi_clk", camss_csi0rdi_clk.c, "fda08000.qcom,csid"),
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("csi_ahb_clk", camss_csi0_ahb_clk.c,
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("csi_src_clk", csi0_clk_src.c,
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("csi_phy_clk", camss_csi0phy_clk.c,
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("csi_clk", camss_csi0_clk.c,
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("csi_pix_clk", camss_csi0pix_clk.c,
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("csi_rdi_clk", camss_csi0rdi_clk.c,
+ "fda08000.qcom,csid"),
- CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
- "fda08400.qcom,csid"),
CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
- "fda08400.qcom,csid"),
- CLK_LOOKUP("csi0_ahb_clk", camss_csi0_ahb_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi1_ahb_clk", camss_csi1_ahb_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi0_src_clk", csi0_clk_src.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi1_src_clk", csi1_clk_src.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi0_phy_clk", camss_csi0phy_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi1_phy_clk", camss_csi1phy_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi0_clk", camss_csi0_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi1_clk", camss_csi1_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi0_pix_clk", camss_csi0pix_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi1_pix_clk", camss_csi1pix_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi0_rdi_clk", camss_csi0rdi_clk.c, "fda08400.qcom,csid"),
- CLK_LOOKUP("csi1_rdi_clk", camss_csi1rdi_clk.c, "fda08400.qcom,csid"),
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("csi_ahb_clk", camss_csi1_ahb_clk.c,
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("csi_src_clk", csi1_clk_src.c,
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("csi_phy_clk", camss_csi1phy_clk.c,
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("csi_clk", camss_csi1_clk.c,
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("csi_pix_clk", camss_csi1pix_clk.c,
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("csi_rdi_clk", camss_csi1rdi_clk.c,
+ "fda08400.qcom,csid"),
- CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
- "fda08800.qcom,csid"),
CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
- "fda08800.qcom,csid"),
- CLK_LOOKUP("csi0_ahb_clk", camss_csi0_ahb_clk.c, "fda08800.qcom,csid"),
- CLK_LOOKUP("csi2_ahb_clk", camss_csi2_ahb_clk.c, "fda08800.qcom,csid"),
- CLK_LOOKUP("csi0_src_clk", csi0_clk_src.c, "fda08800.qcom,csid"),
- CLK_LOOKUP("csi2_src_clk", csi2_clk_src.c, "fda08800.qcom,csid"),
- CLK_LOOKUP("csi0_phy_clk", camss_csi0phy_clk.c, "fda08800.qcom,csid"),
- CLK_LOOKUP("csi2_phy_clk", camss_csi2phy_clk.c, "fda08800.qcom,csid"),
- CLK_LOOKUP("csi0_clk", camss_csi0_clk.c, "fda08800.qcom,csid"),
- CLK_LOOKUP("csi2_clk", camss_csi2_clk.c, "fda08800.qcom,csid"),
- CLK_LOOKUP("csi0_pix_clk", camss_csi0pix_clk.c, "fda08800.qcom,csid"),
- CLK_LOOKUP("csi2_pix_clk", camss_csi2pix_clk.c, "fda08800.qcom,csid"),
- CLK_LOOKUP("csi0_rdi_clk", camss_csi0rdi_clk.c, "fda08800.qcom,csid"),
- CLK_LOOKUP("csi2_rdi_clk", camss_csi2rdi_clk.c, "fda08800.qcom,csid"),
+ "fda08800.qcom,csid"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda08800.qcom,csid"),
+ CLK_LOOKUP("csi_ahb_clk", camss_csi2_ahb_clk.c,
+ "fda08800.qcom,csid"),
+ CLK_LOOKUP("csi_src_clk", csi2_clk_src.c,
+ "fda08800.qcom,csid"),
+ CLK_LOOKUP("csi_phy_clk", camss_csi2phy_clk.c,
+ "fda08800.qcom,csid"),
+ CLK_LOOKUP("csi_clk", camss_csi2_clk.c,
+ "fda08800.qcom,csid"),
+ CLK_LOOKUP("csi_pix_clk", camss_csi2pix_clk.c,
+ "fda08800.qcom,csid"),
+ CLK_LOOKUP("csi_rdi_clk", camss_csi2rdi_clk.c,
+ "fda08800.qcom,csid"),
- CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
- "fda08c00.qcom,csid"),
CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
- "fda08c00.qcom,csid"),
- CLK_LOOKUP("csi0_ahb_clk", camss_csi0_ahb_clk.c, "fda08c00.qcom,csid"),
- CLK_LOOKUP("csi3_ahb_clk", camss_csi3_ahb_clk.c, "fda08c00.qcom,csid"),
- CLK_LOOKUP("csi0_src_clk", csi0_clk_src.c, "fda08c00.qcom,csid"),
- CLK_LOOKUP("csi3_src_clk", csi3_clk_src.c, "fda08c00.qcom,csid"),
- CLK_LOOKUP("csi0_phy_clk", camss_csi0phy_clk.c, "fda08c00.qcom,csid"),
- CLK_LOOKUP("csi3_phy_clk", camss_csi3phy_clk.c, "fda08c00.qcom,csid"),
- CLK_LOOKUP("csi0_clk", camss_csi0_clk.c, "fda08c00.qcom,csid"),
- CLK_LOOKUP("csi3_clk", camss_csi3_clk.c, "fda08c00.qcom,csid"),
- CLK_LOOKUP("csi0_pix_clk", camss_csi0pix_clk.c, "fda08c00.qcom,csid"),
- CLK_LOOKUP("csi3_pix_clk", camss_csi3pix_clk.c, "fda08c00.qcom,csid"),
- CLK_LOOKUP("csi0_rdi_clk", camss_csi0rdi_clk.c, "fda08c00.qcom,csid"),
- CLK_LOOKUP("csi3_rdi_clk", camss_csi3rdi_clk.c, "fda08c00.qcom,csid"),
+ "fda08c00.qcom,csid"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda08c00.qcom,csid"),
+ CLK_LOOKUP("csi_ahb_clk", camss_csi3_ahb_clk.c,
+ "fda08c00.qcom,csid"),
+ CLK_LOOKUP("csi_src_clk", csi3_clk_src.c,
+ "fda08c00.qcom,csid"),
+ CLK_LOOKUP("csi_phy_clk", camss_csi3phy_clk.c,
+ "fda08c00.qcom,csid"),
+ CLK_LOOKUP("csi_clk", camss_csi3_clk.c,
+ "fda08c00.qcom,csid"),
+ CLK_LOOKUP("csi_pix_clk", camss_csi3pix_clk.c,
+ "fda08c00.qcom,csid"),
+ CLK_LOOKUP("csi_rdi_clk", camss_csi3rdi_clk.c,
+ "fda08c00.qcom,csid"),
/* ISPIF clocks */
CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
@@ -5086,7 +5150,6 @@
CLK_LOOKUP("core_clk", camss_vfe_vfe_axi_clk.c, "fda44000.qcom,iommu"),
CLK_LOOKUP("alt_core_clk", camss_top_ahb_clk.c, "fda44000.qcom,iommu"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "mdp.0"),
- CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "mdss_dsi_clk_ctrl"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd923400.qcom,mdss_edp"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd928000.qcom,iommu"),
CLK_LOOKUP("core_clk", mdss_axi_clk.c, "fd928000.qcom,iommu"),
@@ -5114,6 +5177,17 @@
CLK_LOOKUP("bus_clk", venus0_axi_clk.c, "fdc00000.qcom,vidc"),
CLK_LOOKUP("mem_clk", venus0_ocmemnoc_clk.c, "fdc00000.qcom,vidc"),
+ CLK_LOOKUP("core_clk", venus0_vcodec0_clk.c, "fd8c1024.qcom,gdsc"),
+ CLK_LOOKUP("core_clk", mdss_mdp_clk.c, "fd8c2304.qcom,gdsc"),
+ CLK_LOOKUP("lut_clk", mdss_mdp_lut_clk.c, "fd8c2304.qcom,gdsc"),
+ CLK_LOOKUP("core0_clk", camss_jpeg_jpeg0_clk.c, "fd8c35a4.qcom,gdsc"),
+ CLK_LOOKUP("core1_clk", camss_jpeg_jpeg1_clk.c, "fd8c35a4.qcom,gdsc"),
+ CLK_LOOKUP("core2_clk", camss_jpeg_jpeg2_clk.c, "fd8c35a4.qcom,gdsc"),
+ CLK_LOOKUP("core0_clk", camss_vfe_vfe0_clk.c, "fd8c36a4.qcom,gdsc"),
+ CLK_LOOKUP("core1_clk", camss_vfe_vfe1_clk.c, "fd8c36a4.qcom,gdsc"),
+ CLK_LOOKUP("csi0_clk", camss_csi_vfe0_clk.c, "fd8c36a4.qcom,gdsc"),
+ CLK_LOOKUP("csi1_clk", camss_csi_vfe1_clk.c, "fd8c36a4.qcom,gdsc"),
+ CLK_LOOKUP("cpp_clk", camss_vfe_cpp_clk.c, "fd8c36a4.qcom,gdsc"),
CLK_LOOKUP("core_clk", oxili_gfx3d_clk.c, "fd8c4024.qcom,gdsc"),
/* LPASS clocks */
@@ -5145,6 +5219,7 @@
CLK_LOOKUP("bus_clk", snoc_msmbus_clk.c, "msm_sys_noc"),
CLK_LOOKUP("bus_a_clk", snoc_msmbus_a_clk.c, "msm_sys_noc"),
CLK_LOOKUP("bus_clk", pnoc_msmbus_clk.c, "msm_periph_noc"),
+ CLK_LOOKUP("bus_clk", pnoc_pm_clk.c, "pm_8x60"),
CLK_LOOKUP("bus_a_clk", pnoc_msmbus_a_clk.c, "msm_periph_noc"),
CLK_LOOKUP("mem_clk", bimc_msmbus_clk.c, "msm_bimc"),
CLK_LOOKUP("mem_a_clk", bimc_msmbus_a_clk.c, "msm_bimc"),
@@ -5187,6 +5262,7 @@
CLK_LOOKUP("core_clk", qdss_clk.c, "fc342000.cti"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc343000.cti"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc344000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fdf30018.hwevent"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc322000.tmc"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc318000.tpiu"),
@@ -5216,12 +5292,23 @@
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc342000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc343000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc344000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fdf30018.hwevent"),
+
+ CLK_LOOKUP("core_mmss_clk", mmss_misc_ahb_clk.c, "fdf30018.hwevent"),
CLK_LOOKUP("l2_m_clk", l2_m_clk, ""),
CLK_LOOKUP("krait0_m_clk", krait0_m_clk, ""),
CLK_LOOKUP("krait1_m_clk", krait1_m_clk, ""),
CLK_LOOKUP("krait2_m_clk", krait2_m_clk, ""),
CLK_LOOKUP("krait3_m_clk", krait3_m_clk, ""),
+
+ /* DSI PLL clocks */
+ CLK_LOOKUP("", dsi_vco_clk_8974.c, ""),
+ CLK_LOOKUP("", analog_postdiv_clk_8974.c, ""),
+ CLK_LOOKUP("", indirect_path_div2_clk_8974.c, ""),
+ CLK_LOOKUP("", pixel_clk_src_8974.c, ""),
+ CLK_LOOKUP("", byte_mux_8974.c, ""),
+ CLK_LOOKUP("", byte_clk_src_8974.c, ""),
};
static struct pll_config_regs mmpll0_regs __initdata = {
@@ -5336,20 +5423,6 @@
.main_output_mask = BIT(0),
};
-#define PWR_ON_MASK BIT(31)
-#define EN_REST_WAIT_MASK (0xF << 20)
-#define EN_FEW_WAIT_MASK (0xF << 16)
-#define CLK_DIS_WAIT_MASK (0xF << 12)
-#define SW_OVERRIDE_MASK BIT(2)
-#define HW_CONTROL_MASK BIT(1)
-#define SW_COLLAPSE_MASK BIT(0)
-
-/* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */
-#define EN_REST_WAIT_VAL (0x2 << 20)
-#define EN_FEW_WAIT_VAL (0x2 << 16)
-#define CLK_DIS_WAIT_VAL (0x2 << 12)
-#define GDSC_TIMEOUT_US 50000
-
static void __init reg_init(void)
{
u32 regval;
@@ -5379,12 +5452,6 @@
writel_relaxed(regval | BIT(26) | BIT(25),
GCC_REG_BASE(APCS_CLOCK_BRANCH_ENA_VOTE));
}
-
- /*
- * TODO: Confirm that no clocks need to be voted on in this sleep vote
- * register.
- */
- writel_relaxed(0x0, GCC_REG_BASE(APCS_CLOCK_SLEEP_ENA_VOTE));
}
static void __init msm8974_clock_post_init(void)
@@ -5524,6 +5591,10 @@
qup_i2c_clks[i][0]->parent = qup_i2c_clks[i][1];
}
+ clk_ops_pixel_clock = clk_ops_pixel;
+ clk_ops_pixel_clock.set_rate = set_rate_pixel;
+ clk_ops_pixel_clock.round_rate = round_rate_pixel;
+
/*
* MDSS needs the ahb clock and needs to init before we register the
* lookup table.
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
index 4984255..facd6ba 100644
--- a/arch/arm/mach-msm/clock-9625.c
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -280,14 +280,14 @@
VDD_DIG_NUM
};
-static const int *vdd_corner[] = {
- [VDD_DIG_NONE] = VDD_UV(RPM_REGULATOR_CORNER_NONE),
- [VDD_DIG_LOW] = VDD_UV(RPM_REGULATOR_CORNER_SVS_SOC),
- [VDD_DIG_NOMINAL] = VDD_UV(RPM_REGULATOR_CORNER_NORMAL),
- [VDD_DIG_HIGH] = VDD_UV(RPM_REGULATOR_CORNER_SUPER_TURBO),
+static int vdd_corner[] = {
+ RPM_REGULATOR_CORNER_NONE, /* VDD_DIG_NONE */
+ RPM_REGULATOR_CORNER_SVS_SOC, /* VDD_DIG_LOW */
+ RPM_REGULATOR_CORNER_NORMAL, /* VDD_DIG_NOMINAL */
+ RPM_REGULATOR_CORNER_SUPER_TURBO, /* VDD_DIG_HIGH */
};
-static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner);
+static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL);
/* TODO: Needs to confirm the below values */
#define RPM_MISC_CLK_TYPE 0x306b6c63
@@ -390,10 +390,6 @@
PLL_F_END
};
-/*
- * Need to skip handoff of the acpu pll to avoid handoff code
- * to turn off the pll when the acpu is running off this pll.
- */
static struct pll_clk apcspll_clk_src = {
.mode_reg = (void __iomem *)APCS_CPU_PLL_MODE_REG,
.l_reg = (void __iomem *)APCS_CPU_PLL_L_REG,
@@ -415,7 +411,6 @@
.dbg_name = "apcspll_clk_src",
.ops = &clk_ops_local_pll,
CLK_INIT(apcspll_clk_src.c),
- .flags = CLKFLAG_SKIP_HANDOFF,
},
};
@@ -1579,6 +1574,10 @@
};
struct measure_mux_entry measure_mux_common[] __initdata = {
+ {&snoc_clk.c, GCC_BASE, 0x0000},
+ {&cnoc_clk.c, GCC_BASE, 0x0008},
+ {&pnoc_clk.c, GCC_BASE, 0x0010},
+ {&bimc_clk.c, GCC_BASE, 0x0155},
{&gcc_pdm_ahb_clk.c, GCC_BASE, 0x00d0},
{&gcc_usb_hsic_io_cal_sleep_clk.c, GCC_BASE, 0x005c},
{&gcc_usb_hsic_xcvr_fs_clk.c, GCC_BASE, 0x005d},
@@ -1620,11 +1619,6 @@
{&gcc_sdcc2_ahb_clk.c, GCC_BASE, 0x0071},
{&gcc_ce1_clk.c, GCC_BASE, 0x0138},
{&gcc_sys_noc_ipa_axi_clk.c, GCC_BASE, 0x0007},
- {&gcc_ipa_clk.c, GCC_BASE, 0x01E0},
- {&gcc_ipa_cnoc_clk.c, GCC_BASE, 0x01E1},
- {&gcc_ipa_sleep_clk.c, GCC_BASE, 0x01E2},
- {&gcc_qpic_clk.c, GCC_BASE, 0x01D8},
- {&gcc_qpic_ahb_clk.c, GCC_BASE, 0x01D9},
{&a5_m_clk, APCS_BASE, 0x3},
@@ -1929,6 +1923,7 @@
CLK_LOOKUP("core_clk", qdss_clk.c, "fc30f000.cti"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc310000.cti"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc333000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "f9011038.hwevent"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc322000.tmc"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc318000.tpiu"),
@@ -1950,7 +1945,7 @@
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30f000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc310000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc333000.cti"),
-
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "f9011038.hwevent"),
};
#define PLL_AUX_OUTPUT_BIT 1
diff --git a/arch/arm/mach-msm/clock-debug.c b/arch/arm/mach-msm/clock-debug.c
index d540f2b..fe43b72 100644
--- a/arch/arm/mach-msm/clock-debug.c
+++ b/arch/arm/mach-msm/clock-debug.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -21,10 +21,23 @@
#include <linux/clk.h>
#include <linux/list.h>
#include <linux/clkdev.h>
+#include <linux/uaccess.h>
#include <mach/clk-provider.h>
#include "clock.h"
+static LIST_HEAD(clk_list);
+static DEFINE_SPINLOCK(clk_list_lock);
+
+static struct dentry *debugfs_base;
+static u32 debug_suspend;
+
+struct clk_table {
+ struct list_head node;
+ struct clk_lookup *clocks;
+ size_t num_clocks;
+};
+
static int clock_debug_rate_set(void *data, u64 val)
{
struct clk *clock = data;
@@ -218,35 +231,68 @@
.release = seq_release,
};
-static int clock_parent_show(struct seq_file *m, void *unused)
+static ssize_t clock_parent_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
{
- struct clk *clock = m->private;
- struct clk *parent = clk_get_parent(clock);
+ struct clk *clock = filp->private_data;
+ struct clk *p = clock->parent;
+ char name[256] = {0};
- seq_printf(m, "%s\n", (parent ? parent->dbg_name : "None"));
+ snprintf(name, sizeof(name), "%s\n", p ? p->dbg_name : "None\n");
- return 0;
+ return simple_read_from_buffer(ubuf, cnt, ppos, name, strlen(name));
}
-static int clock_parent_open(struct inode *inode, struct file *file)
+
+static ssize_t clock_parent_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
{
- return single_open(file, clock_parent_show, inode->i_private);
+ struct clk *clock = filp->private_data;
+ char buf[256];
+ char *cmp;
+ unsigned long flags;
+ struct clk_table *table;
+ int i, ret;
+ struct clk *parent = NULL;
+
+ cnt = min(cnt, sizeof(buf) - 1);
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+ buf[cnt] = '\0';
+ cmp = strstrip(buf);
+
+ spin_lock_irqsave(&clk_list_lock, flags);
+ list_for_each_entry(table, &clk_list, node) {
+ for (i = 0; i < table->num_clocks; i++)
+ if (!strcmp(cmp, table->clocks[i].clk->dbg_name)) {
+ parent = table->clocks[i].clk;
+ break;
+ }
+ if (parent)
+ break;
+ }
+
+ if (!parent) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ spin_unlock_irqrestore(&clk_list_lock, flags);
+ ret = clk_set_parent(clock, table->clocks[i].clk);
+ if (ret)
+ return ret;
+
+ return cnt;
+err:
+ spin_unlock_irqrestore(&clk_list_lock, flags);
+ return ret;
}
+
static const struct file_operations clock_parent_fops = {
- .open = clock_parent_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-
-static struct dentry *debugfs_base;
-static u32 debug_suspend;
-
-struct clk_table {
- struct list_head node;
- struct clk_lookup *clocks;
- size_t num_clocks;
+ .open = simple_open,
+ .read = clock_parent_read,
+ .write = clock_parent_write,
};
static int clock_debug_add(struct clk *clock)
@@ -305,8 +351,6 @@
debugfs_remove_recursive(clk_dir);
return -ENOMEM;
}
-static LIST_HEAD(clk_list);
-static DEFINE_SPINLOCK(clk_list_lock);
/**
* clock_debug_register() - Add additional clocks to clock debugfs hierarchy
diff --git a/arch/arm/mach-msm/clock-dsi-8610.c b/arch/arm/mach-msm/clock-dsi-8610.c
new file mode 100644
index 0000000..44b332e
--- /dev/null
+++ b/arch/arm/mach-msm/clock-dsi-8610.c
@@ -0,0 +1,408 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/iopoll.h>
+#include <linux/clk.h>
+
+#include <asm/processor.h>
+#include <mach/msm_iomap.h>
+#include <mach/clk-provider.h>
+
+#include "clock-dsi-8610.h"
+
+#define DSI_PHY_PHYS 0xFDD00000
+#define DSI_PHY_SIZE 0x00100000
+
+#define DSI_CTRL 0x0000
+#define DSI_DSIPHY_PLL_CTRL_0 0x0200
+#define DSI_DSIPHY_PLL_CTRL_1 0x0204
+#define DSI_DSIPHY_PLL_CTRL_2 0x0208
+#define DSI_DSIPHY_PLL_CTRL_3 0x020C
+#define DSI_DSIPHY_PLL_RDY 0x0280
+#define DSI_DSIPHY_PLL_CTRL_8 0x0220
+#define DSI_DSIPHY_PLL_CTRL_9 0x0224
+#define DSI_DSIPHY_PLL_CTRL_10 0x0228
+
+#define DSI_BPP 3
+#define DSI_PLL_RDY_BIT 0x01
+#define DSI_PLL_RDY_LOOP_COUNT 80000
+#define DSI_MAX_DIVIDER 256
+
+static unsigned char *dsi_base;
+static struct clk *dsi_ahb_clk;
+
+int __init dsi_clk_ctrl_init(struct clk *ahb_clk)
+{
+ dsi_base = ioremap(DSI_PHY_PHYS, DSI_PHY_SIZE);
+ if (!dsi_base) {
+ pr_err("unable to remap dsi base\n");
+ return -ENODEV;
+ }
+
+ dsi_ahb_clk = ahb_clk;
+ return 0;
+}
+
+static int dsi_pll_vco_enable(struct clk *c)
+{
+ u32 status;
+ int i = 0, ret = 0;
+
+ ret = clk_enable(dsi_ahb_clk);
+ if (ret) {
+ pr_err("fail to enable dsi ahb clk\n");
+ return ret;
+ }
+
+ writel_relaxed(0x01, dsi_base + DSI_DSIPHY_PLL_CTRL_0);
+
+ do {
+ status = readl_relaxed(dsi_base + DSI_DSIPHY_PLL_RDY);
+ } while (!(status & DSI_PLL_RDY_BIT) && (i++ < DSI_PLL_RDY_LOOP_COUNT));
+
+ if (!(status & DSI_PLL_RDY_BIT)) {
+ pr_err("DSI PLL not ready, polling time out!\n");
+ ret = -ETIMEDOUT;
+ }
+
+ clk_disable(dsi_ahb_clk);
+
+ return ret;
+}
+
+static void dsi_pll_vco_disable(struct clk *c)
+{
+ int ret;
+
+ ret = clk_enable(dsi_ahb_clk);
+ if (ret) {
+ pr_err("fail to enable dsi ahb clk\n");
+ return;
+ }
+
+ writel_relaxed(0x00, dsi_base + DSI_DSIPHY_PLL_CTRL_0);
+ clk_disable(dsi_ahb_clk);
+}
+
+static int dsi_pll_vco_set_rate(struct clk *c, unsigned long rate)
+{
+ int ret;
+ u32 temp, val;
+ unsigned long fb_divider;
+ struct clk *parent = c->parent;
+ struct dsi_pll_vco_clk *vco_clk =
+ container_of(c, struct dsi_pll_vco_clk, c);
+
+ if (!rate)
+ return 0;
+
+ ret = clk_prepare_enable(dsi_ahb_clk);
+ if (ret) {
+ pr_err("fail to enable dsi ahb clk\n");
+ return ret;
+ }
+
+ temp = rate / 10;
+ val = parent->rate / 10;
+ fb_divider = (temp * vco_clk->pref_div_ratio) / val;
+ fb_divider = fb_divider / 2 - 1;
+
+ temp = readl_relaxed(dsi_base + DSI_DSIPHY_PLL_CTRL_1);
+ val = (temp & 0xFFFFFF00) | (fb_divider & 0xFF);
+ writel_relaxed(val, dsi_base + DSI_DSIPHY_PLL_CTRL_1);
+
+ temp = readl_relaxed(dsi_base + DSI_DSIPHY_PLL_CTRL_2);
+ val = (temp & 0xFFFFFFF8) | ((fb_divider >> 8) & 0x07);
+ writel_relaxed(val, dsi_base + DSI_DSIPHY_PLL_CTRL_2);
+
+ temp = readl_relaxed(dsi_base + DSI_DSIPHY_PLL_CTRL_3);
+ val = (temp & 0xFFFFFFC0) | (vco_clk->pref_div_ratio - 1);
+ writel_relaxed(val, dsi_base + DSI_DSIPHY_PLL_CTRL_3);
+
+ clk_disable_unprepare(dsi_ahb_clk);
+
+ return 0;
+}
+
+/* rate is the bit clk rate */
+static long dsi_pll_vco_round_rate(struct clk *c, unsigned long rate)
+{
+ long vco_rate;
+ struct dsi_pll_vco_clk *vco_clk =
+ container_of(c, struct dsi_pll_vco_clk, c);
+
+
+ vco_rate = rate;
+ if (rate < vco_clk->vco_clk_min)
+ vco_rate = vco_clk->vco_clk_min;
+ else if (rate > vco_clk->vco_clk_max)
+ vco_rate = vco_clk->vco_clk_max;
+
+ return vco_rate;
+}
+
+static unsigned long dsi_pll_vco_get_rate(struct clk *c)
+{
+ u32 fb_divider, ref_divider, vco_rate;
+ u32 temp, status;
+ struct clk *parent = c->parent;
+
+ status = readl_relaxed(dsi_base + DSI_DSIPHY_PLL_RDY);
+ if (status & DSI_PLL_RDY_BIT) {
+ fb_divider = readl_relaxed(dsi_base + DSI_DSIPHY_PLL_CTRL_1);
+ fb_divider &= 0xFF;
+ temp = readl_relaxed(dsi_base + DSI_DSIPHY_PLL_CTRL_2) & 0x07;
+ fb_divider = (temp << 8) | fb_divider;
+ fb_divider += 1;
+
+ ref_divider = readl_relaxed(dsi_base + DSI_DSIPHY_PLL_CTRL_3);
+ ref_divider &= 0x3F;
+ ref_divider += 1;
+
+ vco_rate = (parent->rate / ref_divider) * fb_divider;
+ } else {
+ vco_rate = 0;
+ }
+ return vco_rate;
+}
+
+static enum handoff dsi_pll_vco_handoff(struct clk *c)
+{
+ u32 status;
+
+ if (clk_prepare_enable(dsi_ahb_clk)) {
+ pr_err("fail to enable dsi ahb clk\n");
+ return HANDOFF_DISABLED_CLK;
+ }
+
+ status = readl_relaxed(dsi_base + DSI_DSIPHY_PLL_CTRL_0);
+ if (!status & DSI_PLL_RDY_BIT) {
+ pr_err("DSI PLL not ready\n");
+ clk_disable(dsi_ahb_clk);
+ return HANDOFF_DISABLED_CLK;
+ }
+
+ c->rate = dsi_pll_vco_get_rate(c);
+
+ clk_disable_unprepare(dsi_ahb_clk);
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+static int dsi_byteclk_set_rate(struct clk *c, unsigned long rate)
+{
+ int div, ret;
+ long vco_rate;
+ unsigned long bitclk_rate;
+ u32 temp, val;
+ struct clk *parent = clk_get_parent(c);
+
+ if (rate == 0) {
+ ret = clk_set_rate(parent, 0);
+ return ret;
+ }
+
+ bitclk_rate = rate * 8;
+ for (div = 1; div < DSI_MAX_DIVIDER; div++) {
+ vco_rate = clk_round_rate(parent, bitclk_rate * div);
+
+ if (vco_rate == bitclk_rate * div)
+ break;
+
+ if (vco_rate < bitclk_rate * div)
+ return -EINVAL;
+ }
+
+ if (vco_rate != bitclk_rate * div)
+ return -EINVAL;
+
+ ret = clk_set_rate(parent, vco_rate);
+ if (ret) {
+ pr_err("fail to set vco rate\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(dsi_ahb_clk);
+ if (ret) {
+ pr_err("fail to enable dsi ahb clk\n");
+ return ret;
+ }
+
+ /* set the bit clk divider */
+ temp = readl_relaxed(dsi_base + DSI_DSIPHY_PLL_CTRL_8);
+ val = (temp & 0xFFFFFFF0) | (div - 1);
+ writel_relaxed(val, dsi_base + DSI_DSIPHY_PLL_CTRL_8);
+
+ /* set the byte clk divider */
+ temp = readl_relaxed(dsi_base + DSI_DSIPHY_PLL_CTRL_9);
+ val = (temp & 0xFFFFFF00) | (vco_rate / rate - 1);
+ writel_relaxed(val, dsi_base + DSI_DSIPHY_PLL_CTRL_9);
+ clk_disable_unprepare(dsi_ahb_clk);
+
+ return 0;
+}
+
+static long dsi_byteclk_round_rate(struct clk *c, unsigned long rate)
+{
+ int div;
+ long vco_rate;
+ unsigned long bitclk_rate;
+ struct clk *parent = clk_get_parent(c);
+
+ if (rate == 0)
+ return -EINVAL;
+
+ bitclk_rate = rate * 8;
+ for (div = 1; div < DSI_MAX_DIVIDER; div++) {
+ vco_rate = clk_round_rate(parent, bitclk_rate * div);
+ if (vco_rate == bitclk_rate * div)
+ break;
+ if (vco_rate < bitclk_rate * div)
+ return -EINVAL;
+ }
+
+ if (vco_rate != bitclk_rate * div)
+ return -EINVAL;
+
+ return rate;
+}
+
+static enum handoff dsi_byteclk_handoff(struct clk *c)
+{
+ struct clk *parent = clk_get_parent(c);
+ unsigned long vco_rate = clk_get_rate(parent);
+ u32 out_div2;
+
+ if (vco_rate == 0)
+ return HANDOFF_DISABLED_CLK;
+
+ if (clk_prepare_enable(dsi_ahb_clk)) {
+ pr_err("fail to enable dsi ahb clk\n");
+ return HANDOFF_DISABLED_CLK;
+ }
+
+ out_div2 = readl_relaxed(dsi_base + DSI_DSIPHY_PLL_CTRL_9);
+ out_div2 &= 0xFF;
+ c->rate = vco_rate / (out_div2 + 1);
+ clk_disable_unprepare(dsi_ahb_clk);
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+static int dsi_dsiclk_set_rate(struct clk *c, unsigned long rate)
+{
+ u32 temp, val;
+ int ret;
+ struct clk *parent = clk_get_parent(c);
+ unsigned long vco_rate = clk_get_rate(parent);
+
+ if (rate == 0)
+ return 0;
+
+ if (vco_rate % rate != 0) {
+ pr_err("dsiclk_set_rate invalid rate\n");
+ return -EINVAL;
+ }
+
+ ret = clk_prepare_enable(dsi_ahb_clk);
+ if (ret) {
+ pr_err("fail to enable dsi ahb clk\n");
+ return ret;
+ }
+ temp = readl_relaxed(dsi_base + DSI_DSIPHY_PLL_CTRL_10);
+ val = (temp & 0xFFFFFF00) | (vco_rate / rate - 1);
+ writel_relaxed(val, dsi_base + DSI_DSIPHY_PLL_CTRL_10);
+ clk_disable_unprepare(dsi_ahb_clk);
+
+ return 0;
+}
+
+static long dsi_dsiclk_round_rate(struct clk *c, unsigned long rate)
+{
+ /* rate is the pixel clk rate, translate into dsi clk rate*/
+ struct clk *parent = clk_get_parent(c);
+ unsigned long vco_rate = clk_get_rate(parent);
+
+ rate *= DSI_BPP;
+
+ if (vco_rate < rate)
+ return -EINVAL;
+
+ if (vco_rate % rate != 0)
+ return -EINVAL;
+
+ return rate;
+}
+
+static enum handoff dsi_dsiclk_handoff(struct clk *c)
+{
+ struct clk *parent = clk_get_parent(c);
+ unsigned long vco_rate = clk_get_rate(parent);
+ u32 out_div3;
+
+ if (vco_rate == 0)
+ return HANDOFF_DISABLED_CLK;
+
+ if (clk_prepare_enable(dsi_ahb_clk)) {
+ pr_err("fail to enable dsi ahb clk\n");
+ return HANDOFF_DISABLED_CLK;
+ }
+
+ out_div3 = readl_relaxed(dsi_base + DSI_DSIPHY_PLL_CTRL_10);
+ out_div3 &= 0xFF;
+ c->rate = vco_rate / (out_div3 + 1);
+ clk_disable_unprepare(dsi_ahb_clk);
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+int dsi_prepare(struct clk *clk)
+{
+ return clk_prepare(dsi_ahb_clk);
+}
+
+void dsi_unprepare(struct clk *clk)
+{
+ clk_unprepare(dsi_ahb_clk);
+}
+
+struct clk_ops clk_ops_dsi_dsiclk = {
+ .prepare = dsi_prepare,
+ .unprepare = dsi_unprepare,
+ .set_rate = dsi_dsiclk_set_rate,
+ .round_rate = dsi_dsiclk_round_rate,
+ .handoff = dsi_dsiclk_handoff,
+};
+
+struct clk_ops clk_ops_dsi_byteclk = {
+ .prepare = dsi_prepare,
+ .unprepare = dsi_unprepare,
+ .set_rate = dsi_byteclk_set_rate,
+ .round_rate = dsi_byteclk_round_rate,
+ .handoff = dsi_byteclk_handoff,
+};
+
+struct clk_ops clk_ops_dsi_vco = {
+ .prepare = dsi_prepare,
+ .unprepare = dsi_unprepare,
+ .enable = dsi_pll_vco_enable,
+ .disable = dsi_pll_vco_disable,
+ .set_rate = dsi_pll_vco_set_rate,
+ .round_rate = dsi_pll_vco_round_rate,
+ .handoff = dsi_pll_vco_handoff,
+};
+
diff --git a/arch/arm/mach-msm/clock-dsi-8610.h b/arch/arm/mach-msm/clock-dsi-8610.h
new file mode 100644
index 0000000..4c09790
--- /dev/null
+++ b/arch/arm/mach-msm/clock-dsi-8610.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_CLOCK_DSI_8610
+#define __ARCH_ARM_MACH_MSM_CLOCK_DSI_8610
+
+#include <mach/clk-provider.h>
+
+struct dsi_pll_vco_clk {
+ const unsigned long vco_clk_min;
+ const unsigned long vco_clk_max;
+ const unsigned pref_div_ratio;
+ int factor;
+ struct clk c;
+};
+
+extern struct clk_ops clk_ops_dsi_vco;
+extern struct clk_ops clk_ops_dsi_byteclk;
+extern struct clk_ops clk_ops_dsi_dsiclk;
+
+int dsi_clk_ctrl_init(struct clk *ahb_clk);
+
+#endif
diff --git a/arch/arm/mach-msm/clock-generic.c b/arch/arm/mach-msm/clock-generic.c
new file mode 100644
index 0000000..4d74533
--- /dev/null
+++ b/arch/arm/mach-msm/clock-generic.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include <linux/clk.h>
+#include <mach/clk-provider.h>
+#include <mach/clock-generic.h>
+
+/* ==================== Mux clock ==================== */
+
+static int parent_to_src_sel(struct mux_clk *mux, struct clk *p)
+{
+ int i;
+
+ for (i = 0; i < mux->num_parents; i++) {
+ if (mux->parents[i].src == p)
+ return mux->parents[i].sel;
+ }
+
+ return -EINVAL;
+}
+
+static int mux_set_parent(struct clk *c, struct clk *p)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ int sel = parent_to_src_sel(mux, p);
+ struct clk *old_parent;
+ int rc = 0;
+ unsigned long flags;
+
+ if (sel < 0)
+ return sel;
+
+ rc = __clk_pre_reparent(c, p, &flags);
+ if (rc)
+ goto out;
+
+ rc = mux->ops->set_mux_sel(mux, sel);
+ if (rc)
+ goto set_fail;
+
+ old_parent = c->parent;
+ c->parent = p;
+ __clk_post_reparent(c, old_parent, &flags);
+
+ return 0;
+
+set_fail:
+ __clk_post_reparent(c, p, &flags);
+out:
+ return rc;
+}
+
+static long mux_round_rate(struct clk *c, unsigned long rate)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ int i;
+ long prate, max_prate = 0, rrate = LONG_MAX;
+
+ for (i = 0; i < mux->num_parents; i++) {
+ prate = clk_round_rate(mux->parents[i].src, rate);
+ if (prate < rate) {
+ max_prate = max(prate, max_prate);
+ continue;
+ }
+
+ rrate = min(rrate, prate);
+ }
+ if (rrate == LONG_MAX)
+ rrate = max_prate;
+
+ return rrate ? rrate : -EINVAL;
+}
+
+static int mux_set_rate(struct clk *c, unsigned long rate)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ struct clk *new_parent = NULL;
+ int rc = 0, i;
+ unsigned long new_par_curr_rate;
+
+ for (i = 0; i < mux->num_parents; i++) {
+ if (clk_round_rate(mux->parents[i].src, rate) == rate) {
+ new_parent = mux->parents[i].src;
+ break;
+ }
+ }
+ if (new_parent == NULL)
+ return -EINVAL;
+
+ /*
+ * Switch to safe parent since the old and new parent might be the
+ * same and the parent might temporarily turn off while switching
+ * rates.
+ */
+ if (mux->safe_sel >= 0)
+ rc = mux->ops->set_mux_sel(mux, mux->safe_sel);
+ if (rc)
+ return rc;
+
+ new_par_curr_rate = clk_get_rate(new_parent);
+ rc = clk_set_rate(new_parent, rate);
+ if (rc)
+ goto set_rate_fail;
+
+ rc = mux_set_parent(c, new_parent);
+ if (rc)
+ goto set_par_fail;
+
+ return 0;
+
+set_par_fail:
+ clk_set_rate(new_parent, new_par_curr_rate);
+set_rate_fail:
+ WARN(mux->ops->set_mux_sel(mux, parent_to_src_sel(mux, c->parent)),
+ "Set rate failed for %s. Also in bad state!\n", c->dbg_name);
+ return rc;
+}
+
+static int mux_enable(struct clk *c)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ if (mux->ops->enable)
+ return mux->ops->enable(mux);
+ return 0;
+}
+
+static void mux_disable(struct clk *c)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ if (mux->ops->disable)
+ return mux->ops->disable(mux);
+}
+
+static struct clk *mux_get_parent(struct clk *c)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ int sel = mux->ops->get_mux_sel(mux);
+ int i;
+
+ for (i = 0; i < mux->num_parents; i++) {
+ if (mux->parents[i].sel == sel)
+ return mux->parents[i].src;
+ }
+
+ /* Unfamiliar parent. */
+ return NULL;
+}
+
+static enum handoff mux_handoff(struct clk *c)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+
+ c->rate = clk_get_rate(c->parent);
+ mux->safe_sel = parent_to_src_sel(mux, mux->safe_parent);
+
+ if (mux->en_mask && mux->ops && mux->ops->is_enabled)
+ return mux->ops->is_enabled(mux)
+ ? HANDOFF_ENABLED_CLK
+ : HANDOFF_DISABLED_CLK;
+
+ /*
+ * If this function returns 'enabled' even when the clock downstream
+ * of this clock is disabled, then handoff code will unnecessarily
+ * enable the current parent of this clock. If this function always
+ * returns 'disabled' and a clock downstream is on, the clock handoff
+ * code will bump up the ref count for this clock and its current
+ * parent as necessary. So, clocks without an actual HW gate can
+ * always return disabled.
+ */
+ return HANDOFF_DISABLED_CLK;
+}
+
+struct clk_ops clk_ops_gen_mux = {
+ .enable = mux_enable,
+ .disable = mux_disable,
+ .set_parent = mux_set_parent,
+ .round_rate = mux_round_rate,
+ .set_rate = mux_set_rate,
+ .handoff = mux_handoff,
+ .get_parent = mux_get_parent,
+};
+
+
+/* ==================== Divider clock ==================== */
+
+static long __div_round_rate(struct clk *c, unsigned long rate, int *best_div)
+{
+ struct div_clk *d = to_div_clk(c);
+ unsigned int div, min_div, max_div;
+ long p_rrate, rrate = LONG_MAX;
+
+ rate = max(rate, 1UL);
+
+ if (!d->ops || !d->ops->set_div)
+ min_div = max_div = d->div;
+ else {
+ min_div = max(d->min_div, 1U);
+ max_div = min(d->max_div, (unsigned int) (LONG_MAX / rate));
+ }
+
+ for (div = min_div; div <= max_div; div++) {
+ p_rrate = clk_round_rate(c->parent, rate * div);
+ if (p_rrate < 0)
+ break;
+
+ p_rrate /= div;
+ /*
+ * Trying higher dividers is only going to ask the parent for
+ * a higher rate. If it can't even output a rate higher than
+ * the one we request for this divider, the parent is not
+ * going to be able to output an even higher rate required
+ * for a higher divider. So, stop trying higher dividers.
+ */
+ if (p_rrate < rate) {
+ if (rrate == LONG_MAX) {
+ rrate = p_rrate;
+ if (best_div)
+ *best_div = div;
+ }
+ break;
+ }
+ if (p_rrate < rrate) {
+ rrate = p_rrate;
+ if (best_div)
+ *best_div = div;
+ }
+
+ if (rrate <= rate + d->rate_margin)
+ break;
+ }
+
+ if (rrate == LONG_MAX)
+ return -EINVAL;
+
+ return rrate;
+}
+
+static long div_round_rate(struct clk *c, unsigned long rate)
+{
+ return __div_round_rate(c, rate, NULL);
+}
+
+static int div_set_rate(struct clk *c, unsigned long rate)
+{
+ struct div_clk *d = to_div_clk(c);
+ int div, rc = 0;
+ long rrate, old_prate;
+
+ rrate = __div_round_rate(c, rate, &div);
+ if (rrate != rate)
+ return -EINVAL;
+
+ if (div > d->div)
+ rc = d->ops->set_div(d, div);
+ if (rc)
+ return rc;
+
+ old_prate = clk_get_rate(c->parent);
+ rc = clk_set_rate(c->parent, rate * div);
+ if (rc)
+ goto set_rate_fail;
+
+ if (div < d->div)
+ rc = d->ops->set_div(d, div);
+ if (rc)
+ goto div_dec_fail;
+
+ d->div = div;
+
+ return 0;
+
+div_dec_fail:
+ WARN(clk_set_rate(c->parent, old_prate),
+ "Set rate failed for %s. Also in bad state!\n", c->dbg_name);
+set_rate_fail:
+ if (div > d->div)
+ WARN(d->ops->set_div(d, d->div),
+ "Set rate failed for %s. Also in bad state!\n",
+ c->dbg_name);
+ return rc;
+}
+
+static int div_enable(struct clk *c)
+{
+ struct div_clk *d = to_div_clk(c);
+ if (d->ops->enable)
+ return d->ops->enable(d);
+ return 0;
+}
+
+static void div_disable(struct clk *c)
+{
+ struct div_clk *d = to_div_clk(c);
+ if (d->ops->disable)
+ return d->ops->disable(d);
+}
+
+static enum handoff div_handoff(struct clk *c)
+{
+ struct div_clk *d = to_div_clk(c);
+
+ if (d->ops->get_div)
+ d->div = max(d->ops->get_div(d), 1);
+ d->div = max(d->div, 1U);
+ c->rate = clk_get_rate(c->parent) / d->div;
+
+ if (d->en_mask && d->ops && d->ops->is_enabled)
+ return d->ops->is_enabled(d)
+ ? HANDOFF_ENABLED_CLK
+ : HANDOFF_DISABLED_CLK;
+
+ /*
+ * If this function returns 'enabled' even when the clock downstream
+ * of this clock is disabled, then handoff code will unnecessarily
+ * enable the current parent of this clock. If this function always
+ * returns 'disabled' and a clock downstream is on, the clock handoff
+ * code will bump up the ref count for this clock and its current
+ * parent as necessary. So, clocks without an actual HW gate can
+ * always return disabled.
+ */
+ return HANDOFF_DISABLED_CLK;
+}
+
+struct clk_ops clk_ops_div = {
+ .enable = div_enable,
+ .disable = div_disable,
+ .round_rate = div_round_rate,
+ .set_rate = div_set_rate,
+ .handoff = div_handoff,
+};
+
+static long __slave_div_round_rate(struct clk *c, unsigned long rate,
+ int *best_div)
+{
+ struct div_clk *d = to_div_clk(c);
+ unsigned int div, min_div, max_div;
+ long p_rate;
+
+ rate = max(rate, 1UL);
+
+ if (!d->ops || !d->ops->set_div)
+ min_div = max_div = d->div;
+ else {
+ min_div = d->min_div;
+ max_div = d->max_div;
+ }
+
+ p_rate = clk_get_rate(c->parent);
+ div = p_rate / rate;
+ div = max(div, min_div);
+ div = min(div, max_div);
+ if (best_div)
+ *best_div = div;
+
+ return p_rate / div;
+}
+
+static long slave_div_round_rate(struct clk *c, unsigned long rate)
+{
+ return __slave_div_round_rate(c, rate, NULL);
+}
+
+static int slave_div_set_rate(struct clk *c, unsigned long rate)
+{
+ struct div_clk *d = to_div_clk(c);
+ int div, rc = 0;
+ long rrate;
+
+ rrate = __slave_div_round_rate(c, rate, &div);
+ if (rrate != rate)
+ return -EINVAL;
+
+ if (div == d->div)
+ return 0;
+
+ if (d->ops->set_div)
+ rc = d->ops->set_div(d, div);
+ if (rc)
+ return rc;
+
+ d->div = div;
+
+ return 0;
+}
+
+struct clk_ops clk_ops_slave_div = {
+ .enable = div_enable,
+ .disable = div_disable,
+ .round_rate = slave_div_round_rate,
+ .set_rate = slave_div_set_rate,
+ .handoff = div_handoff,
+};
diff --git a/arch/arm/mach-msm/clock-local2.c b/arch/arm/mach-msm/clock-local2.c
index 0d1104e..fd790e2 100644
--- a/arch/arm/mach-msm/clock-local2.c
+++ b/arch/arm/mach-msm/clock-local2.c
@@ -175,39 +175,19 @@
cf = rcg->current_freq;
- /* Enable source clock dependency for the new freq. */
- if (c->prepare_count) {
- rc = clk_prepare(nf->src_clk);
- if (rc)
- return rc;
- }
-
- spin_lock_irqsave(&c->lock, flags);
- if (c->count) {
- rc = clk_enable(nf->src_clk);
- if (rc) {
- spin_unlock_irqrestore(&c->lock, flags);
- clk_unprepare(nf->src_clk);
- return rc;
- }
- }
+ rc = __clk_pre_reparent(c, nf->src_clk, &flags);
+ if (rc)
+ return rc;
BUG_ON(!rcg->set_rate);
/* Perform clock-specific frequency switch operations. */
rcg->set_rate(rcg, nf);
-
- /* Release source requirements of the old freq. */
- if (c->count)
- clk_disable(cf->src_clk);
- spin_unlock_irqrestore(&c->lock, flags);
-
- if (c->prepare_count)
- clk_unprepare(cf->src_clk);
-
rcg->current_freq = nf;
c->parent = nf->src_clk;
+ __clk_post_reparent(c, cf->src_clk, &flags);
+
return 0;
}
@@ -476,10 +456,7 @@
if (branch->max_div)
return branch->c.rate;
- if (!branch->has_sibling)
- return clk_get_rate(c->parent);
-
- return 0;
+ return clk_get_rate(c->parent);
}
static int branch_clk_list_rate(struct clk *c, unsigned n)
@@ -562,11 +539,8 @@
{
struct branch_clk *branch = to_branch_clk(c);
- if (!branch->bcr_reg) {
- WARN("clk_reset called on an unsupported clock (%s)\n",
- c->dbg_name);
+ if (!branch->bcr_reg)
return -EPERM;
- }
return __branch_clk_reset(BCR_REG(branch), action);
}
@@ -670,7 +644,7 @@
return HANDOFF_ENABLED_CLK;
}
-static enum handoff byte_rcg_handoff(struct clk *clk)
+enum handoff byte_rcg_handoff(struct clk *clk)
{
struct rcg_clk *rcg = to_rcg_clk(clk);
u32 div_val;
@@ -721,10 +695,10 @@
return 0;
}
-static enum handoff pixel_rcg_handoff(struct clk *clk)
+enum handoff pixel_rcg_handoff(struct clk *clk)
{
struct rcg_clk *rcg = to_rcg_clk(clk);
- u32 div_val, mval, nval, cfg_regval;
+ u32 div_val = 0, mval = 0, nval = 0, cfg_regval;
unsigned long pre_div_rate, parent_rate = clk_get_rate(clk->parent);
cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg));
@@ -738,6 +712,15 @@
clk->rate = pre_div_rate;
+ /*
+ * Pixel clocks have one frequency entry in their frequency table.
+ * Update that entry.
+ */
+ if (rcg->current_freq) {
+ rcg->current_freq->div_src_val &= ~CFG_RCGR_DIV_MASK;
+ rcg->current_freq->div_src_val |= div_val;
+ }
+
/* If MND is used, find the rate after the MND division */
if ((cfg_regval & MND_MODE_MASK) == MND_DUAL_EDGE_MODE_BVAL) {
mval = readl_relaxed(M_REG(rcg));
@@ -745,6 +728,11 @@
if (!nval)
return HANDOFF_DISABLED_CLK;
nval = (~nval) + mval;
+ if (rcg->current_freq) {
+ rcg->current_freq->n_val = ~(nval - mval);
+ rcg->current_freq->m_val = mval;
+ rcg->current_freq->d_val = ~nval;
+ }
clk->rate = (pre_div_rate * mval) / nval;
}
@@ -812,6 +800,155 @@
}
+#define ENABLE_REG(x) (*(x)->base + (x)->enable_reg)
+#define SELECT_REG(x) (*(x)->base + (x)->select_reg)
+
+/*
+ * mux clock functions
+ */
+static void cam_mux_clk_halt_check(void)
+{
+ /* Ensure that the delay starts after the mux disable/enable. */
+ mb();
+ udelay(HALT_CHECK_DELAY_US);
+}
+
+static int cam_mux_clk_enable(struct clk *c)
+{
+ unsigned long flags;
+ u32 regval;
+ struct cam_mux_clk *mux = to_cam_mux_clk(c);
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ regval = readl_relaxed(ENABLE_REG(mux));
+ regval |= mux->enable_mask;
+ writel_relaxed(regval, ENABLE_REG(mux));
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ /* Wait for clock to enable before continuing. */
+ cam_mux_clk_halt_check();
+
+ return 0;
+}
+
+static void cam_mux_clk_disable(struct clk *c)
+{
+ unsigned long flags;
+ struct cam_mux_clk *mux = to_cam_mux_clk(c);
+ u32 regval;
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ regval = readl_relaxed(ENABLE_REG(mux));
+ regval &= ~mux->enable_mask;
+ writel_relaxed(regval, ENABLE_REG(mux));
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ /* Wait for clock to disable before continuing. */
+ cam_mux_clk_halt_check();
+}
+
+static int mux_source_switch(struct cam_mux_clk *mux, struct mux_source *dest)
+{
+ unsigned long flags;
+ u32 regval;
+ int ret = 0;
+
+ ret = __clk_pre_reparent(&mux->c, dest->clk, &flags);
+ if (ret)
+ goto out;
+
+ regval = readl_relaxed(SELECT_REG(mux));
+ regval &= ~mux->select_mask;
+ regval |= dest->select_val;
+ writel_relaxed(regval, SELECT_REG(mux));
+
+ /* Make sure switch request goes through before proceeding. */
+ mb();
+
+ __clk_post_reparent(&mux->c, mux->c.parent, &flags);
+out:
+ return ret;
+}
+
+static int cam_mux_clk_set_parent(struct clk *c, struct clk *parent)
+{
+ struct cam_mux_clk *mux = to_cam_mux_clk(c);
+ struct mux_source *dest = NULL;
+ int ret;
+
+ if (!mux->sources || !parent)
+ return -EPERM;
+
+ dest = mux->sources;
+
+ while (dest->clk) {
+ if (dest->clk == parent)
+ break;
+ dest++;
+ }
+
+ if (!dest->clk)
+ return -EPERM;
+
+ ret = mux_source_switch(mux, dest);
+ if (ret)
+ return ret;
+
+ mux->c.rate = clk_get_rate(dest->clk);
+
+ return 0;
+}
+
+static enum handoff cam_mux_clk_handoff(struct clk *c)
+{
+ struct cam_mux_clk *mux = to_cam_mux_clk(c);
+ u32 mask = mux->enable_mask;
+ u32 regval = readl_relaxed(ENABLE_REG(mux));
+
+ c->rate = clk_get_rate(c->parent);
+
+ if (mask == (regval & mask))
+ return HANDOFF_ENABLED_CLK;
+
+ return HANDOFF_DISABLED_CLK;
+}
+
+static struct clk *cam_mux_clk_get_parent(struct clk *c)
+{
+ struct cam_mux_clk *mux = to_cam_mux_clk(c);
+ struct mux_source *parent = NULL;
+ u32 regval = readl_relaxed(SELECT_REG(mux));
+
+ if (!mux->sources)
+ return ERR_PTR(-EPERM);
+
+ parent = mux->sources;
+
+ while (parent->clk) {
+ if ((regval & mux->select_mask) == parent->select_val)
+ return parent->clk;
+
+ parent++;
+ }
+
+ return ERR_PTR(-EPERM);
+}
+
+static int cam_mux_clk_list_rate(struct clk *c, unsigned n)
+{
+ struct cam_mux_clk *mux = to_cam_mux_clk(c);
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (!mux->sources[i].clk)
+ break;
+
+ if (!mux->sources[i].clk)
+ return -ENXIO;
+
+ return clk_get_rate(mux->sources[i].clk);
+}
+
struct clk_ops clk_ops_empty;
struct clk_ops clk_ops_rcg = {
@@ -875,3 +1012,14 @@
.reset = local_vote_clk_reset,
.handoff = local_vote_clk_handoff,
};
+
+struct clk_ops clk_ops_cam_mux = {
+ .enable = cam_mux_clk_enable,
+ .disable = cam_mux_clk_disable,
+ .set_parent = cam_mux_clk_set_parent,
+ .get_parent = cam_mux_clk_get_parent,
+ .handoff = cam_mux_clk_handoff,
+ .list_rate = cam_mux_clk_list_rate,
+};
+
+
diff --git a/arch/arm/mach-msm/clock-local2.h b/arch/arm/mach-msm/clock-local2.h
index 7ac7bd3..cee5b8c 100644
--- a/arch/arm/mach-msm/clock-local2.h
+++ b/arch/arm/mach-msm/clock-local2.h
@@ -34,9 +34,9 @@
struct clk_freq_tbl {
unsigned long freq_hz;
struct clk *src_clk;
- const u32 m_val;
- const u32 n_val;
- const u32 d_val;
+ u32 m_val;
+ u32 n_val;
+ u32 d_val;
u32 div_src_val;
const unsigned sys_vdd;
};
@@ -157,6 +157,38 @@
return container_of(clk, struct measure_clk, c);
}
+struct mux_source {
+ struct clk *const clk;
+ const u32 select_val;
+};
+
+/**
+ * struct cam_mux_clk - branch clock
+ * @c: clk
+ * @enable_reg: register that contains the enable bit(s) for the mux
+ * @select_reg: register that contains the source selection bits for the mux
+ * @enable_mask: mask that enables the mux
+ * @select_mask: mask for the source selection bits
+ * @sources: list of mux sources
+ * @base: pointer to base address of ioremapped registers.
+ */
+struct cam_mux_clk {
+ struct clk c;
+ const u32 enable_reg;
+ const u32 select_reg;
+ const u32 enable_mask;
+ const u32 select_mask;
+
+ struct mux_source *sources;
+
+ void *const __iomem *base;
+};
+
+static inline struct cam_mux_clk *to_cam_mux_clk(struct clk *clk)
+{
+ return container_of(clk, struct cam_mux_clk, c);
+}
+
/*
* Generic set-rate implementations
*/
@@ -168,6 +200,7 @@
*/
extern spinlock_t local_clock_reg_lock;
+extern struct clk_ops clk_ops_cam_mux;
extern struct clk_ops clk_ops_empty;
extern struct clk_ops clk_ops_rcg;
extern struct clk_ops clk_ops_rcg_mnd;
@@ -177,6 +210,9 @@
extern struct clk_ops clk_ops_byte;
extern struct clk_ops clk_ops_pixel;
+enum handoff pixel_rcg_handoff(struct clk *clk);
+enum handoff byte_rcg_handoff(struct clk *clk);
+
/*
* Clock definition macros
*/
diff --git a/arch/arm/mach-msm/clock-mdss-8226.c b/arch/arm/mach-msm/clock-mdss-8226.c
deleted file mode 100644
index edfaf90..0000000
--- a/arch/arm/mach-msm/clock-mdss-8226.c
+++ /dev/null
@@ -1,454 +0,0 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/io.h>
-#include <linux/err.h>
-#include <linux/delay.h>
-#include <linux/string.h>
-#include <linux/iopoll.h>
-#include <linux/clk.h>
-
-#include <asm/processor.h>
-#include <mach/msm_iomap.h>
-#include <mach/clk-provider.h>
-
-#include "clock-mdss-8226.h"
-
-#define REG_R(addr) readl_relaxed(addr)
-#define REG_W(data, addr) writel_relaxed(data, addr)
-
-#define GDSC_PHYS 0xFD8C2304
-#define GDSC_SIZE 0x4
-
-#define DSI_PHY_PHYS 0xFD922800
-#define DSI_PHY_SIZE 0x00000800
-
-static unsigned char *mdss_dsi_base;
-static unsigned char *gdsc_base;
-static int pll_byte_clk_rate;
-static int pll_pclk_rate;
-static int pll_initialized;
-static struct clk *mdss_dsi_ahb_clk;
-static unsigned long dsi_pll_rate;
-
-void __init mdss_clk_ctrl_pre_init(struct clk *ahb_clk)
-{
- BUG_ON(ahb_clk == NULL);
-
- gdsc_base = ioremap(GDSC_PHYS, GDSC_SIZE);
- if (!gdsc_base)
- pr_err("%s: unable to remap gdsc base", __func__);
-
- mdss_dsi_base = ioremap(DSI_PHY_PHYS, DSI_PHY_SIZE);
- if (!mdss_dsi_base)
- pr_err("%s: unable to remap dsi base", __func__);
-
- mdss_dsi_ahb_clk = ahb_clk;
-}
-
-#define PLL_POLL_MAX_READS 10
-#define PLL_POLL_TIMEOUT_US 50
-
-static int mdss_gdsc_enabled(void)
-{
- if (!gdsc_base)
- return 0;
-
- return !!(readl_relaxed(gdsc_base) & BIT(31));
-}
-
-static int mdss_dsi_check_pll_lock(void)
-{
- u32 status;
-
- /* poll for PLL ready status */
- if (readl_poll_timeout_noirq((mdss_dsi_base + 0x02c0),
- status,
- ((status & BIT(0)) == 1),
- PLL_POLL_MAX_READS, PLL_POLL_TIMEOUT_US)) {
- pr_err("%s: DSI PLL status=%x failed to Lock\n",
- __func__, status);
- pll_initialized = 0;
- } else {
- pll_initialized = 1;
- }
-
- return pll_initialized;
-}
-
-static long mdss_dsi_pll_byte_round_rate(struct clk *c, unsigned long rate)
-{
- if (pll_initialized) {
- return pll_byte_clk_rate;
- } else {
- pr_err("%s: DSI PLL not configured\n", __func__);
- return -EINVAL;
- }
-}
-
-static long mdss_dsi_pll_pixel_round_rate(struct clk *c, unsigned long rate)
-{
- if (pll_initialized) {
- return pll_pclk_rate;
- } else {
- pr_err("%s: Configure Byte clk first\n", __func__);
- return -EINVAL;
- }
-}
-
-static int mdss_dsi_pll_pixel_set_rate(struct clk *c, unsigned long rate)
-{
- if (pll_initialized) {
- pll_pclk_rate = rate;
- pr_debug("%s: pll_pclk_rate=%d\n", __func__, pll_pclk_rate);
- return 0;
- } else {
- pr_err("%s: Configure Byte clk first\n", __func__);
- return -EINVAL;
- }
-}
-
-static int __mdss_dsi_pll_byte_set_rate(struct clk *c, unsigned long rate)
-{
- pr_debug("%s: rate=%ld\n", __func__, rate);
-
- if (pll_initialized)
- return 0;
-
- REG_W(0x70, mdss_dsi_base + 0x0230); /* LPFC1 CFG */
- REG_W(0x08, mdss_dsi_base + 0x022c); /* LPFR CFG */
- REG_W(0x02, mdss_dsi_base + 0x0210); /* VREG CFG */
- REG_W(0x00, mdss_dsi_base + 0x0204); /* postDiv1 */
- REG_W(0x01, mdss_dsi_base + 0x0200); /* REFCLK CFG */
- REG_W(0x03, mdss_dsi_base + 0x0224); /* postDiv2 */
- REG_W(0x00, mdss_dsi_base + 0x0238); /* SDM CFG0 */
- REG_W(0x0b, mdss_dsi_base + 0x023c); /* SDM CFG1 */
- REG_W(0x00, mdss_dsi_base + 0x0240); /* SDM CFG2 */
- REG_W(0x6c, mdss_dsi_base + 0x0244); /* SDM CFG3 */
- REG_W(0x02, mdss_dsi_base + 0x0208); /* ChgPump */
- REG_W(0x31, mdss_dsi_base + 0x020c); /* VCOLPF CFG */
- REG_W(0x15, mdss_dsi_base + 0x0234); /* LPFC2 CFG */
-
- REG_W(0x30, mdss_dsi_base + 0x0284); /* CAL CFG6 */
- REG_W(0x00, mdss_dsi_base + 0x0288); /* CAL CFG7 */
- REG_W(0x60, mdss_dsi_base + 0x028c); /* CAL CFG8 */
- REG_W(0x00, mdss_dsi_base + 0x0290); /* CAL CFG9 */
- REG_W(0xdd, mdss_dsi_base + 0x0294); /* CAL CFG10 */
- REG_W(0x01, mdss_dsi_base + 0x0298); /* CAL CFG11 */
-
- REG_W(0x05, mdss_dsi_base + 0x0228); /* postDiv3 */
- REG_W(0x2b, mdss_dsi_base + 0x0278); /* Cal CFG3 */
- REG_W(0x66, mdss_dsi_base + 0x027c); /* Cal CFG4 */
- REG_W(0x05, mdss_dsi_base + 0x0264); /* LKDET CFG2 */
- REG_W(0x00, mdss_dsi_base + 0x0248); /* SDM CFG4 */
- REG_W(0x00, mdss_dsi_base + 0x0214); /* PWRGEN CFG */
- REG_W(0x0a, mdss_dsi_base + 0x026c); /* CAL CFG0 */
- REG_W(0x20, mdss_dsi_base + 0x029c); /* EFUSE CFG */
-
- dsi_pll_rate = rate;
- pll_byte_clk_rate = rate;
-
- pr_debug("%s: PLL initialized. bcl=%d\n", __func__, pll_byte_clk_rate);
- pll_initialized = 1;
-
- return 0;
-}
-
-static int mdss_dsi_pll_byte_set_rate(struct clk *c, unsigned long rate)
-{
- int ret;
-
- clk_prepare_enable(mdss_dsi_ahb_clk);
- ret = __mdss_dsi_pll_byte_set_rate(c, rate);
- clk_disable_unprepare(mdss_dsi_ahb_clk);
-
- return ret;
-}
-
-static void mdss_dsi_uniphy_pll_sw_reset(void)
-{
- /*
- * Add hardware recommended delays after toggling the
- * software reset bit off and back on.
- */
- REG_W(0x01, mdss_dsi_base + 0x0268); /* PLL TEST CFG */
- udelay(300);
- REG_W(0x00, mdss_dsi_base + 0x0268); /* PLL TEST CFG */
- udelay(300);
-}
-
-static void mdss_dsi_pll_enable_casem(void)
-{
- int i;
-
- /*
- * Add hardware recommended delays between register writes for
- * the updates to take effect. These delays are necessary for the
- * PLL to successfully lock.
- */
- REG_W(0x01, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(200);
- REG_W(0x05, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(200);
- REG_W(0x0f, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1000);
-
- for (i = 0; (i < 3) && !mdss_dsi_check_pll_lock(); i++) {
- REG_W(0x07, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1);
-
- REG_W(0x0f, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1000);
- }
-
- if (pll_initialized)
- pr_debug("%s: PLL Locked after %d attempts\n", __func__, i);
- else
- pr_debug("%s: PLL failed to lock\n", __func__);
-}
-
-static void mdss_dsi_pll_enable_casef1(void)
-{
- /*
- * Add hardware recommended delays between register writes for
- * the updates to take effect. These delays are necessary for the
- * PLL to successfully lock.
- */
- REG_W(0x01, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(200);
- REG_W(0x05, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(200);
- REG_W(0x0f, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(200);
- REG_W(0x0d, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(200);
- REG_W(0x0f, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1000);
-
- if (mdss_dsi_check_pll_lock())
- pr_debug("%s: PLL Locked\n", __func__);
- else
- pr_debug("%s: PLL failed to lock\n", __func__);
-}
-
-static void mdss_dsi_pll_enable_cased(void)
-{
- /*
- * Add hardware recommended delays between register writes for
- * the updates to take effect. These delays are necessary for the
- * PLL to successfully lock.
- */
- REG_W(0x01, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1);
- REG_W(0x05, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1);
- REG_W(0x07, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1);
- REG_W(0x05, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1);
- REG_W(0x07, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1);
- REG_W(0x0f, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1);
-
- if (mdss_dsi_check_pll_lock())
- pr_debug("%s: PLL Locked\n", __func__);
- else
- pr_debug("%s: PLL failed to lock\n", __func__);
-}
-
-static void mdss_dsi_pll_enable_casec(void)
-{
- /*
- * Add hardware recommended delays between register writes for
- * the updates to take effect. These delays are necessary for the
- * PLL to successfully lock.
- */
- REG_W(0x01, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(200);
- REG_W(0x05, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(200);
- REG_W(0x0f, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1000);
-
- if (mdss_dsi_check_pll_lock())
- pr_debug("%s: PLL Locked\n", __func__);
- else
- pr_debug("%s: PLL failed to lock\n", __func__);
-}
-
-static void mdss_dsi_pll_enable_casee(void)
-{
- /*
- * Add hardware recommended delays between register writes for
- * the updates to take effect. These delays are necessary for the
- * PLL to successfully lock.
- */
- REG_W(0x01, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(200);
- REG_W(0x05, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(200);
- REG_W(0x0d, mdss_dsi_base + 0x0220); /* GLB CFG */
- REG_W(0x0f, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1000);
-
- if (mdss_dsi_check_pll_lock())
- pr_debug("%s: PLL Locked\n", __func__);
- else
- pr_debug("%s: PLL failed to lock\n", __func__);
-}
-
-static int __mdss_dsi_pll_enable(struct clk *c)
-{
- if (!pll_initialized) {
- if (dsi_pll_rate)
- __mdss_dsi_pll_byte_set_rate(c, dsi_pll_rate);
- else
- pr_err("%s: Calling clk_en before set_rate\n",
- __func__);
- }
-
- /*
- * Try all PLL power-up sequences one-by-one until
- * PLL lock is detected
- */
- mdss_dsi_uniphy_pll_sw_reset();
- mdss_dsi_pll_enable_casem();
- if (pll_initialized)
- goto pll_locked;
-
- mdss_dsi_uniphy_pll_sw_reset();
- mdss_dsi_pll_enable_cased();
- if (pll_initialized)
- goto pll_locked;
-
- mdss_dsi_uniphy_pll_sw_reset();
- mdss_dsi_pll_enable_cased();
- if (pll_initialized)
- goto pll_locked;
-
- mdss_dsi_uniphy_pll_sw_reset();
- mdss_dsi_pll_enable_casef1();
- if (pll_initialized)
- goto pll_locked;
-
- mdss_dsi_uniphy_pll_sw_reset();
- mdss_dsi_pll_enable_casec();
- if (pll_initialized)
- goto pll_locked;
-
- mdss_dsi_uniphy_pll_sw_reset();
- mdss_dsi_pll_enable_casee();
- if (pll_initialized)
- goto pll_locked;
-
- pr_err("%s: DSI PLL failed to Lock\n", __func__);
- return -EINVAL;
-
-pll_locked:
- pr_debug("%s: PLL Lock success\n", __func__);
-
- return 0;
-}
-
-static void __mdss_dsi_pll_disable(void)
-{
- writel_relaxed(0x00, mdss_dsi_base + 0x0220); /* GLB CFG */
- pr_debug("%s: PLL disabled\n", __func__);
- pll_initialized = 0;
-}
-
-static DEFINE_SPINLOCK(dsipll_lock);
-static int dsipll_refcount;
-
-static void mdss_dsi_pll_disable(struct clk *c)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dsipll_lock, flags);
- if (WARN(dsipll_refcount == 0, "DSI PLL clock is unbalanced"))
- goto out;
- if (dsipll_refcount == 1)
- __mdss_dsi_pll_disable();
- dsipll_refcount--;
-out:
- spin_unlock_irqrestore(&dsipll_lock, flags);
-}
-
-static int mdss_dsi_pll_enable(struct clk *c)
-{
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&dsipll_lock, flags);
- if (dsipll_refcount == 0) {
- ret = __mdss_dsi_pll_enable(c);
- if (ret < 0)
- goto out;
- }
- dsipll_refcount++;
-out:
- spin_unlock_irqrestore(&dsipll_lock, flags);
- return ret;
-}
-
-/* todo: Adjust these values appropriately */
-static enum handoff mdss_dsi_pll_byte_handoff(struct clk *c)
-{
- if (mdss_gdsc_enabled()) {
- clk_prepare_enable(mdss_dsi_ahb_clk);
- if (mdss_dsi_check_pll_lock()) {
- c->rate = 59000000;
- dsi_pll_rate = 59000000;
- pll_byte_clk_rate = 59000000;
- pll_pclk_rate = 117000000;
- dsipll_refcount++;
- return HANDOFF_ENABLED_CLK;
- }
- clk_disable_unprepare(mdss_dsi_ahb_clk);
- }
-
- return HANDOFF_DISABLED_CLK;
-}
-
-/* todo: Adjust these values appropriately */
-static enum handoff mdss_dsi_pll_pixel_handoff(struct clk *c)
-{
- if (mdss_gdsc_enabled()) {
- clk_prepare_enable(mdss_dsi_ahb_clk);
- if (mdss_dsi_check_pll_lock()) {
- c->rate = 117000000;
- dsipll_refcount++;
- return HANDOFF_ENABLED_CLK;
- }
- clk_disable_unprepare(mdss_dsi_ahb_clk);
- }
-
- return HANDOFF_DISABLED_CLK;
-}
-
-struct clk_ops clk_ops_dsi_pixel_pll = {
- .enable = mdss_dsi_pll_enable,
- .disable = mdss_dsi_pll_disable,
- .set_rate = mdss_dsi_pll_pixel_set_rate,
- .round_rate = mdss_dsi_pll_pixel_round_rate,
- .handoff = mdss_dsi_pll_pixel_handoff,
-};
-
-struct clk_ops clk_ops_dsi_byte_pll = {
- .enable = mdss_dsi_pll_enable,
- .disable = mdss_dsi_pll_disable,
- .set_rate = mdss_dsi_pll_byte_set_rate,
- .round_rate = mdss_dsi_pll_byte_round_rate,
- .handoff = mdss_dsi_pll_byte_handoff,
-};
diff --git a/arch/arm/mach-msm/clock-mdss-8226.h b/arch/arm/mach-msm/clock-mdss-8226.h
deleted file mode 100644
index dcf4f92..0000000
--- a/arch/arm/mach-msm/clock-mdss-8226.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Copyright (c) 2013, 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.
- */
-
-#ifndef __ARCH_ARM_MACH_MSM_CLOCK_MDSS_8226
-#define __ARCH_ARM_MACH_MSM_CLOCK_MDSS_8226
-
-extern struct clk_ops clk_ops_dsi_byte_pll;
-extern struct clk_ops clk_ops_dsi_pixel_pll;
-
-void mdss_clk_ctrl_pre_init(struct clk *ahb_clk);
-void mdss_clk_ctrl_post_init(void);
-
-#endif /* __ARCH_ARM_MACH_MSM_CLOCK_MDSS_8226 */
diff --git a/arch/arm/mach-msm/clock-mdss-8974.c b/arch/arm/mach-msm/clock-mdss-8974.c
index d866874..1245287 100644
--- a/arch/arm/mach-msm/clock-mdss-8974.c
+++ b/arch/arm/mach-msm/clock-mdss-8974.c
@@ -21,17 +21,21 @@
#include <asm/processor.h>
#include <mach/msm_iomap.h>
#include <mach/clk-provider.h>
+#include <mach/clk.h>
+#include <mach/clock-generic.h>
#include "clock-mdss-8974.h"
-#define REG_R(addr) readl_relaxed(addr)
-#define REG_W(data, addr) writel_relaxed(data, addr)
+#define REG_R(addr) readl_relaxed(addr)
+#define REG_W(data, addr) writel_relaxed(data, addr)
+#define DSS_REG_W(base, offset, data) REG_W((data), (base) + (offset))
+#define DSS_REG_R(base, offset) REG_R((base) + (offset))
#define GDSC_PHYS 0xFD8C2304
#define GDSC_SIZE 0x4
-#define DSI_PHY_PHYS 0xFD922800
-#define DSI_PHY_SIZE 0x00000800
+#define DSI_PHY_PHYS 0xFD922A00
+#define DSI_PHY_SIZE 0x000000D4
#define HDMI_PHY_PHYS 0xFD922500
#define HDMI_PHY_SIZE 0x0000007C
@@ -100,45 +104,60 @@
#define HDMI_UNI_PLL_CAL_CFG11 (0x0098)
#define HDMI_UNI_PLL_STATUS (0x00C0)
-#define VCO_CLK 424000000
+#define DSI_0_PHY_PLL_UNIPHY_PLL_REFCLK_CFG (0x00000000)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG (0x00000004)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_CHGPUMP_CFG (0x00000008)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG (0x0000000C)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_VREG_CFG (0x00000010)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG (0x00000014)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_DMUX_CFG (0x00000018)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_AMUX_CFG (0x0000001C)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG (0x00000020)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG (0x00000024)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG (0x00000028)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_LPFR_CFG (0x0000002C)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_LPFC1_CFG (0x00000030)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_LPFC2_CFG (0x00000034)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_SDM_CFG0 (0x00000038)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_SDM_CFG1 (0x0000003C)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_SDM_CFG2 (0x00000040)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_SDM_CFG3 (0x00000044)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_SDM_CFG4 (0x00000048)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_SSC_CFG0 (0x0000004C)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_SSC_CFG1 (0x00000050)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_SSC_CFG2 (0x00000054)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_SSC_CFG3 (0x00000058)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_LKDET_CFG0 (0x0000005C)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_LKDET_CFG1 (0x00000060)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_LKDET_CFG2 (0x00000064)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_TEST_CFG (0x00000068)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG0 (0x0000006C)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG1 (0x00000070)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG2 (0x00000074)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG3 (0x00000078)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG4 (0x0000007C)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG5 (0x00000080)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG6 (0x00000084)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG7 (0x00000088)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG8 (0x0000008C)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG9 (0x00000090)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG10 (0x00000094)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG11 (0x00000098)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_EFUSE_CFG (0x0000009C)
+#define DSI_0_PHY_PLL_UNIPHY_PLL_STATUS (0x000000C0)
+
+#define PLL_POLL_MAX_READS 10
+#define PLL_POLL_TIMEOUT_US 50
+
+static long vco_cached_rate;
static unsigned char *mdss_dsi_base;
static unsigned char *gdsc_base;
-static int pll_byte_clk_rate;
-static int pll_pclk_rate;
-static int pll_initialized;
-static struct clk *mdss_dsi_ahb_clk;
-static unsigned long dsi_pll_rate;
+static struct clk *mdss_ahb_clk;
static void __iomem *hdmi_phy_base;
static void __iomem *hdmi_phy_pll_base;
static unsigned hdmi_pll_on;
-void __init mdss_clk_ctrl_pre_init(struct clk *ahb_clk)
-{
- BUG_ON(ahb_clk == NULL);
-
- gdsc_base = ioremap(GDSC_PHYS, GDSC_SIZE);
- if (!gdsc_base)
- pr_err("%s: unable to remap gdsc base", __func__);
-
- mdss_dsi_base = ioremap(DSI_PHY_PHYS, DSI_PHY_SIZE);
- if (!mdss_dsi_base)
- pr_err("%s: unable to remap dsi base", __func__);
-
- mdss_dsi_ahb_clk = ahb_clk;
-
- hdmi_phy_base = ioremap(HDMI_PHY_PHYS, HDMI_PHY_SIZE);
- if (!hdmi_phy_base)
- pr_err("%s: unable to ioremap hdmi phy base", __func__);
-
- hdmi_phy_pll_base = ioremap(HDMI_PHY_PLL_PHYS, HDMI_PHY_PLL_SIZE);
- if (!hdmi_phy_pll_base)
- pr_err("%s: unable to ioremap hdmi phy pll base", __func__);
-}
-
-#define PLL_POLL_MAX_READS 10
-#define PLL_POLL_TIMEOUT_US 50
-
static int mdss_gdsc_enabled(void)
{
if (!gdsc_base)
@@ -147,297 +166,13 @@
return !!(readl_relaxed(gdsc_base) & BIT(31));
}
-static int mdss_dsi_check_pll_lock(void)
-{
- u32 status;
-
- clk_prepare_enable(mdss_dsi_ahb_clk);
- /* poll for PLL ready status */
- if (readl_poll_timeout_noirq((mdss_dsi_base + 0x02c0),
- status,
- ((status & BIT(0)) == 1),
- PLL_POLL_MAX_READS, PLL_POLL_TIMEOUT_US)) {
- pr_err("%s: DSI PLL status=%x failed to Lock\n",
- __func__, status);
- pll_initialized = 0;
- } else {
- pll_initialized = 1;
- }
- clk_disable_unprepare(mdss_dsi_ahb_clk);
-
- return pll_initialized;
-}
-
-static long mdss_dsi_pll_byte_round_rate(struct clk *c, unsigned long rate)
-{
- if (pll_initialized)
- return pll_byte_clk_rate;
- else {
- pr_err("%s: DSI PLL not configured\n",
- __func__);
- return -EINVAL;
- }
-}
-
-static long mdss_dsi_pll_pixel_round_rate(struct clk *c, unsigned long rate)
-{
- if (pll_initialized)
- return pll_pclk_rate;
- else {
- pr_err("%s: Configure Byte clk first\n",
- __func__);
- return -EINVAL;
- }
-}
-
-static int mdss_dsi_pll_pixel_set_rate(struct clk *c, unsigned long rate)
-{
- if (pll_initialized) {
- pll_pclk_rate = rate;
- pr_debug("%s: pll_pclk_rate=%d\n", __func__, pll_pclk_rate);
- return 0;
- } else {
- pr_err("%s: Configure Byte clk first\n", __func__);
- return -EINVAL;
- }
-}
-
-static int __mdss_dsi_pll_byte_set_rate(struct clk *c, unsigned long rate)
-{
- int pll_divcfg1, pll_divcfg2;
- int half_bitclk_rate;
-
- pr_debug("%s:\n", __func__);
- if (pll_initialized)
- return 0;
-
- half_bitclk_rate = rate * 4;
-
- pll_divcfg1 = (VCO_CLK / half_bitclk_rate) - 2;
-
- /* Configuring the VCO to 424 Mhz */
- /* Configuring the half rate Bit clk to 212 Mhz */
-
- pll_divcfg2 = 3; /* ByteClk is 1/4 the half-bitClk rate */
-
- /* Configure the Loop filter */
- /* Loop filter resistance value */
- REG_W(0x08, mdss_dsi_base + 0x022c);
- /* Loop filter capacitance values : c1 and c2 */
- REG_W(0x70, mdss_dsi_base + 0x0230);
- REG_W(0x15, mdss_dsi_base + 0x0234);
-
- REG_W(0x02, mdss_dsi_base + 0x0208); /* ChgPump */
- REG_W(pll_divcfg1, mdss_dsi_base + 0x0204); /* postDiv1 */
- REG_W(pll_divcfg2, mdss_dsi_base + 0x0224); /* postDiv2 */
- REG_W(0x05, mdss_dsi_base + 0x0228); /* postDiv3 */
-
- REG_W(0x2b, mdss_dsi_base + 0x0278); /* Cal CFG3 */
- REG_W(0x66, mdss_dsi_base + 0x027c); /* Cal CFG4 */
- REG_W(0x05, mdss_dsi_base + 0x0264); /* LKDET CFG2 */
-
- REG_W(0x0a, mdss_dsi_base + 0x023c); /* SDM CFG1 */
- REG_W(0xab, mdss_dsi_base + 0x0240); /* SDM CFG2 */
- REG_W(0x0a, mdss_dsi_base + 0x0244); /* SDM CFG3 */
- REG_W(0x00, mdss_dsi_base + 0x0248); /* SDM CFG4 */
-
- REG_W(0x01, mdss_dsi_base + 0x0200); /* REFCLK CFG */
- REG_W(0x00, mdss_dsi_base + 0x0214); /* PWRGEN CFG */
- REG_W(0x71, mdss_dsi_base + 0x020c); /* VCOLPF CFG */
- REG_W(0x02, mdss_dsi_base + 0x0210); /* VREG CFG */
- REG_W(0x00, mdss_dsi_base + 0x0238); /* SDM CFG0 */
-
- REG_W(0x5f, mdss_dsi_base + 0x028c); /* CAL CFG8 */
- REG_W(0xa8, mdss_dsi_base + 0x0294); /* CAL CFG10 */
- REG_W(0x01, mdss_dsi_base + 0x0298); /* CAL CFG11 */
- REG_W(0x0a, mdss_dsi_base + 0x026c); /* CAL CFG0 */
- REG_W(0x30, mdss_dsi_base + 0x0284); /* CAL CFG6 */
- REG_W(0x00, mdss_dsi_base + 0x0288); /* CAL CFG7 */
- REG_W(0x00, mdss_dsi_base + 0x0290); /* CAL CFG9 */
- REG_W(0x20, mdss_dsi_base + 0x029c); /* EFUSE CFG */
-
- dsi_pll_rate = rate;
- pll_byte_clk_rate = rate;
-
- pr_debug("%s: PLL initialized. bcl=%d\n", __func__, pll_byte_clk_rate);
- pll_initialized = 1;
-
- return 0;
-}
-
-static int mdss_dsi_pll_byte_set_rate(struct clk *c, unsigned long rate)
-{
- int ret;
-
- clk_prepare_enable(mdss_dsi_ahb_clk);
- ret = __mdss_dsi_pll_byte_set_rate(c, rate);
- clk_disable_unprepare(mdss_dsi_ahb_clk);
-
- return ret;
-}
-
-static void mdss_dsi_uniphy_pll_lock_detect_setting(void)
-{
- REG_W(0x04, mdss_dsi_base + 0x0264); /* LKDetect CFG2 */
- udelay(100);
- REG_W(0x05, mdss_dsi_base + 0x0264); /* LKDetect CFG2 */
- udelay(500);
-}
-
-static void mdss_dsi_uniphy_pll_sw_reset(void)
-{
- REG_W(0x01, mdss_dsi_base + 0x0268); /* PLL TEST CFG */
- udelay(1);
- REG_W(0x00, mdss_dsi_base + 0x0268); /* PLL TEST CFG */
- udelay(1);
-}
-
-static int __mdss_dsi_pll_enable(struct clk *c)
-{
- u32 status;
- u32 max_reads, timeout_us;
- int i;
-
- if (!pll_initialized) {
- if (dsi_pll_rate)
- __mdss_dsi_pll_byte_set_rate(c, dsi_pll_rate);
- else
- pr_err("%s: Calling clk_en before set_rate\n",
- __func__);
- }
-
- mdss_dsi_uniphy_pll_sw_reset();
- /* PLL power up */
- /* Add HW recommended delay between
- register writes for the update to propagate */
- REG_W(0x01, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1000);
- REG_W(0x05, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1000);
- REG_W(0x07, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1000);
- REG_W(0x0f, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1000);
-
- for (i = 0; i < 3; i++) {
- mdss_dsi_uniphy_pll_lock_detect_setting();
- /* poll for PLL ready status */
- max_reads = 5;
- timeout_us = 100;
- if (readl_poll_timeout_noirq((mdss_dsi_base + 0x02c0),
- status,
- ((status & 0x01) == 1),
- max_reads, timeout_us)) {
- pr_debug("%s: DSI PLL status=%x failed to Lock\n",
- __func__, status);
- pr_debug("%s:Trying to power UP PLL again\n",
- __func__);
- } else
- break;
-
- mdss_dsi_uniphy_pll_sw_reset();
- udelay(1000);
- /* Add HW recommended delay between
- register writes for the update to propagate */
- REG_W(0x01, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1000);
- REG_W(0x05, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1000);
- REG_W(0x07, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1000);
- REG_W(0x05, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1000);
- REG_W(0x07, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(1000);
- REG_W(0x0f, mdss_dsi_base + 0x0220); /* GLB CFG */
- udelay(2000);
-
- }
-
- if ((status & 0x01) != 1) {
- pr_err("%s: DSI PLL status=%x failed to Lock\n",
- __func__, status);
- return -EINVAL;
- }
-
- pr_debug("%s: **** PLL Lock success\n", __func__);
-
- return 0;
-}
-
-static void __mdss_dsi_pll_disable(void)
-{
- writel_relaxed(0x00, mdss_dsi_base + 0x0220); /* GLB CFG */
- pr_debug("%s: **** disable pll Initialize\n", __func__);
- pll_initialized = 0;
-}
-
-static DEFINE_SPINLOCK(dsipll_lock);
-static int dsipll_refcount;
-
-static void mdss_dsi_pll_disable(struct clk *c)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dsipll_lock, flags);
- if (WARN(dsipll_refcount == 0, "DSI PLL clock is unbalanced"))
- goto out;
- if (dsipll_refcount == 1)
- __mdss_dsi_pll_disable();
- dsipll_refcount--;
-out:
- spin_unlock_irqrestore(&dsipll_lock, flags);
-}
-
-static int mdss_dsi_pll_enable(struct clk *c)
-{
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&dsipll_lock, flags);
- if (dsipll_refcount == 0) {
- ret = __mdss_dsi_pll_enable(c);
- if (ret < 0)
- goto out;
- }
- dsipll_refcount++;
-out:
- spin_unlock_irqrestore(&dsipll_lock, flags);
- return ret;
-}
-
-static enum handoff mdss_dsi_pll_byte_handoff(struct clk *c)
-{
- if (mdss_gdsc_enabled() && mdss_dsi_check_pll_lock()) {
- c->rate = 53000000;
- dsi_pll_rate = 53000000;
- pll_byte_clk_rate = 53000000;
- pll_pclk_rate = 105000000;
- dsipll_refcount++;
- return HANDOFF_ENABLED_CLK;
- }
-
- return HANDOFF_DISABLED_CLK;
-}
-
-static enum handoff mdss_dsi_pll_pixel_handoff(struct clk *c)
-{
- if (mdss_gdsc_enabled() && mdss_dsi_check_pll_lock()) {
- c->rate = 105000000;
- dsipll_refcount++;
- return HANDOFF_ENABLED_CLK;
- }
-
- return HANDOFF_DISABLED_CLK;
-}
-
void hdmi_pll_disable(void)
{
- clk_enable(mdss_dsi_ahb_clk);
+ clk_enable(mdss_ahb_clk);
REG_W(0x0, hdmi_phy_pll_base + HDMI_UNI_PLL_GLB_CFG);
udelay(5);
REG_W(0x0, hdmi_phy_base + HDMI_PHY_GLB_CFG);
- clk_disable(mdss_dsi_ahb_clk);
+ clk_disable(mdss_ahb_clk);
hdmi_pll_on = 0;
} /* hdmi_pll_disable */
@@ -447,7 +182,7 @@
u32 status;
u32 max_reads, timeout_us;
- clk_enable(mdss_dsi_ahb_clk);
+ clk_enable(mdss_ahb_clk);
/* Global Enable */
REG_W(0x81, hdmi_phy_base + HDMI_PHY_GLB_CFG);
/* Power up power gen */
@@ -473,7 +208,7 @@
pr_err("%s: hdmi phy pll status=%x failed to Lock\n",
__func__, status);
hdmi_pll_disable();
- clk_disable(mdss_dsi_ahb_clk);
+ clk_disable(mdss_ahb_clk);
return -EINVAL;
}
pr_debug("%s: hdmi phy pll is locked\n", __func__);
@@ -487,11 +222,11 @@
pr_err("%s: hdmi phy status=%x failed to Lock\n",
__func__, status);
hdmi_pll_disable();
- clk_disable(mdss_dsi_ahb_clk);
+ clk_disable(mdss_ahb_clk);
return -EINVAL;
}
pr_debug("%s: hdmi phy is locked\n", __func__);
- clk_disable(mdss_dsi_ahb_clk);
+ clk_disable(mdss_ahb_clk);
hdmi_pll_on = 1;
@@ -507,7 +242,7 @@
set_power_dwn = 1;
}
- clk_enable(mdss_dsi_ahb_clk);
+ clk_enable(mdss_ahb_clk);
pr_debug("%s: rate=%ld\n", __func__, rate);
switch (rate) {
case 0:
@@ -648,7 +383,49 @@
REG_W(0x05, hdmi_phy_base + HDMI_PHY_TXCAL_CFG3);
udelay(200);
break;
+ case 65000000:
+ REG_W(0x81, hdmi_phy_base + HDMI_PHY_GLB_CFG);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_GLB_CFG);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_REFCLK_CFG);
+ REG_W(0x19, hdmi_phy_pll_base + HDMI_UNI_PLL_VCOLPF_CFG);
+ REG_W(0x0E, hdmi_phy_pll_base + HDMI_UNI_PLL_LPFR_CFG);
+ REG_W(0x20, hdmi_phy_pll_base + HDMI_UNI_PLL_LPFC1_CFG);
+ REG_W(0x0D, hdmi_phy_pll_base + HDMI_UNI_PLL_LPFC2_CFG);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG0);
+ REG_W(0x4F, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG1);
+ REG_W(0x55, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG2);
+ REG_W(0xED, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG3);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG4);
+ REG_W(0x10, hdmi_phy_pll_base + HDMI_UNI_PLL_LKDET_CFG0);
+ REG_W(0x1A, hdmi_phy_pll_base + HDMI_UNI_PLL_LKDET_CFG1);
+ REG_W(0x05, hdmi_phy_pll_base + HDMI_UNI_PLL_LKDET_CFG2);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_POSTDIV1_CFG);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_POSTDIV2_CFG);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_POSTDIV3_CFG);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG2);
+ REG_W(0x60, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG8);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG9);
+ REG_W(0x8A, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG10);
+ REG_W(0x02, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG11);
+ REG_W(0x1F, hdmi_phy_base + HDMI_PHY_PD_CTRL0);
+ udelay(50);
+ REG_W(0x0F, hdmi_phy_pll_base + HDMI_UNI_PLL_GLB_CFG);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_PD_CTRL1);
+ REG_W(0x10, hdmi_phy_base + HDMI_PHY_ANA_CFG2);
+ REG_W(0xDB, hdmi_phy_base + HDMI_PHY_ANA_CFG0);
+ REG_W(0x43, hdmi_phy_base + HDMI_PHY_ANA_CFG1);
+ REG_W(0x02, hdmi_phy_base + HDMI_PHY_ANA_CFG2);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_ANA_CFG3);
+ REG_W(0x04, hdmi_phy_pll_base + HDMI_UNI_PLL_VREG_CFG);
+ REG_W(0xD0, hdmi_phy_base + HDMI_PHY_DCC_CFG0);
+ REG_W(0x1A, hdmi_phy_base + HDMI_PHY_DCC_CFG1);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_TXCAL_CFG0);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_TXCAL_CFG1);
+ REG_W(0x02, hdmi_phy_base + HDMI_PHY_TXCAL_CFG2);
+ REG_W(0x05, hdmi_phy_base + HDMI_PHY_TXCAL_CFG3);
+ udelay(200);
+ break;
case 74250000:
/*
* 720p60/720p50/1080i60/1080i50
@@ -697,6 +474,50 @@
udelay(200);
break;
+ case 108000000:
+ REG_W(0x81, hdmi_phy_base + HDMI_PHY_GLB_CFG);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_GLB_CFG);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_REFCLK_CFG);
+ REG_W(0x19, hdmi_phy_pll_base + HDMI_UNI_PLL_VCOLPF_CFG);
+ REG_W(0x0E, hdmi_phy_pll_base + HDMI_UNI_PLL_LPFR_CFG);
+ REG_W(0x20, hdmi_phy_pll_base + HDMI_UNI_PLL_LPFC1_CFG);
+ REG_W(0x0D, hdmi_phy_pll_base + HDMI_UNI_PLL_LPFC2_CFG);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG0);
+ REG_W(0x5B, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG1);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG2);
+ REG_W(0x20, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG3);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG4);
+ REG_W(0x10, hdmi_phy_pll_base + HDMI_UNI_PLL_LKDET_CFG0);
+ REG_W(0x1A, hdmi_phy_pll_base + HDMI_UNI_PLL_LKDET_CFG1);
+ REG_W(0x05, hdmi_phy_pll_base + HDMI_UNI_PLL_LKDET_CFG2);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_POSTDIV1_CFG);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_POSTDIV2_CFG);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_POSTDIV3_CFG);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG2);
+ REG_W(0x60, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG8);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG9);
+ REG_W(0x38, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG10);
+ REG_W(0x04, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG11);
+ REG_W(0x1F, hdmi_phy_base + HDMI_PHY_PD_CTRL0);
+ udelay(50);
+
+ REG_W(0x0F, hdmi_phy_pll_base + HDMI_UNI_PLL_GLB_CFG);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_PD_CTRL1);
+ REG_W(0x10, hdmi_phy_base + HDMI_PHY_ANA_CFG2);
+ REG_W(0xDB, hdmi_phy_base + HDMI_PHY_ANA_CFG0);
+ REG_W(0x43, hdmi_phy_base + HDMI_PHY_ANA_CFG1);
+ REG_W(0x02, hdmi_phy_base + HDMI_PHY_ANA_CFG2);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_ANA_CFG3);
+ REG_W(0x04, hdmi_phy_pll_base + HDMI_UNI_PLL_VREG_CFG);
+ REG_W(0xD0, hdmi_phy_base + HDMI_PHY_DCC_CFG0);
+ REG_W(0x1A, hdmi_phy_base + HDMI_PHY_DCC_CFG1);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_TXCAL_CFG0);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_TXCAL_CFG1);
+ REG_W(0x02, hdmi_phy_base + HDMI_PHY_TXCAL_CFG2);
+ REG_W(0x05, hdmi_phy_base + HDMI_PHY_TXCAL_CFG3);
+ udelay(200);
+ break;
+
case 148500000:
REG_W(0x81, hdmi_phy_base + HDMI_PHY_GLB_CFG);
REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_GLB_CFG);
@@ -836,7 +657,7 @@
/* Make sure writes complete before disabling iface clock */
mb();
- clk_disable(mdss_dsi_ahb_clk);
+ clk_disable(mdss_ahb_clk);
if (set_power_dwn)
hdmi_pll_enable();
@@ -844,18 +665,977 @@
return 0;
} /* hdmi_pll_set_rate */
-struct clk_ops clk_ops_dsi_pixel_pll = {
- .enable = mdss_dsi_pll_enable,
- .disable = mdss_dsi_pll_disable,
- .set_rate = mdss_dsi_pll_pixel_set_rate,
- .round_rate = mdss_dsi_pll_pixel_round_rate,
- .handoff = mdss_dsi_pll_pixel_handoff,
+/* Auto PLL calibaration */
+int mdss_ahb_clk_enable(int enable)
+{
+ int rc = 0;
+
+ /* todo: Ideally, we should enable/disable GDSC whenever we are
+ * attempting to enable/disable MDSS AHB clock.
+ * For now, just return error if GDSC is not enabled.
+ */
+ if (!mdss_gdsc_enabled())
+ return -EPERM;
+
+ if (enable)
+ rc = clk_prepare_enable(mdss_ahb_clk);
+ else
+ clk_disable_unprepare(mdss_ahb_clk);
+
+ return rc;
+}
+
+int set_byte_mux_sel(struct mux_clk *clk, int sel)
+{
+ pr_debug("%s: byte mux set to %s mode\n", __func__,
+ sel ? "indirect" : "direct");
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_VREG_CFG,
+ (sel << 1));
+ return 0;
+}
+
+int get_byte_mux_sel(struct mux_clk *clk)
+{
+ int mux_mode;
+
+ if (mdss_ahb_clk_enable(1)) {
+ pr_debug("%s: Failed to enable mdss ahb clock\n", __func__);
+ return 0;
+ }
+
+ mux_mode = DSS_REG_R(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_VREG_CFG)
+ & BIT(1);
+ pr_debug("%s: byte mux mode = %s", __func__,
+ mux_mode ? "indirect" : "direct");
+
+ mdss_ahb_clk_enable(0);
+ return !!mux_mode;
+}
+
+static inline struct dsi_pll_vco_clk *to_vco_clk(struct clk *clk)
+{
+ return container_of(clk, struct dsi_pll_vco_clk, c);
+}
+
+/*
+ * When the display is turned off, the display registers are wiped out.
+ * Temporarily use the prepare ops to restore the register values.
+ *
+*/
+int div_prepare(struct clk *c)
+{
+ struct div_clk *div = to_div_clk(c);
+ /* Restore the divider's value */
+ return div->ops->set_div(div, div->div);
+}
+
+int mux_prepare(struct clk *c)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ int i, rc, sel = 0;
+
+ rc = mdss_ahb_clk_enable(1);
+ if (rc) {
+ pr_err("%s: failed to enable mdss ahb clock. rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ for (i = 0; i < mux->num_parents; i++)
+ if (mux->parents[i].src == c->parent) {
+ sel = mux->parents[i].sel;
+ break;
+ }
+
+ if (i == mux->num_parents) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ /* Restore the mux source select value */
+ rc = mux->ops->set_mux_sel(mux, sel);
+
+error:
+ mdss_ahb_clk_enable(0);
+ return rc;
+}
+
+static int fixed_4div_set_div(struct div_clk *clk, int div)
+{
+ int rc = 0;
+
+ rc = mdss_ahb_clk_enable(1);
+ if (rc) {
+ pr_err("%s: failed to enable mdss ahb clock. rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG,
+ (div - 1));
+
+ mdss_ahb_clk_enable(0);
+ return 0;
+}
+
+static int fixed_4div_get_div(struct div_clk *clk)
+{
+ int div = 0;
+
+ if (mdss_ahb_clk_enable(1)) {
+ pr_debug("%s: Failed to enable mdss ahb clock\n", __func__);
+ return 1;
+ }
+ div = DSS_REG_R(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG);
+ mdss_ahb_clk_enable(0);
+ return div + 1;
+}
+
+static int digital_set_div(struct div_clk *clk, int div)
+{
+ int rc = 0;
+
+ rc = mdss_ahb_clk_enable(1);
+ if (rc) {
+ pr_err("%s: failed to enable mdss ahb clock. rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG,
+ (div - 1));
+
+ mdss_ahb_clk_enable(0);
+ return 0;
+}
+
+static int digital_get_div(struct div_clk *clk)
+{
+ int div = 0;
+
+ if (mdss_ahb_clk_enable(1)) {
+ pr_debug("%s: Failed to enable mdss ahb clock\n", __func__);
+ return 1;
+ }
+ div = DSS_REG_R(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG);
+ mdss_ahb_clk_enable(0);
+ return div + 1;
+}
+
+static int analog_set_div(struct div_clk *clk, int div)
+{
+ int rc = 0;
+
+ rc = mdss_ahb_clk_enable(1);
+ if (rc) {
+ pr_err("%s: failed to enable mdss ahb clock. rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG,
+ div - 1);
+
+ mdss_ahb_clk_enable(0);
+ return 0;
+}
+
+static int analog_get_div(struct div_clk *clk)
+{
+ int div = 0;
+
+ if (mdss_ahb_clk_enable(1)) {
+ pr_debug("%s: Failed to enable mdss ahb clock\n", __func__);
+ return 1;
+ }
+ div = DSS_REG_R(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG) + 1;
+ mdss_ahb_clk_enable(0);
+ return div;
+}
+
+static int dsi_pll_lock_status(void)
+{
+ u32 status;
+ int pll_locked = 0;
+
+ /* poll for PLL ready status */
+ if (readl_poll_timeout_noirq((mdss_dsi_base +
+ DSI_0_PHY_PLL_UNIPHY_PLL_STATUS),
+ status,
+ ((status & BIT(0)) == 1),
+ PLL_POLL_MAX_READS, PLL_POLL_TIMEOUT_US)) {
+ pr_debug("%s: DSI PLL status=%x failed to Lock\n",
+ __func__, status);
+ pll_locked = 0;
+ } else {
+ pll_locked = 1;
+ }
+
+ return pll_locked;
+}
+
+static void dsi_pll_software_reset(void)
+{
+ /*
+ * Add HW recommended delays after toggling the software
+ * reset bit off and back on.
+ */
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x01);
+ udelay(1000);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x00);
+ udelay(1000);
+}
+
+static int dsi_pll_enable_seq_m(void)
+{
+ int i = 0;
+ int pll_locked = 0;
+
+ dsi_pll_software_reset();
+
+ /*
+ * Add hardware recommended delays between register writes for
+ * the updates to take effect. These delays are necessary for the
+ * PLL to successfully lock
+ */
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
+ udelay(200);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
+ udelay(200);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
+ udelay(1000);
+
+ do {
+ pll_locked = dsi_pll_lock_status();
+ if (!pll_locked) {
+ DSS_REG_W(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
+ udelay(1);
+ DSS_REG_W(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
+ udelay(1000);
+ i++;
+ }
+ } while ((i < 3) && !pll_locked);
+
+ if (pll_locked)
+ pr_debug("%s: PLL Locked at attempt #%d\n", __func__, i);
+ else
+ pr_debug("%s: PLL failed to lock after %d attempt(s)\n",
+ __func__, i);
+
+ return pll_locked ? 0 : -EINVAL;
+}
+
+static int dsi_pll_enable_seq_d(void)
+{
+ int pll_locked = 0;
+
+ dsi_pll_software_reset();
+
+ /*
+ * Add hardware recommended delays between register writes for
+ * the updates to take effect. These delays are necessary for the
+ * PLL to successfully lock
+ */
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
+ udelay(1);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
+ udelay(1);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
+ udelay(1);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
+ udelay(1);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
+ udelay(1);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
+ udelay(1);
+
+ pll_locked = dsi_pll_lock_status();
+ pr_debug("%s: PLL status = %s\n", __func__,
+ pll_locked ? "Locked" : "Unlocked");
+
+ return pll_locked ? 0 : -EINVAL;
+}
+
+static int dsi_pll_enable_seq_f1(void)
+{
+ int pll_locked = 0;
+
+ dsi_pll_software_reset();
+
+ /*
+ * Add hardware recommended delays between register writes for
+ * the updates to take effect. These delays are necessary for the
+ * PLL to successfully lock
+ */
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
+ udelay(200);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
+ udelay(200);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
+ udelay(200);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0d);
+ udelay(200);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
+ udelay(1000);
+
+ pll_locked = dsi_pll_lock_status();
+ pr_debug("%s: PLL status = %s\n", __func__,
+ pll_locked ? "Locked" : "Unlocked");
+
+ return pll_locked ? 0 : -EINVAL;
+}
+
+static int dsi_pll_enable_seq_c(void)
+{
+ int pll_locked = 0;
+
+ dsi_pll_software_reset();
+
+ /*
+ * Add hardware recommended delays between register writes for
+ * the updates to take effect. These delays are necessary for the
+ * PLL to successfully lock
+ */
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
+ udelay(200);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
+ udelay(200);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
+ udelay(1000);
+
+ pll_locked = dsi_pll_lock_status();
+ pr_debug("%s: PLL status = %s\n", __func__,
+ pll_locked ? "Locked" : "Unlocked");
+
+ return pll_locked ? 0 : -EINVAL;
+}
+
+static int dsi_pll_enable_seq_e(void)
+{
+ int pll_locked = 0;
+
+ dsi_pll_software_reset();
+
+ /*
+ * Add hardware recommended delays between register writes for
+ * the updates to take effect. These delays are necessary for the
+ * PLL to successfully lock
+ */
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
+ udelay(200);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
+ udelay(200);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0d);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
+ udelay(1000);
+
+ pll_locked = dsi_pll_lock_status();
+ pr_debug("%s: PLL status = %s\n", __func__,
+ pll_locked ? "Locked" : "Unlocked");
+
+ return pll_locked ? 0 : -EINVAL;
+}
+
+static int dsi_pll_enable_seq_8974(void)
+{
+ int i, rc = 0;
+ u32 status, max_reads, timeout_us;
+
+ dsi_pll_software_reset();
+
+ /*
+ * PLL power up sequence.
+ * Add necessary delays recommeded by hardware.
+ */
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
+ udelay(1000);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
+ udelay(1000);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
+ udelay(1000);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
+ udelay(1000);
+
+ for (i = 0; i < 3; i++) {
+ /* DSI Uniphy lock detect setting */
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_LKDET_CFG2,
+ 0x04);
+ udelay(100);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_LKDET_CFG2,
+ 0x05);
+ udelay(500);
+ /* poll for PLL ready status */
+ max_reads = 5;
+ timeout_us = 100;
+ if (readl_poll_timeout_noirq((mdss_dsi_base +
+ DSI_0_PHY_PLL_UNIPHY_PLL_STATUS),
+ status,
+ ((status & 0x01) == 1),
+ max_reads, timeout_us)) {
+ pr_debug("%s: DSI PLL status=%x failed to Lock\n",
+ __func__, status);
+ pr_debug("%s:Trying to power UP PLL again\n",
+ __func__);
+ } else {
+ break;
+ }
+
+ dsi_pll_software_reset();
+ /*
+ * PLL power up sequence.
+ * Add necessary delays recommeded by hardware.
+ */
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x1);
+ udelay(1000);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x5);
+ udelay(1000);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x7);
+ udelay(1000);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x5);
+ udelay(1000);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x7);
+ udelay(1000);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0xf);
+ udelay(2000);
+
+ }
+
+ if ((status & 0x01) != 1) {
+ pr_debug("%s: DSI PLL status=%x failed to Lock\n",
+ __func__, status);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ pr_debug("%s: DSI PLL Lock success\n", __func__);
+
+error:
+ return rc;
+}
+
+static int vco_enable(struct clk *c)
+{
+ int i, rc = 0;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+
+ rc = clk_enable(mdss_ahb_clk);
+ if (rc) {
+ pr_err("%s: failed to enable mdss ahb clock. rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ /* Try all enable sequences until one succeeds */
+ for (i = 0; i < vco->pll_en_seq_cnt; i++) {
+ rc = vco->pll_enable_seqs[i]();
+ pr_debug("%s: DSI PLL %s after sequence #%d\n", __func__,
+ rc ? "unlocked" : "locked", i + 1);
+ if (!rc)
+ break;
+ }
+ clk_disable(mdss_ahb_clk);
+
+ if (rc)
+ pr_err("%s: DSI PLL failed to lock\n", __func__);
+
+ return rc;
+}
+
+static void vco_disable(struct clk *c)
+{
+ int rc = 0;
+
+ rc = clk_enable(mdss_ahb_clk);
+ if (rc) {
+ pr_err("%s: failed to enable mdss ahb clock. rc=%d\n",
+ __func__, rc);
+ return;
+ }
+
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x00);
+
+ clk_disable(mdss_ahb_clk);
+ pr_debug("%s: DSI PLL Disabled\n", __func__);
+ return;
+}
+
+static int vco_set_rate(struct clk *c, unsigned long rate)
+{
+ s64 vco_clk_rate = rate;
+ s32 rem;
+ s64 refclk_cfg, frac_n_mode, ref_doubler_en_b;
+ s64 ref_clk_to_pll, div_fbx1000, frac_n_value;
+ s64 sdm_cfg0, sdm_cfg1, sdm_cfg2, sdm_cfg3;
+ s64 gen_vco_clk, cal_cfg10, cal_cfg11;
+ u32 res;
+ int i, rc = 0;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+
+ rc = mdss_ahb_clk_enable(1);
+ if (rc) {
+ pr_err("%s: failed to enable mdss ahb clock. rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ /* Configure the Loop filter resistance */
+ for (i = 0; i < vco->lpfr_lut_size; i++)
+ if (vco_clk_rate <= vco->lpfr_lut[i].vco_rate)
+ break;
+ if (i == vco->lpfr_lut_size) {
+ pr_err("%s: unable to get loop filter resistance. vco=%ld\n",
+ __func__, rate);
+ rc = -EINVAL;
+ goto error;
+ }
+ res = vco->lpfr_lut[i].r;
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_LPFR_CFG, res);
+
+ /* Loop filter capacitance values : c1 and c2 */
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_LPFC1_CFG, 0x70);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_LPFC2_CFG, 0x15);
+
+ div_s64_rem(vco_clk_rate, vco->ref_clk_rate, &rem);
+ if (rem) {
+ refclk_cfg = 0x1;
+ frac_n_mode = 1;
+ ref_doubler_en_b = 0;
+ } else {
+ refclk_cfg = 0x0;
+ frac_n_mode = 0;
+ ref_doubler_en_b = 1;
+ }
+
+ pr_debug("%s:refclk_cfg = %lld\n", __func__, refclk_cfg);
+
+ ref_clk_to_pll = ((vco->ref_clk_rate * 2 * (refclk_cfg))
+ + (ref_doubler_en_b * vco->ref_clk_rate));
+ div_fbx1000 = div_s64((vco_clk_rate * 1000), ref_clk_to_pll);
+
+ div_s64_rem(div_fbx1000, 1000, &rem);
+ frac_n_value = div_s64((rem * (1 << 16)), 1000);
+ gen_vco_clk = div_s64(div_fbx1000 * ref_clk_to_pll, 1000);
+
+ pr_debug("%s:ref_clk_to_pll = %lld\n", __func__, ref_clk_to_pll);
+ pr_debug("%s:div_fb = %lld\n", __func__, div_fbx1000);
+ pr_debug("%s:frac_n_value = %lld\n", __func__, frac_n_value);
+
+ pr_debug("%s:Generated VCO Clock: %lld\n", __func__, gen_vco_clk);
+ rem = 0;
+ if (frac_n_mode) {
+ sdm_cfg0 = (0x0 << 5);
+ sdm_cfg0 |= (0x0 & 0x3f);
+ sdm_cfg1 = (div_s64(div_fbx1000, 1000) & 0x3f) - 1;
+ sdm_cfg3 = div_s64_rem(frac_n_value, 256, &rem);
+ sdm_cfg2 = rem;
+ } else {
+ sdm_cfg0 = (0x1 << 5);
+ sdm_cfg0 |= (div_s64(div_fbx1000, 1000) & 0x3f) - 1;
+ sdm_cfg1 = (0x0 & 0x3f);
+ sdm_cfg2 = 0;
+ sdm_cfg3 = 0;
+ }
+
+ pr_debug("%s: sdm_cfg0=%lld\n", __func__, sdm_cfg0);
+ pr_debug("%s: sdm_cfg1=%lld\n", __func__, sdm_cfg1);
+ pr_debug("%s: sdm_cfg2=%lld\n", __func__, sdm_cfg2);
+ pr_debug("%s: sdm_cfg3=%lld\n", __func__, sdm_cfg3);
+
+ cal_cfg11 = div_s64_rem(gen_vco_clk, 256 * 1000000, &rem);
+ cal_cfg10 = rem / 1000000;
+ pr_debug("%s: cal_cfg10=%lld, cal_cfg11=%lld\n", __func__,
+ cal_cfg10, cal_cfg11);
+
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_CHGPUMP_CFG, 0x02);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG3, 0x2b);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG4, 0x66);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x05);
+
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_SDM_CFG1,
+ (u32)(sdm_cfg1 & 0xff));
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_SDM_CFG2,
+ (u32)(sdm_cfg2 & 0xff));
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_SDM_CFG3,
+ (u32)(sdm_cfg3 & 0xff));
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_SDM_CFG4, 0x00);
+
+ /* Add hardware recommended delay for correct PLL configuration */
+ udelay(1000);
+
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_REFCLK_CFG,
+ (u32)refclk_cfg);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG, 0x00);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG, 0x71);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_SDM_CFG0,
+ (u32)sdm_cfg0);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG0, 0x0a);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG6, 0x30);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG7, 0x00);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG8, 0x60);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG9, 0x00);
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG10,
+ (u32)(cal_cfg10 & 0xff));
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG11,
+ (u32)(cal_cfg11 & 0xff));
+ DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_EFUSE_CFG, 0x20);
+
+error:
+ mdss_ahb_clk_enable(0);
+ return rc;
+}
+
+/* rate is the bit clk rate */
+static long vco_round_rate(struct clk *c, unsigned long rate)
+{
+ unsigned long rrate = rate;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+
+ if (rate < vco->min_rate)
+ rrate = vco->min_rate;
+ if (rate > vco->max_rate)
+ rrate = vco->max_rate;
+
+ return rrate;
+}
+
+static unsigned long vco_get_rate(struct clk *c)
+{
+ u32 sdm0, doubler, sdm_byp_div;
+ u64 vco_rate;
+ u32 sdm_dc_off, sdm_freq_seed, sdm2, sdm3;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ u64 ref_clk = vco->ref_clk_rate;
+
+ /* Check to see if the ref clk doubler is enabled */
+ doubler = DSS_REG_R(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_REFCLK_CFG)
+ & BIT(0);
+ ref_clk += (doubler * vco->ref_clk_rate);
+
+ /* see if it is integer mode or sdm mode */
+ sdm0 = DSS_REG_R(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_SDM_CFG0);
+ if (sdm0 & BIT(6)) {
+ /* integer mode */
+ sdm_byp_div = (DSS_REG_R(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_SDM_CFG0) & 0x3f) + 1;
+ vco_rate = ref_clk * sdm_byp_div;
+ } else {
+ /* sdm mode */
+ sdm_dc_off = DSS_REG_R(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_SDM_CFG1) & 0xFF;
+ pr_debug("%s: sdm_dc_off = %d\n", __func__, sdm_dc_off);
+ sdm2 = DSS_REG_R(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_SDM_CFG2) & 0xFF;
+ sdm3 = DSS_REG_R(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_SDM_CFG3) & 0xFF;
+ sdm_freq_seed = (sdm3 << 8) | sdm2;
+ pr_debug("%s: sdm_freq_seed = %d\n", __func__, sdm_freq_seed);
+
+ vco_rate = (ref_clk * (sdm_dc_off + 1)) +
+ mult_frac(ref_clk, sdm_freq_seed, BIT(16));
+ pr_debug("%s: vco rate = %lld", __func__, vco_rate);
+ }
+
+ pr_debug("%s: returning vco rate = %lu\n", __func__,
+ (unsigned long)vco_rate);
+ return (unsigned long)vco_rate;
+}
+
+static enum handoff vco_handoff(struct clk *c)
+{
+ int rc = 0;
+ enum handoff ret = HANDOFF_DISABLED_CLK;
+
+ rc = mdss_ahb_clk_enable(1);
+ if (rc) {
+ pr_err("%s: failed to enable mdss ahb clock. rc=%d\n",
+ __func__, rc);
+ return ret;
+ }
+ if (dsi_pll_lock_status()) {
+ c->rate = vco_get_rate(c);
+ ret = HANDOFF_ENABLED_CLK;
+ }
+
+ mdss_ahb_clk_enable(0);
+ return ret;
+}
+
+static int vco_prepare(struct clk *c)
+{
+ return vco_set_rate(c, vco_cached_rate);
+}
+
+static void vco_unprepare(struct clk *c)
+{
+ vco_cached_rate = c->rate;
+}
+
+/* Op structures */
+
+static struct clk_ops clk_ops_dsi_vco = {
+ .enable = vco_enable,
+ .disable = vco_disable,
+ .set_rate = vco_set_rate,
+ .round_rate = vco_round_rate,
+ .handoff = vco_handoff,
+ .prepare = vco_prepare,
+ .unprepare = vco_unprepare,
};
-struct clk_ops clk_ops_dsi_byte_pll = {
- .enable = mdss_dsi_pll_enable,
- .disable = mdss_dsi_pll_disable,
- .set_rate = mdss_dsi_pll_byte_set_rate,
- .round_rate = mdss_dsi_pll_byte_round_rate,
- .handoff = mdss_dsi_pll_byte_handoff,
+static struct clk_div_ops fixed_2div_ops;
+
+static struct clk_div_ops fixed_4div_ops = {
+ .set_div = fixed_4div_set_div,
+ .get_div = fixed_4div_get_div,
};
+
+static struct clk_div_ops analog_postdiv_ops = {
+ .set_div = analog_set_div,
+ .get_div = analog_get_div,
+};
+
+static struct clk_div_ops digital_postdiv_ops = {
+ .set_div = digital_set_div,
+ .get_div = digital_get_div,
+};
+
+struct clk_mux_ops byte_mux_ops = {
+ .set_mux_sel = set_byte_mux_sel,
+ .get_mux_sel = get_byte_mux_sel,
+};
+
+struct clk_ops byte_mux_clk_ops;
+
+static struct clk_ops pixel_clk_src_ops;
+static struct clk_ops byte_clk_src_ops;
+static struct clk_ops analog_potsdiv_clk_ops;
+
+/* Display clocks */
+
+struct dsi_pll_vco_clk dsi_vco_clk_8226 = {
+ .ref_clk_rate = 19200000,
+ .min_rate = 350000000,
+ .max_rate = 750000000,
+ .pll_en_seq_cnt = 6,
+ .pll_enable_seqs[0] = dsi_pll_enable_seq_m,
+ .pll_enable_seqs[1] = dsi_pll_enable_seq_d,
+ .pll_enable_seqs[2] = dsi_pll_enable_seq_d,
+ .pll_enable_seqs[3] = dsi_pll_enable_seq_f1,
+ .pll_enable_seqs[4] = dsi_pll_enable_seq_c,
+ .pll_enable_seqs[5] = dsi_pll_enable_seq_e,
+ .lpfr_lut_size = 10,
+ .lpfr_lut = (struct lpfr_cfg[]){
+ {479500000, 8},
+ {480000000, 11},
+ {575500000, 8},
+ {576000000, 12},
+ {610500000, 8},
+ {659500000, 9},
+ {671500000, 10},
+ {672000000, 14},
+ {708500000, 10},
+ {750000000, 11},
+ },
+ .c = {
+ .dbg_name = "dsi_vco_clk",
+ .ops = &clk_ops_dsi_vco,
+ CLK_INIT(dsi_vco_clk_8226.c),
+ },
+};
+
+struct div_clk analog_postdiv_clk_8226 = {
+ .max_div = 255,
+ .min_div = 1,
+ .ops = &analog_postdiv_ops,
+ .c = {
+ .parent = &dsi_vco_clk_8226.c,
+ .dbg_name = "analog_postdiv_clk",
+ .ops = &analog_potsdiv_clk_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(analog_postdiv_clk_8226.c),
+ },
+};
+
+struct div_clk indirect_path_div2_clk_8226 = {
+ .ops = &fixed_2div_ops,
+ .div = 2,
+ .c = {
+ .parent = &analog_postdiv_clk_8226.c,
+ .dbg_name = "indirect_path_div2_clk",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(indirect_path_div2_clk_8226.c),
+ },
+};
+
+struct div_clk pixel_clk_src_8226 = {
+ .max_div = 255,
+ .min_div = 1,
+ .ops = &digital_postdiv_ops,
+ .c = {
+ .parent = &dsi_vco_clk_8226.c,
+ .dbg_name = "pixel_clk_src",
+ .ops = &pixel_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(pixel_clk_src_8226.c),
+ },
+};
+
+struct mux_clk byte_mux_8226 = {
+ .num_parents = 2,
+ .parents = (struct clk_src[]){
+ {&dsi_vco_clk_8226.c, 0},
+ {&indirect_path_div2_clk_8226.c, 1},
+ },
+ .ops = &byte_mux_ops,
+ .c = {
+ .parent = &dsi_vco_clk_8226.c,
+ .dbg_name = "byte_mux",
+ .ops = &byte_mux_clk_ops,
+ CLK_INIT(byte_mux_8226.c),
+ },
+};
+
+struct div_clk byte_clk_src_8226 = {
+ .ops = &fixed_4div_ops,
+ .min_div = 4,
+ .max_div = 4,
+ .c = {
+ .parent = &byte_mux_8226.c,
+ .dbg_name = "byte_clk_src",
+ .ops = &byte_clk_src_ops,
+ CLK_INIT(byte_clk_src_8226.c),
+ },
+};
+
+struct dsi_pll_vco_clk dsi_vco_clk_8974 = {
+ .ref_clk_rate = 19200000,
+ .min_rate = 350000000,
+ .max_rate = 750000000,
+ .pll_en_seq_cnt = 3,
+ .pll_enable_seqs[0] = dsi_pll_enable_seq_8974,
+ .pll_enable_seqs[1] = dsi_pll_enable_seq_8974,
+ .pll_enable_seqs[2] = dsi_pll_enable_seq_8974,
+ .lpfr_lut_size = 10,
+ .lpfr_lut = (struct lpfr_cfg[]){
+ {479500000, 8},
+ {480000000, 11},
+ {575500000, 8},
+ {576000000, 12},
+ {610500000, 8},
+ {659500000, 9},
+ {671500000, 10},
+ {672000000, 14},
+ {708500000, 10},
+ {750000000, 11},
+ },
+ .c = {
+ .dbg_name = "dsi_vco_clk",
+ .ops = &clk_ops_dsi_vco,
+ CLK_INIT(dsi_vco_clk_8974.c),
+ },
+};
+
+struct div_clk analog_postdiv_clk_8974 = {
+ .max_div = 255,
+ .min_div = 1,
+ .ops = &analog_postdiv_ops,
+ .c = {
+ .parent = &dsi_vco_clk_8974.c,
+ .dbg_name = "analog_postdiv_clk",
+ .ops = &analog_potsdiv_clk_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(analog_postdiv_clk_8974.c),
+ },
+};
+
+struct div_clk indirect_path_div2_clk_8974 = {
+ .ops = &fixed_2div_ops,
+ .div = 2,
+ .c = {
+ .parent = &analog_postdiv_clk_8974.c,
+ .dbg_name = "indirect_path_div2_clk",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(indirect_path_div2_clk_8974.c),
+ },
+};
+
+struct div_clk pixel_clk_src_8974 = {
+ .max_div = 255,
+ .min_div = 1,
+ .ops = &digital_postdiv_ops,
+ .c = {
+ .parent = &dsi_vco_clk_8974.c,
+ .dbg_name = "pixel_clk_src",
+ .ops = &pixel_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(pixel_clk_src_8974.c),
+ },
+};
+
+struct mux_clk byte_mux_8974 = {
+ .num_parents = 2,
+ .parents = (struct clk_src[]){
+ {&dsi_vco_clk_8974.c, 0},
+ {&indirect_path_div2_clk_8974.c, 1},
+ },
+ .ops = &byte_mux_ops,
+ .c = {
+ .parent = &dsi_vco_clk_8974.c,
+ .dbg_name = "byte_mux",
+ .ops = &byte_mux_clk_ops,
+ CLK_INIT(byte_mux_8974.c),
+ },
+};
+
+struct div_clk byte_clk_src_8974 = {
+ .ops = &fixed_4div_ops,
+ .min_div = 4,
+ .max_div = 4,
+ .c = {
+ .parent = &byte_mux_8974.c,
+ .dbg_name = "byte_clk_src",
+ .ops = &byte_clk_src_ops,
+ CLK_INIT(byte_clk_src_8974.c),
+ },
+};
+
+void __init mdss_clk_ctrl_pre_init(struct clk *ahb_clk)
+{
+ BUG_ON(ahb_clk == NULL);
+
+ gdsc_base = ioremap(GDSC_PHYS, GDSC_SIZE);
+ if (!gdsc_base)
+ pr_err("%s: unable to remap gdsc base", __func__);
+
+ mdss_dsi_base = ioremap(DSI_PHY_PHYS, DSI_PHY_SIZE);
+ if (!mdss_dsi_base)
+ pr_err("%s: unable to remap dsi base", __func__);
+
+ mdss_ahb_clk = ahb_clk;
+
+ hdmi_phy_base = ioremap(HDMI_PHY_PHYS, HDMI_PHY_SIZE);
+ if (!hdmi_phy_base)
+ pr_err("%s: unable to ioremap hdmi phy base", __func__);
+
+ hdmi_phy_pll_base = ioremap(HDMI_PHY_PLL_PHYS, HDMI_PHY_PLL_SIZE);
+ if (!hdmi_phy_pll_base)
+ pr_err("%s: unable to ioremap hdmi phy pll base", __func__);
+
+ pixel_clk_src_ops = clk_ops_slave_div;
+ pixel_clk_src_ops.prepare = div_prepare;
+
+ byte_clk_src_ops = clk_ops_div;
+ byte_clk_src_ops.prepare = div_prepare;
+
+ analog_potsdiv_clk_ops = clk_ops_div;
+ analog_potsdiv_clk_ops.prepare = div_prepare;
+
+ byte_mux_clk_ops = clk_ops_gen_mux;
+ byte_mux_clk_ops.prepare = mux_prepare;
+}
+
diff --git a/arch/arm/mach-msm/clock-mdss-8974.h b/arch/arm/mach-msm/clock-mdss-8974.h
index e242669..9fd3026 100644
--- a/arch/arm/mach-msm/clock-mdss-8974.h
+++ b/arch/arm/mach-msm/clock-mdss-8974.h
@@ -13,6 +13,10 @@
#ifndef __ARCH_ARM_MACH_MSM_CLOCK_MDSS_8974
#define __ARCH_ARM_MACH_MSM_CLOCK_MDSS_8974
+#include <linux/clk.h>
+
+#define MAX_DSI_PLL_EN_SEQS 10
+
extern struct clk_ops clk_ops_dsi_byte_pll;
extern struct clk_ops clk_ops_dsi_pixel_pll;
@@ -22,4 +26,35 @@
void hdmi_pll_disable(void);
int hdmi_pll_set_rate(unsigned long rate);
+struct lpfr_cfg {
+ unsigned long vco_rate;
+ u32 r;
+};
+
+struct dsi_pll_vco_clk {
+ unsigned long ref_clk_rate;
+ unsigned long min_rate;
+ unsigned long max_rate;
+ int (*pll_enable_seqs[MAX_DSI_PLL_EN_SEQS])(void);
+ u32 pll_en_seq_cnt;
+ struct lpfr_cfg *lpfr_lut;
+ u32 lpfr_lut_size;
+
+ struct clk c;
+};
+
+extern struct dsi_pll_vco_clk dsi_vco_clk_8974;
+extern struct div_clk analog_postdiv_clk_8974;
+extern struct div_clk indirect_path_div2_clk_8974;
+extern struct div_clk pixel_clk_src_8974;
+extern struct mux_clk byte_mux_8974;
+extern struct div_clk byte_clk_src_8974;
+
+extern struct dsi_pll_vco_clk dsi_vco_clk_8226;
+extern struct div_clk analog_postdiv_clk_8226;
+extern struct div_clk indirect_path_div2_clk_8226;
+extern struct div_clk pixel_clk_src_8226;
+extern struct mux_clk byte_mux_8226;
+extern struct div_clk byte_clk_src_8226;
+
#endif
diff --git a/arch/arm/mach-msm/clock-pll.c b/arch/arm/mach-msm/clock-pll.c
index d2be1f9..908107e 100644
--- a/arch/arm/mach-msm/clock-pll.c
+++ b/arch/arm/mach-msm/clock-pll.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, 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
@@ -15,14 +15,15 @@
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/err.h>
+#include <linux/clk.h>
#include <linux/remote_spinlock.h>
#include <mach/scm-io.h>
#include <mach/msm_iomap.h>
+#include <mach/msm_smem.h>
#include "clock.h"
#include "clock-pll.h"
-#include "smd_private.h"
#ifdef CONFIG_MSM_SECURE_IO
#undef readl_relaxed
@@ -262,24 +263,38 @@
struct pll_clk *pll = to_pll_clk(c);
u32 mode = readl_relaxed(PLL_MODE_REG(pll));
u32 mask = PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL;
+ unsigned long parent_rate;
+ u32 lval, mval, nval, userval;
- if ((mode & mask) == mask)
+ if ((mode & mask) != mask)
+ return HANDOFF_DISABLED_CLK;
+
+ /* Assume bootloaders configure PLL to c->rate */
+ if (c->rate)
return HANDOFF_ENABLED_CLK;
- return HANDOFF_DISABLED_CLK;
+ parent_rate = clk_get_rate(c->parent);
+ lval = readl_relaxed(PLL_L_REG(pll));
+ mval = readl_relaxed(PLL_M_REG(pll));
+ nval = readl_relaxed(PLL_N_REG(pll));
+ userval = readl_relaxed(PLL_CONFIG_REG(pll));
+
+ c->rate = parent_rate * lval;
+
+ if (pll->masks.mn_en_mask && userval) {
+ if (!nval)
+ nval = 1;
+ c->rate += (parent_rate * mval) / nval;
+ }
+
+ return HANDOFF_ENABLED_CLK;
}
static int local_pll_clk_set_rate(struct clk *c, unsigned long rate)
{
struct pll_freq_tbl *nf;
struct pll_clk *pll = to_pll_clk(c);
- u32 mode;
-
- mode = readl_relaxed(PLL_MODE_REG(pll));
-
- /* Don't change PLL's rate if it is enabled */
- if ((mode & PLL_MODE_MASK) == PLL_MODE_MASK)
- return -EBUSY;
+ unsigned long flags;
for (nf = pll->freq_tbl; nf->freq_hz != PLL_FREQ_END
&& nf->freq_hz != rate; nf++)
@@ -288,12 +303,24 @@
if (nf->freq_hz == PLL_FREQ_END)
return -EINVAL;
+ /*
+ * Ensure PLL is off before changing rate. For optimization reasons,
+ * assume no downstream clock is using actively using it.
+ */
+ spin_lock_irqsave(&c->lock, flags);
+ if (c->count)
+ c->ops->disable(c);
+
writel_relaxed(nf->l_val, PLL_L_REG(pll));
writel_relaxed(nf->m_val, PLL_M_REG(pll));
writel_relaxed(nf->n_val, PLL_N_REG(pll));
__pll_config_reg(PLL_CONFIG_REG(pll), nf, &pll->masks);
+ if (c->count)
+ c->ops->enable(c);
+
+ spin_unlock_irqrestore(&c->lock, flags);
return 0;
}
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index 044fc2c..582bccf 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -62,35 +62,55 @@
{
int level, rc = 0, i;
struct regulator **r = vdd_class->regulator;
- const int **vdd_uv = vdd_class->vdd_uv;
- int max_level = vdd_class->num_levels - 1;
+ int *uv = vdd_class->vdd_uv;
+ int *ua = vdd_class->vdd_ua;
+ int n_reg = vdd_class->num_regulators;
+ int max_lvl = vdd_class->num_levels - 1;
+ int lvl_base;
- for (level = max_level; level > 0; level--)
+ for (level = max_lvl; level > 0; level--)
if (vdd_class->level_votes[level])
break;
if (level == vdd_class->cur_level)
return 0;
+ max_lvl = max_lvl * n_reg;
+ lvl_base = level * n_reg;
for (i = 0; i < vdd_class->num_regulators; i++) {
- rc = regulator_set_voltage(r[i], vdd_uv[level][i],
- vdd_uv[max_level][i]);
+ rc = regulator_set_voltage(r[i], uv[lvl_base + i],
+ uv[max_lvl + i]);
if (rc)
goto set_voltage_fail;
+
+ if (!ua)
+ continue;
+
+ rc = regulator_set_optimum_mode(r[i], ua[lvl_base + i]);
+ if (rc < 0)
+ goto set_mode_fail;
}
if (vdd_class->set_vdd && !vdd_class->num_regulators)
rc = vdd_class->set_vdd(vdd_class, level);
- if (!rc)
+ if (rc < 0)
vdd_class->cur_level = level;
- return rc;
+ return 0;
+
+set_mode_fail:
+ regulator_set_voltage(r[i], uv[vdd_class->cur_level * n_reg + i],
+ uv[max_lvl + i]);
set_voltage_fail:
- level = vdd_class->cur_level;
- for (i--; i >= 0; i--)
- regulator_set_voltage(r[i], vdd_uv[level][i],
- vdd_uv[max_level][i]);
+ lvl_base = vdd_class->cur_level * n_reg;
+ for (i--; i >= 0; i--) {
+ regulator_set_voltage(r[i], uv[lvl_base + i], uv[max_lvl + i]);
+
+ if (!ua)
+ continue;
+ regulator_set_optimum_mode(r[i], ua[lvl_base + i]);
+ }
return rc;
}
@@ -455,7 +475,7 @@
mutex_lock(&clk->prepare_lock);
/* Return early if the rate isn't going to change */
- if (clk->rate == rate)
+ if (clk->rate == rate && !(clk->flags & CLKFLAG_NO_RATE_CACHE))
goto out;
trace_clock_set_rate(name, rate, raw_smp_processor_id());
diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h
index 9ca1965..2a65d2f 100644
--- a/arch/arm/mach-msm/clock.h
+++ b/arch/arm/mach-msm/clock.h
@@ -53,6 +53,8 @@
extern struct clock_init_data msm8610_rumi_clock_init_data;
extern struct clock_init_data msm8226_clock_init_data;
extern struct clock_init_data msm8226_rumi_clock_init_data;
+extern struct clock_init_data msm8084_clock_init_data;
+extern struct clock_init_data mpq8092_clock_init_data;
int msm_clock_init(struct clock_init_data *data);
int find_vdd_level(struct clk *clk, unsigned long rate);
diff --git a/arch/arm/mach-msm/cpr-regulator.c b/arch/arm/mach-msm/cpr-regulator.c
index 08923e4..5550282 100644
--- a/arch/arm/mach-msm/cpr-regulator.c
+++ b/arch/arm/mach-msm/cpr-regulator.c
@@ -24,26 +24,144 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/interrupt.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/cpr-regulator.h>
+/* Register Offsets for RB-CPR and Bit Definitions */
+
+/* RBCPR Gate Count and Target Registers */
+#define REG_RBCPR_GCNT_TARGET(n) (0x60 + 4 * n)
+
+#define RBCPR_GCNT_TARGET_GCNT_BITS 10
+#define RBCPR_GCNT_TARGET_GCNT_SHIFT 12
+#define RBCPR_GCNT_TARGET_GCNT_MASK ((1<<RBCPR_GCNT_TARGET_GCNT_BITS)-1)
+
+/* RBCPR Timer Control */
+#define REG_RBCPR_TIMER_INTERVAL 0x44
+#define REG_RBIF_TIMER_ADJUST 0x4C
+
+#define RBIF_TIMER_ADJ_CONS_UP_BITS 4
+#define RBIF_TIMER_ADJ_CONS_UP_MASK ((1<<RBIF_TIMER_ADJ_CONS_UP_BITS)-1)
+#define RBIF_TIMER_ADJ_CONS_DOWN_BITS 4
+#define RBIF_TIMER_ADJ_CONS_DOWN_MASK ((1<<RBIF_TIMER_ADJ_CONS_DOWN_BITS)-1)
+#define RBIF_TIMER_ADJ_CONS_DOWN_SHIFT 4
+
+/* RBCPR Config Register */
+#define REG_RBIF_LIMIT 0x48
+#define REG_RBCPR_STEP_QUOT 0x80
+#define REG_RBIF_SW_VLEVEL 0x94
+
+#define RBIF_LIMIT_CEILING_BITS 6
+#define RBIF_LIMIT_CEILING_MASK ((1<<RBIF_LIMIT_CEILING_BITS)-1)
+#define RBIF_LIMIT_CEILING_SHIFT 6
+#define RBIF_LIMIT_FLOOR_BITS 6
+#define RBIF_LIMIT_FLOOR_MASK ((1<<RBIF_LIMIT_FLOOR_BITS)-1)
+
+#define RBIF_LIMIT_CEILING_DEFAULT RBIF_LIMIT_CEILING_MASK
+#define RBIF_LIMIT_FLOOR_DEFAULT 0
+#define RBIF_SW_VLEVEL_DEFAULT 0x20
+
+#define RBCPR_STEP_QUOT_STEPQUOT_BITS 8
+#define RBCPR_STEP_QUOT_STEPQUOT_MASK ((1<<RBCPR_STEP_QUOT_STEPQUOT_BITS)-1)
+#define RBCPR_STEP_QUOT_IDLE_CLK_BITS 4
+#define RBCPR_STEP_QUOT_IDLE_CLK_MASK ((1<<RBCPR_STEP_QUOT_IDLE_CLK_BITS)-1)
+#define RBCPR_STEP_QUOT_IDLE_CLK_SHIFT 8
+
+/* RBCPR Control Register */
+#define REG_RBCPR_CTL 0x90
+
+#define RBCPR_CTL_LOOP_EN BIT(0)
+#define RBCPR_CTL_TIMER_EN BIT(3)
+#define RBCPR_CTL_SW_AUTO_CONT_ACK_EN BIT(5)
+#define RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN BIT(6)
+#define RBCPR_CTL_COUNT_MODE BIT(10)
+#define RBCPR_CTL_UP_THRESHOLD_BITS 4
+#define RBCPR_CTL_UP_THRESHOLD_MASK ((1<<RBCPR_CTL_UP_THRESHOLD_BITS)-1)
+#define RBCPR_CTL_UP_THRESHOLD_SHIFT 24
+#define RBCPR_CTL_DN_THRESHOLD_BITS 4
+#define RBCPR_CTL_DN_THRESHOLD_MASK ((1<<RBCPR_CTL_DN_THRESHOLD_BITS)-1)
+#define RBCPR_CTL_DN_THRESHOLD_SHIFT 28
+
+/* RBCPR Ack/Nack Response */
+#define REG_RBIF_CONT_ACK_CMD 0x98
+#define REG_RBIF_CONT_NACK_CMD 0x9C
+
+/* RBCPR Result status Register */
+#define REG_RBCPR_RESULT_0 0xA0
+
+#define RBCPR_RESULT0_ERROR_STEPS_SHIFT 2
+#define RBCPR_RESULT0_ERROR_STEPS_BITS 4
+#define RBCPR_RESULT0_ERROR_STEPS_MASK ((1<<RBCPR_RESULT0_ERROR_STEPS_BITS)-1)
+
+/* RBCPR Interrupt Control Register */
+#define REG_RBIF_IRQ_EN(n) (0x100 + 4 * n)
+#define REG_RBIF_IRQ_CLEAR 0x110
+#define REG_RBIF_IRQ_STATUS 0x114
+
+#define CPR_INT_DONE BIT(0)
+#define CPR_INT_MIN BIT(1)
+#define CPR_INT_DOWN BIT(2)
+#define CPR_INT_MID BIT(3)
+#define CPR_INT_UP BIT(4)
+#define CPR_INT_MAX BIT(5)
+#define CPR_INT_CLAMP BIT(6)
+#define CPR_INT_ALL (CPR_INT_DONE | CPR_INT_MIN | CPR_INT_DOWN | \
+ CPR_INT_MID | CPR_INT_UP | CPR_INT_MAX | CPR_INT_CLAMP)
+#define CPR_INT_DEFAULT (CPR_INT_UP | CPR_INT_DOWN)
+
+#define CPR_NUM_RING_OSC 8
+#define CPR_NUM_SAVE_REGS 10
+
+/* RBCPR Clock Control Register */
+#define RBCPR_CLK_SEL_MASK BIT(0)
+#define RBCPR_CLK_SEL_19P2_MHZ 0
+#define RBCPR_CLK_SEL_AHB_CLK BIT(0)
+
+/* CPR eFuse parameters */
+#define CPR_FUSE_TARGET_QUOT_BITS 12
+#define CPR_FUSE_TARGET_QUOT_BITS_MASK ((1<<CPR_FUSE_TARGET_QUOT_BITS)-1)
+#define CPR_FUSE_RO_SEL_BITS 3
+#define CPR_FUSE_RO_SEL_BITS_MASK ((1<<CPR_FUSE_RO_SEL_BITS)-1)
+
+#define CPR_FUSE_TARGET_QUOT_TURBO_SHIFT 0
+#define CPR_FUSE_TARGET_QUOT_NOMINAL_SHIFT 12
+#define CPR_FUSE_TARGET_QUOT_SVS_SHIFT 24
+
+#define CPR_FUSE_DISABLE_CPR_SHIFT 36
+#define CPR_FUSE_LOCAL_APPROACH_SHIFT 37
+#define CPR_FUSE_REDUNDANT_SHIFT 57
+
+/* PVS eFuse parameters */
+#define PVS_FUSE_REDUNDANT_SHIFT 24
+#define PVS_FUSE_REDUNDANT_BITS 3
+#define PVS_FUSE_REDUNDANT_MASK ((1<<PVS_FUSE_REDUNDANT_BITS)-1)
+
+#define PVS_FUSE_BINS_SHIFT 6
+#define PVS_FUSE_BINS_REDUNDANT_SHIFT 27
+
+enum voltage_change_dir {
+ NO_CHANGE,
+ DOWN,
+ UP,
+};
+
struct cpr_regulator {
struct regulator_desc rdesc;
struct regulator_dev *rdev;
- bool enabled;
+ bool vreg_enabled;
int corner;
+ int ceiling_max;
/* Process voltage parameters */
- phys_addr_t efuse_phys;
+ phys_addr_t pvs_efuse;
u32 num_efuse_bits;
- u32 efuse_bit_pos[CPR_PVS_EFUSE_BITS_MAX];
u32 pvs_bin_process[CPR_PVS_EFUSE_BINS_MAX];
- u32 pvs_corner_ceiling[NUM_APC_PVS][CPR_CORNER_MAX];
+ u32 pvs_corner_v[NUM_APC_PVS][CPR_CORNER_MAX];
/* Process voltage variables */
u32 pvs_bin;
- u32 pvs_process;
- u32 *corner_ceiling;
+ u32 process;
/* APC voltage regulator */
struct regulator *vdd_apc;
@@ -53,13 +171,500 @@
int vdd_mx_vmax;
int vdd_mx_vmin_method;
int vdd_mx_vmin;
+
+ /* CPR parameters */
+ phys_addr_t cpr_fuse_addr;
+ u64 cpr_fuse_bits;
+ u64 cpr_fuse_bits_2;
+ bool cpr_fuse_disable;
+ bool cpr_fuse_local;
+ bool cpr_fuse_redundancy;
+ int cpr_fuse_target_quot[CPR_CORNER_MAX];
+ int cpr_fuse_ro_sel[CPR_CORNER_MAX];
+ int gcnt;
+
+ unsigned int cpr_irq;
+ void __iomem *rbcpr_base;
+ phys_addr_t rbcpr_clk_addr;
+ struct mutex cpr_mutex;
+
+ int ceiling_volt[CPR_CORNER_MAX];
+ int floor_volt[CPR_CORNER_MAX];
+ int last_volt[CPR_CORNER_MAX];
+ int step_volt;
+
+ int save_ctl[CPR_CORNER_MAX];
+ int save_irq[CPR_CORNER_MAX];
+
+ u32 save_regs[CPR_NUM_SAVE_REGS];
+ u32 save_reg_val[CPR_NUM_SAVE_REGS];
+
+ /* Config parameters */
+ bool enable;
+ u32 ref_clk_khz;
+ u32 timer_delay_us;
+ u32 timer_cons_up;
+ u32 timer_cons_down;
+ u32 irq_line;
+ u32 step_quotient;
+ u32 up_threshold;
+ u32 down_threshold;
+ u32 idle_clocks;
+ u32 gcnt_time_us;
+ u32 vdd_apc_step_up_limit;
+ u32 vdd_apc_step_down_limit;
};
+static int cpr_debug_enable;
+static int cpr_enable;
+static struct cpr_regulator *the_cpr;
+
+module_param_named(debug_enable, cpr_debug_enable, int, S_IRUGO | S_IWUSR);
+#define cpr_debug(message, ...) \
+ do { \
+ if (cpr_debug_enable) \
+ pr_info(message, ##__VA_ARGS__); \
+ } while (0)
+
+static bool cpr_is_allowed(struct cpr_regulator *cpr_vreg)
+{
+ if (cpr_vreg->cpr_fuse_disable || !cpr_enable)
+ return false;
+ else
+ return true;
+}
+
+static void cpr_write(struct cpr_regulator *cpr_vreg, u32 offset, u32 value)
+{
+ writel_relaxed(value, cpr_vreg->rbcpr_base + offset);
+}
+
+static u32 cpr_read(struct cpr_regulator *cpr_vreg, u32 offset)
+{
+ return readl_relaxed(cpr_vreg->rbcpr_base + offset);
+}
+
+static void cpr_masked_write(struct cpr_regulator *cpr_vreg, u32 offset,
+ u32 mask, u32 value)
+{
+ u32 reg_val;
+
+ reg_val = readl_relaxed(cpr_vreg->rbcpr_base + offset);
+ reg_val &= ~mask;
+ reg_val |= value & mask;
+ writel_relaxed(reg_val, cpr_vreg->rbcpr_base + offset);
+}
+
+static void cpr_irq_clr(struct cpr_regulator *cpr_vreg)
+{
+ cpr_write(cpr_vreg, REG_RBIF_IRQ_CLEAR, CPR_INT_ALL);
+}
+
+static void cpr_irq_clr_nack(struct cpr_regulator *cpr_vreg)
+{
+ cpr_irq_clr(cpr_vreg);
+ cpr_write(cpr_vreg, REG_RBIF_CONT_NACK_CMD, 1);
+}
+
+static void cpr_irq_clr_ack(struct cpr_regulator *cpr_vreg)
+{
+ cpr_irq_clr(cpr_vreg);
+ cpr_write(cpr_vreg, REG_RBIF_CONT_ACK_CMD, 1);
+}
+
+static void cpr_irq_set(struct cpr_regulator *cpr_vreg, u32 int_bits)
+{
+ cpr_write(cpr_vreg, REG_RBIF_IRQ_EN(cpr_vreg->irq_line), int_bits);
+}
+
+static void cpr_ctl_modify(struct cpr_regulator *cpr_vreg, u32 mask, u32 value)
+{
+ cpr_masked_write(cpr_vreg, REG_RBCPR_CTL, mask, value);
+}
+
+static void cpr_ctl_enable(struct cpr_regulator *cpr_vreg)
+{
+ u32 val;
+
+ if (cpr_is_allowed(cpr_vreg))
+ val = RBCPR_CTL_LOOP_EN;
+ else
+ val = 0;
+ cpr_ctl_modify(cpr_vreg, RBCPR_CTL_LOOP_EN, val);
+}
+
+static void cpr_ctl_disable(struct cpr_regulator *cpr_vreg)
+{
+ cpr_ctl_modify(cpr_vreg, RBCPR_CTL_LOOP_EN, 0);
+}
+
+static void cpr_regs_save(struct cpr_regulator *cpr_vreg)
+{
+ int i, offset;
+
+ for (i = 0; i < CPR_NUM_SAVE_REGS; i++) {
+ offset = cpr_vreg->save_regs[i];
+ cpr_vreg->save_reg_val[i] = cpr_read(cpr_vreg, offset);
+ }
+}
+
+static void cpr_regs_restore(struct cpr_regulator *cpr_vreg)
+{
+ int i, offset;
+ u32 val;
+
+ for (i = 0; i < CPR_NUM_SAVE_REGS; i++) {
+ offset = cpr_vreg->save_regs[i];
+ val = cpr_vreg->save_reg_val[i];
+ cpr_write(cpr_vreg, offset, val);
+ }
+}
+
+static void cpr_corner_save(struct cpr_regulator *cpr_vreg, int corner)
+{
+ cpr_vreg->save_ctl[corner] = cpr_read(cpr_vreg, REG_RBCPR_CTL);
+ cpr_vreg->save_irq[corner] =
+ cpr_read(cpr_vreg, REG_RBIF_IRQ_EN(cpr_vreg->irq_line));
+}
+
+static void cpr_corner_restore(struct cpr_regulator *cpr_vreg, int corner)
+{
+ u32 gcnt, ctl, irq, ro_sel;
+
+ ro_sel = cpr_vreg->cpr_fuse_ro_sel[corner];
+ gcnt = cpr_vreg->gcnt | cpr_vreg->cpr_fuse_target_quot[corner];
+ cpr_write(cpr_vreg, REG_RBCPR_GCNT_TARGET(ro_sel), gcnt);
+ ctl = cpr_vreg->save_ctl[corner];
+ cpr_write(cpr_vreg, REG_RBCPR_CTL, ctl);
+ irq = cpr_vreg->save_irq[corner];
+ cpr_irq_set(cpr_vreg, irq);
+ cpr_debug("gcnt = 0x%08x, ctl = 0x%08x, irq = 0x%08x\n",
+ gcnt, ctl, irq);
+}
+
+static void cpr_corner_switch(struct cpr_regulator *cpr_vreg, int corner)
+{
+ if (cpr_vreg->corner == corner)
+ return;
+
+ cpr_corner_restore(cpr_vreg, corner);
+}
+
+/* Module parameter ops */
+static int cpr_enable_param_set(const char *val, const struct kernel_param *kp)
+{
+ int rc;
+ int old_cpr_enable;
+
+ if (!the_cpr) {
+ pr_err("the_cpr = NULL\n");
+ return -ENXIO;
+ }
+
+ mutex_lock(&the_cpr->cpr_mutex);
+
+ old_cpr_enable = cpr_enable;
+ rc = param_set_int(val, kp);
+ if (rc) {
+ pr_err("param_set_int: rc = %d\n", rc);
+ goto _exit;
+ }
+
+ cpr_debug("%d -> %d [corner=%d]\n",
+ old_cpr_enable, cpr_enable, the_cpr->corner);
+
+ if (the_cpr->cpr_fuse_disable) {
+ /* Already disabled */
+ pr_info("CPR disabled by fuse\n");
+ goto _exit;
+ }
+
+ if ((old_cpr_enable != cpr_enable) && the_cpr->corner) {
+ if (cpr_enable) {
+ cpr_ctl_disable(the_cpr);
+ cpr_irq_clr(the_cpr);
+ cpr_corner_restore(the_cpr, the_cpr->corner);
+ cpr_ctl_enable(the_cpr);
+ } else {
+ cpr_ctl_disable(the_cpr);
+ cpr_irq_set(the_cpr, 0);
+ }
+ }
+
+_exit:
+ mutex_unlock(&the_cpr->cpr_mutex);
+ return 0;
+}
+
+static struct kernel_param_ops cpr_enable_ops = {
+ .set = cpr_enable_param_set,
+ .get = param_get_int,
+};
+
+module_param_cb(cpr_enable, &cpr_enable_ops, &cpr_enable, S_IRUGO | S_IWUSR);
+
+static int cpr_apc_set(struct cpr_regulator *cpr_vreg, u32 new_volt)
+{
+ int max_volt, rc;
+
+ max_volt = cpr_vreg->ceiling_max;
+ rc = regulator_set_voltage(cpr_vreg->vdd_apc, new_volt, max_volt);
+ if (rc)
+ pr_err("set: vdd_apc = %d uV: rc=%d\n", new_volt, rc);
+ return rc;
+}
+
+static int cpr_mx_get(struct cpr_regulator *cpr_vreg, int corner, int apc_volt)
+{
+ int vdd_mx;
+
+ switch (cpr_vreg->vdd_mx_vmin_method) {
+ case VDD_MX_VMIN_APC:
+ vdd_mx = apc_volt;
+ break;
+ case VDD_MX_VMIN_APC_CORNER_CEILING:
+ vdd_mx = cpr_vreg->ceiling_volt[corner];
+ break;
+ case VDD_MX_VMIN_APC_SLOW_CORNER_CEILING:
+ vdd_mx = cpr_vreg->pvs_corner_v[APC_PVS_SLOW]
+ [CPR_CORNER_TURBO];
+ break;
+ case VDD_MX_VMIN_MX_VMAX:
+ vdd_mx = cpr_vreg->vdd_mx_vmax;
+ break;
+ default:
+ vdd_mx = 0;
+ break;
+ }
+
+ return vdd_mx;
+}
+
+static int cpr_mx_set(struct cpr_regulator *cpr_vreg, int corner,
+ int vdd_mx_vmin)
+{
+ int rc;
+
+ rc = regulator_set_voltage(cpr_vreg->vdd_mx, vdd_mx_vmin,
+ cpr_vreg->vdd_mx_vmax);
+ cpr_debug("[corner:%d] %d uV\n", corner, vdd_mx_vmin);
+ if (!rc)
+ cpr_vreg->vdd_mx_vmin = vdd_mx_vmin;
+ else
+ pr_err("set: vdd_mx [%d] = %d uV: rc=%d\n",
+ corner, vdd_mx_vmin, rc);
+ return rc;
+}
+
+static int cpr_scale_voltage(struct cpr_regulator *cpr_vreg, int corner,
+ int new_apc_volt, enum voltage_change_dir dir)
+{
+ int rc = 0, vdd_mx_vmin = 0;
+
+ /* No MX scaling if no vdd_mx */
+ if (cpr_vreg->vdd_mx == NULL)
+ dir = NO_CHANGE;
+
+ if (dir != NO_CHANGE) {
+ /* Determine the vdd_mx voltage */
+ vdd_mx_vmin = cpr_mx_get(cpr_vreg, corner, new_apc_volt);
+ }
+
+ if (vdd_mx_vmin && dir == UP) {
+ if (vdd_mx_vmin != cpr_vreg->vdd_mx_vmin)
+ rc = cpr_mx_set(cpr_vreg, corner, vdd_mx_vmin);
+ }
+
+ if (!rc)
+ rc = cpr_apc_set(cpr_vreg, new_apc_volt);
+
+ if (!rc && vdd_mx_vmin && dir == DOWN) {
+ if (vdd_mx_vmin != cpr_vreg->vdd_mx_vmin)
+ rc = cpr_mx_set(cpr_vreg, corner, vdd_mx_vmin);
+ }
+
+ return rc;
+}
+
+static void cpr_scale(struct cpr_regulator *cpr_vreg,
+ enum voltage_change_dir dir)
+{
+ u32 reg_val, error_steps, reg_mask;
+ int last_volt, new_volt, corner;
+
+ corner = cpr_vreg->corner;
+
+ reg_val = cpr_read(cpr_vreg, REG_RBCPR_RESULT_0);
+
+ error_steps = (reg_val >> RBCPR_RESULT0_ERROR_STEPS_SHIFT)
+ & RBCPR_RESULT0_ERROR_STEPS_MASK;
+ last_volt = cpr_vreg->last_volt[corner];
+
+ cpr_debug("last_volt[corner:%d] = %d uV\n", corner, last_volt);
+
+ if (dir == UP) {
+ cpr_debug("Up: cpr status = 0x%08x (error_steps=%d)\n",
+ reg_val, error_steps);
+
+ if (last_volt >= cpr_vreg->ceiling_volt[corner]) {
+ cpr_debug("[corn:%d] @ ceiling: %d >= %d: NACK\n",
+ corner, last_volt,
+ cpr_vreg->ceiling_volt[corner]);
+ cpr_irq_clr_nack(cpr_vreg);
+
+ /* Maximize the UP threshold */
+ reg_mask = RBCPR_CTL_UP_THRESHOLD_MASK <<
+ RBCPR_CTL_UP_THRESHOLD_SHIFT;
+ reg_val = reg_mask;
+ cpr_ctl_modify(cpr_vreg, reg_mask, reg_val);
+ return;
+ }
+
+ if (error_steps > cpr_vreg->vdd_apc_step_up_limit) {
+ cpr_debug("%d is over up-limit(%d): Clamp\n",
+ error_steps,
+ cpr_vreg->vdd_apc_step_up_limit);
+ error_steps = cpr_vreg->vdd_apc_step_up_limit;
+ }
+
+ /* Calculate new voltage */
+ new_volt = last_volt + (error_steps * cpr_vreg->step_volt);
+ if (new_volt > cpr_vreg->ceiling_volt[corner]) {
+ cpr_debug("new_volt(%d) >= ceiling_volt(%d): Clamp\n",
+ new_volt, cpr_vreg->ceiling_volt[corner]);
+ new_volt = cpr_vreg->ceiling_volt[corner];
+ }
+
+ if (cpr_scale_voltage(cpr_vreg, corner, new_volt, dir)) {
+ cpr_irq_clr_nack(cpr_vreg);
+ return;
+ }
+ cpr_vreg->last_volt[corner] = new_volt;
+
+ /* Restore default threshold for DOWN */
+ reg_mask = RBCPR_CTL_DN_THRESHOLD_MASK <<
+ RBCPR_CTL_DN_THRESHOLD_SHIFT;
+ reg_val = cpr_vreg->down_threshold <<
+ RBCPR_CTL_DN_THRESHOLD_SHIFT;
+ /* and disable auto nack down */
+ reg_mask |= RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN;
+
+ cpr_ctl_modify(cpr_vreg, reg_mask, reg_val);
+
+ /* Re-enable default interrupts */
+ cpr_irq_set(cpr_vreg, CPR_INT_DEFAULT);
+
+ /* Ack */
+ cpr_irq_clr_ack(cpr_vreg);
+
+ cpr_debug("UP: -> new_volt = %d uV\n", new_volt);
+ } else if (dir == DOWN) {
+ cpr_debug("Down: cpr status = 0x%08x (error_steps=%d)\n",
+ reg_val, error_steps);
+
+ if (last_volt <= cpr_vreg->floor_volt[corner]) {
+ cpr_debug("[corn:%d] @ floor: %d <= %d: NACK\n",
+ corner, last_volt,
+ cpr_vreg->floor_volt[corner]);
+ cpr_irq_clr_nack(cpr_vreg);
+
+ /* Maximize the DOWN threshold */
+ reg_mask = RBCPR_CTL_DN_THRESHOLD_MASK <<
+ RBCPR_CTL_DN_THRESHOLD_SHIFT;
+ reg_val = reg_mask;
+
+ /* Enable auto nack down */
+ reg_mask |= RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN;
+ reg_val |= RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN;
+
+ cpr_ctl_modify(cpr_vreg, reg_mask, reg_val);
+
+ /* Disable DOWN interrupt */
+ cpr_irq_set(cpr_vreg, CPR_INT_DEFAULT & ~CPR_INT_DOWN);
+
+ return;
+ }
+
+ if (error_steps > cpr_vreg->vdd_apc_step_down_limit) {
+ cpr_debug("%d is over down-limit(%d): Clamp\n",
+ error_steps,
+ cpr_vreg->vdd_apc_step_down_limit);
+ error_steps = cpr_vreg->vdd_apc_step_down_limit;
+ }
+
+ /* Calculte new voltage */
+ new_volt = last_volt - (error_steps * cpr_vreg->step_volt);
+ if (new_volt < cpr_vreg->floor_volt[corner]) {
+ cpr_debug("new_volt(%d) < floor_volt(%d): Clamp\n",
+ new_volt, cpr_vreg->floor_volt[corner]);
+ new_volt = cpr_vreg->floor_volt[corner];
+ }
+
+ if (cpr_scale_voltage(cpr_vreg, corner, new_volt, dir)) {
+ cpr_irq_clr_nack(cpr_vreg);
+ return;
+ }
+ cpr_vreg->last_volt[corner] = new_volt;
+
+ /* Restore default threshold for UP */
+ reg_mask = RBCPR_CTL_UP_THRESHOLD_MASK <<
+ RBCPR_CTL_UP_THRESHOLD_SHIFT;
+ reg_val = cpr_vreg->up_threshold <<
+ RBCPR_CTL_UP_THRESHOLD_SHIFT;
+ cpr_ctl_modify(cpr_vreg, reg_mask, reg_val);
+
+ /* Ack */
+ cpr_irq_clr_ack(cpr_vreg);
+
+ cpr_debug("DOWN: -> new_volt = %d uV\n", new_volt);
+ }
+}
+
+static irqreturn_t cpr_irq_handler(int irq, void *dev)
+{
+ struct cpr_regulator *cpr_vreg = dev;
+ u32 reg_val;
+
+ mutex_lock(&cpr_vreg->cpr_mutex);
+
+ reg_val = cpr_read(cpr_vreg, REG_RBIF_IRQ_STATUS);
+ cpr_debug("IRQ_STATUS = 0x%02X\n", reg_val);
+
+ if (!cpr_is_allowed(cpr_vreg)) {
+ reg_val = cpr_read(cpr_vreg, REG_RBCPR_CTL);
+ pr_err("Interrupt broken? RBCPR_CTL = 0x%02X\n", reg_val);
+ goto _exit;
+ }
+
+ /* Following sequence of handling is as per each IRQ's priority */
+ if (reg_val & CPR_INT_UP) {
+ cpr_scale(cpr_vreg, UP);
+ } else if (reg_val & CPR_INT_DOWN) {
+ cpr_scale(cpr_vreg, DOWN);
+ } else if (reg_val & CPR_INT_MIN) {
+ cpr_irq_clr_nack(cpr_vreg);
+ } else if (reg_val & CPR_INT_MAX) {
+ cpr_irq_clr_nack(cpr_vreg);
+ } else if (reg_val & CPR_INT_MID) {
+ /* RBCPR_CTL_SW_AUTO_CONT_ACK_EN is enabled */
+ cpr_debug("IRQ occured for Mid Flag\n");
+ } else {
+ pr_err("IRQ occured for unknown flag (0x%08x)\n", reg_val);
+ }
+
+ /* Save register values for the corner */
+ cpr_corner_save(cpr_vreg, cpr_vreg->corner);
+
+_exit:
+ mutex_unlock(&cpr_vreg->cpr_mutex);
+ return IRQ_HANDLED;
+}
+
static int cpr_regulator_is_enabled(struct regulator_dev *rdev)
{
struct cpr_regulator *cpr_vreg = rdev_get_drvdata(rdev);
- return cpr_vreg->enabled;
+ return cpr_vreg->vreg_enabled;
}
static int cpr_regulator_enable(struct regulator_dev *rdev)
@@ -77,10 +682,20 @@
}
rc = regulator_enable(cpr_vreg->vdd_apc);
- if (!rc)
- cpr_vreg->enabled = true;
- else
+ if (rc) {
pr_err("regulator_enable: vdd_apc: rc=%d\n", rc);
+ return rc;
+ }
+
+ cpr_vreg->vreg_enabled = true;
+
+ mutex_lock(&cpr_vreg->cpr_mutex);
+ if (cpr_is_allowed(cpr_vreg) && cpr_vreg->corner) {
+ cpr_irq_clr(cpr_vreg);
+ cpr_corner_switch(cpr_vreg, cpr_vreg->corner);
+ cpr_ctl_enable(cpr_vreg);
+ }
+ mutex_unlock(&cpr_vreg->cpr_mutex);
return rc;
}
@@ -95,10 +710,17 @@
if (cpr_vreg->vdd_mx)
rc = regulator_disable(cpr_vreg->vdd_mx);
- if (rc)
+ if (rc) {
pr_err("regulator_disable: vdd_mx: rc=%d\n", rc);
- else
- cpr_vreg->enabled = false;
+ return rc;
+ }
+
+ cpr_vreg->vreg_enabled = false;
+
+ mutex_lock(&cpr_vreg->cpr_mutex);
+ if (cpr_is_allowed(cpr_vreg))
+ cpr_ctl_disable(cpr_vreg);
+ mutex_unlock(&cpr_vreg->cpr_mutex);
} else {
pr_err("regulator_disable: vdd_apc: rc=%d\n", rc);
}
@@ -107,90 +729,44 @@
}
static int cpr_regulator_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV, unsigned *selector)
+ int corner, int corner_max, unsigned *selector)
{
struct cpr_regulator *cpr_vreg = rdev_get_drvdata(rdev);
int rc;
- int vdd_apc_min, vdd_apc_max, vdd_mx_vmin = 0;
- int change_dir = 0;
+ int new_volt;
+ enum voltage_change_dir change_dir = NO_CHANGE;
- if (cpr_vreg->vdd_mx) {
- if (min_uV > cpr_vreg->corner)
- change_dir = 1;
- else if (min_uV < cpr_vreg->corner)
- change_dir = -1;
- }
+ mutex_lock(&cpr_vreg->cpr_mutex);
- vdd_apc_min = cpr_vreg->corner_ceiling[min_uV];
- vdd_apc_max = cpr_vreg->corner_ceiling[CPR_CORNER_SUPER_TURBO];
-
- if (change_dir) {
- /* Determine the vdd_mx voltage */
- switch (cpr_vreg->vdd_mx_vmin_method) {
- case VDD_MX_VMIN_APC:
- vdd_mx_vmin = vdd_apc_min;
- break;
- case VDD_MX_VMIN_APC_CORNER_CEILING:
- vdd_mx_vmin = vdd_apc_min;
- break;
- case VDD_MX_VMIN_APC_SLOW_CORNER_CEILING:
- vdd_mx_vmin = cpr_vreg->pvs_corner_ceiling
- [APC_PVS_SLOW][min_uV];
- break;
- case VDD_MX_VMIN_MX_VMAX:
- default:
- vdd_mx_vmin = cpr_vreg->vdd_mx_vmax;
- break;
- }
- }
-
- if (change_dir > 0) {
- if (vdd_mx_vmin < cpr_vreg->vdd_mx_vmin) {
- /* Check and report the value in case */
- pr_err("Up: but new %d < old %d uV\n", vdd_mx_vmin,
- cpr_vreg->vdd_mx_vmin);
- }
-
- rc = regulator_set_voltage(cpr_vreg->vdd_mx, vdd_mx_vmin,
- cpr_vreg->vdd_mx_vmax);
- if (!rc) {
- cpr_vreg->vdd_mx_vmin = vdd_mx_vmin;
- } else {
- pr_err("set: vdd_mx [%d] = %d uV: rc=%d\n",
- min_uV, vdd_mx_vmin, rc);
- return rc;
- }
- }
-
- rc = regulator_set_voltage(cpr_vreg->vdd_apc,
- vdd_apc_min, vdd_apc_max);
- if (!rc) {
- cpr_vreg->corner = min_uV;
+ if (cpr_is_allowed(cpr_vreg)) {
+ cpr_ctl_disable(cpr_vreg);
+ new_volt = cpr_vreg->last_volt[corner];
} else {
- pr_err("set: vdd_apc [%d] = %d uV: rc=%d\n",
- min_uV, vdd_apc_min, rc);
- return rc;
+ new_volt = cpr_vreg->pvs_corner_v[cpr_vreg->process][corner];
}
- if (change_dir < 0) {
- if (vdd_mx_vmin > cpr_vreg->vdd_mx_vmin) {
- /* Check and report the value in case */
- pr_err("Down: but new %d >= old %d uV\n", vdd_mx_vmin,
- cpr_vreg->vdd_mx_vmin);
- }
+ cpr_debug("[corner:%d] = %d uV\n", corner, new_volt);
- rc = regulator_set_voltage(cpr_vreg->vdd_mx, vdd_mx_vmin,
- cpr_vreg->vdd_mx_vmax);
- if (!rc) {
- cpr_vreg->vdd_mx_vmin = vdd_mx_vmin;
- } else {
- pr_err("set: vdd_mx [%d] = %d uV: rc=%d\n",
- min_uV, vdd_mx_vmin, rc);
- return rc;
- }
+ if (corner > cpr_vreg->corner)
+ change_dir = UP;
+ else if (corner < cpr_vreg->corner)
+ change_dir = DOWN;
+
+ rc = cpr_scale_voltage(cpr_vreg, corner, new_volt, change_dir);
+ if (rc)
+ goto _exit;
+
+ if (cpr_is_allowed(cpr_vreg) && cpr_vreg->vreg_enabled) {
+ cpr_irq_clr(cpr_vreg);
+ cpr_corner_switch(cpr_vreg, corner);
+ cpr_ctl_enable(cpr_vreg);
}
- pr_debug("set [corner:%d] = %d uV: rc=%d\n", min_uV, vdd_apc_min, rc);
+ cpr_vreg->corner = corner;
+
+_exit:
+ mutex_unlock(&cpr_vreg->cpr_mutex);
+
return rc;
}
@@ -209,56 +785,217 @@
.get_voltage = cpr_regulator_get_voltage,
};
-static int __init cpr_regulator_pvs_init(struct cpr_regulator *cpr_vreg)
+#ifdef CONFIG_PM
+static int cpr_suspend(struct cpr_regulator *cpr_vreg)
+{
+ cpr_debug("suspend\n");
+
+ cpr_ctl_disable(cpr_vreg);
+ disable_irq(cpr_vreg->cpr_irq);
+
+ cpr_irq_clr(cpr_vreg);
+ cpr_regs_save(cpr_vreg);
+
+ return 0;
+}
+
+static int cpr_resume(struct cpr_regulator *cpr_vreg)
+
+{
+ cpr_debug("resume\n");
+
+ cpr_regs_restore(cpr_vreg);
+ cpr_irq_clr(cpr_vreg);
+
+ enable_irq(cpr_vreg->cpr_irq);
+ cpr_ctl_enable(cpr_vreg);
+
+ return 0;
+}
+
+static int cpr_regulator_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct cpr_regulator *cpr_vreg = platform_get_drvdata(pdev);
+
+ if (cpr_is_allowed(cpr_vreg))
+ return cpr_suspend(cpr_vreg);
+ else
+ return 0;
+}
+
+static int cpr_regulator_resume(struct platform_device *pdev)
+{
+ struct cpr_regulator *cpr_vreg = platform_get_drvdata(pdev);
+
+ if (cpr_is_allowed(cpr_vreg))
+ return cpr_resume(cpr_vreg);
+ else
+ return 0;
+}
+#else
+#define cpr_regulator_suspend NULL
+#define cpr_regulator_resume NULL
+#endif
+
+static int cpr_config(struct cpr_regulator *cpr_vreg)
+{
+ int i;
+ u32 val, gcnt, reg;
+ void __iomem *rbcpr_clk;
+
+ /* Use 19.2 MHz clock for CPR. */
+ rbcpr_clk = ioremap(cpr_vreg->rbcpr_clk_addr, 4);
+ if (!rbcpr_clk) {
+ pr_err("Unable to map rbcpr_clk\n");
+ return -EINVAL;
+ }
+ reg = readl_relaxed(rbcpr_clk);
+ reg &= ~RBCPR_CLK_SEL_MASK;
+ reg |= RBCPR_CLK_SEL_19P2_MHZ & RBCPR_CLK_SEL_MASK;
+ writel_relaxed(reg, rbcpr_clk);
+ iounmap(rbcpr_clk);
+
+ /* Disable interrupt and CPR */
+ cpr_write(cpr_vreg, REG_RBIF_IRQ_EN(cpr_vreg->irq_line), 0);
+ cpr_write(cpr_vreg, REG_RBCPR_CTL, 0);
+
+ /* Program the default HW Ceiling, Floor and vlevel */
+ val = ((RBIF_LIMIT_CEILING_DEFAULT & RBIF_LIMIT_CEILING_MASK)
+ << RBIF_LIMIT_CEILING_SHIFT)
+ | (RBIF_LIMIT_FLOOR_DEFAULT & RBIF_LIMIT_FLOOR_MASK);
+ cpr_write(cpr_vreg, REG_RBIF_LIMIT, val);
+ cpr_write(cpr_vreg, REG_RBIF_SW_VLEVEL, RBIF_SW_VLEVEL_DEFAULT);
+
+ /* Clear the target quotient value and gate count of all ROs */
+ for (i = 0; i < CPR_NUM_RING_OSC; i++)
+ cpr_write(cpr_vreg, REG_RBCPR_GCNT_TARGET(i), 0);
+
+ /* Init and save gcnt */
+ gcnt = (cpr_vreg->ref_clk_khz * cpr_vreg->gcnt_time_us) / 1000;
+ gcnt = (gcnt & RBCPR_GCNT_TARGET_GCNT_MASK) <<
+ RBCPR_GCNT_TARGET_GCNT_SHIFT;
+ cpr_vreg->gcnt = gcnt;
+
+ /* Program the step quotient and idle clocks */
+ val = ((cpr_vreg->idle_clocks & RBCPR_STEP_QUOT_IDLE_CLK_MASK)
+ << RBCPR_STEP_QUOT_IDLE_CLK_SHIFT) |
+ (cpr_vreg->step_quotient & RBCPR_STEP_QUOT_STEPQUOT_MASK);
+ cpr_write(cpr_vreg, REG_RBCPR_STEP_QUOT, val);
+
+ /* Program the delay count for the timer */
+ val = (cpr_vreg->ref_clk_khz * cpr_vreg->timer_delay_us) / 1000;
+ cpr_write(cpr_vreg, REG_RBCPR_TIMER_INTERVAL, val);
+ pr_info("Timer count: 0x%0x (for %d us)\n", val,
+ cpr_vreg->timer_delay_us);
+
+ /* Program Consecutive Up & Down */
+ val = ((cpr_vreg->timer_cons_down & RBIF_TIMER_ADJ_CONS_DOWN_MASK)
+ << RBIF_TIMER_ADJ_CONS_DOWN_SHIFT) |
+ (cpr_vreg->timer_cons_up & RBIF_TIMER_ADJ_CONS_UP_MASK);
+ cpr_write(cpr_vreg, REG_RBIF_TIMER_ADJUST, val);
+
+ /* Program the control register */
+ cpr_vreg->up_threshold &= RBCPR_CTL_UP_THRESHOLD_MASK;
+ cpr_vreg->down_threshold &= RBCPR_CTL_DN_THRESHOLD_MASK;
+ val = (cpr_vreg->up_threshold << RBCPR_CTL_UP_THRESHOLD_SHIFT)
+ | (cpr_vreg->down_threshold << RBCPR_CTL_DN_THRESHOLD_SHIFT);
+ val |= RBCPR_CTL_TIMER_EN | RBCPR_CTL_COUNT_MODE;
+ val |= RBCPR_CTL_SW_AUTO_CONT_ACK_EN;
+ cpr_write(cpr_vreg, REG_RBCPR_CTL, val);
+
+ /* Registers to save & restore for suspend */
+ cpr_vreg->save_regs[0] = REG_RBCPR_TIMER_INTERVAL;
+ cpr_vreg->save_regs[1] = REG_RBCPR_STEP_QUOT;
+ cpr_vreg->save_regs[2] = REG_RBIF_TIMER_ADJUST;
+ cpr_vreg->save_regs[3] = REG_RBIF_LIMIT;
+ cpr_vreg->save_regs[4] = REG_RBIF_SW_VLEVEL;
+ cpr_vreg->save_regs[5] = REG_RBIF_IRQ_EN(cpr_vreg->irq_line);
+ cpr_vreg->save_regs[6] = REG_RBCPR_CTL;
+ cpr_vreg->save_regs[7] = REG_RBCPR_GCNT_TARGET
+ (cpr_vreg->cpr_fuse_ro_sel[CPR_CORNER_SVS]);
+ cpr_vreg->save_regs[8] = REG_RBCPR_GCNT_TARGET
+ (cpr_vreg->cpr_fuse_ro_sel[CPR_CORNER_NORMAL]);
+ cpr_vreg->save_regs[9] = REG_RBCPR_GCNT_TARGET
+ (cpr_vreg->cpr_fuse_ro_sel[CPR_CORNER_TURBO]);
+
+ cpr_irq_set(cpr_vreg, CPR_INT_DEFAULT);
+
+ cpr_corner_save(cpr_vreg, CPR_CORNER_SVS);
+ cpr_corner_save(cpr_vreg, CPR_CORNER_NORMAL);
+ cpr_corner_save(cpr_vreg, CPR_CORNER_TURBO);
+
+ return 0;
+}
+
+static int __init cpr_pvs_init(struct cpr_regulator *cpr_vreg)
{
void __iomem *efuse_base;
- u32 efuse_bits;
- int i, bit_pos;
- u32 vmax;
+ u32 efuse_bits, redundant, shift, mask;
+ int i, process;
- efuse_base = ioremap(cpr_vreg->efuse_phys, 4);
+ efuse_base = ioremap(cpr_vreg->pvs_efuse, 4);
if (!efuse_base) {
- pr_err("Unable to map efuse_phys 0x%x\n",
- cpr_vreg->efuse_phys);
+ pr_err("Unable to map pvs_efuse 0x%08x\n",
+ cpr_vreg->pvs_efuse);
return -EINVAL;
}
efuse_bits = readl_relaxed(efuse_base);
/* Construct PVS process # from the efuse bits */
- for (i = 0; i < cpr_vreg->num_efuse_bits; i++) {
- bit_pos = cpr_vreg->efuse_bit_pos[i];
- cpr_vreg->pvs_bin |= (efuse_bits & BIT(bit_pos)) ? BIT(i) : 0;
- }
+ redundant = (efuse_bits >> PVS_FUSE_REDUNDANT_SHIFT)
+ & PVS_FUSE_REDUNDANT_MASK;
+ if (redundant == 2)
+ shift = PVS_FUSE_BINS_REDUNDANT_SHIFT;
+ else
+ shift = PVS_FUSE_BINS_SHIFT;
+ mask = (1 << cpr_vreg->num_efuse_bits) - 1;
+ cpr_vreg->pvs_bin = (efuse_bits >> shift) & mask;
- cpr_vreg->pvs_process = cpr_vreg->pvs_bin_process[cpr_vreg->pvs_bin];
- if (cpr_vreg->pvs_process >= NUM_APC_PVS)
- cpr_vreg->pvs_process = APC_PVS_NO;
-
- /* Use ceiling voltage of Turbo@Slow for all corners of APC_PVS_NO
- but use SuperTurbo@Slow for its SuperTurbo */
- vmax = cpr_vreg->pvs_corner_ceiling[APC_PVS_SLOW][CPR_CORNER_TURBO];
- for (i = CPR_CORNER_SVS; i <= CPR_CORNER_TURBO; i++)
- cpr_vreg->pvs_corner_ceiling[APC_PVS_NO][i] = vmax;
- cpr_vreg->pvs_corner_ceiling[APC_PVS_NO][CPR_CORNER_SUPER_TURBO]
- = cpr_vreg->pvs_corner_ceiling[APC_PVS_SLOW]
- [CPR_CORNER_SUPER_TURBO];
-
- cpr_vreg->corner_ceiling =
- cpr_vreg->pvs_corner_ceiling[cpr_vreg->pvs_process];
+ /* Set ceiling max and use it for APC_PVS_NO */
+ cpr_vreg->ceiling_max =
+ cpr_vreg->pvs_corner_v[APC_PVS_SLOW][CPR_CORNER_TURBO];
iounmap(efuse_base);
- pr_info("PVS Info: efuse_phys=0x%08X, n_bits=%d\n",
- cpr_vreg->efuse_phys, cpr_vreg->num_efuse_bits);
- pr_info("PVS Info: efuse=0x%08X, bin=%d, process=%d\n",
- efuse_bits, cpr_vreg->pvs_bin, cpr_vreg->pvs_process);
+ process = cpr_vreg->pvs_bin_process[cpr_vreg->pvs_bin];
+ pr_info("[0x%08X] = 0x%08X, n_bits=%d, bin=%d (%d) [redundant=%d]\n",
+ cpr_vreg->pvs_efuse, efuse_bits, cpr_vreg->num_efuse_bits,
+ cpr_vreg->pvs_bin, process, redundant);
+ for (i = APC_PVS_SLOW; i < NUM_APC_PVS; i++) {
+ pr_info("[%d] [%d %d %d] uV\n", i,
+ cpr_vreg->pvs_corner_v[i][CPR_CORNER_SVS],
+ cpr_vreg->pvs_corner_v[i][CPR_CORNER_NORMAL],
+ cpr_vreg->pvs_corner_v[i][CPR_CORNER_TURBO]);
+ }
+
+ if (process == APC_PVS_NO || process >= NUM_APC_PVS) {
+ pr_err("Bin=%d (%d) is out of spec. Assume SLOW.\n",
+ cpr_vreg->pvs_bin, process);
+ process = APC_PVS_SLOW;
+ }
+
+ cpr_vreg->process = process;
return 0;
}
-static int __init cpr_regulator_apc_init(struct platform_device *pdev,
- struct cpr_regulator *cpr_vreg)
+#define CPR_PROP_READ_U32(of_node, cpr_property, cpr_config, rc) \
+do { \
+ if (!rc) { \
+ rc = of_property_read_u32(of_node, \
+ "qcom," cpr_property, \
+ cpr_config); \
+ if (rc) { \
+ pr_err("Missing " #cpr_property \
+ ": rc = %d\n", rc); \
+ } \
+ } \
+} while (0)
+
+static int __init cpr_apc_init(struct platform_device *pdev,
+ struct cpr_regulator *cpr_vreg)
{
struct device_node *of_node = pdev->dev.of_node;
int rc;
@@ -308,9 +1045,9 @@
return 0;
}
-static void cpr_regulator_apc_exit(struct cpr_regulator *cpr_vreg)
+static void cpr_apc_exit(struct cpr_regulator *cpr_vreg)
{
- if (cpr_vreg->enabled) {
+ if (cpr_vreg->vreg_enabled) {
regulator_disable(cpr_vreg->vdd_apc);
if (cpr_vreg->vdd_mx)
@@ -318,8 +1055,240 @@
}
}
-static int __init cpr_regulator_parse_dt(struct platform_device *pdev,
- struct cpr_regulator *cpr_vreg)
+static int __init cpr_init_cpr_efuse(struct cpr_regulator *cpr_vreg)
+{
+ void __iomem *efuse_base;
+ u32 ro_sel, val;
+ u64 fuse_bits;
+ int ro_sel_shift[CPR_CORNER_MAX];
+
+ efuse_base = ioremap(cpr_vreg->cpr_fuse_addr, 16);
+ if (!efuse_base) {
+ pr_err("Unable to map cpr_fuse_addr 0x%08x\n",
+ cpr_vreg->cpr_fuse_addr);
+ return -EINVAL;
+ }
+
+ cpr_vreg->cpr_fuse_bits = readll_relaxed(efuse_base);
+ cpr_vreg->cpr_fuse_bits_2 = readll_relaxed(efuse_base + 8);
+
+ iounmap(efuse_base);
+
+ /* Read the control bits of eFuse */
+ cpr_vreg->cpr_fuse_disable = (cpr_vreg->cpr_fuse_bits >>
+ CPR_FUSE_DISABLE_CPR_SHIFT) & 0x01;
+ cpr_vreg->cpr_fuse_local = (cpr_vreg->cpr_fuse_bits >>
+ CPR_FUSE_LOCAL_APPROACH_SHIFT) & 0x01;
+ cpr_vreg->cpr_fuse_redundancy = (cpr_vreg->cpr_fuse_bits >>
+ CPR_FUSE_REDUNDANT_SHIFT) & 0x01;
+
+ pr_info("[0x%08X] = 0x%llx\n", cpr_vreg->cpr_fuse_addr,
+ cpr_vreg->cpr_fuse_bits);
+ pr_info("disable = %d, local = %d, redundancy = %d\n",
+ cpr_vreg->cpr_fuse_disable,
+ cpr_vreg->cpr_fuse_local,
+ cpr_vreg->cpr_fuse_redundancy);
+ pr_info("[0x%08X] = 0x%llx\n", cpr_vreg->cpr_fuse_addr + 8,
+ cpr_vreg->cpr_fuse_bits_2);
+
+ if (cpr_vreg->cpr_fuse_redundancy == 0) {
+ fuse_bits = cpr_vreg->cpr_fuse_bits;
+ ro_sel_shift[CPR_CORNER_SVS] = 54;
+ ro_sel_shift[CPR_CORNER_NORMAL] = 38;
+ ro_sel_shift[CPR_CORNER_TURBO] = 41;
+ } else {
+ fuse_bits = cpr_vreg->cpr_fuse_bits_2;
+ ro_sel_shift[CPR_CORNER_SVS] = 46;
+ ro_sel_shift[CPR_CORNER_NORMAL] = 36;
+ ro_sel_shift[CPR_CORNER_TURBO] = 39;
+ }
+
+ /* SVS */
+ ro_sel = (fuse_bits >> ro_sel_shift[CPR_CORNER_SVS])
+ & CPR_FUSE_RO_SEL_BITS_MASK;
+ val = (fuse_bits >> CPR_FUSE_TARGET_QUOT_SVS_SHIFT)
+ & CPR_FUSE_TARGET_QUOT_BITS_MASK;
+ cpr_vreg->cpr_fuse_target_quot[CPR_CORNER_SVS] = val;
+ cpr_vreg->cpr_fuse_ro_sel[CPR_CORNER_SVS] = ro_sel;
+ pr_info("SVS: ro_sel = %d, target quot = 0x%04x\n", ro_sel, val);
+
+ /* Nominal */
+ ro_sel = (fuse_bits >> ro_sel_shift[CPR_CORNER_NORMAL])
+ & CPR_FUSE_RO_SEL_BITS_MASK;
+ val = (fuse_bits >> CPR_FUSE_TARGET_QUOT_NOMINAL_SHIFT)
+ & CPR_FUSE_TARGET_QUOT_BITS_MASK;
+ cpr_vreg->cpr_fuse_target_quot[CPR_CORNER_NORMAL] = val;
+ cpr_vreg->cpr_fuse_ro_sel[CPR_CORNER_NORMAL] = ro_sel;
+ pr_info("Nominal: ro_sel = %d, target quot = 0x%04x\n", ro_sel, val);
+
+ /* Turbo */
+ ro_sel = (fuse_bits >> ro_sel_shift[CPR_CORNER_TURBO])
+ & CPR_FUSE_RO_SEL_BITS_MASK;
+ val = (fuse_bits >> CPR_FUSE_TARGET_QUOT_TURBO_SHIFT)
+ & CPR_FUSE_TARGET_QUOT_BITS_MASK;
+ cpr_vreg->cpr_fuse_target_quot[CPR_CORNER_TURBO] = val;
+ cpr_vreg->cpr_fuse_ro_sel[CPR_CORNER_TURBO] = ro_sel;
+ pr_info("Turbo: ro_sel = %d, target quot = 0x%04x\n", ro_sel, val);
+
+ if (!cpr_vreg->cpr_fuse_bits) {
+ cpr_vreg->cpr_fuse_disable = 1;
+ pr_err("cpr_fuse_bits = 0: set cpr_fuse_disable = 1\n");
+ }
+
+ return 0;
+}
+
+static int __init cpr_init_cpr_voltages(struct cpr_regulator *cpr_vreg)
+{
+ int i;
+
+ /* Construct CPR voltage limits */
+ for (i = CPR_CORNER_SVS; i < CPR_CORNER_MAX; i++) {
+ cpr_vreg->floor_volt[i] =
+ cpr_vreg->pvs_corner_v[APC_PVS_FAST][i];
+ cpr_vreg->ceiling_volt[i] =
+ cpr_vreg->pvs_corner_v[APC_PVS_SLOW][i];
+ cpr_vreg->last_volt[i] =
+ cpr_vreg->pvs_corner_v[cpr_vreg->process][i];
+ }
+
+ return 0;
+}
+
+static int __init cpr_init_cpr_parameters(struct platform_device *pdev,
+ struct cpr_regulator *cpr_vreg)
+{
+ struct device_node *of_node = pdev->dev.of_node;
+ int rc = 0;
+
+ CPR_PROP_READ_U32(of_node, "cpr-ref-clk",
+ &cpr_vreg->ref_clk_khz, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-timer-delay",
+ &cpr_vreg->timer_delay_us, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-timer-cons-up",
+ &cpr_vreg->timer_cons_up, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-timer-cons-down",
+ &cpr_vreg->timer_cons_down, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-irq-line",
+ &cpr_vreg->irq_line, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-step-quotient",
+ &cpr_vreg->step_quotient, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-up-threshold",
+ &cpr_vreg->up_threshold, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-down-threshold",
+ &cpr_vreg->down_threshold, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-idle-clocks",
+ &cpr_vreg->idle_clocks, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-gcnt-time",
+ &cpr_vreg->gcnt_time_us, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "vdd-apc-step-up-limit",
+ &cpr_vreg->vdd_apc_step_up_limit, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "vdd-apc-step-down-limit",
+ &cpr_vreg->vdd_apc_step_down_limit, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-apc-volt-step",
+ &cpr_vreg->step_volt, rc);
+ if (rc)
+ return rc;
+
+ /* Init module parameter with the DT value */
+ cpr_vreg->enable = of_property_read_bool(of_node, "qcom,cpr-enable");
+ cpr_enable = (int) cpr_vreg->enable;
+ pr_info("CPR is %s by default.\n",
+ cpr_vreg->enable ? "enabled" : "disabled");
+
+ return rc;
+}
+
+static int __init cpr_init_cpr(struct platform_device *pdev,
+ struct cpr_regulator *cpr_vreg)
+{
+ struct resource *res;
+ int rc = 0;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "cpr_efuse");
+ if (!res || !res->start) {
+ pr_err("cpr_efuse missing: res=%p\n", res);
+ return -EINVAL;
+ }
+ cpr_vreg->cpr_fuse_addr = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rbcpr_clk");
+ if (!res || !res->start) {
+ pr_err("missing rbcpr_clk address: res=%p\n", res);
+ return -EINVAL;
+ }
+ cpr_vreg->rbcpr_clk_addr = res->start;
+
+ rc = cpr_init_cpr_efuse(cpr_vreg);
+ if (rc)
+ return rc;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rbcpr");
+ if (!res || !res->start) {
+ pr_err("missing rbcpr address: res=%p\n", res);
+ return -EINVAL;
+ }
+ cpr_vreg->rbcpr_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+
+ /* Init all voltage set points of APC regulator for CPR */
+ cpr_init_cpr_voltages(cpr_vreg);
+
+ /* Init CPR configuration parameters */
+ rc = cpr_init_cpr_parameters(pdev, cpr_vreg);
+ if (rc)
+ return rc;
+
+ /* Get and Init interrupt */
+ cpr_vreg->cpr_irq = platform_get_irq(pdev, 0);
+ if (!cpr_vreg->cpr_irq) {
+ pr_err("missing CPR IRQ\n");
+ return -EINVAL;
+ }
+
+ /* Configure CPR HW but keep it disabled */
+ rc = cpr_config(cpr_vreg);
+ if (rc)
+ return rc;
+
+ rc = request_threaded_irq(cpr_vreg->cpr_irq, NULL, cpr_irq_handler,
+ IRQF_TRIGGER_RISING, "cpr", cpr_vreg);
+ if (rc) {
+ pr_err("CPR: request irq failed for IRQ %d\n",
+ cpr_vreg->cpr_irq);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int __init cpr_pvs_parse_dt(struct platform_device *pdev,
+ struct cpr_regulator *cpr_vreg)
{
struct device_node *of_node = pdev->dev.of_node;
struct resource *res;
@@ -327,13 +1296,12 @@
size_t pvs_bins;
/* Parse process voltage parameters */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "efuse_phys");
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pvs_efuse");
if (!res || !res->start) {
- pr_err("efuse_phys missing: res=%p\n", res);
+ pr_err("pvs_efuse missing: res=%p\n", res);
return -EINVAL;
}
- cpr_vreg->efuse_phys = res->start;
+ cpr_vreg->pvs_efuse = res->start;
rc = of_property_read_u32(of_node, "qcom,num-efuse-bits",
&cpr_vreg->num_efuse_bits);
@@ -349,14 +1317,6 @@
return -EINVAL;
}
- rc = of_property_read_u32_array(of_node, "qcom,efuse-bit-pos",
- cpr_vreg->efuse_bit_pos,
- cpr_vreg->num_efuse_bits);
- if (rc < 0) {
- pr_err("efuse-bit-pos missing: rc=%d\n", rc);
- return rc;
- }
-
pvs_bins = 1 << cpr_vreg->num_efuse_bits;
rc = of_property_read_u32_array(of_node, "qcom,pvs-bin-process",
cpr_vreg->pvs_bin_process,
@@ -368,7 +1328,7 @@
rc = of_property_read_u32_array(of_node,
"qcom,pvs-corner-ceiling-slow",
- &cpr_vreg->pvs_corner_ceiling[APC_PVS_SLOW][CPR_CORNER_SVS],
+ &cpr_vreg->pvs_corner_v[APC_PVS_SLOW][CPR_CORNER_SVS],
CPR_CORNER_MAX - CPR_CORNER_SVS);
if (rc < 0) {
pr_err("pvs-corner-ceiling-slow missing: rc=%d\n", rc);
@@ -377,7 +1337,7 @@
rc = of_property_read_u32_array(of_node,
"qcom,pvs-corner-ceiling-nom",
- &cpr_vreg->pvs_corner_ceiling[APC_PVS_NOM][CPR_CORNER_SVS],
+ &cpr_vreg->pvs_corner_v[APC_PVS_NOM][CPR_CORNER_SVS],
CPR_CORNER_MAX - CPR_CORNER_SVS);
if (rc < 0) {
pr_err("pvs-corner-ceiling-norm missing: rc=%d\n", rc);
@@ -386,7 +1346,7 @@
rc = of_property_read_u32_array(of_node,
"qcom,pvs-corner-ceiling-fast",
- &cpr_vreg->pvs_corner_ceiling[APC_PVS_FAST][CPR_CORNER_SVS],
+ &cpr_vreg->pvs_corner_v[APC_PVS_FAST][CPR_CORNER_SVS],
CPR_CORNER_MAX - CPR_CORNER_SVS);
if (rc < 0) {
pr_err("pvs-corner-ceiling-fast missing: rc=%d\n", rc);
@@ -426,25 +1386,33 @@
return -ENOMEM;
}
- rc = cpr_regulator_parse_dt(pdev, cpr_vreg);
+ rc = cpr_pvs_parse_dt(pdev, cpr_vreg);
if (rc) {
pr_err("Wrong DT parameter specified: rc=%d\n", rc);
return rc;
}
- rc = cpr_regulator_pvs_init(cpr_vreg);
+ rc = cpr_pvs_init(cpr_vreg);
if (rc) {
pr_err("Initialize PVS wrong: rc=%d\n", rc);
return rc;
}
- rc = cpr_regulator_apc_init(pdev, cpr_vreg);
+ rc = cpr_apc_init(pdev, cpr_vreg);
if (rc) {
if (rc != -EPROBE_DEFER)
pr_err("Initialize APC wrong: rc=%d\n", rc);
return rc;
}
+ rc = cpr_init_cpr(pdev, cpr_vreg);
+ if (rc) {
+ pr_err("Initialize CPR failed: rc=%d\n", rc);
+ return rc;
+ }
+
+ mutex_init(&cpr_vreg->cpr_mutex);
+
rdesc = &cpr_vreg->rdesc;
rdesc->owner = THIS_MODULE;
rdesc->type = REGULATOR_VOLTAGE;
@@ -457,17 +1425,12 @@
rc = PTR_ERR(cpr_vreg->rdev);
pr_err("regulator_register failed: rc=%d\n", rc);
- cpr_regulator_apc_exit(cpr_vreg);
+ cpr_apc_exit(cpr_vreg);
return rc;
}
platform_set_drvdata(pdev, cpr_vreg);
-
- pr_info("PVS [%d %d %d %d] uV\n",
- cpr_vreg->corner_ceiling[CPR_CORNER_SVS],
- cpr_vreg->corner_ceiling[CPR_CORNER_NORMAL],
- cpr_vreg->corner_ceiling[CPR_CORNER_TURBO],
- cpr_vreg->corner_ceiling[CPR_CORNER_SUPER_TURBO]);
+ the_cpr = cpr_vreg;
return 0;
}
@@ -478,7 +1441,13 @@
cpr_vreg = platform_get_drvdata(pdev);
if (cpr_vreg) {
- cpr_regulator_apc_exit(cpr_vreg);
+ /* Disable CPR */
+ if (cpr_is_allowed(cpr_vreg)) {
+ cpr_ctl_disable(cpr_vreg);
+ cpr_irq_set(cpr_vreg, 0);
+ }
+
+ cpr_apc_exit(cpr_vreg);
regulator_unregister(cpr_vreg->rdev);
}
@@ -498,6 +1467,8 @@
},
.probe = cpr_regulator_probe,
.remove = __devexit_p(cpr_regulator_remove),
+ .suspend = cpr_regulator_suspend,
+ .resume = cpr_regulator_resume,
};
/**
diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c
index d02ab66..2e70c83 100644
--- a/arch/arm/mach-msm/cpufreq.c
+++ b/arch/arm/mach-msm/cpufreq.c
@@ -3,7 +3,7 @@
* MSM architecture cpufreq driver
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2013, The Linux Foundation. All rights reserved.
* Author: Mike A. Chan <mikechan@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -261,6 +261,7 @@
{
int cur_freq;
int index;
+ int ret = 0;
struct cpufreq_frequency_table *table;
struct cpufreq_work_struct *cpu_work = NULL;
@@ -296,19 +297,16 @@
policy->cpu, cur_freq);
return -EINVAL;
}
-
- if (cur_freq != table[index].frequency) {
- int ret = 0;
- ret = acpuclk_set_rate(policy->cpu, table[index].frequency,
- SETRATE_CPUFREQ);
- if (ret)
- return ret;
- pr_info("cpufreq: cpu%d init at %d switching to %d\n",
- policy->cpu, cur_freq, table[index].frequency);
- cur_freq = table[index].frequency;
- }
-
- policy->cur = cur_freq;
+ /*
+ * Call set_cpu_freq unconditionally so that when cpu is set to
+ * online, frequency limit will always be updated.
+ */
+ ret = set_cpu_freq(policy, table[index].frequency);
+ if (ret)
+ return ret;
+ pr_debug("cpufreq: cpu%d init at %d switching to %d\n",
+ policy->cpu, cur_freq, table[index].frequency);
+ policy->cur = table[index].frequency;
policy->cpuinfo.transition_latency =
acpuclk_get_switch_time() * NSEC_PER_USEC;
@@ -404,10 +402,11 @@
per_cpu(cpufreq_suspend, cpu).device_suspended = 0;
}
- msm_cpufreq_wq = create_workqueue("msm-cpufreq");
+ msm_cpufreq_wq = alloc_workqueue("msm-cpufreq",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI, 1);
register_hotcpu_notifier(&msm_cpufreq_cpu_notifier);
return cpufreq_register_driver(&msm_cpufreq_driver);
}
-late_initcall(msm_cpufreq_register);
+device_initcall(msm_cpufreq_register);
diff --git a/arch/arm/mach-msm/cpuidle.c b/arch/arm/mach-msm/cpuidle.c
index e87c7b5..7c06268 100644
--- a/arch/arm/mach-msm/cpuidle.c
+++ b/arch/arm/mach-msm/cpuidle.c
@@ -14,7 +14,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cpuidle.h>
-#include <linux/cpu_pm.h>
#include <mach/cpuidle.h>
@@ -75,8 +74,6 @@
int i;
enum msm_pm_sleep_mode pm_mode;
- cpu_pm_enter();
-
pm_mode = msm_pm_idle_enter(dev, drv, index);
for (i = 0; i < dev->state_count; i++) {
@@ -90,7 +87,6 @@
}
}
- cpu_pm_exit();
local_irq_enable();
return ret;
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index 2f44566..4daccb1 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -904,6 +904,7 @@
.gpios = tspp_gpios,
.tsif_pclk = "iface_clk",
.tsif_ref_clk = "ref_clk",
+ .tsif_vreg_present = 0,
};
struct platform_device msm_8064_device_tspp = {
@@ -2229,11 +2230,12 @@
static struct fs_driver_data gfx3d_fs_data = {
.clks = (struct fs_clk_data[]){
- { .name = "core_clk", .reset_rate = 27000000 },
+ { .name = "core_clk", .reset_rate = 1800000 },
{ .name = "iface_clk" },
{ .name = "bus_clk" },
{ 0 }
},
+ .reset_delay_us = 10,
.bus_port0 = MSM_BUS_MASTER_GRAPHICS_3D,
.bus_port1 = MSM_BUS_MASTER_GRAPHICS_3D_PORT1,
};
@@ -2322,7 +2324,7 @@
FS_8X60(FS_IJPEG, "vdd", "msm_gemini.0", &ijpeg_fs_data),
FS_8X60(FS_VFE, "vdd", "msm_vfe.0", &vfe_fs_data),
FS_8X60(FS_VPE, "vdd", "msm_vpe.0", &vpe_fs_data),
- FS_8X60(FS_GFX3D, "vdd", "kgsl-3d0.0", &gfx3d_fs_data),
+ FS_8X60(FS_GFX3D_8064, "vdd", "kgsl-3d0.0", &gfx3d_fs_data),
FS_8X60(FS_VED, "vdd", "msm_vidc.0", &ved_fs_data),
FS_8X60(FS_VCAP, "vdd", "msm_vcap.0", &vcap_fs_data),
};
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index 837aef3..71f58a6 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -2510,6 +2510,11 @@
.id = -1,
};
+struct platform_device msm_fm_loopback = {
+ .name = "msm-pcm-loopback",
+ .id = -1,
+};
+
static struct fs_driver_data gfx2d0_fs_data = {
.clks = (struct fs_clk_data[]){
{ .name = "core_clk" },
@@ -3507,7 +3512,6 @@
.num_levels = ARRAY_SIZE(grp3d_freq) + 1,
.set_grp_async = NULL,
.idle_timeout = HZ/12,
- .nap_allowed = true,
.clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
.bus_scale_table = &grp3d_bus_scale_pdata,
@@ -3574,7 +3578,6 @@
.num_levels = ARRAY_SIZE(grp2d_freq) + 1,
.set_grp_async = NULL,
.idle_timeout = HZ/5,
- .nap_allowed = true,
.clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
.bus_scale_table = &grp2d0_bus_scale_pdata,
@@ -3641,7 +3644,6 @@
.num_levels = ARRAY_SIZE(grp2d_freq) + 1,
.set_grp_async = NULL,
.idle_timeout = HZ/5,
- .nap_allowed = true,
.clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
.bus_scale_table = &grp2d1_bus_scale_pdata,
diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c
index bf89321..483d8b3 100644
--- a/arch/arm/mach-msm/devices-9615.c
+++ b/arch/arm/mach-msm/devices-9615.c
@@ -1270,6 +1270,7 @@
LPASS_SCSS_GP_HIGH_IRQ,
SPS_MTI_31,
A2_BAM_IRQ,
+ USB1_HS_BAM_IRQ,
};
struct msm_mpm_device_data msm9615_mpm_dev_data __initdata = {
diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c
index 907af68..446d6b6 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -942,7 +942,6 @@
.set_grp_async = set_grp_xbar_async,
.idle_timeout = HZ,
.strtstp_sleepwake = true,
- .nap_allowed = false,
.clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM,
};
diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c
index 397a9d4..cdbf2ea 100644
--- a/arch/arm/mach-msm/devices-msm7x30.c
+++ b/arch/arm/mach-msm/devices-msm7x30.c
@@ -1294,7 +1294,6 @@
.num_levels = 3,
.set_grp_async = set_grp3d_async,
.idle_timeout = HZ/20,
- .nap_allowed = true,
.idle_needed = true,
.clk_map = KGSL_CLK_SRC | KGSL_CLK_CORE |
KGSL_CLK_IFACE | KGSL_CLK_MEM,
@@ -1337,7 +1336,6 @@
/* HW workaround, run Z180 SYNC @ 192 MHZ */
.set_grp_async = NULL,
.idle_timeout = HZ/10,
- .nap_allowed = true,
.idle_needed = true,
.clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE,
};
diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c
index 91a7394..05858a5 100644
--- a/arch/arm/mach-msm/devices-msm8x60.c
+++ b/arch/arm/mach-msm/devices-msm8x60.c
@@ -802,7 +802,6 @@
.num_levels = 5,
.set_grp_async = NULL,
.idle_timeout = HZ/5,
- .nap_allowed = true,
.clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
.bus_scale_table = &grp3d_bus_scale_pdata,
@@ -849,7 +848,6 @@
.num_levels = 2,
.set_grp_async = NULL,
.idle_timeout = HZ/10,
- .nap_allowed = true,
.clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
.bus_scale_table = &grp2d0_bus_scale_pdata,
@@ -896,7 +894,6 @@
.num_levels = 2,
.set_grp_async = NULL,
.idle_timeout = HZ/10,
- .nap_allowed = true,
.clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
.bus_scale_table = &grp2d1_bus_scale_pdata,
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 327c11d..eb12383 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -260,6 +260,7 @@
extern struct platform_device msm_i2s_cpudai4;
extern struct platform_device msm_i2s_cpudai5;
extern struct platform_device msm_cpudai_stub;
+extern struct platform_device msm_fm_loopback;
extern struct platform_device msm_pil_q6v3;
extern struct platform_device msm_pil_modem;
diff --git a/arch/arm/mach-msm/footswitch-8x60.c b/arch/arm/mach-msm/footswitch-8x60.c
index 76ad9b8..67bbd5e 100644
--- a/arch/arm/mach-msm/footswitch-8x60.c
+++ b/arch/arm/mach-msm/footswitch-8x60.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013, 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
@@ -25,7 +25,10 @@
#include <mach/msm_bus.h>
#include <mach/scm-io.h>
#include <mach/clk.h>
+#include <mach/rpm.h>
+
#include "footswitch.h"
+#include "rpm_resources.h"
#ifdef CONFIG_MSM_SECURE_IO
#undef readl_relaxed
@@ -52,7 +55,7 @@
#define GFS_DELAY_CNT 31
-#define RESET_DELAY_US 1
+#define DEFAULT_RESET_DELAY_US 1
/* Clock rate to use if one has not previously been set. */
#define DEFAULT_RATE 27000000
#define MAX_CLKS 10
@@ -72,6 +75,7 @@
bool is_claimed;
struct fs_clk_data *clk_data;
struct clk *core_clk;
+ unsigned long reset_delay_us;
};
static int setup_clocks(struct footswitch *fs)
@@ -181,7 +185,7 @@
for (clock--; clock >= fs->clk_data; clock--)
clk_reset(clock->clk, CLK_RESET_ASSERT);
/* Wait for synchronous resets to propagate. */
- udelay(RESET_DELAY_US);
+ udelay(fs->reset_delay_us);
/* Enable the power rail at the footswitch. */
regval |= ENABLE_BIT;
@@ -200,9 +204,9 @@
/* Toggle core reset again after first power-on (required for GFX3D). */
if (fs->desc.id == FS_GFX3D) {
clk_reset(fs->core_clk, CLK_RESET_ASSERT);
- udelay(RESET_DELAY_US);
+ udelay(fs->reset_delay_us);
clk_reset(fs->core_clk, CLK_RESET_DEASSERT);
- udelay(RESET_DELAY_US);
+ udelay(fs->reset_delay_us);
}
/* Prevent core memory from collapsing when its clock is gated. */
@@ -238,7 +242,8 @@
return rc;
/* Allow core memory to collapse when its clock is gated. */
- clk_set_flags(fs->core_clk, CLKFLAG_NORETAIN_MEM);
+ if (fs->desc.id != FS_GFX3D_8064)
+ clk_set_flags(fs->core_clk, CLKFLAG_NORETAIN_MEM);
/* Halt all bus ports in the power domain. */
if (fs->bus_port0) {
@@ -265,7 +270,7 @@
for (clock--; clock >= fs->clk_data; clock--)
clk_reset(clock->clk, CLK_RESET_ASSERT);
/* Wait for synchronous resets to propagate. */
- udelay(RESET_DELAY_US);
+ udelay(fs->reset_delay_us);
/*
* Return clocks to their state before this function. For robustness
@@ -339,7 +344,7 @@
for (clock--; clock >= fs->clk_data; clock--)
clk_reset(clock->clk, CLK_RESET_ASSERT);
/* Wait for synchronous resets to propagate. */
- udelay(RESET_DELAY_US);
+ udelay(fs->reset_delay_us);
/* Enable the power rail at the footswitch. */
regval |= ENABLE_BIT;
@@ -354,7 +359,7 @@
/* Deassert resets for all clocks in the power domain. */
for (clock = fs->clk_data; clock->clk; clock++)
clk_reset(clock->clk, CLK_RESET_DEASSERT);
- udelay(RESET_DELAY_US);
+ udelay(fs->reset_delay_us);
/* Re-enable core clock. */
clk_prepare_enable(fs->core_clk);
@@ -441,6 +446,120 @@
return rc;
}
+static void force_bus_clocks(bool enforce)
+{
+ static struct msm_rpm_iv_pair iv;
+ int ret;
+
+ if (enforce) {
+ iv.id = MSM_RPM_STATUS_ID_RPM_CTL;
+ ret = msm_rpm_get_status(&iv, 1);
+ if (ret)
+ pr_err("Failed to read RPM_CTL resource status\n");
+
+ iv.id = MSM_RPM_ID_RPM_CTL;
+ iv.value |= BIT(6);
+ } else {
+ iv.id = MSM_RPM_ID_RPM_CTL;
+ iv.value &= ~BIT(6);
+ }
+
+ ret = msm_rpmrs_set(MSM_RPM_CTX_SET_0, &iv, 1);
+ if (ret)
+ pr_err("Force bus clocks request=%d failed\n", enforce);
+}
+
+static int gfx3d_8064_footswitch_enable(struct regulator_dev *rdev)
+{
+ struct footswitch *fs = rdev_get_drvdata(rdev);
+ struct fs_clk_data *clock;
+ uint32_t regval, rc = 0;
+
+ mutex_lock(&claim_lock);
+ fs->is_claimed = true;
+ mutex_unlock(&claim_lock);
+
+ /* Return early if already enabled. */
+ regval = readl_relaxed(fs->gfs_ctl_reg);
+ if ((regval & (ENABLE_BIT | CLAMP_BIT)) == ENABLE_BIT)
+ return 0;
+
+ /* Un-halt all bus ports in the power domain. */
+ if (fs->bus_port0) {
+ rc = msm_bus_axi_portunhalt(fs->bus_port0);
+ if (rc) {
+ pr_err("%s port 0 unhalt failed.\n", fs->desc.name);
+ goto err;
+ }
+ }
+ if (fs->bus_port1) {
+ rc = msm_bus_axi_portunhalt(fs->bus_port1);
+ if (rc) {
+ pr_err("%s port 1 unhalt failed.\n", fs->desc.name);
+ goto err_port2_halt;
+ }
+ }
+
+ /* Apply AFAB/EBI clock limits. */
+ force_bus_clocks(true);
+
+ /* Enable the power rail at the footswitch. */
+ regval |= ENABLE_BIT;
+ writel_relaxed(regval, fs->gfs_ctl_reg);
+ /* Wait for the rail to fully charge. */
+ mb();
+ udelay(1);
+
+ /* Make sure required clocks are on at the correct rates. */
+ rc = setup_clocks(fs);
+ if (rc)
+ goto err_setup_clocks;
+
+ /*
+ * (Re-)Assert resets for all clocks in the clock domain, since
+ * footswitch_enable() is first called before footswitch_disable()
+ * and resets should be asserted before power is restored.
+ */
+ for (clock = fs->clk_data; clock->clk; clock++)
+ ; /* Do nothing */
+ for (clock--; clock >= fs->clk_data; clock--)
+ clk_reset(clock->clk, CLK_RESET_ASSERT);
+ /* Wait for synchronous resets to propagate. */
+ udelay(fs->reset_delay_us);
+
+ /* Un-clamp the I/O ports. */
+ regval &= ~CLAMP_BIT;
+ writel_relaxed(regval, fs->gfs_ctl_reg);
+
+ /* Deassert resets for all clocks in the power domain. */
+ for (clock = fs->clk_data; clock->clk; clock++)
+ clk_reset(clock->clk, CLK_RESET_DEASSERT);
+
+ /* Prevent core memory from collapsing when its clock is gated. */
+ clk_set_flags(fs->core_clk, CLKFLAG_RETAIN_MEM);
+
+ /* Return clocks to their state before this function. */
+ restore_clocks(fs);
+
+ /* Remove AFAB/EBI clock limits after any transients have settled. */
+ udelay(30);
+ force_bus_clocks(false);
+
+ fs->is_enabled = true;
+ return 0;
+
+err_setup_clocks:
+ regval &= ~ENABLE_BIT;
+ writel_relaxed(regval, fs->gfs_ctl_reg);
+ force_bus_clocks(false);
+ msm_bus_axi_porthalt(fs->bus_port1);
+err_port2_halt:
+ msm_bus_axi_porthalt(fs->bus_port0);
+err:
+ return rc;
+}
+
+
static struct regulator_ops standard_fs_ops = {
.is_enabled = footswitch_is_enabled,
.enable = footswitch_enable,
@@ -453,6 +572,12 @@
.disable = gfx2d_footswitch_disable,
};
+static struct regulator_ops gfx3d_8064_fs_ops = {
+ .is_enabled = footswitch_is_enabled,
+ .enable = gfx3d_8064_footswitch_enable,
+ .disable = footswitch_disable,
+};
+
#define FOOTSWITCH(_id, _name, _ops, _gfs_ctl_reg) \
[(_id)] = { \
.desc = { \
@@ -467,6 +592,8 @@
static struct footswitch footswitches[] = {
FOOTSWITCH(FS_GFX2D0, "fs_gfx2d0", &gfx2d_fs_ops, GFX2D0_GFS_CTL_REG),
FOOTSWITCH(FS_GFX2D1, "fs_gfx2d1", &gfx2d_fs_ops, GFX2D1_GFS_CTL_REG),
+ FOOTSWITCH(FS_GFX3D_8064, "fs_gfx3d", &gfx3d_8064_fs_ops,
+ GFX3D_GFS_CTL_REG),
FOOTSWITCH(FS_GFX3D, "fs_gfx3d", &standard_fs_ops, GFX3D_GFS_CTL_REG),
FOOTSWITCH(FS_IJPEG, "fs_ijpeg", &standard_fs_ops, GEMINI_GFS_CTL_REG),
FOOTSWITCH(FS_MDP, "fs_mdp", &standard_fs_ops, MDP_GFS_CTL_REG),
@@ -497,6 +624,8 @@
fs->clk_data = driver_data->clks;
fs->bus_port0 = driver_data->bus_port0;
fs->bus_port1 = driver_data->bus_port1;
+ fs->reset_delay_us =
+ driver_data->reset_delay_us ? : DEFAULT_RESET_DELAY_US;
for (clock = fs->clk_data; clock->name; clock++) {
clock->clk = clk_get(&pdev->dev, clock->name);
diff --git a/arch/arm/mach-msm/footswitch.h b/arch/arm/mach-msm/footswitch.h
index 2a49426..b961f42 100644
--- a/arch/arm/mach-msm/footswitch.h
+++ b/arch/arm/mach-msm/footswitch.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013 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
@@ -28,7 +28,8 @@
#define FS_VFE 8
#define FS_VPE 9
#define FS_VCAP 10
-#define MAX_FS 11
+#define FS_GFX3D_8064 11
+#define MAX_FS 12
struct fs_clk_data {
const char *name;
@@ -41,6 +42,7 @@
struct fs_driver_data {
int bus_port0, bus_port1;
struct fs_clk_data *clks;
+ unsigned long reset_delay_us;
};
#define FS_GENERIC(_drv_name, _id, _name, _dev_id, _data) \
diff --git a/arch/arm/mach-msm/gdsc.c b/arch/arm/mach-msm/gdsc.c
index a07b13d..774548c 100644
--- a/arch/arm/mach-msm/gdsc.c
+++ b/arch/arm/mach-msm/gdsc.c
@@ -22,6 +22,7 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
#include <linux/clk.h>
#include <mach/clk.h>
@@ -38,19 +39,27 @@
#define EN_FEW_WAIT_VAL (0x8 << 16)
#define CLK_DIS_WAIT_VAL (0x2 << 12)
-#define TIMEOUT_US 1000
+#define TIMEOUT_US 100
struct gdsc {
struct regulator_dev *rdev;
struct regulator_desc rdesc;
void __iomem *gdscr;
- struct clk *core_clk;
+ struct clk **clocks;
+ int clock_count;
+ bool toggle_mem;
+ bool toggle_periph;
+ bool toggle_logic;
+ bool resets_asserted;
};
static int gdsc_is_enabled(struct regulator_dev *rdev)
{
struct gdsc *sc = rdev_get_drvdata(rdev);
+ if (!sc->toggle_logic)
+ return !sc->resets_asserted;
+
return !!(readl_relaxed(sc->gdscr) & PWR_ON_MASK);
}
@@ -58,22 +67,38 @@
{
struct gdsc *sc = rdev_get_drvdata(rdev);
uint32_t regval;
- int ret;
+ int i, ret;
- regval = readl_relaxed(sc->gdscr);
- regval &= ~SW_COLLAPSE_MASK;
- writel_relaxed(regval, sc->gdscr);
+ if (sc->toggle_logic) {
+ regval = readl_relaxed(sc->gdscr);
+ regval &= ~SW_COLLAPSE_MASK;
+ writel_relaxed(regval, sc->gdscr);
- ret = readl_tight_poll_timeout(sc->gdscr, regval, regval & PWR_ON_MASK,
- TIMEOUT_US);
- if (ret) {
- dev_err(&rdev->dev, "%s enable timed out\n", sc->rdesc.name);
- return ret;
+ ret = readl_tight_poll_timeout(sc->gdscr, regval,
+ regval & PWR_ON_MASK, TIMEOUT_US);
+ if (ret) {
+ dev_err(&rdev->dev, "%s enable timed out\n",
+ sc->rdesc.name);
+ return ret;
+ }
+ } else {
+ for (i = 0; i < sc->clock_count; i++)
+ clk_reset(sc->clocks[i], CLK_RESET_DEASSERT);
+ sc->resets_asserted = false;
+ }
+
+ for (i = 0; i < sc->clock_count; i++) {
+ if (sc->toggle_mem)
+ clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_MEM);
+ if (sc->toggle_periph)
+ clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_PERIPH);
}
/*
* If clocks to this power domain were already on, they will take an
* additional 4 clock cycles to re-enable after the rail is enabled.
+ * Delay to account for this. A delay is also needed to ensure clocks
+ * are not enabled within 400ns of enabling power to the memories.
*/
udelay(1);
@@ -84,16 +109,31 @@
{
struct gdsc *sc = rdev_get_drvdata(rdev);
uint32_t regval;
- int ret;
+ int i, ret = 0;
- regval = readl_relaxed(sc->gdscr);
- regval |= SW_COLLAPSE_MASK;
- writel_relaxed(regval, sc->gdscr);
+ if (sc->toggle_logic) {
+ regval = readl_relaxed(sc->gdscr);
+ regval |= SW_COLLAPSE_MASK;
+ writel_relaxed(regval, sc->gdscr);
- ret = readl_tight_poll_timeout(sc->gdscr, regval,
- !(regval & PWR_ON_MASK), TIMEOUT_US);
- if (ret)
- dev_err(&rdev->dev, "%s disable timed out\n", sc->rdesc.name);
+ ret = readl_tight_poll_timeout(sc->gdscr, regval,
+ !(regval & PWR_ON_MASK),
+ TIMEOUT_US);
+ if (ret)
+ dev_err(&rdev->dev, "%s disable timed out\n",
+ sc->rdesc.name);
+ } else {
+ for (i = 0; i < sc->clock_count; i++)
+ clk_reset(sc->clocks[i], CLK_RESET_ASSERT);
+ sc->resets_asserted = true;
+ }
+
+ for (i = 0; i < sc->clock_count; i++) {
+ if (sc->toggle_mem)
+ clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_MEM);
+ if (sc->toggle_periph)
+ clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_PERIPH);
+ }
return ret;
}
@@ -111,8 +151,8 @@
struct resource *res;
struct gdsc *sc;
uint32_t regval;
- bool retain_mems;
- int ret;
+ bool retain_mem, retain_periph;
+ int i, ret;
sc = devm_kzalloc(&pdev->dev, sizeof(struct gdsc), GFP_KERNEL);
if (sc == NULL)
@@ -137,6 +177,34 @@
if (sc->gdscr == NULL)
return -ENOMEM;
+ sc->clock_count = of_property_count_strings(pdev->dev.of_node,
+ "qcom,clock-names");
+ if (sc->clock_count == -EINVAL) {
+ sc->clock_count = 0;
+ } else if (IS_ERR_VALUE(sc->clock_count)) {
+ dev_err(&pdev->dev, "Failed to get clock names\n");
+ return -EINVAL;
+ }
+
+ sc->clocks = devm_kzalloc(&pdev->dev,
+ sizeof(struct clk *) * sc->clock_count, GFP_KERNEL);
+ if (!sc->clocks)
+ return -ENOMEM;
+ for (i = 0; i < sc->clock_count; i++) {
+ const char *clock_name;
+ of_property_read_string_index(pdev->dev.of_node,
+ "qcom,clock-names", i,
+ &clock_name);
+ sc->clocks[i] = devm_clk_get(&pdev->dev, clock_name);
+ if (IS_ERR(sc->clocks[i])) {
+ int rc = PTR_ERR(sc->clocks[i]);
+ if (rc != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get %s\n",
+ clock_name);
+ return rc;
+ }
+ }
+
sc->rdesc.id = atomic_inc_return(&gdsc_count);
sc->rdesc.ops = &gdsc_ops;
sc->rdesc.type = REGULATOR_VOLTAGE;
@@ -155,14 +223,36 @@
regval |= EN_REST_WAIT_VAL | EN_FEW_WAIT_VAL | CLK_DIS_WAIT_VAL;
writel_relaxed(regval, sc->gdscr);
- retain_mems = of_property_read_bool(pdev->dev.of_node,
- "qcom,retain-mems");
- if (retain_mems) {
- sc->core_clk = devm_clk_get(&pdev->dev, "core_clk");
- if (IS_ERR(sc->core_clk))
- return PTR_ERR(sc->core_clk);
- clk_set_flags(sc->core_clk, CLKFLAG_RETAIN_MEM);
- clk_set_flags(sc->core_clk, CLKFLAG_RETAIN_PERIPH);
+ retain_mem = of_property_read_bool(pdev->dev.of_node,
+ "qcom,retain-mem");
+ retain_periph = of_property_read_bool(pdev->dev.of_node,
+ "qcom,retain-periph");
+ for (i = 0; i < sc->clock_count; i++) {
+ if (retain_mem || (regval & PWR_ON_MASK))
+ clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_MEM);
+ else
+ clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_MEM);
+
+ if (retain_periph || (regval & PWR_ON_MASK))
+ clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_PERIPH);
+ else
+ clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_PERIPH);
+ }
+ sc->toggle_mem = !retain_mem;
+ sc->toggle_periph = !retain_periph;
+ sc->toggle_logic = !of_property_read_bool(pdev->dev.of_node,
+ "qcom,skip-logic-collapse");
+ if (!sc->toggle_logic) {
+ regval &= ~SW_COLLAPSE_MASK;
+ writel_relaxed(regval, sc->gdscr);
+
+ ret = readl_tight_poll_timeout(sc->gdscr, regval,
+ regval & PWR_ON_MASK, TIMEOUT_US);
+ if (ret) {
+ dev_err(&pdev->dev, "%s enable timed out\n",
+ sc->rdesc.name);
+ return ret;
+ }
}
sc->rdev = regulator_register(&sc->rdesc, &pdev->dev, init_data, sc,
diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c
index 0e97c27..20e3c3b 100644
--- a/arch/arm/mach-msm/hotplug.c
+++ b/arch/arm/mach-msm/hotplug.c
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2002 ARM Ltd.
* All Rights Reserved
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013, 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 as
@@ -24,13 +24,9 @@
extern volatile int pen_release;
-struct msm_hotplug_device {
- struct completion cpu_killed;
- unsigned int warm_boot;
-};
+static cpumask_t cpu_dying_mask;
-static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_hotplug_device,
- msm_hotplug_devices);
+static DEFINE_PER_CPU(unsigned int, warm_boot_flag);
static inline void cpu_enter_lowpower(void)
{
@@ -70,12 +66,12 @@
int platform_cpu_kill(unsigned int cpu)
{
- int ret;
+ int ret = 0;
- ret = msm_pm_wait_cpu_shutdown(cpu);
- if (ret)
- return 0;
- return 1;
+ if (cpumask_test_and_clear_cpu(cpu, &cpu_dying_mask))
+ ret = msm_pm_wait_cpu_shutdown(cpu);
+
+ return ret ? 0 : 1;
}
/*
@@ -92,7 +88,6 @@
__func__, smp_processor_id(), cpu);
BUG();
}
- complete(&__get_cpu_var(msm_hotplug_devices).cpu_killed);
/*
* we're ready for shutdown now, so do it
*/
@@ -142,6 +137,7 @@
uncached_logk(LOGK_HOTPLUG, (void *)(cpudata | this_cpumask));
break;
case CPU_DYING:
+ cpumask_set_cpu((unsigned long)hcpu, &cpu_dying_mask);
uncached_logk(LOGK_HOTPLUG, (void *)(cpudata & ~this_cpumask));
break;
default:
@@ -157,11 +153,10 @@
int msm_platform_secondary_init(unsigned int cpu)
{
int ret;
- struct msm_hotplug_device *dev = &__get_cpu_var(msm_hotplug_devices);
+ unsigned int *warm_boot = &__get_cpu_var(warm_boot_flag);
- if (!dev->warm_boot) {
- dev->warm_boot = 1;
- init_completion(&dev->cpu_killed);
+ if (!(*warm_boot)) {
+ *warm_boot = 1;
return 0;
}
msm_jtag_restore_state();
@@ -175,9 +170,6 @@
static int __init init_hotplug(void)
{
-
- struct msm_hotplug_device *dev = &__get_cpu_var(msm_hotplug_devices);
- init_completion(&dev->cpu_killed);
return register_hotcpu_notifier(&hotplug_rtb_notifier);
}
early_initcall(init_hotplug);
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index 6110ee7..7b26bd6 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -510,6 +510,15 @@
bool mhl_enabled;
};
+/**
+ * msm_i2c_platform_data: i2c-qup driver configuration data
+ *
+ * @active_only when set, votes when system active and removes the vote when
+ * system goes idle (optimises for performance). When unset, voting using
+ * runtime pm (optimizes for power).
+ * @master_id master id number of the i2c core or its wrapper (BLSP/GSBI).
+ * When zero, clock path voting is disabled.
+ */
struct msm_i2c_platform_data {
int clk_freq;
uint32_t rmutex;
@@ -523,6 +532,8 @@
int use_gsbi_shared_mode;
int keep_ahb_clk_on;
void (*msm_i2c_config_gpio)(int iface, int config_type);
+ bool active_only;
+ uint32_t master_id;
};
struct msm_i2c_ssbi_platform_data {
@@ -599,9 +610,12 @@
void msm_map_apq8064_io(void);
void msm_map_msm7x30_io(void);
void msm_map_fsm9xxx_io(void);
+void msm_map_fsm9900_io(void);
+void fsm9900_init_gpiomux(void);
void msm_map_8974_io(void);
-void msm_map_zinc_io(void);
+void msm_map_8084_io(void);
void msm_map_msmkrypton_io(void);
+void msm_map_msmsamarium_io(void);
void msm_map_msm8625_io(void);
void msm_map_msm9625_io(void);
void msm_init_irq(void);
@@ -610,9 +624,10 @@
void msm_8974_reserve(void);
void msm_8974_very_early(void);
void msm_8974_init_gpiomux(void);
-void msmzinc_init_gpiomux(void);
+void apq8084_init_gpiomux(void);
void msm9625_init_gpiomux(void);
void msmkrypton_init_gpiomux(void);
+void msmsamarium_init_gpiomux(void);
void msm_map_mpq8092_io(void);
void mpq8092_init_gpiomux(void);
void msm_map_msm8226_io(void);
diff --git a/arch/arm/mach-msm/include/mach/camera2.h b/arch/arm/mach-msm/include/mach/camera2.h
index 248c9b0..3e7e5fd 100644
--- a/arch/arm/mach-msm/include/mach/camera2.h
+++ b/arch/arm/mach-msm/include/mach/camera2.h
@@ -79,6 +79,7 @@
struct msm_camera_i2c_conf *i2c_conf;
struct msm_sensor_info_t *sensor_info;
struct msm_sensor_init_params *sensor_init_params;
+ const char *misc_regulator;
};
enum msm_camera_i2c_cmd_type {
diff --git a/arch/arm/mach-msm/include/mach/clk-provider.h b/arch/arm/mach-msm/include/mach/clk-provider.h
index 2a33228..27c6df4 100644
--- a/arch/arm/mach-msm/include/mach/clk-provider.h
+++ b/arch/arm/mach-msm/include/mach/clk-provider.h
@@ -49,6 +49,8 @@
* @set_vdd: function to call when applying a new voltage setting.
* @vdd_uv: sorted 2D array of legal voltage settings. Indexed by level, then
regulator.
+ * @vdd_ua: sorted 2D array of legal cureent settings. Indexed by level, then
+ regulator. Optional parameter.
* @level_votes: array of votes for each level.
* @num_levels: specifies the size of level_votes array.
* @cur_level: the currently set voltage level
@@ -59,7 +61,8 @@
struct regulator **regulator;
int num_regulators;
int (*set_vdd)(struct clk_vdd_class *v_class, int level);
- const int **vdd_uv;
+ int *vdd_uv;
+ int *vdd_ua;
int *level_votes;
int num_levels;
unsigned long cur_level;
@@ -76,10 +79,12 @@
.lock = __MUTEX_INITIALIZER(_name.lock) \
}
-#define DEFINE_VDD_REGULATORS(_name, _num_levels, _num_regulators, _vdd_uv) \
+#define DEFINE_VDD_REGULATORS(_name, _num_levels, _num_regulators, _vdd_uv, \
+ _vdd_ua) \
struct clk_vdd_class _name = { \
.class_name = #_name, \
.vdd_uv = _vdd_uv, \
+ .vdd_ua = _vdd_ua, \
.regulator = (struct regulator * [_num_regulators]) {}, \
.num_regulators = _num_regulators, \
.level_votes = (int [_num_levels]) {}, \
@@ -88,8 +93,6 @@
.lock = __MUTEX_INITIALIZER(_name.lock) \
}
-#define VDD_UV(...) ((int []){__VA_ARGS__})
-
enum handoff {
HANDOFF_ENABLED_CLK,
HANDOFF_DISABLED_CLK,
diff --git a/arch/arm/mach-msm/include/mach/clk.h b/arch/arm/mach-msm/include/mach/clk.h
index 1809456..fae0777 100644
--- a/arch/arm/mach-msm/include/mach/clk.h
+++ b/arch/arm/mach-msm/include/mach/clk.h
@@ -25,6 +25,7 @@
#define CLKFLAG_MAX 0x00000800
#define CLKFLAG_INIT_DONE 0x00001000
#define CLKFLAG_INIT_ERR 0x00002000
+#define CLKFLAG_NO_RATE_CACHE 0x00004000
struct clk_lookup;
struct clk;
diff --git a/arch/arm/mach-msm/include/mach/clock-generic.h b/arch/arm/mach-msm/include/mach/clock-generic.h
new file mode 100644
index 0000000..0f689f1
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/clock-generic.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2013, 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.
+ */
+
+#ifndef __MACH_CLOCK_GENERIC_H
+#define __MACH_CLOCK_GENERIC_H
+
+#include <mach/clk-provider.h>
+
+/* ==================== Mux clock ==================== */
+
+struct clk_src {
+ struct clk *src;
+ int sel;
+};
+
+struct mux_clk;
+
+struct clk_mux_ops {
+ int (*set_mux_sel)(struct mux_clk *clk, int sel);
+ int (*get_mux_sel)(struct mux_clk *clk);
+
+ /* Optional */
+ bool (*is_enabled)(struct mux_clk *clk);
+ int (*enable)(struct mux_clk *clk);
+ void (*disable)(struct mux_clk *clk);
+};
+
+#define MUX_SRC_LIST(...) \
+ .parents = (struct clk_src[]){__VA_ARGS__}, \
+ .num_parents = ARRAY_SIZE(((struct clk_src[]){__VA_ARGS__}))
+
+struct mux_clk {
+ /* Parents in decreasing order of preference for obtaining rates. */
+ struct clk_src *parents;
+ int num_parents;
+ struct clk *safe_parent;
+ int safe_sel;
+ struct clk_mux_ops *ops;
+
+ /* Fields not used by helper function. */
+ void *const __iomem *base;
+ u32 offset;
+ u32 mask;
+ u32 shift;
+ u32 en_mask;
+ void *priv;
+
+ struct clk c;
+};
+
+static inline struct mux_clk *to_mux_clk(struct clk *c)
+{
+ return container_of(c, struct mux_clk, c);
+}
+
+extern struct clk_ops clk_ops_gen_mux;
+
+/* ==================== Divider clock ==================== */
+
+struct div_clk;
+
+struct clk_div_ops {
+ int (*set_div)(struct div_clk *clk, int div);
+ int (*get_div)(struct div_clk *clk);
+
+ /* Optional */
+ bool (*is_enabled)(struct div_clk *clk);
+ int (*enable)(struct div_clk *clk);
+ void (*disable)(struct div_clk *clk);
+};
+
+struct div_clk {
+ unsigned int div;
+ unsigned int min_div;
+ unsigned int max_div;
+ unsigned long rate_margin;
+ struct clk_div_ops *ops;
+
+ /* Fields not used by helper function. */
+ void *const __iomem *base;
+ u32 offset;
+ u32 mask;
+ u32 shift;
+ u32 en_mask;
+ void *priv;
+ struct clk c;
+};
+
+static inline struct div_clk *to_div_clk(struct clk *c)
+{
+ return container_of(c, struct div_clk, c);
+}
+
+extern struct clk_ops clk_ops_div;
+extern struct clk_ops clk_ops_slave_div;
+
+#define DEFINE_FIXED_DIV_CLK(clk_name, _div, _parent) \
+static struct div_clk clk_name = { \
+ .div = _div, \
+ .c = { \
+ .parent = _parent, \
+ .dbg_name = #clk_name, \
+ .ops = &clk_ops_div, \
+ CLK_INIT(clk_name.c), \
+ } \
+}
+
+#define DEFINE_FIXED_SLAVE_DIV_CLK(clk_name, _div, _parent) \
+static struct div_clk clk_name = { \
+ .div = _div, \
+ .c = { \
+ .parent = _parent, \
+ .dbg_name = #clk_name, \
+ .ops = &clk_ops_slave_div, \
+ CLK_INIT(clk_name.c), \
+ } \
+}
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/ecm_ipa.h b/arch/arm/mach-msm/include/mach/ecm_ipa.h
index 008a659..f6afb2a 100644
--- a/arch/arm/mach-msm/include/mach/ecm_ipa.h
+++ b/arch/arm/mach-msm/include/mach/ecm_ipa.h
@@ -24,15 +24,35 @@
enum ipa_dp_evt_type evt,
unsigned long data);
+/*
+ * struct ecm_ipa_params - parameters for ecm_ipa initialization API
+ *
+ * @ecm_ipa_rx_dp_notify: ecm_ipa will set this callback (out parameter).
+ * this callback shall be supplied for ipa_connect upon pipe
+ * connection (USB->IPA), once IPA driver receive data packets
+ * from USB pipe destined for Apps this callback will be called.
+ * @ecm_ipa_tx_dp_notify: ecm_ipa will set this callback (out parameter).
+ * this callback shall be supplied for ipa_connect upon pipe
+ * connection (IPA->USB), once IPA driver send packets destined
+ * for USB, IPA BAM will notify for Tx-complete.
+ * @priv: ecm_ipa will set this pointer (out parameter).
+ * This pointer will hold the network device for later interaction
+ * with ecm_ipa APIs
+ * @host_ethaddr: host Ethernet address in network order
+ * @device_ethaddr: device Ethernet address in network order
+ */
+struct ecm_ipa_params {
+ ecm_ipa_callback ecm_ipa_rx_dp_notify;
+ ecm_ipa_callback ecm_ipa_tx_dp_notify;
+ u8 host_ethaddr[ETH_ALEN];
+ u8 device_ethaddr[ETH_ALEN];
+ void *private;
+};
+
#ifdef CONFIG_ECM_IPA
-int ecm_ipa_init(ecm_ipa_callback * ecm_ipa_rx_dp_notify,
- ecm_ipa_callback * ecm_ipa_tx_dp_notify,
- void **priv);
-
-int ecm_ipa_configure(u8 host_ethaddr[], u8 device_ethaddr[],
- void *priv);
+int ecm_ipa_init(struct ecm_ipa_params *params);
int ecm_ipa_connect(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl,
void *priv);
@@ -43,15 +63,7 @@
#else /* CONFIG_ECM_IPA*/
-static inline int ecm_ipa_init(ecm_ipa_callback *ecm_ipa_rx_dp_notify,
- ecm_ipa_callback *ecm_ipa_tx_dp_notify,
- void **priv)
-{
- return 0;
-}
-
-static inline int ecm_ipa_configure(u8 host_ethaddr[], u8 device_ethaddr[],
- void *priv)
+int ecm_ipa_init(struct ecm_ipa_params *params)
{
return 0;
}
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index f750dc8..68cfb2e 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -21,6 +21,8 @@
extern pgprot_t pgprot_kernel;
extern struct bus_type msm_iommu_sec_bus_type;
+extern struct iommu_access_ops iommu_access_ops_v0;
+extern struct iommu_access_ops iommu_access_ops_v1;
/* Domain attributes */
#define MSM_IOMMU_DOMAIN_PT_CACHEABLE 0x1
@@ -98,6 +100,7 @@
* @halt_enabled: Set to 1 if IOMMU halt is supported in the IOMMU, 0 otherwise.
* @asid: List of ASID and their usage count (index is ASID value).
* @ctx_attach_count: Count of how many context are attached.
+ * @bus_client : Bus client needed to vote for bus bandwidth.
*
* A msm_iommu_drvdata holds the global driver data about a single piece
* of an IOMMU hardware instance.
@@ -121,22 +124,28 @@
int halt_enabled;
int *asid;
unsigned int ctx_attach_count;
+ unsigned int bus_client;
};
/**
* struct iommu_access_ops - Callbacks for accessing IOMMU
* @iommu_power_on: Turn on power to unit
* @iommu_power_off: Turn off power to unit
+ * @iommu_bus_vote: Vote for bus bandwidth
* @iommu_clk_on: Turn on clks to unit
* @iommu_clk_off: Turn off clks to unit
+ * @iommu_lock_initialize: Initialize the remote lock
* @iommu_lock_acquire: Acquire any locks needed
* @iommu_lock_release: Release locks needed
*/
struct iommu_access_ops {
int (*iommu_power_on)(struct msm_iommu_drvdata *);
void (*iommu_power_off)(struct msm_iommu_drvdata *);
+ int (*iommu_bus_vote)(struct msm_iommu_drvdata *drvdata,
+ unsigned int vote);
int (*iommu_clk_on)(struct msm_iommu_drvdata *);
void (*iommu_clk_off)(struct msm_iommu_drvdata *);
+ void * (*iommu_lock_initialize)(void);
void (*iommu_lock_acquire)(void);
void (*iommu_lock_release)(void);
};
@@ -179,6 +188,22 @@
int attach_count;
};
+struct msm_iommu_context_regs {
+ uint32_t far;
+ uint32_t par;
+ uint32_t fsr;
+ uint32_t fsynr0;
+ uint32_t fsynr1;
+ uint32_t ttbr0;
+ uint32_t ttbr1;
+ uint32_t sctlr;
+ uint32_t actlr;
+ uint32_t prrr;
+ uint32_t nmrr;
+};
+
+void print_ctx_regs(struct msm_iommu_context_regs *regs);
+
/*
* Interrupt handler for the IOMMU context fault interrupt. Hooking the
* interrupt is not supported in the API yet, but this will print an error
@@ -186,6 +211,7 @@
*/
irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id);
irqreturn_t msm_iommu_fault_handler_v2(int irq, void *dev_id);
+irqreturn_t msm_iommu_secure_fault_handler_v2(int irq, void *dev_id);
enum {
PROC_APPS,
@@ -205,6 +231,8 @@
void *msm_iommu_lock_initialize(void);
void msm_iommu_mutex_lock(void);
void msm_iommu_mutex_unlock(void);
+void msm_set_iommu_access_ops(struct iommu_access_ops *ops);
+struct iommu_access_ops *msm_get_iommu_access_ops(void);
#else
static inline void *msm_iommu_lock_initialize(void)
{
@@ -212,6 +240,14 @@
}
static inline void msm_iommu_mutex_lock(void) { }
static inline void msm_iommu_mutex_unlock(void) { }
+static inline void msm_set_iommu_access_ops(struct iommu_access_ops *ops)
+{
+
+}
+static inline struct iommu_access_ops *msm_get_iommu_access_ops(void)
+{
+ return NULL;
+}
#endif
#ifdef CONFIG_MSM_IOMMU_GPU_SYNC
diff --git a/arch/arm/mach-msm/include/mach/iommu_domains.h b/arch/arm/mach-msm/include/mach/iommu_domains.h
index 9a89508..7157d12 100644
--- a/arch/arm/mach-msm/include/mach/iommu_domains.h
+++ b/arch/arm/mach-msm/include/mach/iommu_domains.h
@@ -122,6 +122,7 @@
unsigned long size);
extern int msm_register_domain(struct msm_iova_layout *layout);
+extern int msm_unregister_domain(struct iommu_domain *domain);
#else
static inline void msm_iommu_set_client_name(struct iommu_domain *domain,
@@ -195,6 +196,11 @@
{
return -ENODEV;
}
+
+static inline int msm_unregister_domain(struct iommu_domain *domain)
+{
+ return -ENODEV;
+}
#endif
#endif
diff --git a/arch/arm/mach-msm/include/mach/iommu_hw-v1.h b/arch/arm/mach-msm/include/mach/iommu_hw-v1.h
index 554f7e0..1c20d04 100644
--- a/arch/arm/mach-msm/include/mach/iommu_hw-v1.h
+++ b/arch/arm/mach-msm/include/mach/iommu_hw-v1.h
@@ -93,6 +93,8 @@
#define SET_NSCR0(b, v) SET_GLOBAL_REG(NSCR0, (b), (v))
#define SET_NSCR2(b, v) SET_GLOBAL_REG(NSCR2, (b), (v))
#define SET_NSACR(b, v) SET_GLOBAL_REG(NSACR, (b), (v))
+#define SET_NSGFAR(b, v) SET_GLOBAL_REG(NSGFAR, (b), (v))
+#define SET_NSGFSRRESTORE(b, v) SET_GLOBAL_REG(NSGFSRRESTORE, (b), (v))
#define SET_PMCR(b, v) SET_GLOBAL_REG(PMCR, (b), (v))
#define SET_SMR_N(b, N, v) SET_GLOBAL_REG_N(SMR, N, (b), (v))
#define SET_S2CR_N(b, N, v) SET_GLOBAL_REG_N(S2CR, N, (b), (v))
@@ -215,26 +217,34 @@
#define GET_ATSR(b, c) GET_CTX_REG(CB_ATSR, (b), (c))
/* Global Register field setters / getters */
-/* Configuration Register: CR0 */
+/* Configuration Register: CR0/NSCR0 */
#define SET_CR0_NSCFG(b, v) SET_GLOBAL_FIELD(b, CR0, NSCFG, v)
#define SET_CR0_WACFG(b, v) SET_GLOBAL_FIELD(b, CR0, WACFG, v)
#define SET_CR0_RACFG(b, v) SET_GLOBAL_FIELD(b, CR0, RACFG, v)
#define SET_CR0_SHCFG(b, v) SET_GLOBAL_FIELD(b, CR0, SHCFG, v)
#define SET_CR0_SMCFCFG(b, v) SET_GLOBAL_FIELD(b, CR0, SMCFCFG, v)
+#define SET_NSCR0_SMCFCFG(b, v) SET_GLOBAL_FIELD(b, NSCR0, SMCFCFG, v)
#define SET_CR0_MTCFG(b, v) SET_GLOBAL_FIELD(b, CR0, MTCFG, v)
#define SET_CR0_BSU(b, v) SET_GLOBAL_FIELD(b, CR0, BSU, v)
#define SET_CR0_FB(b, v) SET_GLOBAL_FIELD(b, CR0, FB, v)
#define SET_CR0_PTM(b, v) SET_GLOBAL_FIELD(b, CR0, PTM, v)
#define SET_CR0_VMIDPNE(b, v) SET_GLOBAL_FIELD(b, CR0, VMIDPNE, v)
#define SET_CR0_USFCFG(b, v) SET_GLOBAL_FIELD(b, CR0, USFCFG, v)
+#define SET_NSCR0_USFCFG(b, v) SET_GLOBAL_FIELD(b, NSCR0, USFCFG, v)
#define SET_CR0_GSE(b, v) SET_GLOBAL_FIELD(b, CR0, GSE, v)
#define SET_CR0_STALLD(b, v) SET_GLOBAL_FIELD(b, CR0, STALLD, v)
+#define SET_NSCR0_STALLD(b, v) SET_GLOBAL_FIELD(b, NSCR0, STALLD, v)
#define SET_CR0_TRANSIENTCFG(b, v) SET_GLOBAL_FIELD(b, CR0, TRANSIENTCFG, v)
#define SET_CR0_GCFGFIE(b, v) SET_GLOBAL_FIELD(b, CR0, GCFGFIE, v)
+#define SET_NSCR0_GCFGFIE(b, v) SET_GLOBAL_FIELD(b, NSCR0, GCFGFIE, v)
#define SET_CR0_GCFGFRE(b, v) SET_GLOBAL_FIELD(b, CR0, GCFGFRE, v)
+#define SET_NSCR0_GCFGFRE(b, v) SET_GLOBAL_FIELD(b, NSCR0, GCFGFRE, v)
#define SET_CR0_GFIE(b, v) SET_GLOBAL_FIELD(b, CR0, GFIE, v)
+#define SET_NSCR0_GFIE(b, v) SET_GLOBAL_FIELD(b, NSCR0, GFIE, v)
#define SET_CR0_GFRE(b, v) SET_GLOBAL_FIELD(b, CR0, GFRE, v)
+#define SET_NSCR0_GFRE(b, v) SET_GLOBAL_FIELD(b, NSCR0, GFRE, v)
#define SET_CR0_CLIENTPD(b, v) SET_GLOBAL_FIELD(b, CR0, CLIENTPD, v)
+#define SET_NSCR0_CLIENTPD(b, v) SET_GLOBAL_FIELD(b, NSCR0, CLIENTPD, v)
#define GET_CR0_NSCFG(b) GET_GLOBAL_FIELD(b, CR0, NSCFG)
#define GET_CR0_WACFG(b) GET_GLOBAL_FIELD(b, CR0, WACFG)
@@ -949,6 +959,8 @@
#define NSCR0 (0x0400)
#define NSCR2 (0x0408)
#define NSACR (0x0410)
+#define NSGFAR (0x0440)
+#define NSGFSRRESTORE (0x044C)
#define SMR (0x0800)
#define S2CR (0x0C00)
@@ -1400,6 +1412,7 @@
#define CR0_RACFG_MASK 0x03
#define CR0_SHCFG_MASK 0x03
#define CR0_SMCFCFG_MASK 0x01
+#define NSCR0_SMCFCFG_MASK 0x01
#define CR0_MTCFG_MASK 0x01
#define CR0_MEMATTR_MASK 0x0F
#define CR0_BSU_MASK 0x03
@@ -1407,14 +1420,21 @@
#define CR0_PTM_MASK 0x01
#define CR0_VMIDPNE_MASK 0x01
#define CR0_USFCFG_MASK 0x01
+#define NSCR0_USFCFG_MASK 0x01
#define CR0_GSE_MASK 0x01
#define CR0_STALLD_MASK 0x01
+#define NSCR0_STALLD_MASK 0x01
#define CR0_TRANSIENTCFG_MASK 0x03
#define CR0_GCFGFIE_MASK 0x01
+#define NSCR0_GCFGFIE_MASK 0x01
#define CR0_GCFGFRE_MASK 0x01
+#define NSCR0_GCFGFRE_MASK 0x01
#define CR0_GFIE_MASK 0x01
+#define NSCR0_GFIE_MASK 0x01
#define CR0_GFRE_MASK 0x01
+#define NSCR0_GFRE_MASK 0x01
#define CR0_CLIENTPD_MASK 0x01
+#define NSCR0_CLIENTPD_MASK 0x01
/* Configuration Register 2 */
#define CR2_BPVMID_MASK 0xFF
@@ -1764,6 +1784,7 @@
#define CR0_RACFG_SHIFT 24
#define CR0_SHCFG_SHIFT 22
#define CR0_SMCFCFG_SHIFT 21
+#define NSCR0_SMCFCFG_SHIFT 21
#define CR0_MTCFG_SHIFT 20
#define CR0_MEMATTR_SHIFT 16
#define CR0_BSU_SHIFT 14
@@ -1771,14 +1792,21 @@
#define CR0_PTM_SHIFT 12
#define CR0_VMIDPNE_SHIFT 11
#define CR0_USFCFG_SHIFT 10
+#define NSCR0_USFCFG_SHIFT 10
#define CR0_GSE_SHIFT 9
#define CR0_STALLD_SHIFT 8
+#define NSCR0_STALLD_SHIFT 8
#define CR0_TRANSIENTCFG_SHIFT 6
#define CR0_GCFGFIE_SHIFT 5
+#define NSCR0_GCFGFIE_SHIFT 5
#define CR0_GCFGFRE_SHIFT 4
+#define NSCR0_GCFGFRE_SHIFT 4
#define CR0_GFIE_SHIFT 2
+#define NSCR0_GFIE_SHIFT 2
#define CR0_GFRE_SHIFT 1
+#define NSCR0_GFRE_SHIFT 1
#define CR0_CLIENTPD_SHIFT 0
+#define NSCR0_CLIENTPD_SHIFT 0
/* Configuration Register: CR2 */
#define CR2_BPVMID_SHIFT 0
diff --git a/arch/arm/mach-msm/include/mach/iommu_perfmon.h b/arch/arm/mach-msm/include/mach/iommu_perfmon.h
index dc4671c..45683f4 100644
--- a/arch/arm/mach-msm/include/mach/iommu_perfmon.h
+++ b/arch/arm/mach-msm/include/mach/iommu_perfmon.h
@@ -144,8 +144,6 @@
unsigned int (*read_counter)(struct iommu_pmon_counter *);
};
-extern struct iommu_access_ops iommu_access_ops_v0;
-extern struct iommu_access_ops iommu_access_ops_v1;
#define MSM_IOMMU_PMU_NO_EVENT_CLASS -1
#ifdef CONFIG_MSM_IOMMU_PMON
diff --git a/arch/arm/mach-msm/include/mach/ipa.h b/arch/arm/mach-msm/include/mach/ipa.h
index cc03c48..697de5e 100644
--- a/arch/arm/mach-msm/include/mach/ipa.h
+++ b/arch/arm/mach-msm/include/mach/ipa.h
@@ -186,6 +186,16 @@
};
/**
+ * struct ipa_ep_cfg_holb - head of line blocking configuration in IPA end-point
+ * @en: enable(1 => ok to drop pkt)/disable(0 => never drop pkt)
+ * @tmr_val: duration in units of 128 IPA clk clock cyles [0,511], 1 clk=1.28us
+ */
+struct ipa_ep_cfg_holb {
+ u16 en;
+ u16 tmr_val;
+};
+
+/**
* struct ipa_ep_cfg - configuration of IPA end-point
* @nat: NAT parmeters
* @hdr: Header parameters
@@ -464,6 +474,13 @@
int ipa_disconnect(u32 clnt_hdl);
/*
+ * Resume / Suspend
+ */
+int ipa_resume(u32 clnt_hdl);
+
+int ipa_suspend(u32 clnt_hdl);
+
+/*
* Configuration
*/
int ipa_cfg_ep(u32 clnt_hdl, const struct ipa_ep_cfg *ipa_ep_cfg);
@@ -478,6 +495,11 @@
int ipa_cfg_ep_route(u32 clnt_hdl, const struct ipa_ep_cfg_route *ipa_ep_cfg);
+int ipa_cfg_ep_holb(u32 clnt_hdl, const struct ipa_ep_cfg_holb *ipa_ep_cfg);
+
+int ipa_cfg_ep_holb_by_client(enum ipa_client_type client,
+ const struct ipa_ep_cfg_holb *ipa_ep_cfg);
+
/*
* Header removal / addition
*/
@@ -557,17 +579,6 @@
int ipa_set_single_ndp_per_mbim(bool enable);
/*
- * rmnet bridge
- */
-int rmnet_bridge_init(void);
-
-int rmnet_bridge_disconnect(void);
-
-int rmnet_bridge_connect(u32 producer_hdl,
- u32 consumer_hdl,
- int wwan_logical_channel_id);
-
-/*
* SW bridge (between IPA and A2)
*/
int ipa_bridge_setup(enum ipa_bridge_dir dir, enum ipa_bridge_type type,
@@ -594,6 +605,8 @@
*/
int ipa_rm_create_resource(struct ipa_rm_create_params *create_params);
+int ipa_rm_delete_resource(enum ipa_rm_resource_name resource_name);
+
int ipa_rm_register(enum ipa_rm_resource_name resource_name,
struct ipa_rm_register_params *reg_params);
@@ -705,6 +718,19 @@
}
/*
+ * Resume / Suspend
+ */
+static inline int ipa_resume(u32 clnt_hdl)
+{
+ return -EPERM;
+}
+
+static inline int ipa_suspend(u32 clnt_hdl)
+{
+ return -EPERM;
+}
+
+/*
* Configuration
*/
static inline int ipa_cfg_ep(u32 clnt_hdl,
@@ -743,6 +769,12 @@
return -EPERM;
}
+static inline int ipa_cfg_ep_holb(u32 clnt_hdl,
+ const struct ipa_ep_cfg_holb *ipa_ep_cfg)
+{
+ return -EPERM;
+}
+
/*
* Header removal / addition
*/
@@ -917,26 +949,6 @@
}
/*
- * rmnet bridge
- */
-static inline int rmnet_bridge_init(void)
-{
- return -EPERM;
-}
-
-static inline int rmnet_bridge_disconnect(void)
-{
- return -EPERM;
-}
-
-static inline int rmnet_bridge_connect(u32 producer_hdl,
- u32 consumer_hdl,
- int wwan_logical_channel_id)
-{
- return -EPERM;
-}
-
-/*
* SW bridge (between IPA and A2)
*/
static inline int ipa_bridge_setup(enum ipa_bridge_dir dir,
@@ -986,6 +998,12 @@
return -EPERM;
}
+static inline int ipa_rm_delete_resource(
+ enum ipa_rm_resource_name resource_name)
+{
+ return -EPERM;
+}
+
static inline int ipa_rm_register(enum ipa_rm_resource_name resource_name,
struct ipa_rm_register_params *reg_params)
{
diff --git a/arch/arm/mach-msm/include/mach/kgsl.h b/arch/arm/mach-msm/include/mach/kgsl.h
index b68aff8..2d7e8df 100644
--- a/arch/arm/mach-msm/include/mach/kgsl.h
+++ b/arch/arm/mach-msm/include/mach/kgsl.h
@@ -20,6 +20,7 @@
#define KGSL_CLK_MEM 0x00000008
#define KGSL_CLK_MEM_IFACE 0x00000010
#define KGSL_CLK_AXI 0x00000020
+#define KGSL_CLK_ALT_MEM_IFACE 0x00000040
#define KGSL_MAX_PWRLEVELS 10
@@ -50,9 +51,19 @@
enum kgsl_iommu_context_id ctx_id;
};
+/*
+ * struct kgsl_device_iommu_data - Struct holding iommu context data obtained
+ * from dtsi file
+ * @iommu_ctxs: Pointer to array of struct hoding context name and id
+ * @iommu_ctx_count: Number of contexts defined in the dtsi file
+ * @iommu_halt_enable: Indicated if smmu halt h/w feature is supported
+ * @physstart: Start of iommu registers physical address
+ * @physend: End of iommu registers physical address
+ */
struct kgsl_device_iommu_data {
const struct kgsl_iommu_ctx *iommu_ctxs;
int iommu_ctx_count;
+ int iommu_halt_enable;
unsigned int physstart;
unsigned int physend;
};
@@ -70,7 +81,6 @@
int (*set_grp_async)(void);
unsigned int idle_timeout;
bool strtstp_sleepwake;
- unsigned int nap_allowed;
unsigned int clk_map;
unsigned int idle_needed;
unsigned int step_mul;
@@ -78,6 +88,8 @@
struct kgsl_device_iommu_data *iommu_data;
int iommu_count;
struct msm_dcvs_core_info *core_info;
+ struct coresight_device *csdev;
+ struct coresight_platform_data *coresight_pdata;
unsigned int chipid;
};
diff --git a/arch/arm/mach-msm/include/mach/mdm2.h b/arch/arm/mach-msm/include/mach/mdm2.h
index 46069d2..c6c03e4 100644
--- a/arch/arm/mach-msm/include/mach/mdm2.h
+++ b/arch/arm/mach-msm/include/mach/mdm2.h
@@ -12,6 +12,7 @@
#ifndef _ARCH_ARM_MACH_MSM_MDM2_H
#define _ARCH_ARM_MACH_MSM_MDM2_H
+#include "sysmon.h"
struct mdm_vddmin_resource {
int rpm_id;
@@ -35,6 +36,9 @@
int image_upgrade_supported;
struct gpiomux_setting *mdm2ap_status_gpio_run_cfg;
int send_shdn;
+ int cascading_ssr;
+ int sysmon_subsys_id_valid;
+ enum subsys_id sysmon_subsys_id;
};
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-zinc.h b/arch/arm/mach-msm/include/mach/msm_iomap-8084.h
similarity index 74%
rename from arch/arm/mach-msm/include/mach/msm_iomap-zinc.h
rename to arch/arm/mach-msm/include/mach/msm_iomap-8084.h
index 0a33055..43f1de0 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-zinc.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8084.h
@@ -11,8 +11,8 @@
*/
-#ifndef __ASM_ARCH_MSM_IOMAP_zinc_H
-#define __ASM_ARCH_MSM_IOMAP_zinc_H
+#ifndef __ASM_ARCH_MSM_IOMAP_8084_H
+#define __ASM_ARCH_MSM_IOMAP_8084_H
/* Physical base address and size of peripherals.
* Ordered by the virtual base addresses they will be mapped at.
@@ -23,15 +23,15 @@
*
*/
-#define MSMZINC_SHARED_RAM_PHYS 0x0FA00000
+#define APQ8084_SHARED_RAM_PHYS 0x0FA00000
-#define MSMZINC_QGIC_DIST_PHYS 0xF9000000
-#define MSMZINC_QGIC_DIST_SIZE SZ_4K
+#define APQ8084_QGIC_DIST_PHYS 0xF9000000
+#define APQ8084_QGIC_DIST_SIZE SZ_4K
-#define MSMZINC_TLMM_PHYS 0xFD510000
-#define MSMZINC_TLMM_SIZE SZ_16K
+#define APQ8084_TLMM_PHYS 0xFD510000
+#define APQ8084_TLMM_SIZE SZ_16K
-#ifdef CONFIG_DEBUG_MSMZINC_UART
+#ifdef CONFIG_DEBUG_APQ8084_UART
#define MSM_DEBUG_UART_BASE IOMEM(0xFA71E000)
#define MSM_DEBUG_UART_PHYS 0xF991E000
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8610.h b/arch/arm/mach-msm/include/mach/msm_iomap-8610.h
index 2a62460..18e448d 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-8610.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8610.h
@@ -24,6 +24,9 @@
#define MSM8610_MSM_SHARED_RAM_PHYS 0x0D900000
+#define MSM8610_QGIC_DIST_PHYS 0xF9000000
+#define MSM8610_QGIC_DIST_SIZE SZ_4K
+
#define MSM8610_APCS_GCC_PHYS 0xF9011000
#define MSM8610_APCS_GCC_SIZE SZ_4K
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-9625.h b/arch/arm/mach-msm/include/mach/msm_iomap-9625.h
index 9a8bfc1..31b19b3 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-9625.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-9625.h
@@ -38,8 +38,8 @@
#define MSM9625_MPM2_PSHOLD_SIZE SZ_4K
#ifdef CONFIG_DEBUG_MSM9625_UART
-#define MSM_DEBUG_UART_BASE IOMEM(0xFA71E000)
-#define MSM_DEBUG_UART_PHYS 0xF991E000
+#define MSM_DEBUG_UART_BASE IOMEM(0xFA71F000)
+#define MSM_DEBUG_UART_PHYS 0xF991F000
#endif
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-fsm9900.h b/arch/arm/mach-msm/include/mach/msm_iomap-fsm9900.h
new file mode 100644
index 0000000..02b8917
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-fsm9900.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __ASM_ARCH_MSM_IOMAP_FSM9900_H
+#define __ASM_ARCH_MSM_IOMAP_FSM9900_H
+
+/* Physical base address and size of peripherals.
+ * Ordered by the virtual base addresses they will be mapped at.
+ *
+ * If you add or remove entries here, you'll want to edit the
+ * io desc array in arch/arm/mach-msm/io.c to reflect your
+ * changes.
+ *
+ */
+
+#define FSM9900_SHARED_RAM_PHYS 0x98000000
+
+#define FSM9900_QGIC_DIST_PHYS 0xF9000000
+#define FSM9900_QGIC_DIST_SIZE SZ_4K
+
+#define FSM9900_TLMM_PHYS 0xFD510000
+#define FSM9900_TLMM_SIZE SZ_16K
+
+#ifdef CONFIG_DEBUG_FSM9900_UART
+#define MSM_DEBUG_UART_BASE IOMEM(0xFA71E000)
+#define MSM_DEBUG_UART_PHYS 0xF9960000
+#endif
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-samarium.h b/arch/arm/mach-msm/include/mach/msm_iomap-samarium.h
new file mode 100644
index 0000000..7a6b626
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-samarium.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __ASM_ARCH_MSM_IOMAP_SAMARIUM_H
+#define __ASM_ARCH_MSM_IOMAP_SAMARIUM_H
+
+/* Physical base address and size of peripherals.
+ * Ordered by the virtual base addresses they will be mapped at.
+ *
+ * If you add or remove entries here, you'll want to edit the
+ * io desc array in arch/arm/mach-msm/io.c to reflect your
+ * changes.
+ *
+ */
+
+#define MSMSAMARIUM_SHARED_RAM_PHYS 0x0FA00000
+
+#define MSMSAMARIUM_QGIC_DIST_PHYS 0xF9000000
+#define MSMSAMARIUM_QGIC_DIST_SIZE SZ_4K
+
+#define MSMSAMARIUM_TLMM_PHYS 0xFD510000
+#define MSMSAMARIUM_TLMM_SIZE SZ_16K
+
+#define MSMSAMARIUM_MPM2_PSHOLD_PHYS 0xFC4AB000
+#define MSMSAMARIUM_MPM2_PSHOLD_SIZE SZ_4K
+
+#ifdef CONFIG_DEBUG_MSMSAMARIUM_UART
+#define MSM_DEBUG_UART_BASE IOMEM(0xFA71E000)
+#define MSM_DEBUG_UART_PHYS 0xF991E000
+#endif
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap.h b/arch/arm/mach-msm/include/mach/msm_iomap.h
index f27eb36..7b73333 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap.h
@@ -81,16 +81,6 @@
#define MSM_LPASS_CLK_CTL_BASE IOMEM(0xFA015000) /* 4K */
#define MSM_HFPLL_BASE IOMEM(0xFA016000) /* 4K */
#define MSM_TLMM_BASE IOMEM(0xFA017000) /* 16K */
-#define MSM_SHARED_RAM_BASE IOMEM(0xFA400000) /* 2M */
-#define MSM_SIC_NON_SECURE_BASE IOMEM(0xFA600000) /* 64K */
-#define MSM_HDMI_BASE IOMEM(0xFA800000) /* 4K */
-#define MSM_RPM_BASE IOMEM(0xFA801000) /* 4K */
-#define MSM_RPM_MPM_BASE IOMEM(0xFA802000) /* 4K */
-#define MSM_QFPROM_BASE IOMEM(0xFA700000) /* 4K */
-#define MSM_L2CC_BASE IOMEM(0xFA701000) /* 4K */
-#define MSM_APCS_GLB_BASE IOMEM(0xFA702000) /* 4K */
-#define MSM_SAW2_BASE IOMEM(0xFA703000) /* 4k */
-#define MSM_SAW3_BASE IOMEM(0xFA704000) /* 4k */
#define MSM_VIC_BASE IOMEM(0xFA100000) /* 4K */
#define MSM_CSR_BASE IOMEM(0xFA101000) /* 4K */
#define MSM_GPIO1_BASE IOMEM(0xFA102000) /* 4K */
@@ -99,7 +89,16 @@
#define MSM_CFG_CTL_BASE IOMEM(0xFA105000) /* 4K */
#define MSM_CLK_CTL_SH2_BASE IOMEM(0xFA106000) /* 4K */
#define MSM_MPM2_PSHOLD_BASE IOMEM(0xFA107000) /* 4k */
-#define MSM_MDC_BASE IOMEM(0xFA400000) /* 1M */
+#define MSM_SHARED_RAM_BASE IOMEM(0xFA400000) /* 2M */
+#define MSM_SIC_NON_SECURE_BASE IOMEM(0xFA600000) /* 64K */
+#define MSM_QFPROM_BASE IOMEM(0xFA700000) /* 4K */
+#define MSM_L2CC_BASE IOMEM(0xFA701000) /* 4K */
+#define MSM_APCS_GLB_BASE IOMEM(0xFA702000) /* 4K */
+#define MSM_SAW2_BASE IOMEM(0xFA703000) /* 4k */
+#define MSM_SAW3_BASE IOMEM(0xFA704000) /* 4k */
+#define MSM_HDMI_BASE IOMEM(0xFA800000) /* 4K */
+#define MSM_RPM_BASE IOMEM(0xFA801000) /* 4K */
+#define MSM_RPM_MPM_BASE IOMEM(0xFA802000) /* 4K */
#define MSM_AD5_BASE IOMEM(0xFA900000) /* 13M (D00000)
0xFB600000 */
/* MSM9625 has unaligned imem so we need to map excess 2K virtually
@@ -115,7 +114,8 @@
#if defined(CONFIG_ARCH_MSM9615) || defined(CONFIG_ARCH_MSM7X27) \
- || defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM9625)
+ || defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM9625) \
+ || defined(CONFIG_ARCH_MSM8610) || defined(CONFIG_ARCH_MSM8226)
#define MSM_SHARED_RAM_SIZE SZ_1M
#else
#define MSM_SHARED_RAM_SIZE SZ_2M
@@ -129,12 +129,14 @@
#include "msm_iomap-8064.h"
#include "msm_iomap-9615.h"
#include "msm_iomap-8974.h"
-#include "msm_iomap-zinc.h"
+#include "msm_iomap-8084.h"
#include "msm_iomap-9625.h"
#include "msm_iomap-8092.h"
#include "msm_iomap-8226.h"
#include "msm_iomap-8610.h"
#include "msm_iomap-krypton.h"
+#include "msm_iomap-fsm9900.h"
+#include "msm_iomap-samarium.h"
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_ipc_logging.h b/arch/arm/mach-msm/include/mach/msm_ipc_logging.h
index b675c00..0a91719 100644
--- a/arch/arm/mach-msm/include/mach/msm_ipc_logging.h
+++ b/arch/arm/mach-msm/include/mach/msm_ipc_logging.h
@@ -183,6 +183,14 @@
int add_deserialization_func(void *ctxt, int type,
void (*dfunc)(struct encode_context *,
struct decode_context *));
+
+/*
+ * ipc_log_context_destroy: Destroy debug log context
+ *
+ * @ctxt: debug log context created by calling ipc_log_context_create API.
+ */
+int ipc_log_context_destroy(void *ctxt);
+
#else
static inline void *ipc_log_context_create(int max_num_pages,
@@ -232,6 +240,9 @@
struct decode_context *))
{ return 0; }
+static inline int ipc_log_context_destroy(void *ctxt)
+{ return 0; }
+
#endif
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_ipc_router.h b/arch/arm/mach-msm/include/mach/msm_ipc_router.h
index c68c783..a87380c 100644
--- a/arch/arm/mach-msm/include/mach/msm_ipc_router.h
+++ b/arch/arm/mach-msm/include/mach/msm_ipc_router.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -31,6 +31,11 @@
MSM_IPC_ROUTER_WRITE_DONE,
};
+struct comm_mode_info {
+ int mode;
+ void *xprt_info;
+};
+
struct msm_ipc_port {
struct list_head list;
@@ -39,12 +44,10 @@
uint32_t type;
unsigned flags;
spinlock_t port_lock;
-
- struct list_head incomplete;
- struct mutex incomplete_lock;
+ struct comm_mode_info mode_info;
struct list_head port_rx_q;
- struct mutex port_rx_q_lock;
+ struct mutex port_rx_q_lock_lhb3;
char rx_wakelock_name[MAX_WAKELOCK_NAME_SZ];
struct wake_lock port_rx_wake_lock;
wait_queue_head_t port_rx_wait_q;
diff --git a/arch/arm/mach-msm/include/mach/msm_memtypes.h b/arch/arm/mach-msm/include/mach/msm_memtypes.h
index 3bf05e6..5c8f525 100644
--- a/arch/arm/mach-msm/include/mach/msm_memtypes.h
+++ b/arch/arm/mach-msm/include/mach/msm_memtypes.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2011, 2013 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
@@ -39,6 +39,15 @@
MEMTYPE_MAX,
};
+enum {
+ SYS_MEMORY = 1, /* system memory*/
+ BOOT_REGION_MEMORY1, /* boot loader memory 1*/
+ BOOT_REGION_MEMORY2, /* boot loader memory 2,reserved*/
+ APPSBL_MEMORY, /* apps boot loader memory*/
+ APPS_MEMORY, /* apps usage memory*/
+};
+
+
void msm_reserve(void);
#define MEMTYPE_FLAGS_FIXED 0x1
diff --git a/arch/arm/mach-msm/include/mach/msm_smd.h b/arch/arm/mach-msm/include/mach/msm_smd.h
index d155c6f..2cc7b10 100644
--- a/arch/arm/mach-msm/include/mach/msm_smd.h
+++ b/arch/arm/mach-msm/include/mach/msm_smd.h
@@ -19,7 +19,9 @@
#define __ASM_ARCH_MSM_SMD_H
#include <linux/io.h>
-#include <mach/msm_smsm.h>
+#include <linux/notifier.h>
+
+#include <mach/msm_smem.h>
typedef struct smd_channel smd_channel_t;
@@ -40,13 +42,13 @@
* SMD, the entry will only exist in this enum.
*/
enum {
- SMD_APPS = SMSM_APPS,
- SMD_MODEM = SMSM_MODEM,
- SMD_Q6 = SMSM_Q6,
- SMD_WCNSS = SMSM_WCNSS,
- SMD_DSPS = SMSM_DSPS,
- SMD_MODEM_Q6_FW,
- SMD_RPM,
+ SMD_APPS = SMEM_APPS,
+ SMD_MODEM = SMEM_MODEM,
+ SMD_Q6 = SMEM_Q6,
+ SMD_DSPS = SMEM_DSPS,
+ SMD_WCNSS = SMEM_WCNSS,
+ SMD_MODEM_Q6_FW = SMEM_MODEM_Q6_FW,
+ SMD_RPM = SMEM_RPM,
NUM_SMD_SUBSYSTEMS,
};
diff --git a/arch/arm/mach-msm/include/mach/msm_smem.h b/arch/arm/mach-msm/include/mach/msm_smem.h
new file mode 100644
index 0000000..57f22cc
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_smem.h
@@ -0,0 +1,180 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_SMEM_H_
+#define _ARCH_ARM_MACH_MSM_SMEM_H_
+
+#include <linux/types.h>
+
+enum {
+ SMEM_APPS,
+ SMEM_MODEM,
+ SMEM_Q6,
+ SMEM_DSPS,
+ SMEM_WCNSS,
+ SMEM_MODEM_Q6_FW,
+ SMEM_RPM,
+ NUM_SMEM_SUBSYSTEMS,
+};
+
+#define SMEM_NUM_SMD_STREAM_CHANNELS 64
+#define SMEM_NUM_SMD_BLOCK_CHANNELS 64
+
+enum {
+ /* fixed items */
+ SMEM_PROC_COMM = 0,
+ SMEM_HEAP_INFO,
+ SMEM_ALLOCATION_TABLE,
+ SMEM_VERSION_INFO,
+ SMEM_HW_RESET_DETECT,
+ SMEM_AARM_WARM_BOOT,
+ SMEM_DIAG_ERR_MESSAGE,
+ SMEM_SPINLOCK_ARRAY,
+ SMEM_MEMORY_BARRIER_LOCATION,
+ SMEM_FIXED_ITEM_LAST = SMEM_MEMORY_BARRIER_LOCATION,
+
+ /* dynamic items */
+ SMEM_AARM_PARTITION_TABLE,
+ SMEM_AARM_BAD_BLOCK_TABLE,
+ SMEM_RESERVE_BAD_BLOCKS,
+ SMEM_WM_UUID,
+ SMEM_CHANNEL_ALLOC_TBL,
+ SMEM_SMD_BASE_ID,
+ SMEM_SMEM_LOG_IDX = SMEM_SMD_BASE_ID + SMEM_NUM_SMD_STREAM_CHANNELS,
+ SMEM_SMEM_LOG_EVENTS,
+ SMEM_SMEM_STATIC_LOG_IDX,
+ SMEM_SMEM_STATIC_LOG_EVENTS,
+ SMEM_SMEM_SLOW_CLOCK_SYNC,
+ SMEM_SMEM_SLOW_CLOCK_VALUE,
+ SMEM_BIO_LED_BUF,
+ SMEM_SMSM_SHARED_STATE,
+ SMEM_SMSM_INT_INFO,
+ SMEM_SMSM_SLEEP_DELAY,
+ SMEM_SMSM_LIMIT_SLEEP,
+ SMEM_SLEEP_POWER_COLLAPSE_DISABLED,
+ SMEM_KEYPAD_KEYS_PRESSED,
+ SMEM_KEYPAD_STATE_UPDATED,
+ SMEM_KEYPAD_STATE_IDX,
+ SMEM_GPIO_INT,
+ SMEM_MDDI_LCD_IDX,
+ SMEM_MDDI_HOST_DRIVER_STATE,
+ SMEM_MDDI_LCD_DISP_STATE,
+ SMEM_LCD_CUR_PANEL,
+ SMEM_MARM_BOOT_SEGMENT_INFO,
+ SMEM_AARM_BOOT_SEGMENT_INFO,
+ SMEM_SLEEP_STATIC,
+ SMEM_SCORPION_FREQUENCY,
+ SMEM_SMD_PROFILES,
+ SMEM_TSSC_BUSY,
+ SMEM_HS_SUSPEND_FILTER_INFO,
+ SMEM_BATT_INFO,
+ SMEM_APPS_BOOT_MODE,
+ SMEM_VERSION_FIRST,
+ SMEM_VERSION_SMD = SMEM_VERSION_FIRST,
+ SMEM_VERSION_LAST = SMEM_VERSION_FIRST + 24,
+ SMEM_OSS_RRCASN1_BUF1,
+ SMEM_OSS_RRCASN1_BUF2,
+ SMEM_ID_VENDOR0,
+ SMEM_ID_VENDOR1,
+ SMEM_ID_VENDOR2,
+ SMEM_HW_SW_BUILD_ID,
+ SMEM_SMD_BLOCK_PORT_BASE_ID,
+ SMEM_SMD_BLOCK_PORT_PROC0_HEAP = SMEM_SMD_BLOCK_PORT_BASE_ID +
+ SMEM_NUM_SMD_BLOCK_CHANNELS,
+ SMEM_SMD_BLOCK_PORT_PROC1_HEAP = SMEM_SMD_BLOCK_PORT_PROC0_HEAP +
+ SMEM_NUM_SMD_BLOCK_CHANNELS,
+ SMEM_I2C_MUTEX = SMEM_SMD_BLOCK_PORT_PROC1_HEAP +
+ SMEM_NUM_SMD_BLOCK_CHANNELS,
+ SMEM_SCLK_CONVERSION,
+ SMEM_SMD_SMSM_INTR_MUX,
+ SMEM_SMSM_CPU_INTR_MASK,
+ SMEM_APPS_DEM_SLAVE_DATA,
+ SMEM_QDSP6_DEM_SLAVE_DATA,
+ SMEM_CLKREGIM_BSP,
+ SMEM_CLKREGIM_SOURCES,
+ SMEM_SMD_FIFO_BASE_ID,
+ SMEM_USABLE_RAM_PARTITION_TABLE = SMEM_SMD_FIFO_BASE_ID +
+ SMEM_NUM_SMD_STREAM_CHANNELS,
+ SMEM_POWER_ON_STATUS_INFO,
+ SMEM_DAL_AREA,
+ SMEM_SMEM_LOG_POWER_IDX,
+ SMEM_SMEM_LOG_POWER_WRAP,
+ SMEM_SMEM_LOG_POWER_EVENTS,
+ SMEM_ERR_CRASH_LOG,
+ SMEM_ERR_F3_TRACE_LOG,
+ SMEM_SMD_BRIDGE_ALLOC_TABLE,
+ SMEM_SMDLITE_TABLE,
+ SMEM_SD_IMG_UPGRADE_STATUS,
+ SMEM_SEFS_INFO,
+ SMEM_RESET_LOG,
+ SMEM_RESET_LOG_SYMBOLS,
+ SMEM_MODEM_SW_BUILD_ID,
+ SMEM_SMEM_LOG_MPROC_WRAP,
+ SMEM_BOOT_INFO_FOR_APPS,
+ SMEM_SMSM_SIZE_INFO,
+ SMEM_SMD_LOOPBACK_REGISTER,
+ SMEM_SSR_REASON_MSS0,
+ SMEM_SSR_REASON_WCNSS0,
+ SMEM_SSR_REASON_LPASS0,
+ SMEM_SSR_REASON_DSPS0,
+ SMEM_SSR_REASON_VCODEC0,
+ SMEM_SMP2P_APPS_BASE = 427,
+ SMEM_SMP2P_MODEM_BASE = SMEM_SMP2P_APPS_BASE + 8, /* 435 */
+ SMEM_SMP2P_AUDIO_BASE = SMEM_SMP2P_MODEM_BASE + 8, /* 443 */
+ SMEM_SMP2P_WIRLESS_BASE = SMEM_SMP2P_AUDIO_BASE + 8, /* 451 */
+ SMEM_SMP2P_POWER_BASE = SMEM_SMP2P_WIRLESS_BASE + 8, /* 459 */
+ SMEM_FLASH_DEVICE_INFO = SMEM_SMP2P_POWER_BASE + 8, /* 467 */
+ SMEM_BAM_PIPE_MEMORY, /* 468 */
+ SMEM_IMAGE_VERSION_TABLE, /* 469 */
+ SMEM_LC_DEBUGGER, /* 470 */
+ SMEM_NUM_ITEMS,
+};
+
+#ifdef CONFIG_MSM_SMD
+void *smem_alloc(unsigned id, unsigned size);
+void *smem_alloc2(unsigned id, unsigned size_in);
+void *smem_get_entry(unsigned id, unsigned *size);
+void *smem_find(unsigned id, unsigned size);
+/**
+ * smem_virt_to_phys() - Convert SMEM address to physical address.
+ *
+ * @smem_address: Virtual address returned by smem_alloc()/smem_alloc2()
+ * @returns: Physical address (or NULL if there is a failure)
+ *
+ * This function should only be used if an SMEM item needs to be handed
+ * off to a DMA engine.
+ */
+phys_addr_t smem_virt_to_phys(void *smem_address);
+
+#else
+static inline void *smem_alloc(unsigned id, unsigned size)
+{
+ return NULL;
+}
+static inline void *smem_alloc2(unsigned id, unsigned size_in)
+{
+ return NULL;
+}
+static inline void *smem_get_entry(unsigned id, unsigned *size)
+{
+ return NULL;
+}
+static inline void *smem_find(unsigned id, unsigned size)
+{
+ return NULL;
+}
+static inline phys_addr_t smem_virt_to_phys(void *smem_address)
+{
+ return (phys_addr_t) NULL;
+}
+#endif /* CONFIG_MSM_SMD */
+#endif /* _ARCH_ARM_MACH_MSM_SMEM_H_ */
diff --git a/arch/arm/mach-msm/include/mach/msm_smsm.h b/arch/arm/mach-msm/include/mach/msm_smsm.h
index 81a6399..f97d5e4 100644
--- a/arch/arm/mach-msm/include/mach/msm_smsm.h
+++ b/arch/arm/mach-msm/include/mach/msm_smsm.h
@@ -14,6 +14,9 @@
#define _ARCH_ARM_MACH_MSM_SMSM_H_
#include <linux/notifier.h>
+
+#include <mach/msm_smem.h>
+
#if defined(CONFIG_MSM_N_WAY_SMSM)
enum {
SMSM_APPS_STATE,
@@ -36,10 +39,14 @@
};
#endif
+/*
+ * Ordered by when processors adopted the SMSM protocol. May not be 1-to-1
+ * with SMEM PIDs, despite initial expectations.
+ */
enum {
- SMSM_APPS,
- SMSM_MODEM,
- SMSM_Q6,
+ SMSM_APPS = SMEM_APPS,
+ SMSM_MODEM = SMEM_MODEM,
+ SMSM_Q6 = SMEM_Q6,
SMSM_WCNSS,
SMSM_DSPS,
};
@@ -97,119 +104,6 @@
#define SMSM_SUBSYS2AP_STATUS 0x00008000
-#define SMEM_NUM_SMD_STREAM_CHANNELS 64
-#define SMEM_NUM_SMD_BLOCK_CHANNELS 64
-
-enum {
- /* fixed items */
- SMEM_PROC_COMM = 0,
- SMEM_HEAP_INFO,
- SMEM_ALLOCATION_TABLE,
- SMEM_VERSION_INFO,
- SMEM_HW_RESET_DETECT,
- SMEM_AARM_WARM_BOOT,
- SMEM_DIAG_ERR_MESSAGE,
- SMEM_SPINLOCK_ARRAY,
- SMEM_MEMORY_BARRIER_LOCATION,
- SMEM_FIXED_ITEM_LAST = SMEM_MEMORY_BARRIER_LOCATION,
-
- /* dynamic items */
- SMEM_AARM_PARTITION_TABLE,
- SMEM_AARM_BAD_BLOCK_TABLE,
- SMEM_RESERVE_BAD_BLOCKS,
- SMEM_WM_UUID,
- SMEM_CHANNEL_ALLOC_TBL,
- SMEM_SMD_BASE_ID,
- SMEM_SMEM_LOG_IDX = SMEM_SMD_BASE_ID + SMEM_NUM_SMD_STREAM_CHANNELS,
- SMEM_SMEM_LOG_EVENTS,
- SMEM_SMEM_STATIC_LOG_IDX,
- SMEM_SMEM_STATIC_LOG_EVENTS,
- SMEM_SMEM_SLOW_CLOCK_SYNC,
- SMEM_SMEM_SLOW_CLOCK_VALUE,
- SMEM_BIO_LED_BUF,
- SMEM_SMSM_SHARED_STATE,
- SMEM_SMSM_INT_INFO,
- SMEM_SMSM_SLEEP_DELAY,
- SMEM_SMSM_LIMIT_SLEEP,
- SMEM_SLEEP_POWER_COLLAPSE_DISABLED,
- SMEM_KEYPAD_KEYS_PRESSED,
- SMEM_KEYPAD_STATE_UPDATED,
- SMEM_KEYPAD_STATE_IDX,
- SMEM_GPIO_INT,
- SMEM_MDDI_LCD_IDX,
- SMEM_MDDI_HOST_DRIVER_STATE,
- SMEM_MDDI_LCD_DISP_STATE,
- SMEM_LCD_CUR_PANEL,
- SMEM_MARM_BOOT_SEGMENT_INFO,
- SMEM_AARM_BOOT_SEGMENT_INFO,
- SMEM_SLEEP_STATIC,
- SMEM_SCORPION_FREQUENCY,
- SMEM_SMD_PROFILES,
- SMEM_TSSC_BUSY,
- SMEM_HS_SUSPEND_FILTER_INFO,
- SMEM_BATT_INFO,
- SMEM_APPS_BOOT_MODE,
- SMEM_VERSION_FIRST,
- SMEM_VERSION_SMD = SMEM_VERSION_FIRST,
- SMEM_VERSION_LAST = SMEM_VERSION_FIRST + 24,
- SMEM_OSS_RRCASN1_BUF1,
- SMEM_OSS_RRCASN1_BUF2,
- SMEM_ID_VENDOR0,
- SMEM_ID_VENDOR1,
- SMEM_ID_VENDOR2,
- SMEM_HW_SW_BUILD_ID,
- SMEM_SMD_BLOCK_PORT_BASE_ID,
- SMEM_SMD_BLOCK_PORT_PROC0_HEAP = SMEM_SMD_BLOCK_PORT_BASE_ID +
- SMEM_NUM_SMD_BLOCK_CHANNELS,
- SMEM_SMD_BLOCK_PORT_PROC1_HEAP = SMEM_SMD_BLOCK_PORT_PROC0_HEAP +
- SMEM_NUM_SMD_BLOCK_CHANNELS,
- SMEM_I2C_MUTEX = SMEM_SMD_BLOCK_PORT_PROC1_HEAP +
- SMEM_NUM_SMD_BLOCK_CHANNELS,
- SMEM_SCLK_CONVERSION,
- SMEM_SMD_SMSM_INTR_MUX,
- SMEM_SMSM_CPU_INTR_MASK,
- SMEM_APPS_DEM_SLAVE_DATA,
- SMEM_QDSP6_DEM_SLAVE_DATA,
- SMEM_CLKREGIM_BSP,
- SMEM_CLKREGIM_SOURCES,
- SMEM_SMD_FIFO_BASE_ID,
- SMEM_USABLE_RAM_PARTITION_TABLE = SMEM_SMD_FIFO_BASE_ID +
- SMEM_NUM_SMD_STREAM_CHANNELS,
- SMEM_POWER_ON_STATUS_INFO,
- SMEM_DAL_AREA,
- SMEM_SMEM_LOG_POWER_IDX,
- SMEM_SMEM_LOG_POWER_WRAP,
- SMEM_SMEM_LOG_POWER_EVENTS,
- SMEM_ERR_CRASH_LOG,
- SMEM_ERR_F3_TRACE_LOG,
- SMEM_SMD_BRIDGE_ALLOC_TABLE,
- SMEM_SMDLITE_TABLE,
- SMEM_SD_IMG_UPGRADE_STATUS,
- SMEM_SEFS_INFO,
- SMEM_RESET_LOG,
- SMEM_RESET_LOG_SYMBOLS,
- SMEM_MODEM_SW_BUILD_ID,
- SMEM_SMEM_LOG_MPROC_WRAP,
- SMEM_BOOT_INFO_FOR_APPS,
- SMEM_SMSM_SIZE_INFO,
- SMEM_SMD_LOOPBACK_REGISTER,
- SMEM_SSR_REASON_MSS0,
- SMEM_SSR_REASON_WCNSS0,
- SMEM_SSR_REASON_LPASS0,
- SMEM_SSR_REASON_DSPS0,
- SMEM_SSR_REASON_VCODEC0,
- SMEM_SMP2P_APPS_BASE = 427,
- SMEM_SMP2P_MODEM_BASE = SMEM_SMP2P_APPS_BASE + 8, /* 435 */
- SMEM_SMP2P_AUDIO_BASE = SMEM_SMP2P_MODEM_BASE + 8, /* 443 */
- SMEM_SMP2P_WIRLESS_BASE = SMEM_SMP2P_AUDIO_BASE + 8, /* 451 */
- SMEM_SMP2P_POWER_BASE = SMEM_SMP2P_WIRLESS_BASE + 8, /* 459 */
- SMEM_FLASH_DEVICE_INFO = SMEM_SMP2P_POWER_BASE + 8, /* 467 */
- SMEM_BAM_PIPE_MEMORY, /* 468 */
- SMEM_IMAGE_VERSION_TABLE, /* 469 */
- SMEM_LC_DEBUGGER, /* 470 */
- SMEM_NUM_ITEMS,
-};
-
enum {
SMEM_APPS_Q6_SMSM = 3,
SMEM_Q6_APPS_SMSM = 5,
@@ -217,9 +111,6 @@
};
#ifdef CONFIG_MSM_SMD
-void *smem_alloc(unsigned id, unsigned size);
-void *smem_alloc2(unsigned id, unsigned size_in);
-void *smem_get_entry(unsigned id, unsigned *size);
int smsm_change_state(uint32_t smsm_entry,
uint32_t clear_mask, uint32_t set_mask);
@@ -254,36 +145,8 @@
int smsm_check_for_modem_crash(void);
-void *smem_find(unsigned id, unsigned size);
-void *smem_get_entry(unsigned id, unsigned *size);
-
-/**
- * smem_virt_to_phys() - Convert SMEM address to physical address.
- *
- * @smem_address: Virtual address returned by smem_alloc()/smem_alloc2()
- * @returns: Physical address (or NULL if there is a failure)
- *
- * This function should only be used if an SMEM item needs to be handed
- * off to a DMA engine.
- */
-phys_addr_t smem_virt_to_phys(void *smem_address);
#else
-static inline void *smem_alloc(unsigned id, unsigned size)
-{
- return NULL;
-}
-
-static inline void *smem_alloc2(unsigned id, unsigned size_in)
-{
- return NULL;
-}
-
-static inline void *smem_get_entry(unsigned id, unsigned *size)
-{
- return NULL;
-}
-
static inline int smsm_change_state(uint32_t smsm_entry,
uint32_t clear_mask, uint32_t set_mask)
{
@@ -347,13 +210,5 @@
{
return -ENODEV;
}
-static inline void *smem_find(unsigned id, unsigned size)
-{
- return NULL;
-}
-static inline phys_addr_t smem_virt_to_phys(void *smem_address)
-{
- return NULL;
-}
#endif
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_spi.h b/arch/arm/mach-msm/include/mach/msm_spi.h
index ab5271f..608927c 100644
--- a/arch/arm/mach-msm/include/mach/msm_spi.h
+++ b/arch/arm/mach-msm/include/mach/msm_spi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2009, 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2009, 2012-2013 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
@@ -14,8 +14,19 @@
* SPI driver for Qualcomm MSM platforms.
*/
+/**
+ * msm_spi_platform_data: msm spi-controller's configuration data
+ *
+ * @active_only when set, votes when system active and removes the vote when
+ * system goes idle (optimises for performance). When unset, voting using
+ * runtime pm (optimizes for power).
+ * @master_id master id number of the controller's wrapper (BLSP or GSBI).
+ * When zero, clock path voting is disabled.
+ */
struct msm_spi_platform_data {
u32 max_clock_speed;
+ bool active_only;
+ u32 master_id;
int (*gpio_config)(void);
void (*gpio_release)(void);
int (*dma_config)(void);
diff --git a/arch/arm/mach-msm/include/mach/msm_tspp.h b/arch/arm/mach-msm/include/mach/msm_tspp.h
index 696d4ef..ddc99f3 100644
--- a/arch/arm/mach-msm/include/mach/msm_tspp.h
+++ b/arch/arm/mach-msm/include/mach/msm_tspp.h
@@ -20,6 +20,7 @@
const struct msm_gpio *gpios;
const char *tsif_pclk;
const char *tsif_ref_clk;
+ int tsif_vreg_present;
};
struct tspp_data_descriptor {
diff --git a/arch/arm/mach-msm/include/mach/ocmem_priv.h b/arch/arm/mach-msm/include/mach/ocmem_priv.h
index 8539dcc..6f83c53 100644
--- a/arch/arm/mach-msm/include/mach/ocmem_priv.h
+++ b/arch/arm/mach-msm/include/mach/ocmem_priv.h
@@ -42,7 +42,7 @@
struct ocmem_zone;
struct ocmem_zone_ops {
- unsigned long (*allocate) (struct ocmem_zone *, unsigned long);
+ int (*allocate) (struct ocmem_zone *, unsigned long, unsigned long *);
int (*free) (struct ocmem_zone *, unsigned long, unsigned long);
};
@@ -197,9 +197,9 @@
int zone_active(int);
unsigned long offset_to_phys(unsigned long);
unsigned long phys_to_offset(unsigned long);
-unsigned long allocate_head(struct ocmem_zone *, unsigned long);
+int allocate_head(struct ocmem_zone *, unsigned long, unsigned long *);
int free_head(struct ocmem_zone *, unsigned long, unsigned long);
-unsigned long allocate_tail(struct ocmem_zone *, unsigned long);
+int allocate_tail(struct ocmem_zone *, unsigned long, unsigned long *);
int free_tail(struct ocmem_zone *, unsigned long, unsigned long);
int ocmem_notifier_init(void);
diff --git a/arch/arm/mach-msm/include/mach/qcrypto.h b/arch/arm/mach-msm/include/mach/qcrypto.h
new file mode 100644
index 0000000..bb8048f
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qcrypto.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#ifndef _DRIVERS_CRYPTO_MSM_QCRYPTO_H_
+#define _DRIVERS_CRYPTO_MSM_QCRYPTO_H_
+
+#include <linux/crypto.h>
+#include <crypto/hash.h>
+
+#define QCRYPTO_CTX_KEY_MASK 0x000000ff
+#define QCRYPTO_CTX_USE_HW_KEY 0x00000001
+#define QCRYPTO_CTX_USE_PIPE_KEY 0x00000002
+
+#define QCRYPTO_CTX_XTS_MASK 0x0000ff00
+#define QCRYPTO_CTX_XTS_DU_SIZE_512B 0x00000100
+#define QCRYPTO_CTX_XTS_DU_SIZE_1KB 0x00000200
+
+int qcrypto_cipher_set_flag(struct ablkcipher_request *req, unsigned int flags);
+int qcrypto_ahash_set_flag(struct ahash_request *req, unsigned int flags);
+int qcrypto_aead_set_flag(struct aead_request *req, unsigned int flags);
+
+int qcrypto_cipher_clear_flag(struct ablkcipher_request *req,
+ unsigned int flags);
+int qcrypto_ahash_clear_flag(struct ahash_request *req, unsigned int flags);
+int qcrypto_aead_clear_flag(struct aead_request *req, unsigned int flags);
+
+#endif /* _DRIVERS_CRYPTO_MSM_QCRYPTO_H */
diff --git a/arch/arm/mach-msm/include/mach/qseecomi.h b/arch/arm/mach-msm/include/mach/qseecomi.h
index 20dc851..a4021d4 100644
--- a/arch/arm/mach-msm/include/mach/qseecomi.h
+++ b/arch/arm/mach-msm/include/mach/qseecomi.h
@@ -18,6 +18,14 @@
#define QSEECOM_KEY_ID_SIZE 32
+#define QSEOS_RESULT_FAIL_LOAD_KS -57
+#define QSEOS_RESULT_FAIL_SAVE_KS -58
+#define QSEOS_RESULT_FAIL_MAX_KEYS -59
+#define QSEOS_RESULT_FAIL_KEY_ID_EXISTS -60
+#define QSEOS_RESULT_FAIL_KEY_ID_DNE -61
+#define QSEOS_RESULT_FAIL_KS_OP -62
+#define QSEOS_RESULT_FAIL_CE_PIPE_INVALID -63
+
enum qseecom_command_scm_resp_type {
QSEOS_APP_ID = 0xEE01,
QSEOS_LISTENER_ID
@@ -49,6 +57,22 @@
QSEOS_RESULT_FAILURE = 0xFFFFFFFF
};
+/* Key Management requests */
+enum qseecom_qceos_key_gen_cmd_id {
+ QSEOS_GENERATE_KEY = 0x11,
+ QSEOS_DELETE_KEY,
+ QSEOS_MAX_KEY_COUNT,
+ QSEOS_SET_KEY,
+ QSEOS_KEY_CMD_MAX = 0xEFFFFFFF
+};
+
+enum qseecom_pipe_type {
+ QSEOS_PIPE_ENC = 0x1,
+ QSEOS_PIPE_ENC_XTS = 0x2,
+ QSEOS_PIPE_AUTH = 0x4,
+ QSEOS_PIPE_ENUM_FILL = 0x7FFFFFFF
+};
+
__packed struct qsee_apps_region_info_ireq {
uint32_t qsee_cmd_id;
uint32_t addr;
@@ -143,29 +167,24 @@
unsigned int rsp_len; /* in/out */
};
-/* Key Management requests */
-enum qseecom_qceos_key_gen_cmd_id {
- QSEOS_GENERATE_KEY = 0x02,
- QSEOS_SET_KEY,
- QSEOS_DELETE_KEY,
- QSEOS_MAX_KEY_COUNT,
- QSEOS_KEY_CMD_MAX = 0xEFFFFFFF
-};
-
__packed struct qseecom_key_generate_ireq {
+ uint32_t qsee_command_id;
uint32_t flags;
uint8_t key_id[QSEECOM_KEY_ID_SIZE];
};
__packed struct qseecom_key_select_ireq {
+ uint32_t qsee_command_id;
uint32_t ce;
uint32_t pipe;
+ uint32_t pipe_type;
uint32_t flags;
uint8_t key_id[QSEECOM_KEY_ID_SIZE];
unsigned char hash[QSEECOM_HASH_SIZE];
};
__packed struct qseecom_key_delete_ireq {
+ uint32_t qsee_command_id;
uint32_t flags;
uint8_t key_id[QSEECOM_KEY_ID_SIZE];
};
diff --git a/arch/arm/mach-msm/include/mach/scm.h b/arch/arm/mach-msm/include/mach/scm.h
index 42e04dd..4186603 100644
--- a/arch/arm/mach-msm/include/mach/scm.h
+++ b/arch/arm/mach-msm/include/mach/scm.h
@@ -22,7 +22,6 @@
#define SCM_SVC_FUSE 0x8
#define SCM_SVC_PWR 0x9
#define SCM_SVC_MP 0xC
-#define SCM_SVC_CRYPTO 0xA
#define SCM_SVC_DCVS 0xD
#define SCM_SVC_ES 0x10
#define SCM_SVC_TZSCHEDULER 0xFC
diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h
index 7c9882e..d52686c 100644
--- a/arch/arm/mach-msm/include/mach/socinfo.h
+++ b/arch/arm/mach-msm/include/mach/socinfo.h
@@ -36,37 +36,57 @@
#define of_board_is_rumi() of_machine_is_compatible("qcom,rumi")
#define of_board_is_fluid() of_machine_is_compatible("qcom,fluid")
#define of_board_is_liquid() of_machine_is_compatible("qcom,liquid")
+#define of_board_is_dragonboard() \
+ of_machine_is_compatible("qcom,dragonboard")
+#define of_board_is_cdp() of_machine_is_compatible("qcom,cdp")
+#define of_board_is_mtp() of_machine_is_compatible("qcom,mtp")
+#define of_board_is_qrd() of_machine_is_compatible("qcom,qrd")
+#define of_board_is_xpm() of_machine_is_compatible("qcom,xpm")
#define machine_is_msm8974() of_machine_is_compatible("qcom,msm8974")
#define machine_is_msm9625() of_machine_is_compatible("qcom,msm9625")
#define machine_is_msm8610() of_machine_is_compatible("qcom,msm8610")
#define machine_is_msm8226() of_machine_is_compatible("qcom,msm8226")
+#define machine_is_apq8074() of_machine_is_compatible("qcom,apq8074")
#define early_machine_is_msm8610() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8610")
#define early_machine_is_mpq8092() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,mpq8092")
-#define early_machine_is_msmzinc() \
- of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmzinc")
+#define early_machine_is_apq8084() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,apq8084")
#define early_machine_is_msmkrypton() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmkrypton")
+#define early_machine_is_fsm9900() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,fsm9900")
+#define early_machine_is_msmsamarium() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmsamarium")
#else
#define of_board_is_sim() 0
#define of_board_is_rumi() 0
#define of_board_is_fluid() 0
#define of_board_is_liquid() 0
+#define of_board_is_dragonboard() 0
+#define of_board_is_cdp() 0
+#define of_board_is_mtp() 0
+#define of_board_is_qrd() 0
+#define of_board_is_xpm() 0
#define machine_is_msm8974() 0
#define machine_is_msm9625() 0
#define machine_is_msm8610() 0
#define machine_is_msm8226() 0
+#define machine_is_apq8074() 0
#define early_machine_is_msm8610() 0
#define early_machine_is_mpq8092() 0
-#define early_machine_is_msmzinc() 0
+#define early_machine_is_apq8084() 0
#define early_machine_is_msmkrypton() 0
+#define early_machine_is_fsm9900() 0
+#define early_machine_is_msmsamarium() 0
#endif
+#define PLATFORM_SUBTYPE_MDM 1
#define PLATFORM_SUBTYPE_SGLTE 6
enum msm_cpu {
@@ -102,8 +122,10 @@
MSM_CPU_8226,
MSM_CPU_8610,
MSM_CPU_8625Q,
- MSM_CPU_ZINC,
+ MSM_CPU_8084,
MSM_CPU_KRYPTON,
+ FSM_CPU_9900,
+ MSM_CPU_SAMARIUM,
};
enum pmic_model {
diff --git a/arch/arm/mach-msm/include/mach/sps.h b/arch/arm/mach-msm/include/mach/sps.h
index c5ad35d..3332701 100644
--- a/arch/arm/mach-msm/include/mach/sps.h
+++ b/arch/arm/mach-msm/include/mach/sps.h
@@ -1271,6 +1271,15 @@
int sps_get_bam_debug_info(u32 dev, u32 option, u32 para,
u32 tb_sel, u32 desc_sel);
+/**
+ * Vote for or relinquish BAM DMA clock
+ *
+ * @clk_on - to turn on or turn off the clock
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_ctrl_bam_dma_clk(bool clk_on);
#else
static inline int sps_register_bam_device(const struct sps_bam_props
*bam_props, u32 *dev_handle)
@@ -1433,6 +1442,11 @@
{
return -EPERM;
}
+
+static inline int sps_ctrl_bam_dma_clk(bool clk_on)
+{
+ return -EPERM;
+}
#endif
#endif /* _SPS_H_ */
diff --git a/arch/arm/mach-msm/include/mach/subsystem_restart.h b/arch/arm/mach-msm/include/mach/subsystem_restart.h
index 67f643e..962429e 100644
--- a/arch/arm/mach-msm/include/mach/subsystem_restart.h
+++ b/arch/arm/mach-msm/include/mach/subsystem_restart.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, 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
@@ -41,6 +41,8 @@
* @powerup: Start a subsystem
* @crash_shutdown: Shutdown a subsystem when the system crashes (can't sleep)
* @ramdump: Collect a ramdump of the subsystem
+ * @is_not_loadable: Indicate if subsystem firmware is not loadable via pil
+ * framework
*/
struct subsys_desc {
const char *name;
@@ -55,6 +57,8 @@
int (*powerup)(const struct subsys_desc *desc);
void (*crash_shutdown)(const struct subsys_desc *desc);
int (*ramdump)(int, const struct subsys_desc *desc);
+ unsigned int err_ready_irq;
+ int is_not_loadable;
};
#if defined(CONFIG_MSM_SUBSYSTEM_RESTART)
@@ -71,6 +75,8 @@
extern void subsys_unregister(struct subsys_device *dev);
extern void subsys_default_online(struct subsys_device *dev);
+extern void subsys_set_crash_status(struct subsys_device *dev, bool crashed);
+extern bool subsys_get_crash_status(struct subsys_device *dev);
#else
@@ -110,6 +116,12 @@
static inline void subsys_unregister(struct subsys_device *dev) { }
static inline void subsys_default_online(struct subsys_device *dev) { }
+static inline
+void subsys_set_crash_status(struct subsys_device *dev, bool crashed) { }
+static inline bool subsys_get_crash_status(struct subsys_device *dev)
+{
+ return false;
+}
#endif /* CONFIG_MSM_SUBSYSTEM_RESTART */
diff --git a/arch/arm/mach-msm/trace_msm_low_power.h b/arch/arm/mach-msm/include/mach/trace_msm_low_power.h
similarity index 98%
rename from arch/arm/mach-msm/trace_msm_low_power.h
rename to arch/arm/mach-msm/include/mach/trace_msm_low_power.h
index 4e9da85..faa4209 100644
--- a/arch/arm/mach-msm/trace_msm_low_power.h
+++ b/arch/arm/mach-msm/include/mach/trace_msm_low_power.h
@@ -149,6 +149,6 @@
);
#endif
#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_PATH mach
#define TRACE_INCLUDE_FILE trace_msm_low_power
#include <trace/define_trace.h>
diff --git a/arch/arm/mach-msm/trace_rpm_smd.h b/arch/arm/mach-msm/include/mach/trace_rpm_smd.h
similarity index 98%
rename from arch/arm/mach-msm/trace_rpm_smd.h
rename to arch/arm/mach-msm/include/mach/trace_rpm_smd.h
index eff4860..1b019d8 100644
--- a/arch/arm/mach-msm/trace_rpm_smd.h
+++ b/arch/arm/mach-msm/include/mach/trace_rpm_smd.h
@@ -74,6 +74,6 @@
);
#endif
#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_PATH mach
#define TRACE_INCLUDE_FILE trace_rpm_smd
#include <trace/define_trace.h>
diff --git a/arch/arm/mach-msm/include/mach/usb_bam.h b/arch/arm/mach-msm/include/mach/usb_bam.h
index 68313c5..a3e993d 100644
--- a/arch/arm/mach-msm/include/mach/usb_bam.h
+++ b/arch/arm/mach-msm/include/mach/usb_bam.h
@@ -42,6 +42,12 @@
OCI_MEM, /* Shared memory among peripherals */
};
+enum usb_bam_event_type {
+ USB_BAM_EVENT_WAKEUP_PIPE = 0, /* Wake a pipe */
+ USB_BAM_EVENT_WAKEUP, /* Wake a bam (first pipe waked) */
+ USB_BAM_EVENT_INACTIVITY, /* Inactivity on all pipes */
+};
+
struct usb_bam_connect_ipa_params {
u8 src_idx;
u8 dst_idx;
@@ -57,16 +63,20 @@
void *priv;
void (*notify)(void *priv, enum ipa_dp_evt_type evt,
unsigned long data);
+ int (*activity_notify)(void *priv);
+ int (*inactivity_notify)(void *priv);
};
/**
* struct usb_bam_event_info: suspend/resume event information.
+* @type: usb bam event type.
* @event: holds event data.
* @callback: suspend/resume callback.
* @param: port num (for suspend) or NULL (for resume).
* @event_w: holds work queue parameters.
*/
struct usb_bam_event_info {
+ enum usb_bam_event_type type;
struct sps_register_event event;
int (*callback)(void *);
void *param;
@@ -89,8 +99,13 @@
* @desc_fifo_size: descriptor fifo size.
* @data_mem_buf: data fifo buffer.
* @desc_mem_buf: descriptor fifo buffer.
-* @wake_event: event for wakeup.
+* @event: event for wakeup.
* @enabled: true if pipe is enabled.
+* @ipa_clnt_hdl : pipe handle to ipa api.
+* @priv: private data to return upon activity_notify
+* or inactivity_notify callbacks.
+* @activity_notify: callback to invoke on activity on one of the in pipes.
+* @inactivity_notify: callback to invoke on inactivity on all pipes.
*/
struct usb_bam_pipe_connect {
const char *name;
@@ -109,8 +124,13 @@
u32 desc_fifo_size;
struct sps_mem_buffer data_mem_buf;
struct sps_mem_buffer desc_mem_buf;
- struct usb_bam_event_info wake_event;
+ struct usb_bam_event_info event;
bool enabled;
+ bool suspended;
+ int ipa_clnt_hdl;
+ void *priv;
+ int (*activity_notify)(void *priv);
+ int (*inactivity_notify)(void *priv);
};
/**
@@ -154,7 +174,10 @@
/**
* Connect USB-to-IPA SPS connection.
*
- * This function returns the allocated pipes number and clnt handles.
+ * This function returns the allocated pipes number and clnt
+ * handles. Assumes that the user first connects producer pipes
+ * and only after that consumer pipes, since that's the correct
+ * sequence for the handshake with the IPA.
*
* @ipa_params - in/out parameters
*
@@ -175,6 +198,14 @@
struct usb_bam_connect_ipa_params *ipa_params);
/**
+ * Wait for Consumer granted from Resource Manager.
+ *
+ * @ipa_params - in/out parameters
+ *
+ */
+void usb_bam_wait_for_cons_granted(
+ struct usb_bam_connect_ipa_params *ipa_params);
+/**
* Register a wakeup callback from peer BAM.
*
* @idx - Connection index.
@@ -201,6 +232,39 @@
int usb_bam_register_peer_reset_cb(int (*callback)(void *), void *param);
/**
+ * Register callbacks for start/stop of transfers.
+ *
+ * @start - the callback function that will be called in USB
+ * driver to start transfers
+ * @stop - the callback function that will be called in USB
+ * driver to stop transfers
+ *
+ * @param - context that the caller can supply
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int usb_bam_register_start_stop_cbs(
+ void (*start)(void *, enum usb_bam_pipe_dir),
+ void (*stop)(void *, enum usb_bam_pipe_dir),
+ void *param);
+
+/**
+ * Start usb suspend sequence
+ *
+ * @ipa_params - in/out parameters
+ *
+ */
+void usb_bam_suspend(struct usb_bam_connect_ipa_params *ipa_params);
+
+/**
+ * Start usb resume sequence
+ *
+ * @ipa_params - in/out parameters
+ *
+ */
+void usb_bam_resume(struct usb_bam_connect_ipa_params *ipa_params);
+/**
* Disconnect USB-to-Periperal SPS connection.
*
* @idx - Connection index.
@@ -234,7 +298,7 @@
* Resets the USB BAM that has A2 pipes
*
*/
-int usb_bam_a2_reset(void);
+int usb_bam_a2_reset(bool to_reconnect);
/**
* Indicates if the client of the USB BAM is ready to start
@@ -244,6 +308,14 @@
*
*/
int usb_bam_client_ready(bool ready);
+
+/**
+* Returns upon reset completion if reset is in progress
+* immediately otherwise.
+*
+*/
+void usb_bam_reset_complete(void);
+
/**
* Returns qdss index from the connections array.
*
@@ -295,6 +367,12 @@
return -ENODEV;
}
+static inline void usb_bam_wait_for_cons_granted(
+ struct usb_bam_connect_ipa_params *ipa_params)
+{
+ return;
+}
+
static inline int usb_bam_register_wake_cb(u8 idx,
int (*callback)(void *), void* param)
{
@@ -307,6 +385,20 @@
return -ENODEV;
}
+static inline int usb_bam_register_start_stop_cbs(
+ void (*start)(void *, enum usb_bam_pipe_dir),
+ void (*stop)(void *, enum usb_bam_pipe_dir),
+ void *param)
+{
+ return -ENODEV;
+}
+
+static inline void usb_bam_suspend(
+ struct usb_bam_connect_ipa_params *ipa_params){}
+
+static inline void usb_bam_resume(
+ struct usb_bam_connect_ipa_params *ipa_params) {}
+
static inline int usb_bam_disconnect_pipe(u8 idx)
{
return -ENODEV;
@@ -319,7 +411,7 @@
return -ENODEV;
}
-static inline int usb_bam_a2_reset(void)
+static inline int usb_bam_a2_reset(bool to_reconnect)
{
return -ENODEV;
}
@@ -329,6 +421,11 @@
return -ENODEV;
}
+static inline void usb_bam_reset_complete(void)
+{
+ return;
+}
+
static inline int usb_bam_get_qdss_idx(u8 num)
{
return -ENODEV;
diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c
index 65a5afb..f736b30 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -69,7 +69,6 @@
MSM_CHIP_DEVICE(GPIO2, MSM7XXX),
MSM_CHIP_DEVICE(CLK_CTL, MSM7XXX),
MSM_CHIP_DEVICE(AD5, MSM7XXX),
- MSM_CHIP_DEVICE(MDC, MSM7XXX),
#if defined(CONFIG_DEBUG_MSM_UART1) || defined(CONFIG_DEBUG_MSM_UART2) || \
defined(CONFIG_DEBUG_MSM_UART3)
MSM_DEVICE(DEBUG_UART),
@@ -117,7 +116,6 @@
MSM_DEVICE(SIRC),
MSM_DEVICE(SCPLL),
MSM_DEVICE(AD5),
- MSM_DEVICE(MDC),
MSM_DEVICE(TCSR),
#if defined(CONFIG_DEBUG_MSM_UART1) || defined(CONFIG_DEBUG_MSM_UART2) || \
defined(CONFIG_DEBUG_MSM_UART3)
@@ -321,27 +319,27 @@
}
#endif /* CONFIG_ARCH_MSM8974 */
-#ifdef CONFIG_ARCH_MSMZINC
-static struct map_desc msm_zinc_io_desc[] __initdata = {
- MSM_CHIP_DEVICE(QGIC_DIST, MSMZINC),
- MSM_CHIP_DEVICE(TLMM, MSMZINC),
+#ifdef CONFIG_ARCH_APQ8084
+static struct map_desc msm_8084_io_desc[] __initdata = {
+ MSM_CHIP_DEVICE(QGIC_DIST, APQ8084),
+ MSM_CHIP_DEVICE(TLMM, APQ8084),
{
.virtual = (unsigned long) MSM_SHARED_RAM_BASE,
.length = MSM_SHARED_RAM_SIZE,
.type = MT_DEVICE,
},
-#ifdef CONFIG_DEBUG_MSMZINC_UART
+#ifdef CONFIG_DEBUG_APQ8084_UART
MSM_DEVICE(DEBUG_UART),
#endif
};
-void __init msm_map_zinc_io(void)
+void __init msm_map_8084_io(void)
{
- msm_shared_ram_phys = MSMZINC_SHARED_RAM_PHYS;
- msm_map_io(msm_zinc_io_desc, ARRAY_SIZE(msm_zinc_io_desc));
+ msm_shared_ram_phys = APQ8084_SHARED_RAM_PHYS;
+ msm_map_io(msm_8084_io_desc, ARRAY_SIZE(msm_8084_io_desc));
of_scan_flat_dt(msm_scan_dt_map_imem, NULL);
}
-#endif /* CONFIG_ARCH_MSMZINC */
+#endif /* CONFIG_ARCH_APQ8084 */
#ifdef CONFIG_ARCH_MSM7X30
static struct map_desc msm7x30_io_desc[] __initdata = {
@@ -353,7 +351,6 @@
MSM_CHIP_DEVICE(CLK_CTL, MSM7X30),
MSM_CHIP_DEVICE(CLK_CTL_SH2, MSM7X30),
MSM_CHIP_DEVICE(AD5, MSM7X30),
- MSM_CHIP_DEVICE(MDC, MSM7X30),
MSM_CHIP_DEVICE(ACC0, MSM7X30),
MSM_CHIP_DEVICE(SAW0, MSM7X30),
MSM_CHIP_DEVICE(APCS_GCC, MSM7X30),
@@ -406,6 +403,28 @@
}
#endif /* CONFIG_ARCH_FSM9XXX */
+#ifdef CONFIG_ARCH_FSM9900
+static struct map_desc fsm9900_io_desc[] __initdata = {
+ MSM_CHIP_DEVICE(QGIC_DIST, FSM9900),
+ MSM_CHIP_DEVICE(TLMM, FSM9900),
+ {
+ .virtual = (unsigned long) MSM_SHARED_RAM_BASE,
+ .length = MSM_SHARED_RAM_SIZE,
+ .type = MT_DEVICE,
+ },
+#ifdef CONFIG_DEBUG_FSM9900_UART
+ MSM_DEVICE(DEBUG_UART),
+#endif
+};
+
+void __init msm_map_fsm9900_io(void)
+{
+ msm_shared_ram_phys = FSM9900_SHARED_RAM_PHYS;
+ msm_map_io(fsm9900_io_desc, ARRAY_SIZE(fsm9900_io_desc));
+ of_scan_flat_dt(msm_scan_dt_map_imem, NULL);
+}
+#endif /* CONFIG_ARCH_FSM9900 */
+
#ifdef CONFIG_ARCH_MSM9615
static struct map_desc msm9615_io_desc[] __initdata = {
MSM_CHIP_DEVICE(QGIC_DIST, MSM9615),
@@ -454,7 +473,6 @@
MSM_CHIP_DEVICE(SAW2, MSM8625),
MSM_CHIP_DEVICE(SAW3, MSM8625),
MSM_CHIP_DEVICE(AD5, MSM7XXX),
- MSM_CHIP_DEVICE(MDC, MSM7XXX),
#if defined(CONFIG_DEBUG_MSM_UART1) || defined(CONFIG_DEBUG_MSM_UART2) || \
defined(CONFIG_DEBUG_MSM_UART3)
MSM_DEVICE(DEBUG_UART),
@@ -544,6 +562,7 @@
{
msm_shared_ram_phys = MPQ8092_MSM_SHARED_RAM_PHYS;
msm_map_io(mpq8092_io_desc, ARRAY_SIZE(mpq8092_io_desc));
+ of_scan_flat_dt(msm_scan_dt_map_imem, NULL);
}
#endif /* CONFIG_ARCH_MPQ8092 */
@@ -574,6 +593,7 @@
#ifdef CONFIG_ARCH_MSM8610
static struct map_desc msm8610_io_desc[] __initdata = {
+ MSM_CHIP_DEVICE(QGIC_DIST, MSM8610),
MSM_CHIP_DEVICE(APCS_GCC, MSM8610),
MSM_CHIP_DEVICE(TLMM, MSM8610),
MSM_CHIP_DEVICE(MPM2_PSHOLD, MSM8610),
@@ -591,3 +611,26 @@
of_scan_flat_dt(msm_scan_dt_map_imem, NULL);
}
#endif /* CONFIG_ARCH_MSM8610 */
+
+#ifdef CONFIG_ARCH_MSMSAMARIUM
+static struct map_desc msmsamarium_io_desc[] __initdata = {
+ MSM_CHIP_DEVICE(QGIC_DIST, MSMSAMARIUM),
+ MSM_CHIP_DEVICE(TLMM, MSMSAMARIUM),
+ MSM_CHIP_DEVICE(MPM2_PSHOLD, MSMSAMARIUM),
+ {
+ .virtual = (unsigned long) MSM_SHARED_RAM_BASE,
+ .length = MSM_SHARED_RAM_SIZE,
+ .type = MT_DEVICE,
+ },
+#if defined(CONFIG_DEBUG_MSMSAMARIUM_UART) || defined(CONFIG_DEBUG_MSM8974_UART)
+ MSM_DEVICE(DEBUG_UART),
+#endif
+};
+
+void __init msm_map_msmsamarium_io(void)
+{
+ msm_shared_ram_phys = MSMSAMARIUM_SHARED_RAM_PHYS;
+ msm_map_io(msmsamarium_io_desc, ARRAY_SIZE(msmsamarium_io_desc));
+ of_scan_flat_dt(msm_scan_dt_map_imem, NULL);
+}
+#endif /* CONFIG_ARCH_MSMSAMARIUM */
diff --git a/arch/arm/mach-msm/iommu_domains.c b/arch/arm/mach-msm/iommu_domains.c
index 18562a3..20a5249 100644
--- a/arch/arm/mach-msm/iommu_domains.c
+++ b/arch/arm/mach-msm/iommu_domains.c
@@ -21,6 +21,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
+#include <linux/idr.h>
#include <asm/sizes.h>
#include <asm/page.h>
#include <mach/iommu.h>
@@ -36,9 +37,14 @@
int domain_num;
};
+struct msm_iommu_data_entry {
+ struct list_head list;
+ void *data;
+};
+
static struct rb_root domain_root;
DEFINE_MUTEX(domain_mutex);
-static atomic_t domain_nums = ATOMIC_INIT(-1);
+static DEFINE_IDA(domain_nums);
void msm_iommu_set_client_name(struct iommu_domain *domain, char const *name)
{
@@ -183,6 +189,7 @@
{
unsigned long iova;
int ret;
+ struct iommu_domain *domain;
if (size & (align - 1))
return -EINVAL;
@@ -198,8 +205,14 @@
if (ret)
return -ENOMEM;
- ret = msm_iommu_map_iova_phys(msm_get_iommu_domain(domain_no), iova,
- phys, size, cached);
+ domain = msm_get_iommu_domain(domain_no);
+ if (!domain) {
+ pr_err("%s: Could not find domain %u. Unable to map\n",
+ __func__, domain_no);
+ msm_free_iova_address(iova, domain_no, partition_no, size);
+ return -EINVAL;
+ }
+ ret = msm_iommu_map_iova_phys(domain, iova, phys, size, cached);
if (ret)
msm_free_iova_address(iova, domain_no, partition_no, size);
@@ -215,10 +228,18 @@
unsigned int partition_no,
unsigned long size)
{
+ struct iommu_domain *domain;
+
if (!msm_use_iommu())
return;
- iommu_unmap_range(msm_get_iommu_domain(domain_no), iova, size);
+ domain = msm_get_iommu_domain(domain_no);
+ if (domain) {
+ iommu_unmap_range(domain, iova, size);
+ } else {
+ pr_err("%s: Could not find domain %u. Unable to unmap\n",
+ __func__, domain_no);
+ }
msm_free_iova_address(iova, domain_no, partition_no, size);
}
EXPORT_SYMBOL(msm_iommu_unmap_contig_buffer);
@@ -226,10 +247,10 @@
static struct msm_iova_data *find_domain(int domain_num)
{
struct rb_root *root = &domain_root;
- struct rb_node *p = root->rb_node;
+ struct rb_node *p;
mutex_lock(&domain_mutex);
-
+ p = root->rb_node;
while (p) {
struct msm_iova_data *node;
@@ -273,6 +294,27 @@
return 0;
}
+static int remove_domain(struct iommu_domain *domain)
+{
+ struct rb_root *root = &domain_root;
+ struct rb_node *n;
+ struct msm_iova_data *node;
+ int ret = -EINVAL;
+
+ mutex_lock(&domain_mutex);
+
+ for (n = rb_first(root); n; n = rb_next(n)) {
+ node = rb_entry(n, struct msm_iova_data, node);
+ if (node->domain == domain) {
+ rb_erase(&node->node, &domain_root);
+ ret = 0;
+ break;
+ }
+ }
+ mutex_unlock(&domain_mutex);
+ return ret;
+}
+
struct iommu_domain *msm_get_iommu_domain(int domain_num)
{
struct msm_iova_data *data;
@@ -307,6 +349,27 @@
}
EXPORT_SYMBOL(msm_find_domain_no);
+static struct msm_iova_data *msm_domain_to_iova_data(struct iommu_domain
+ const *domain)
+{
+ struct rb_root *root = &domain_root;
+ struct rb_node *n;
+ struct msm_iova_data *node;
+ struct msm_iova_data *iova_data = ERR_PTR(-EINVAL);
+
+ mutex_lock(&domain_mutex);
+
+ for (n = rb_first(root); n; n = rb_next(n)) {
+ node = rb_entry(n, struct msm_iova_data, node);
+ if (node->domain == domain) {
+ iova_data = node;
+ break;
+ }
+ }
+ mutex_unlock(&domain_mutex);
+ return iova_data;
+}
+
int msm_allocate_iova_address(unsigned int iommu_domain,
unsigned int partition_no,
unsigned long size,
@@ -330,7 +393,9 @@
if (!pool->gpool)
return -EINVAL;
+ mutex_lock(&pool->pool_mutex);
va = gen_pool_alloc_aligned(pool->gpool, size, ilog2(align));
+ mutex_unlock(&pool->pool_mutex);
if (va) {
pool->free -= size;
/* Offset because genpool can't handle 0 addresses */
@@ -375,7 +440,9 @@
if (pool->paddr == 0)
iova += SZ_4K;
+ mutex_lock(&pool->pool_mutex);
gen_pool_free(pool->gpool, iova, size);
+ mutex_unlock(&pool->pool_mutex);
}
int msm_register_domain(struct msm_iova_layout *layout)
@@ -393,11 +460,11 @@
if (!data)
return -ENOMEM;
- pools = kmalloc(sizeof(struct mem_pool) * layout->npartitions,
+ pools = kzalloc(sizeof(struct mem_pool) * layout->npartitions,
GFP_KERNEL);
if (!pools)
- goto out;
+ goto free_data;
for (i = 0; i < layout->npartitions; i++) {
if (layout->partitions[i].size == 0)
@@ -410,6 +477,7 @@
pools[i].paddr = layout->partitions[i].start;
pools[i].size = layout->partitions[i].size;
+ mutex_init(&pools[i].pool_mutex);
/*
* genalloc can't handle a pool starting at address 0.
@@ -435,10 +503,13 @@
data->pools = pools;
data->npools = layout->npartitions;
- data->domain_num = atomic_inc_return(&domain_nums);
+ data->domain_num = ida_simple_get(&domain_nums, 0, 0, GFP_KERNEL);
+ if (data->domain_num < 0)
+ goto free_pools;
+
data->domain = iommu_domain_alloc(bus, layout->domain_flags);
if (!data->domain)
- goto out;
+ goto free_domain_num;
msm_iommu_set_client_name(data->domain, layout->client_name);
@@ -446,13 +517,50 @@
return data->domain_num;
-out:
+free_domain_num:
+ ida_simple_remove(&domain_nums, data->domain_num);
+
+free_pools:
+ for (i = 0; i < layout->npartitions; i++) {
+ if (pools[i].gpool)
+ gen_pool_destroy(pools[i].gpool);
+ }
+ kfree(pools);
+free_data:
kfree(data);
return -EINVAL;
}
EXPORT_SYMBOL(msm_register_domain);
+int msm_unregister_domain(struct iommu_domain *domain)
+{
+ unsigned int i;
+ struct msm_iova_data *data = msm_domain_to_iova_data(domain);
+
+ if (IS_ERR_OR_NULL(data)) {
+ pr_err("%s: Could not find iova_data\n", __func__);
+ return -EINVAL;
+ }
+
+ if (remove_domain(data->domain)) {
+ pr_err("%s: Domain not found. Failed to remove domain\n",
+ __func__);
+ }
+
+ iommu_domain_free(domain);
+
+ ida_simple_remove(&domain_nums, data->domain_num);
+
+ for (i = 0; i < data->npools; ++i)
+ gen_pool_destroy(data->pools[i].gpool);
+
+ kfree(data->pools);
+ kfree(data);
+ return 0;
+}
+EXPORT_SYMBOL(msm_unregister_domain);
+
static int find_and_add_contexts(struct iommu_group *group,
const struct device_node *node,
unsigned int num_contexts)
@@ -477,12 +585,14 @@
goto out;
}
ctx = msm_iommu_get_ctx(name);
- if (!ctx) {
- pr_err("Unable to find context %s\n", name);
- ret_val = -EINVAL;
+ if (IS_ERR(ctx)) {
+ ret_val = PTR_ERR(ctx);
goto out;
}
- iommu_group_add_device(group, ctx);
+
+ ret_val = iommu_group_add_device(group, ctx);
+ if (ret_val)
+ goto out;
}
out:
return ret_val;
@@ -497,7 +607,7 @@
struct msm_iova_layout l;
struct msm_iova_partition *part = 0;
struct iommu_domain *domain = 0;
- unsigned int *addr_array;
+ unsigned int *addr_array = 0;
unsigned int array_size;
int domain_no;
int secure_domain;
@@ -568,11 +678,46 @@
iommu_group_set_iommudata(group, domain, NULL);
free_mem:
+ kfree(addr_array);
kfree(part);
out:
return ret_val;
}
+static int __msm_group_get_domain(struct device *dev, void *data)
+{
+ struct msm_iommu_data_entry *list_entry;
+ struct list_head *dev_list = data;
+ int ret_val = 0;
+
+ list_entry = kmalloc(sizeof(*list_entry), GFP_KERNEL);
+ if (list_entry) {
+ list_entry->data = dev;
+ list_add(&list_entry->list, dev_list);
+ } else {
+ ret_val = -ENOMEM;
+ }
+
+ return ret_val;
+}
+
+static void __msm_iommu_group_remove_device(struct iommu_group *grp)
+{
+ struct msm_iommu_data_entry *tmp;
+ struct msm_iommu_data_entry *list_entry;
+ struct list_head dev_list;
+
+ INIT_LIST_HEAD(&dev_list);
+ iommu_group_for_each_dev(grp, &dev_list, __msm_group_get_domain);
+
+ list_for_each_entry_safe(list_entry, tmp, &dev_list, list) {
+ iommu_group_remove_device(list_entry->data);
+ list_del(&list_entry->list);
+ kfree(list_entry);
+ }
+}
+
+
static int iommu_domain_parse_dt(const struct device_node *dt_node)
{
struct device_node *node;
@@ -581,13 +726,30 @@
int ret_val = 0;
struct iommu_group *group = 0;
const char *name;
+ struct msm_iommu_data_entry *grp_list_entry;
+ struct msm_iommu_data_entry *tmp;
+ struct list_head iommu_group_list;
+ INIT_LIST_HEAD(&iommu_group_list);
for_each_child_of_node(dt_node, node) {
group = iommu_group_alloc();
if (IS_ERR(group)) {
ret_val = PTR_ERR(group);
- goto out;
+ group = 0;
+ goto free_group;
}
+
+ /* This is only needed to clean up memory if something fails */
+ grp_list_entry = kmalloc(sizeof(*grp_list_entry),
+ GFP_KERNEL);
+ if (grp_list_entry) {
+ grp_list_entry->data = group;
+ list_add(&grp_list_entry->list, &iommu_group_list);
+ } else {
+ ret_val = -ENOMEM;
+ goto free_group;
+ }
+
if (of_property_read_string(node, "label", &name)) {
ret_val = -EINVAL;
goto free_group;
@@ -603,7 +765,6 @@
ret_val = find_and_add_contexts(group, node, num_contexts);
if (ret_val) {
- ret_val = -EINVAL;
goto free_group;
}
ret_val = create_and_add_domain(group, node, name);
@@ -611,9 +772,33 @@
ret_val = -EINVAL;
goto free_group;
}
+
+ /* Remove reference to the group that is taken when the group
+ * is allocated. This will ensure that when all the devices in
+ * the group are removed the group will be released.
+ */
+ iommu_group_put(group);
}
+
+ list_for_each_entry_safe(grp_list_entry, tmp, &iommu_group_list, list) {
+ list_del(&grp_list_entry->list);
+ kfree(grp_list_entry);
+ }
+ goto out;
+
free_group:
- /* No iommu_group_free() function */
+ list_for_each_entry_safe(grp_list_entry, tmp, &iommu_group_list, list) {
+ struct iommu_domain *d;
+
+ d = iommu_group_get_iommudata(grp_list_entry->data);
+ if (d)
+ msm_unregister_domain(d);
+
+ __msm_iommu_group_remove_device(grp_list_entry->data);
+ list_del(&grp_list_entry->list);
+ kfree(grp_list_entry);
+ }
+ iommu_group_put(group);
out:
return ret_val;
}
diff --git a/arch/arm/mach-msm/ipc_logging.c b/arch/arm/mach-msm/ipc_logging.c
index 1260a1a..43214cc 100644
--- a/arch/arm/mach-msm/ipc_logging.c
+++ b/arch/arm/mach-msm/ipc_logging.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -32,7 +32,7 @@
#include "ipc_logging.h"
static LIST_HEAD(ipc_log_context_list);
-DEFINE_SPINLOCK(ipc_log_context_list_lock);
+DEFINE_RWLOCK(ipc_log_context_list_lock);
static atomic_t next_log_id = ATOMIC_INIT(0);
static struct ipc_log_page *get_first_page(struct ipc_log_context *ilctxt)
@@ -140,7 +140,7 @@
return;
}
- spin_lock_irqsave(&ipc_log_context_list_lock, flags);
+ read_lock_irqsave(&ipc_log_context_list_lock, flags);
spin_lock(&ilctxt->ipc_log_context_lock);
while (ilctxt->write_avail < ectxt->offset)
msg_read(ilctxt, NULL);
@@ -165,7 +165,7 @@
ilctxt->write_avail -= ectxt->offset;
complete(&ilctxt->read_avail);
spin_unlock(&ilctxt->ipc_log_context_lock);
- spin_unlock_irqrestore(&ipc_log_context_list_lock, flags);
+ read_unlock_irqrestore(&ipc_log_context_list_lock, flags);
}
EXPORT_SYMBOL(ipc_log_write);
@@ -471,13 +471,13 @@
if (!df_info)
return -ENOSPC;
- spin_lock_irqsave(&ipc_log_context_list_lock, flags);
+ read_lock_irqsave(&ipc_log_context_list_lock, flags);
spin_lock(&ilctxt->ipc_log_context_lock);
df_info->type = type;
df_info->dfunc = dfunc;
list_add_tail(&df_info->list, &ilctxt->dfunc_info_list);
spin_unlock(&ilctxt->ipc_log_context_lock);
- spin_unlock_irqrestore(&ipc_log_context_list_lock, flags);
+ read_unlock_irqrestore(&ipc_log_context_list_lock, flags);
return 0;
}
EXPORT_SYMBOL(add_deserialization_func);
@@ -528,9 +528,9 @@
create_ctx_debugfs(ctxt, mod_name);
- spin_lock_irqsave(&ipc_log_context_list_lock, flags);
+ write_lock_irqsave(&ipc_log_context_list_lock, flags);
list_add_tail(&ctxt->list, &ipc_log_context_list);
- spin_unlock_irqrestore(&ipc_log_context_list_lock, flags);
+ write_unlock_irqrestore(&ipc_log_context_list_lock, flags);
return (void *)ctxt;
release_ipc_log_context:
@@ -544,6 +544,35 @@
}
EXPORT_SYMBOL(ipc_log_context_create);
+/*
+ * Destroy debug log context
+ *
+ * @ctxt: debug log context created by calling ipc_log_context_create API.
+ */
+int ipc_log_context_destroy(void *ctxt)
+{
+ struct ipc_log_context *ilctxt = (struct ipc_log_context *)ctxt;
+ struct ipc_log_page *pg = NULL;
+ unsigned long flags;
+
+ if (!ilctxt)
+ return 0;
+
+ while (!list_empty(&ilctxt->page_list)) {
+ pg = get_first_page(ctxt);
+ list_del(&pg->hdr.list);
+ kfree(pg);
+ }
+
+ write_lock_irqsave(&ipc_log_context_list_lock, flags);
+ list_del(&ilctxt->list);
+ write_unlock_irqrestore(&ipc_log_context_list_lock, flags);
+
+ kfree(ilctxt);
+ return 0;
+}
+EXPORT_SYMBOL(ipc_log_context_destroy);
+
static int __init ipc_logging_init(void)
{
check_and_create_debugfs();
diff --git a/arch/arm/mach-msm/ipc_logging.h b/arch/arm/mach-msm/ipc_logging.h
index 0eb82a5..36b4171 100644
--- a/arch/arm/mach-msm/ipc_logging.h
+++ b/arch/arm/mach-msm/ipc_logging.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -65,7 +65,7 @@
#define IS_MSG_TYPE(x) (((x) > TSV_TYPE_MSG_START) && \
((x) < TSV_TYPE_MSG_END))
-extern spinlock_t ipc_log_context_list_lock;
+extern rwlock_t ipc_log_context_list_lock;
extern int msg_read(struct ipc_log_context *ilctxt,
struct encode_context *ectxt);
diff --git a/arch/arm/mach-msm/ipc_logging_debug.c b/arch/arm/mach-msm/ipc_logging_debug.c
index ff947ef..246fb99 100644
--- a/arch/arm/mach-msm/ipc_logging_debug.c
+++ b/arch/arm/mach-msm/ipc_logging_debug.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -62,7 +62,7 @@
dctxt.output_format = OUTPUT_DEBUGFS;
dctxt.buff = buff;
dctxt.size = size;
- spin_lock_irqsave(&ipc_log_context_list_lock, flags);
+ read_lock_irqsave(&ipc_log_context_list_lock, flags);
spin_lock(&ilctxt->ipc_log_context_lock);
while (dctxt.size >= MAX_MSG_DECODED_SIZE &&
!is_ilctxt_empty(ilctxt)) {
@@ -70,19 +70,19 @@
deserialize_func = get_deserialization_func(ilctxt,
ectxt.hdr.type);
spin_unlock(&ilctxt->ipc_log_context_lock);
- spin_unlock_irqrestore(&ipc_log_context_list_lock, flags);
+ read_unlock_irqrestore(&ipc_log_context_list_lock, flags);
if (deserialize_func)
deserialize_func(&ectxt, &dctxt);
else
pr_err("%s: unknown message 0x%x\n",
__func__, ectxt.hdr.type);
- spin_lock_irqsave(&ipc_log_context_list_lock, flags);
+ read_lock_irqsave(&ipc_log_context_list_lock, flags);
spin_lock(&ilctxt->ipc_log_context_lock);
}
if ((size - dctxt.size) == 0)
init_completion(&ilctxt->read_avail);
spin_unlock(&ilctxt->ipc_log_context_lock);
- spin_unlock_irqrestore(&ipc_log_context_list_lock, flags);
+ read_unlock_irqrestore(&ipc_log_context_list_lock, flags);
return size - dctxt.size;
}
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index 0d617a6..56eaa2b 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -27,6 +27,7 @@
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/debugfs.h>
+#include <linux/rwsem.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
@@ -99,11 +100,11 @@
#define IPC_ROUTER_LOG_EVENT_RX 0x12
static LIST_HEAD(control_ports);
-static DEFINE_MUTEX(control_ports_lock);
+static DECLARE_RWSEM(control_ports_lock_lha5);
#define LP_HASH_SIZE 32
static struct list_head local_ports[LP_HASH_SIZE];
-static DEFINE_MUTEX(local_ports_lock);
+static DECLARE_RWSEM(local_ports_lock_lha2);
/*
* Server info is organized as a hash table. The server's service ID is
@@ -114,8 +115,7 @@
*/
#define SRV_HASH_SIZE 32
static struct list_head server_list[SRV_HASH_SIZE];
-static DEFINE_MUTEX(server_list_lock);
-static wait_queue_head_t newserver_wait;
+static DECLARE_RWSEM(server_list_lock_lha2);
struct msm_ipc_server {
struct list_head list;
@@ -133,16 +133,22 @@
struct msm_ipc_router_xprt_info *xprt_info;
};
+struct msm_ipc_resume_tx_port {
+ struct list_head list;
+ uint32_t port_id;
+ uint32_t node_id;
+};
+
#define RP_HASH_SIZE 32
struct msm_ipc_router_remote_port {
struct list_head list;
uint32_t node_id;
uint32_t port_id;
- uint32_t restart_state;
- wait_queue_head_t quota_wait;
uint32_t tx_quota_cnt;
- struct mutex quota_lock;
+ struct mutex quota_lock_lhb2;
+ struct list_head resume_tx_port_list;
void *sec_rule;
+ struct msm_ipc_server *server;
};
struct msm_ipc_router_xprt_info {
@@ -152,8 +158,8 @@
uint32_t initialized;
struct list_head pkt_list;
struct wake_lock wakelock;
- struct mutex rx_lock;
- struct mutex tx_lock;
+ struct mutex rx_lock_lhb2;
+ struct mutex tx_lock_lhb2;
uint32_t need_len;
uint32_t abort_data_read;
struct work_struct read_data;
@@ -167,39 +173,25 @@
uint32_t neighbor_node_id;
struct list_head remote_port_list[RP_HASH_SIZE];
struct msm_ipc_router_xprt_info *xprt_info;
- struct mutex lock;
+ struct rw_semaphore lock_lha4;
unsigned long num_tx_bytes;
unsigned long num_rx_bytes;
};
static struct list_head routing_table[RT_HASH_SIZE];
-static DEFINE_MUTEX(routing_table_lock);
+static DECLARE_RWSEM(routing_table_lock_lha3);
static int routing_table_inited;
-static LIST_HEAD(msm_ipc_board_dev_list);
-static DEFINE_MUTEX(msm_ipc_board_dev_list_lock);
-
static void do_read_data(struct work_struct *work);
-#define RR_STATE_IDLE 0
-#define RR_STATE_HEADER 1
-#define RR_STATE_BODY 2
-#define RR_STATE_ERROR 3
-
-#define RESTART_NORMAL 0
-#define RESTART_PEND 1
-
-/* State for remote ep following restart */
-#define RESTART_QUOTA_ABORT 1
-
static LIST_HEAD(xprt_info_list);
-static DEFINE_MUTEX(xprt_info_list_lock);
+static DECLARE_RWSEM(xprt_info_list_lock_lha5);
static DECLARE_COMPLETION(msm_ipc_local_router_up);
#define IPC_ROUTER_INIT_TIMEOUT (10 * HZ)
static uint32_t next_port_id;
-static DEFINE_MUTEX(next_port_id_lock);
+static DEFINE_MUTEX(next_port_id_lock_lha1);
static atomic_t pending_close_count = ATOMIC_INIT(0);
static wait_queue_head_t subsystem_restart_wait;
static struct workqueue_struct *msm_ipc_router_workqueue;
@@ -233,13 +225,13 @@
for (i = 0; i < RP_HASH_SIZE; i++)
INIT_LIST_HEAD(&rt_entry->remote_port_list[i]);
- mutex_init(&rt_entry->lock);
+ init_rwsem(&rt_entry->lock_lha4);
rt_entry->node_id = node_id;
rt_entry->xprt_info = NULL;
return rt_entry;
}
-/*Please take routing_table_lock before calling this function*/
+/* Must be called with routing_table_lock_lha3 locked. */
static int add_routing_table_entry(
struct msm_ipc_routing_table_entry *rt_entry)
{
@@ -253,7 +245,7 @@
return 0;
}
-/*Please take routing_table_lock before calling this function*/
+/* Must be called with routing_table_lock_lha3 locked. */
static struct msm_ipc_routing_table_entry *lookup_routing_table(
uint32_t node_id)
{
@@ -274,16 +266,16 @@
if (!xprt_info)
return NULL;
- mutex_lock(&xprt_info->rx_lock);
+ mutex_lock(&xprt_info->rx_lock_lhb2);
if (xprt_info->abort_data_read) {
- mutex_unlock(&xprt_info->rx_lock);
+ mutex_unlock(&xprt_info->rx_lock_lhb2);
pr_err("%s detected SSR & exiting now\n",
xprt_info->xprt->name);
return NULL;
}
if (list_empty(&xprt_info->pkt_list)) {
- mutex_unlock(&xprt_info->rx_lock);
+ mutex_unlock(&xprt_info->rx_lock_lhb2);
return NULL;
}
@@ -292,7 +284,7 @@
list_del(&temp_pkt->list);
if (list_empty(&xprt_info->pkt_list))
wake_unlock(&xprt_info->wakelock);
- mutex_unlock(&xprt_info->rx_lock);
+ mutex_unlock(&xprt_info->rx_lock_lhb2);
return temp_pkt;
}
@@ -470,24 +462,47 @@
kfree(skb_head);
}
+static int post_pkt_to_port(struct msm_ipc_port *port_ptr,
+ struct rr_packet *pkt, int clone)
+{
+ struct rr_packet *temp_pkt = pkt;
+ void (*notify)(unsigned event, void *priv);
+
+ if (unlikely(!port_ptr || !pkt))
+ return -EINVAL;
+
+ if (clone) {
+ temp_pkt = clone_pkt(pkt);
+ if (!temp_pkt) {
+ pr_err("%s: Error cloning packet for port %08x:%08x\n",
+ __func__, port_ptr->this_port.node_id,
+ port_ptr->this_port.port_id);
+ return -ENOMEM;
+ }
+ }
+
+ mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
+ wake_lock(&port_ptr->port_rx_wake_lock);
+ list_add_tail(&temp_pkt->list, &port_ptr->port_rx_q);
+ wake_up(&port_ptr->port_rx_wait_q);
+ notify = port_ptr->notify;
+ mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
+ if (notify)
+ notify(MSM_IPC_ROUTER_READ_CB, port_ptr->priv);
+ return 0;
+}
+
static int post_control_ports(struct rr_packet *pkt)
{
struct msm_ipc_port *port_ptr;
- struct rr_packet *cloned_pkt;
if (!pkt)
return -EINVAL;
- mutex_lock(&control_ports_lock);
- list_for_each_entry(port_ptr, &control_ports, list) {
- mutex_lock(&port_ptr->port_rx_q_lock);
- cloned_pkt = clone_pkt(pkt);
- wake_lock(&port_ptr->port_rx_wake_lock);
- list_add_tail(&cloned_pkt->list, &port_ptr->port_rx_q);
- wake_up(&port_ptr->port_rx_wait_q);
- mutex_unlock(&port_ptr->port_rx_q_lock);
- }
- mutex_unlock(&control_ports_lock);
+ down_read(&control_ports_lock_lha5);
+ list_for_each_entry(port_ptr, &control_ports, list)
+ post_pkt_to_port(port_ptr, pkt, 1);
+ up_read(&control_ports_lock_lha5);
return 0;
}
@@ -496,9 +511,9 @@
uint32_t port_id = 0, prev_port_id, key;
struct msm_ipc_port *port_ptr;
- mutex_lock(&next_port_id_lock);
+ mutex_lock(&next_port_id_lock_lha1);
prev_port_id = next_port_id;
- mutex_lock(&local_ports_lock);
+ down_read(&local_ports_lock_lha2);
do {
next_port_id++;
if ((next_port_id & 0xFFFFFFFE) == 0xFFFFFFFE)
@@ -521,8 +536,8 @@
}
port_id = 0;
} while (next_port_id != prev_port_id);
- mutex_unlock(&local_ports_lock);
- mutex_unlock(&next_port_id_lock);
+ up_read(&local_ports_lock_lha2);
+ mutex_unlock(&next_port_id_lock_lha1);
return port_id;
}
@@ -535,9 +550,9 @@
return;
key = (port_ptr->this_port.port_id & (LP_HASH_SIZE - 1));
- mutex_lock(&local_ports_lock);
+ down_write(&local_ports_lock_lha2);
list_add_tail(&port_ptr->list, &local_ports[key]);
- mutex_unlock(&local_ports_lock);
+ up_write(&local_ports_lock_lha2);
}
struct msm_ipc_port *msm_ipc_router_create_raw_port(void *endpoint,
@@ -559,10 +574,8 @@
}
spin_lock_init(&port_ptr->port_lock);
- INIT_LIST_HEAD(&port_ptr->incomplete);
- mutex_init(&port_ptr->incomplete_lock);
INIT_LIST_HEAD(&port_ptr->port_rx_q);
- mutex_init(&port_ptr->port_rx_q_lock);
+ mutex_init(&port_ptr->port_rx_q_lock_lhb3);
init_waitqueue_head(&port_ptr->port_rx_wait_q);
snprintf(port_ptr->rx_wakelock_name, MAX_WAKELOCK_NAME_SZ,
"ipc%08x_%s",
@@ -579,9 +592,7 @@
return port_ptr;
}
-/*
- * Should be called with local_ports_lock locked
- */
+/* Must be called with local_ports_lock_lha2 locked. */
static struct msm_ipc_port *msm_ipc_router_lookup_local_port(uint32_t port_id)
{
int key = (port_id & (LP_HASH_SIZE - 1));
@@ -595,6 +606,7 @@
return NULL;
}
+/* Must be called with routing_table_lock_lha3 locked. */
static struct msm_ipc_router_remote_port *msm_ipc_router_lookup_remote_port(
uint32_t node_id,
uint32_t port_id)
@@ -603,30 +615,25 @@
struct msm_ipc_routing_table_entry *rt_entry;
int key = (port_id & (RP_HASH_SIZE - 1));
- mutex_lock(&routing_table_lock);
rt_entry = lookup_routing_table(node_id);
if (!rt_entry) {
- mutex_unlock(&routing_table_lock);
pr_err("%s: Node is not up\n", __func__);
return NULL;
}
- mutex_lock(&rt_entry->lock);
+ down_read(&rt_entry->lock_lha4);
list_for_each_entry(rport_ptr,
&rt_entry->remote_port_list[key], list) {
if (rport_ptr->port_id == port_id) {
- if (rport_ptr->restart_state != RESTART_NORMAL)
- rport_ptr = NULL;
- mutex_unlock(&rt_entry->lock);
- mutex_unlock(&routing_table_lock);
+ up_read(&rt_entry->lock_lha4);
return rport_ptr;
}
}
- mutex_unlock(&rt_entry->lock);
- mutex_unlock(&routing_table_lock);
+ up_read(&rt_entry->lock_lha4);
return NULL;
}
+/* Must be called with routing_table_lock_lha3 locked. */
static struct msm_ipc_router_remote_port *msm_ipc_router_create_remote_port(
uint32_t node_id,
uint32_t port_id)
@@ -635,37 +642,109 @@
struct msm_ipc_routing_table_entry *rt_entry;
int key = (port_id & (RP_HASH_SIZE - 1));
- mutex_lock(&routing_table_lock);
rt_entry = lookup_routing_table(node_id);
if (!rt_entry) {
- mutex_unlock(&routing_table_lock);
pr_err("%s: Node is not up\n", __func__);
return NULL;
}
- mutex_lock(&rt_entry->lock);
rport_ptr = kmalloc(sizeof(struct msm_ipc_router_remote_port),
GFP_KERNEL);
if (!rport_ptr) {
- mutex_unlock(&rt_entry->lock);
- mutex_unlock(&routing_table_lock);
pr_err("%s: Remote port alloc failed\n", __func__);
return NULL;
}
rport_ptr->port_id = port_id;
rport_ptr->node_id = node_id;
- rport_ptr->restart_state = RESTART_NORMAL;
rport_ptr->sec_rule = NULL;
+ rport_ptr->server = NULL;
rport_ptr->tx_quota_cnt = 0;
- init_waitqueue_head(&rport_ptr->quota_wait);
- mutex_init(&rport_ptr->quota_lock);
+ mutex_init(&rport_ptr->quota_lock_lhb2);
+ INIT_LIST_HEAD(&rport_ptr->resume_tx_port_list);
+ down_write(&rt_entry->lock_lha4);
list_add_tail(&rport_ptr->list,
&rt_entry->remote_port_list[key]);
- mutex_unlock(&rt_entry->lock);
- mutex_unlock(&routing_table_lock);
+ up_write(&rt_entry->lock_lha4);
return rport_ptr;
}
+/**
+ * msm_ipc_router_free_resume_tx_port() - Free the resume_tx ports
+ * @rport_ptr: Pointer to the remote port.
+ *
+ * This function deletes all the resume_tx ports associated with a remote port
+ * and frees the memory allocated to each resume_tx port.
+ *
+ * Must be called with rport_ptr->quota_lock_lhb2 locked.
+ */
+static void msm_ipc_router_free_resume_tx_port(
+ struct msm_ipc_router_remote_port *rport_ptr)
+{
+ struct msm_ipc_resume_tx_port *rtx_port, *tmp_rtx_port;
+
+ list_for_each_entry_safe(rtx_port, tmp_rtx_port,
+ &rport_ptr->resume_tx_port_list, list) {
+ list_del(&rtx_port->list);
+ kfree(rtx_port);
+ }
+}
+
+/**
+ * msm_ipc_router_lookup_resume_tx_port() - Lookup resume_tx port list
+ * @rport_ptr: Remote port whose resume_tx port list needs to be looked.
+ * @port_id: Port ID which needs to be looked from the list.
+ *
+ * return 1 if the port_id is found in the list, else 0.
+ *
+ * This function is used to lookup the existence of a local port in
+ * remote port's resume_tx list. This function is used to ensure that
+ * the same port is not added to the remote_port's resume_tx list repeatedly.
+ *
+ * Must be called with rport_ptr->quota_lock_lhb2 locked.
+ */
+static int msm_ipc_router_lookup_resume_tx_port(
+ struct msm_ipc_router_remote_port *rport_ptr, uint32_t port_id)
+{
+ struct msm_ipc_resume_tx_port *rtx_port;
+
+ list_for_each_entry(rtx_port, &rport_ptr->resume_tx_port_list, list) {
+ if (port_id == rtx_port->port_id)
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * post_resume_tx() - Post the resume_tx event
+ * @rport_ptr: Pointer to the remote port
+ * @pkt : The data packet that is received on a resume_tx event
+ *
+ * This function informs about the reception of the resume_tx message from a
+ * remote port pointed by rport_ptr to all the local ports that are in the
+ * resume_tx_ports_list of this remote port. On posting the information, this
+ * function sequentially deletes each entry in the resume_tx_port_list of the
+ * remote port.
+ *
+ * Must be called with rport_ptr->quota_lock_lhb2 locked.
+ */
+static void post_resume_tx(struct msm_ipc_router_remote_port *rport_ptr,
+ struct rr_packet *pkt)
+{
+ struct msm_ipc_resume_tx_port *rtx_port, *tmp_rtx_port;
+ struct msm_ipc_port *local_port;
+
+ list_for_each_entry_safe(rtx_port, tmp_rtx_port,
+ &rport_ptr->resume_tx_port_list, list) {
+ local_port =
+ msm_ipc_router_lookup_local_port(rtx_port->port_id);
+ if (local_port)
+ post_pkt_to_port(local_port, pkt, 1);
+ list_del(&rtx_port->list);
+ kfree(rtx_port);
+ }
+}
+
+/* Must be called with routing_table_lock_lha3 locked. */
static void msm_ipc_router_destroy_remote_port(
struct msm_ipc_router_remote_port *rport_ptr)
{
@@ -676,19 +755,18 @@
return;
node_id = rport_ptr->node_id;
- mutex_lock(&routing_table_lock);
rt_entry = lookup_routing_table(node_id);
if (!rt_entry) {
- mutex_unlock(&routing_table_lock);
pr_err("%s: Node %d is not up\n", __func__, node_id);
return;
}
-
- mutex_lock(&rt_entry->lock);
+ down_write(&rt_entry->lock_lha4);
list_del(&rport_ptr->list);
+ up_write(&rt_entry->lock_lha4);
+ mutex_lock(&rport_ptr->quota_lock_lhb2);
+ msm_ipc_router_free_resume_tx_port(rport_ptr);
+ mutex_unlock(&rport_ptr->quota_lock_lhb2);
kfree(rport_ptr);
- mutex_unlock(&rt_entry->lock);
- mutex_unlock(&routing_table_lock);
return;
}
@@ -701,7 +779,7 @@
*
* @return: If found Pointer to server structure, else NULL.
*
- * Note1: Lock the server_list_lock before accessing this function.
+ * Note1: Lock the server_list_lock_lha2 before accessing this function.
* Note2: If the <node_id:port_id> are <0:0>, then the lookup is restricted
* to <service:instance>. Used only when a client wants to send a
* message to any QMI server.
@@ -749,7 +827,7 @@
* This function adds the server info to the hash table. If the same
* server(i.e. <service_id:instance_id>) is hosted in different nodes,
* they are maintained as list of "server_port" under "server" structure.
- * Note: Lock the server_list_lock before accessing this function.
+ * Note: Lock the server_list_lock_lha2 before accessing this function.
*/
static struct msm_ipc_server *msm_ipc_router_create_server(
uint32_t service,
@@ -815,7 +893,7 @@
* from the server structure. If the server_port list under server structure
* is empty after removal, then remove the server structure from the server
* hash table.
- * Note: Lock the server_list_lock before accessing this function.
+ * Note: Lock the server_list_lock_lha2 before accessing this function.
*/
static void msm_ipc_router_destroy_server(struct msm_ipc_server *server,
uint32_t node_id, uint32_t port_id)
@@ -910,9 +988,9 @@
pkt->pkt_fragment_q = pkt_fragment_q;
pkt->length = pkt_size;
- mutex_lock(&xprt_info->tx_lock);
+ mutex_lock(&xprt_info->tx_lock_lhb2);
ret = xprt_info->xprt->write(pkt, pkt_size, xprt_info->xprt);
- mutex_unlock(&xprt_info->tx_lock);
+ mutex_unlock(&xprt_info->tx_lock_lhb2);
release_pkt(pkt);
return ret;
@@ -1047,11 +1125,11 @@
{
struct msm_ipc_router_xprt_info *xprt_info;
- mutex_lock(&xprt_info_list_lock);
+ down_read(&xprt_info_list_lock_lha5);
list_for_each_entry(xprt_info, &xprt_info_list, list) {
msm_ipc_router_send_control_msg(xprt_info, ctl);
}
- mutex_unlock(&xprt_info_list_lock);
+ up_read(&xprt_info_list_lock_lha5);
return 0;
}
@@ -1064,12 +1142,12 @@
if (!xprt_info || !ctl)
return -EINVAL;
- mutex_lock(&xprt_info_list_lock);
+ down_read(&xprt_info_list_lock_lha5);
list_for_each_entry(fwd_xprt_info, &xprt_info_list, list) {
if (xprt_info->xprt->link_id != fwd_xprt_info->xprt->link_id)
msm_ipc_router_send_control_msg(fwd_xprt_info, ctl);
}
- mutex_unlock(&xprt_info_list_lock);
+ up_read(&xprt_info_list_lock_lha5);
return 0;
}
@@ -1082,15 +1160,15 @@
if (!xprt_info || !pkt)
return -EINVAL;
- mutex_lock(&xprt_info_list_lock);
+ down_read(&xprt_info_list_lock_lha5);
list_for_each_entry(fwd_xprt_info, &xprt_info_list, list) {
- mutex_lock(&fwd_xprt_info->tx_lock);
+ mutex_lock(&fwd_xprt_info->tx_lock_lhb2);
if (xprt_info->xprt->link_id != fwd_xprt_info->xprt->link_id)
fwd_xprt_info->xprt->write(pkt, pkt->length,
fwd_xprt_info->xprt);
- mutex_unlock(&fwd_xprt_info->tx_lock);
+ mutex_unlock(&fwd_xprt_info->tx_lock_lhb2);
}
- mutex_unlock(&xprt_info_list_lock);
+ up_read(&xprt_info_list_lock_lha5);
return 0;
}
@@ -1102,6 +1180,7 @@
struct rr_header *hdr;
struct msm_ipc_router_xprt_info *fwd_xprt_info;
struct msm_ipc_routing_table_entry *rt_entry;
+ int ret = 0;
if (!xprt_info || !pkt)
return -EINVAL;
@@ -1112,174 +1191,143 @@
hdr = (struct rr_header *)head_pkt->data;
dst_node_id = hdr->dst_node_id;
- mutex_lock(&routing_table_lock);
+ down_read(&routing_table_lock_lha3);
rt_entry = lookup_routing_table(dst_node_id);
if (!(rt_entry) || !(rt_entry->xprt_info)) {
- mutex_unlock(&routing_table_lock);
+ up_read(&routing_table_lock_lha3);
pr_err("%s: Routing table not initialized\n", __func__);
return -ENODEV;
}
- mutex_lock(&rt_entry->lock);
+ down_read(&rt_entry->lock_lha4);
fwd_xprt_info = rt_entry->xprt_info;
- mutex_lock(&fwd_xprt_info->tx_lock);
if (xprt_info->remote_node_id == fwd_xprt_info->remote_node_id) {
- mutex_unlock(&fwd_xprt_info->tx_lock);
- mutex_unlock(&rt_entry->lock);
- mutex_unlock(&routing_table_lock);
pr_err("%s: Discarding Command to route back\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto fwd_msg_out;
}
if (xprt_info->xprt->link_id == fwd_xprt_info->xprt->link_id) {
- mutex_unlock(&fwd_xprt_info->tx_lock);
- mutex_unlock(&rt_entry->lock);
- mutex_unlock(&routing_table_lock);
pr_err("%s: DST in the same cluster\n", __func__);
- return 0;
+ goto fwd_msg_out;
}
+ mutex_lock(&fwd_xprt_info->tx_lock_lhb2);
fwd_xprt_info->xprt->write(pkt, pkt->length, fwd_xprt_info->xprt);
- mutex_unlock(&fwd_xprt_info->tx_lock);
- mutex_unlock(&rt_entry->lock);
- mutex_unlock(&routing_table_lock);
+ mutex_unlock(&fwd_xprt_info->tx_lock_lhb2);
+fwd_msg_out:
+ up_read(&rt_entry->lock_lha4);
+ up_read(&routing_table_lock_lha3);
- return 0;
+ return ret;
}
-static void reset_remote_port_info(uint32_t node_id, uint32_t port_id)
+static int msm_ipc_router_send_remove_client(struct comm_mode_info *mode_info,
+ uint32_t node_id, uint32_t port_id)
{
- struct msm_ipc_router_remote_port *rport_ptr;
+ union rr_control_msg msg;
+ struct msm_ipc_router_xprt_info *tmp_xprt_info;
+ int mode;
+ void *xprt_info;
+ int rc = 0;
- rport_ptr = msm_ipc_router_lookup_remote_port(node_id, port_id);
- if (!rport_ptr) {
- pr_err("%s: No such remote port %08x:%08x\n",
- __func__, node_id, port_id);
+ if (!mode_info) {
+ pr_err("%s: NULL mode_info\n", __func__);
+ return -EINVAL;
+ }
+ mode = mode_info->mode;
+ xprt_info = mode_info->xprt_info;
+
+ msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
+ msg.cli.node_id = node_id;
+ msg.cli.port_id = port_id;
+
+ if ((mode == SINGLE_LINK_MODE) && xprt_info) {
+ down_read(&xprt_info_list_lock_lha5);
+ list_for_each_entry(tmp_xprt_info, &xprt_info_list, list) {
+ if (tmp_xprt_info != xprt_info)
+ continue;
+ msm_ipc_router_send_control_msg(tmp_xprt_info, &msg);
+ break;
+ }
+ up_read(&xprt_info_list_lock_lha5);
+ } else if ((mode == SINGLE_LINK_MODE) && !xprt_info) {
+ broadcast_ctl_msg_locally(&msg);
+ } else if (mode == MULTI_LINK_MODE) {
+ broadcast_ctl_msg(&msg);
+ broadcast_ctl_msg_locally(&msg);
+ } else if (mode != NULL_MODE) {
+ pr_err("%s: Invalid mode(%d) + xprt_inf(%p) for %08x:%08x\n",
+ __func__, mode, xprt_info, node_id, port_id);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static void update_comm_mode_info(struct comm_mode_info *mode_info,
+ struct msm_ipc_router_xprt_info *xprt_info)
+{
+ if (!mode_info) {
+ pr_err("%s: NULL mode_info\n", __func__);
return;
}
- mutex_lock(&rport_ptr->quota_lock);
- rport_ptr->restart_state = RESTART_PEND;
- wake_up(&rport_ptr->quota_wait);
- mutex_unlock(&rport_ptr->quota_lock);
+
+ if (mode_info->mode == NULL_MODE) {
+ mode_info->xprt_info = xprt_info;
+ mode_info->mode = SINGLE_LINK_MODE;
+ } else if (mode_info->mode == SINGLE_LINK_MODE &&
+ mode_info->xprt_info != xprt_info) {
+ mode_info->mode = MULTI_LINK_MODE;
+ }
+
return;
}
-static void msm_ipc_cleanup_remote_server_info(
- struct msm_ipc_router_xprt_info *xprt_info)
+static void cleanup_rmt_server(struct msm_ipc_router_xprt_info *xprt_info,
+ struct msm_ipc_router_remote_port *rport_ptr)
{
- struct msm_ipc_server *svr, *tmp_svr;
- struct msm_ipc_server_port *svr_port, *tmp_svr_port;
- int i;
union rr_control_msg ctl;
+ struct msm_ipc_server *server = rport_ptr->server;
- if (!xprt_info) {
- pr_err("%s: Invalid xprt_info\n", __func__);
- return;
- }
-
+ D("Remove server %08x:%08x - %08x:%08x",
+ server->name.service, server->name.instance,
+ rport_ptr->node_id, rport_ptr->port_id);
ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
- mutex_lock(&server_list_lock);
- for (i = 0; i < SRV_HASH_SIZE; i++) {
- list_for_each_entry_safe(svr, tmp_svr, &server_list[i], list) {
- ctl.srv.service = svr->name.service;
- ctl.srv.instance = svr->name.instance;
- list_for_each_entry_safe(svr_port, tmp_svr_port,
- &svr->server_port_list, list) {
- if (svr_port->xprt_info != xprt_info)
- continue;
- D("Remove server %08x:%08x - %08x:%08x",
- ctl.srv.service, ctl.srv.instance,
- svr_port->server_addr.node_id,
- svr_port->server_addr.port_id);
- reset_remote_port_info(
- svr_port->server_addr.node_id,
- svr_port->server_addr.port_id);
- ctl.srv.node_id = svr_port->server_addr.node_id;
- ctl.srv.port_id = svr_port->server_addr.port_id;
- relay_ctl_msg(xprt_info, &ctl);
- broadcast_ctl_msg_locally(&ctl);
- platform_device_unregister(&svr_port->pdev);
- list_del(&svr_port->list);
- kfree(svr_port);
- }
- if (list_empty(&svr->server_port_list)) {
- list_del(&svr->list);
- kfree(svr);
- }
- }
- }
- mutex_unlock(&server_list_lock);
+ ctl.srv.service = server->name.service;
+ ctl.srv.instance = server->name.instance;
+ ctl.srv.node_id = rport_ptr->node_id;
+ ctl.srv.port_id = rport_ptr->port_id;
+ relay_ctl_msg(xprt_info, &ctl);
+ broadcast_ctl_msg_locally(&ctl);
+ msm_ipc_router_destroy_server(server,
+ rport_ptr->node_id, rport_ptr->port_id);
}
-static void msm_ipc_cleanup_remote_client_info(
- struct msm_ipc_router_xprt_info *xprt_info)
+static void cleanup_rmt_ports(struct msm_ipc_router_xprt_info *xprt_info,
+ struct msm_ipc_routing_table_entry *rt_entry)
{
- struct msm_ipc_routing_table_entry *rt_entry;
- struct msm_ipc_router_remote_port *rport_ptr;
- int i, j;
- union rr_control_msg ctl;
-
- if (!xprt_info) {
- pr_err("%s: Invalid xprt_info\n", __func__);
- return;
- }
-
- ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
- mutex_lock(&routing_table_lock);
- for (i = 0; i < RT_HASH_SIZE; i++) {
- list_for_each_entry(rt_entry, &routing_table[i], list) {
- mutex_lock(&rt_entry->lock);
- if (rt_entry->xprt_info != xprt_info) {
- mutex_unlock(&rt_entry->lock);
- continue;
- }
- for (j = 0; j < RP_HASH_SIZE; j++) {
- list_for_each_entry(rport_ptr,
- &rt_entry->remote_port_list[j], list) {
- if (rport_ptr->restart_state ==
- RESTART_PEND)
- continue;
- mutex_lock(&rport_ptr->quota_lock);
- rport_ptr->restart_state = RESTART_PEND;
- wake_up(&rport_ptr->quota_wait);
- mutex_unlock(&rport_ptr->quota_lock);
- ctl.cli.node_id = rport_ptr->node_id;
- ctl.cli.port_id = rport_ptr->port_id;
- broadcast_ctl_msg_locally(&ctl);
- }
- }
- mutex_unlock(&rt_entry->lock);
- }
- }
- mutex_unlock(&routing_table_lock);
-}
-
-static void msm_ipc_cleanup_remote_port_info(uint32_t node_id)
-{
- struct msm_ipc_routing_table_entry *rt_entry, *tmp_rt_entry;
struct msm_ipc_router_remote_port *rport_ptr, *tmp_rport_ptr;
- int i, j;
+ union rr_control_msg ctl;
+ int j;
- mutex_lock(&routing_table_lock);
- for (i = 0; i < RT_HASH_SIZE; i++) {
- list_for_each_entry_safe(rt_entry, tmp_rt_entry,
- &routing_table[i], list) {
- mutex_lock(&rt_entry->lock);
- if (rt_entry->neighbor_node_id != node_id) {
- mutex_unlock(&rt_entry->lock);
- continue;
- }
- for (j = 0; j < RP_HASH_SIZE; j++) {
- list_for_each_entry_safe(rport_ptr,
- tmp_rport_ptr,
- &rt_entry->remote_port_list[j], list) {
- list_del(&rport_ptr->list);
- kfree(rport_ptr);
- }
- }
- mutex_unlock(&rt_entry->lock);
+ for (j = 0; j < RP_HASH_SIZE; j++) {
+ list_for_each_entry_safe(rport_ptr, tmp_rport_ptr,
+ &rt_entry->remote_port_list[j], list) {
+ list_del(&rport_ptr->list);
+ mutex_lock(&rport_ptr->quota_lock_lhb2);
+ msm_ipc_router_free_resume_tx_port(rport_ptr);
+ mutex_unlock(&rport_ptr->quota_lock_lhb2);
+
+ if (rport_ptr->server)
+ cleanup_rmt_server(xprt_info, rport_ptr);
+
+ ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
+ ctl.cli.node_id = rport_ptr->node_id;
+ ctl.cli.port_id = rport_ptr->port_id;
+ relay_ctl_msg(xprt_info, &ctl);
+ broadcast_ctl_msg_locally(&ctl);
+ kfree(rport_ptr);
}
}
- mutex_unlock(&routing_table_lock);
}
static void msm_ipc_cleanup_routing_table(
@@ -1293,29 +1341,22 @@
return;
}
- mutex_lock(&routing_table_lock);
+ down_write(&server_list_lock_lha2);
+ down_write(&routing_table_lock_lha3);
for (i = 0; i < RT_HASH_SIZE; i++) {
list_for_each_entry(rt_entry, &routing_table[i], list) {
- mutex_lock(&rt_entry->lock);
- if (rt_entry->xprt_info == xprt_info)
- rt_entry->xprt_info = NULL;
- mutex_unlock(&rt_entry->lock);
+ down_write(&rt_entry->lock_lha4);
+ if (rt_entry->xprt_info != xprt_info) {
+ up_write(&rt_entry->lock_lha4);
+ continue;
+ }
+ cleanup_rmt_ports(xprt_info, rt_entry);
+ rt_entry->xprt_info = NULL;
+ up_write(&rt_entry->lock_lha4);
}
}
- mutex_unlock(&routing_table_lock);
-}
-
-static void modem_reset_cleanup(struct msm_ipc_router_xprt_info *xprt_info)
-{
-
- if (!xprt_info) {
- pr_err("%s: Invalid xprt_info\n", __func__);
- return;
- }
-
- msm_ipc_cleanup_remote_server_info(xprt_info);
- msm_ipc_cleanup_remote_client_info(xprt_info);
- msm_ipc_cleanup_routing_table(xprt_info);
+ up_write(&routing_table_lock_lha3);
+ up_write(&server_list_lock_lha2);
}
/**
@@ -1331,6 +1372,7 @@
struct msm_ipc_server_port *server_port;
struct msm_ipc_router_remote_port *rport_ptr = NULL;
+ down_read(&routing_table_lock_lha3);
list_for_each_entry(server_port, &server->server_port_list, list) {
rport_ptr = msm_ipc_router_lookup_remote_port(
server_port->server_addr.node_id,
@@ -1339,6 +1381,7 @@
continue;
rport_ptr->sec_rule = rule;
}
+ up_read(&routing_table_lock_lha3);
server->synced_sec_rule = 1;
}
@@ -1358,7 +1401,7 @@
int key = (service & (SRV_HASH_SIZE - 1));
struct msm_ipc_server *server;
- mutex_lock(&server_list_lock);
+ down_write(&server_list_lock_lha2);
list_for_each_entry(server, &server_list[key], list) {
if (server->name.service != service)
continue;
@@ -1377,7 +1420,7 @@
sync_sec_rule(server, rule);
}
- mutex_unlock(&server_list_lock);
+ up_write(&server_list_lock_lha2);
}
/**
@@ -1395,7 +1438,7 @@
int key;
struct msm_ipc_server *server;
- mutex_lock(&server_list_lock);
+ down_write(&server_list_lock_lha2);
for (key = 0; key < SRV_HASH_SIZE; key++) {
list_for_each_entry(server, &server_list[key], list) {
if (server->synced_sec_rule)
@@ -1404,7 +1447,7 @@
sync_sec_rule(server, rule);
}
}
- mutex_unlock(&server_list_lock);
+ up_write(&server_list_lock_lha2);
}
static int process_hello_msg(struct msm_ipc_router_xprt_info *xprt_info,
@@ -1427,25 +1470,22 @@
* an entry. Update the entry with the Node ID that it corresponds
* to and the XPRT through which it can be reached.
*/
- mutex_lock(&routing_table_lock);
+ down_write(&routing_table_lock_lha3);
rt_entry = lookup_routing_table(hdr->src_node_id);
if (!rt_entry) {
rt_entry = alloc_routing_table_entry(hdr->src_node_id);
if (!rt_entry) {
- mutex_unlock(&routing_table_lock);
+ up_write(&routing_table_lock_lha3);
pr_err("%s: rt_entry allocation failed\n", __func__);
return -ENOMEM;
}
add_routing_table_entry(rt_entry);
}
- mutex_lock(&rt_entry->lock);
+ down_write(&rt_entry->lock_lha4);
rt_entry->neighbor_node_id = xprt_info->remote_node_id;
rt_entry->xprt_info = xprt_info;
- mutex_unlock(&rt_entry->lock);
- mutex_unlock(&routing_table_lock);
-
- /* Cleanup any remote ports, if the node is coming out of reset */
- msm_ipc_cleanup_remote_port_info(xprt_info->remote_node_id);
+ up_write(&rt_entry->lock_lha4);
+ up_write(&routing_table_lock_lha3);
/* Send a reply HELLO message */
memset(&ctl, 0, sizeof(ctl));
@@ -1461,8 +1501,8 @@
* Send list of servers from the local node and from nodes
* outside the mesh network in which this XPRT is part of.
*/
- mutex_lock(&server_list_lock);
- mutex_lock(&routing_table_lock);
+ down_read(&server_list_lock_lha2);
+ down_read(&routing_table_lock_lha3);
for (i = 0; i < RT_HASH_SIZE; i++) {
list_for_each_entry(rt_entry, &routing_table[i], list) {
if ((rt_entry->node_id != IPC_ROUTER_NID_LOCAL) &&
@@ -1473,28 +1513,182 @@
rc = msm_ipc_router_send_server_list(rt_entry->node_id,
xprt_info);
if (rc < 0) {
- mutex_unlock(&routing_table_lock);
- mutex_unlock(&server_list_lock);
+ up_read(&routing_table_lock_lha3);
+ up_read(&server_list_lock_lha2);
return rc;
}
}
}
- mutex_unlock(&routing_table_lock);
- mutex_unlock(&server_list_lock);
+ up_read(&routing_table_lock_lha3);
+ up_read(&server_list_lock_lha2);
RR("HELLO message processed\n");
return rc;
}
+static int process_resume_tx_msg(union rr_control_msg *msg,
+ struct rr_packet *pkt)
+{
+ struct msm_ipc_router_remote_port *rport_ptr;
+ int ret = 0;
+
+ RR("o RESUME_TX id=%d:%08x\n", msg->cli.node_id, msg->cli.port_id);
+
+ down_read(&local_ports_lock_lha2);
+ down_read(&routing_table_lock_lha3);
+ rport_ptr = msm_ipc_router_lookup_remote_port(msg->cli.node_id,
+ msg->cli.port_id);
+ if (!rport_ptr) {
+ pr_err("%s: Unable to resume client\n", __func__);
+ ret = -ENODEV;
+ goto prtm_out;
+ }
+ mutex_lock(&rport_ptr->quota_lock_lhb2);
+ rport_ptr->tx_quota_cnt = 0;
+ post_resume_tx(rport_ptr, pkt);
+ mutex_unlock(&rport_ptr->quota_lock_lhb2);
+prtm_out:
+ up_read(&routing_table_lock_lha3);
+ up_read(&local_ports_lock_lha2);
+ return 0;
+}
+
+static int process_new_server_msg(struct msm_ipc_router_xprt_info *xprt_info,
+ union rr_control_msg *msg, struct rr_packet *pkt)
+{
+ struct msm_ipc_routing_table_entry *rt_entry;
+ struct msm_ipc_server *server;
+ struct msm_ipc_router_remote_port *rport_ptr;
+
+ if (msg->srv.instance == 0) {
+ pr_err("%s: Server %08x create rejected, version = 0\n",
+ __func__, msg->srv.service);
+ return -EINVAL;
+ }
+
+ RR("o NEW_SERVER id=%d:%08x service=%08x:%08x\n", msg->srv.node_id,
+ msg->srv.port_id, msg->srv.service, msg->srv.instance);
+ /*
+ * Find the entry from Routing Table corresponding to Node ID.
+ * Under SSR, an entry will be found. When the subsystem hosting
+ * service is not adjacent, an entry will not be found and hence
+ * allocate an entry. Update the entry with the Node ID that it
+ * corresponds to and the XPRT through which it can be reached.
+ */
+ down_write(&routing_table_lock_lha3);
+ rt_entry = lookup_routing_table(msg->srv.node_id);
+ if (!rt_entry) {
+ rt_entry = alloc_routing_table_entry(msg->srv.node_id);
+ if (!rt_entry) {
+ up_write(&routing_table_lock_lha3);
+ pr_err("%s: rt_entry allocation failed\n", __func__);
+ return -ENOMEM;
+ }
+ down_write(&rt_entry->lock_lha4);
+ rt_entry->neighbor_node_id = xprt_info->remote_node_id;
+ rt_entry->xprt_info = xprt_info;
+ up_write(&rt_entry->lock_lha4);
+ add_routing_table_entry(rt_entry);
+ }
+ up_write(&routing_table_lock_lha3);
+
+ /*
+ * If the service does not exist already in the database, create and
+ * store the service info. Create a remote port structure in which
+ * the service is hosted and cache the security rule for the service
+ * in that remote port structure.
+ */
+ down_write(&server_list_lock_lha2);
+ server = msm_ipc_router_lookup_server(msg->srv.service,
+ msg->srv.instance, msg->srv.node_id, msg->srv.port_id);
+ if (!server) {
+ server = msm_ipc_router_create_server(
+ msg->srv.service, msg->srv.instance,
+ msg->srv.node_id, msg->srv.port_id, xprt_info);
+ if (!server) {
+ up_write(&server_list_lock_lha2);
+ pr_err("%s: Server Create failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ down_read(&routing_table_lock_lha3);
+ if (!msm_ipc_router_lookup_remote_port(
+ msg->srv.node_id, msg->srv.port_id)) {
+ rport_ptr = msm_ipc_router_create_remote_port(
+ msg->srv.node_id, msg->srv.port_id);
+ if (!rport_ptr) {
+ up_read(&routing_table_lock_lha3);
+ up_write(&server_list_lock_lha2);
+ return -ENOMEM;
+ }
+ rport_ptr->server = server;
+ rport_ptr->sec_rule = msm_ipc_get_security_rule(
+ msg->srv.service,
+ msg->srv.instance);
+ }
+ up_read(&routing_table_lock_lha3);
+ }
+ up_write(&server_list_lock_lha2);
+
+ /*
+ * Relay the new server message to other subsystems that do not belong
+ * to the cluster from which this message is received. Notify the
+ * local clients waiting for this service.
+ */
+ relay_msg(xprt_info, pkt);
+ post_control_ports(pkt);
+ return 0;
+}
+
+static int process_rmv_server_msg(struct msm_ipc_router_xprt_info *xprt_info,
+ union rr_control_msg *msg, struct rr_packet *pkt)
+{
+ struct msm_ipc_server *server;
+
+ RR("o REMOVE_SERVER service=%08x:%d\n",
+ msg->srv.service, msg->srv.instance);
+ down_write(&server_list_lock_lha2);
+ server = msm_ipc_router_lookup_server(msg->srv.service,
+ msg->srv.instance, msg->srv.node_id, msg->srv.port_id);
+ if (server) {
+ msm_ipc_router_destroy_server(server, msg->srv.node_id,
+ msg->srv.port_id);
+ /*
+ * Relay the new server message to other subsystems that do not
+ * belong to the cluster from which this message is received.
+ * Notify the local clients communicating with the service.
+ */
+ relay_msg(xprt_info, pkt);
+ post_control_ports(pkt);
+ }
+ up_write(&server_list_lock_lha2);
+ return 0;
+}
+
+static int process_rmv_client_msg(struct msm_ipc_router_xprt_info *xprt_info,
+ union rr_control_msg *msg, struct rr_packet *pkt)
+{
+ struct msm_ipc_router_remote_port *rport_ptr;
+
+ RR("o REMOVE_CLIENT id=%d:%08x\n", msg->cli.node_id, msg->cli.port_id);
+ down_write(&routing_table_lock_lha3);
+ rport_ptr = msm_ipc_router_lookup_remote_port(msg->cli.node_id,
+ msg->cli.port_id);
+ if (rport_ptr)
+ msm_ipc_router_destroy_remote_port(rport_ptr);
+ up_write(&routing_table_lock_lha3);
+
+ relay_msg(xprt_info, pkt);
+ post_control_ports(pkt);
+ return 0;
+}
+
static int process_control_msg(struct msm_ipc_router_xprt_info *xprt_info,
struct rr_packet *pkt)
{
union rr_control_msg *msg;
- struct msm_ipc_router_remote_port *rport_ptr;
int rc = 0;
struct sk_buff *temp_ptr;
struct rr_header *hdr;
- struct msm_ipc_server *server;
- struct msm_ipc_routing_table_entry *rt_entry;
if (pkt->length != (IPC_ROUTER_HDR_SIZE + sizeof(*msg))) {
pr_err("%s: r2r msg size %d != %d\n", __func__, pkt->length,
@@ -1518,115 +1712,17 @@
case IPC_ROUTER_CTRL_CMD_HELLO:
rc = process_hello_msg(xprt_info, hdr);
break;
-
case IPC_ROUTER_CTRL_CMD_RESUME_TX:
- RR("o RESUME_TX id=%d:%08x\n",
- msg->cli.node_id, msg->cli.port_id);
-
- rport_ptr = msm_ipc_router_lookup_remote_port(msg->cli.node_id,
- msg->cli.port_id);
- if (!rport_ptr) {
- pr_err("%s: Unable to resume client\n", __func__);
- break;
- }
- mutex_lock(&rport_ptr->quota_lock);
- rport_ptr->tx_quota_cnt = 0;
- mutex_unlock(&rport_ptr->quota_lock);
- wake_up(&rport_ptr->quota_wait);
+ rc = process_resume_tx_msg(msg, pkt);
break;
-
case IPC_ROUTER_CTRL_CMD_NEW_SERVER:
- if (msg->srv.instance == 0) {
- pr_err(
- "rpcrouter: Server create rejected, version = 0, "
- "service = %08x\n", msg->srv.service);
- break;
- }
-
- RR("o NEW_SERVER id=%d:%08x service=%08x:%08x\n",
- msg->srv.node_id, msg->srv.port_id,
- msg->srv.service, msg->srv.instance);
-
- mutex_lock(&routing_table_lock);
- rt_entry = lookup_routing_table(msg->srv.node_id);
- if (!rt_entry) {
- rt_entry = alloc_routing_table_entry(msg->srv.node_id);
- if (!rt_entry) {
- mutex_unlock(&routing_table_lock);
- pr_err("%s: rt_entry allocation failed\n",
- __func__);
- return -ENOMEM;
- }
- mutex_lock(&rt_entry->lock);
- rt_entry->neighbor_node_id = xprt_info->remote_node_id;
- rt_entry->xprt_info = xprt_info;
- mutex_unlock(&rt_entry->lock);
- add_routing_table_entry(rt_entry);
- }
- mutex_unlock(&routing_table_lock);
-
- mutex_lock(&server_list_lock);
- server = msm_ipc_router_lookup_server(msg->srv.service,
- msg->srv.instance,
- msg->srv.node_id,
- msg->srv.port_id);
- if (!server) {
- server = msm_ipc_router_create_server(
- msg->srv.service, msg->srv.instance,
- msg->srv.node_id, msg->srv.port_id, xprt_info);
- if (!server) {
- mutex_unlock(&server_list_lock);
- pr_err("%s: Server Create failed\n", __func__);
- return -ENOMEM;
- }
-
- if (!msm_ipc_router_lookup_remote_port(
- msg->srv.node_id, msg->srv.port_id)) {
- rport_ptr = msm_ipc_router_create_remote_port(
- msg->srv.node_id, msg->srv.port_id);
- if (!rport_ptr)
- pr_err("%s: Remote port create "
- "failed\n", __func__);
- else
- rport_ptr->sec_rule =
- msm_ipc_get_security_rule(
- msg->srv.service,
- msg->srv.instance);
- }
- wake_up(&newserver_wait);
- }
- mutex_unlock(&server_list_lock);
-
- relay_msg(xprt_info, pkt);
- post_control_ports(pkt);
+ rc = process_new_server_msg(xprt_info, msg, pkt);
break;
case IPC_ROUTER_CTRL_CMD_REMOVE_SERVER:
- RR("o REMOVE_SERVER service=%08x:%d\n",
- msg->srv.service, msg->srv.instance);
- mutex_lock(&server_list_lock);
- server = msm_ipc_router_lookup_server(msg->srv.service,
- msg->srv.instance,
- msg->srv.node_id,
- msg->srv.port_id);
- if (server) {
- msm_ipc_router_destroy_server(server,
- msg->srv.node_id,
- msg->srv.port_id);
- relay_msg(xprt_info, pkt);
- post_control_ports(pkt);
- }
- mutex_unlock(&server_list_lock);
+ rc = process_rmv_server_msg(xprt_info, msg, pkt);
break;
case IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT:
- RR("o REMOVE_CLIENT id=%d:%08x\n",
- msg->cli.node_id, msg->cli.port_id);
- rport_ptr = msm_ipc_router_lookup_remote_port(msg->cli.node_id,
- msg->cli.port_id);
- if (rport_ptr)
- msm_ipc_router_destroy_remote_port(rport_ptr);
-
- relay_msg(xprt_info, pkt);
- post_control_ports(pkt);
+ rc = process_rmv_client_msg(xprt_info, msg, pkt);
break;
case IPC_ROUTER_CTRL_CMD_PING:
/* No action needed for ping messages received */
@@ -1714,19 +1810,19 @@
resume_tx_node_id = hdr->dst_node_id;
resume_tx_port_id = hdr->dst_port_id;
- rport_ptr = msm_ipc_router_lookup_remote_port(hdr->src_node_id,
- hdr->src_port_id);
-
- mutex_lock(&local_ports_lock);
+ down_read(&local_ports_lock_lha2);
port_ptr = msm_ipc_router_lookup_local_port(hdr->dst_port_id);
if (!port_ptr) {
pr_err("%s: No local port id %08x\n", __func__,
hdr->dst_port_id);
- mutex_unlock(&local_ports_lock);
+ up_read(&local_ports_lock_lha2);
release_pkt(pkt);
goto process_done;
}
+ down_read(&routing_table_lock_lha3);
+ rport_ptr = msm_ipc_router_lookup_remote_port(hdr->src_node_id,
+ hdr->src_port_id);
if (!rport_ptr) {
rport_ptr = msm_ipc_router_create_remote_port(
hdr->src_node_id,
@@ -1735,20 +1831,14 @@
pr_err("%s: Rmt Prt %08x:%08x create failed\n",
__func__, hdr->src_node_id,
hdr->src_port_id);
- mutex_unlock(&local_ports_lock);
+ up_read(&routing_table_lock_lha3);
+ up_read(&local_ports_lock_lha2);
goto process_done;
}
}
-
- mutex_lock(&port_ptr->port_rx_q_lock);
- wake_lock(&port_ptr->port_rx_wake_lock);
- list_add_tail(&pkt->list, &port_ptr->port_rx_q);
- wake_up(&port_ptr->port_rx_wait_q);
- if (port_ptr->notify)
- port_ptr->notify(MSM_IPC_ROUTER_READ_CB,
- port_ptr->priv);
- mutex_unlock(&port_ptr->port_rx_q_lock);
- mutex_unlock(&local_ports_lock);
+ up_read(&routing_table_lock_lha3);
+ post_pkt_to_port(port_ptr, pkt, 0);
+ up_read(&local_ports_lock_lha2);
process_done:
if (resume_tx) {
@@ -1784,13 +1874,13 @@
if (name->addrtype != MSM_IPC_ADDR_NAME)
return -EINVAL;
- mutex_lock(&server_list_lock);
+ down_write(&server_list_lock_lha2);
server = msm_ipc_router_lookup_server(name->addr.port_name.service,
name->addr.port_name.instance,
IPC_ROUTER_NID_LOCAL,
port_ptr->this_port.port_id);
if (server) {
- mutex_unlock(&server_list_lock);
+ up_write(&server_list_lock_lha2);
pr_err("%s: Server already present\n", __func__);
return -EINVAL;
}
@@ -1801,7 +1891,7 @@
port_ptr->this_port.port_id,
NULL);
if (!server) {
- mutex_unlock(&server_list_lock);
+ up_write(&server_list_lock_lha2);
pr_err("%s: Server Creation failed\n", __func__);
return -EINVAL;
}
@@ -1811,10 +1901,11 @@
ctl.srv.instance = server->name.instance;
ctl.srv.node_id = IPC_ROUTER_NID_LOCAL;
ctl.srv.port_id = port_ptr->this_port.port_id;
- mutex_unlock(&server_list_lock);
+ up_write(&server_list_lock_lha2);
broadcast_ctl_msg(&ctl);
spin_lock_irqsave(&port_ptr->port_lock, flags);
port_ptr->type = SERVER_PORT;
+ port_ptr->mode_info.mode = MULTI_LINK_MODE;
port_ptr->port_name.service = server->name.service;
port_ptr->port_name.instance = server->name.instance;
spin_unlock_irqrestore(&port_ptr->port_lock, flags);
@@ -1842,13 +1933,13 @@
return -EINVAL;
}
- mutex_lock(&server_list_lock);
+ down_write(&server_list_lock_lha2);
server = msm_ipc_router_lookup_server(port_ptr->port_name.service,
port_ptr->port_name.instance,
port_ptr->this_port.node_id,
port_ptr->this_port.port_id);
if (!server) {
- mutex_unlock(&server_list_lock);
+ up_write(&server_list_lock_lha2);
pr_err("%s: Server lookup failed\n", __func__);
return -ENODEV;
}
@@ -1860,7 +1951,7 @@
ctl.srv.port_id = port_ptr->this_port.port_id;
msm_ipc_router_destroy_server(server, port_ptr->this_port.node_id,
port_ptr->this_port.port_id);
- mutex_unlock(&server_list_lock);
+ up_write(&server_list_lock_lha2);
broadcast_ctl_msg(&ctl);
spin_lock_irqsave(&port_ptr->port_lock, flags);
port_ptr->type = CLIENT_PORT;
@@ -1911,22 +2002,19 @@
hdr->dst_port_id = port_id;
pkt->length += IPC_ROUTER_HDR_SIZE;
- mutex_lock(&local_ports_lock);
+ down_read(&local_ports_lock_lha2);
port_ptr = msm_ipc_router_lookup_local_port(port_id);
if (!port_ptr) {
pr_err("%s: Local port %d not present\n", __func__, port_id);
- mutex_unlock(&local_ports_lock);
+ up_read(&local_ports_lock_lha2);
release_pkt(pkt);
return -ENODEV;
}
- mutex_lock(&port_ptr->port_rx_q_lock);
- wake_lock(&port_ptr->port_rx_wake_lock);
- list_add_tail(&pkt->list, &port_ptr->port_rx_q);
ret_len = pkt->length;
- wake_up(&port_ptr->port_rx_wait_q);
- mutex_unlock(&port_ptr->port_rx_q_lock);
- mutex_unlock(&local_ports_lock);
+ post_pkt_to_port(port_ptr, pkt, 0);
+ update_comm_mode_info(&src->mode_info, NULL);
+ up_read(&local_ports_lock_lha2);
return ret_len;
}
@@ -1939,8 +2027,8 @@
struct rr_header *hdr;
struct msm_ipc_router_xprt_info *xprt_info;
struct msm_ipc_routing_table_entry *rt_entry;
+ struct msm_ipc_resume_tx_port *resume_tx_port;
int ret;
- DEFINE_WAIT(__wait);
if (!rport_ptr || !src || !pkt)
return -EINVAL;
@@ -1965,55 +2053,53 @@
hdr->dst_port_id = rport_ptr->port_id;
pkt->length += IPC_ROUTER_HDR_SIZE;
- for (;;) {
- prepare_to_wait(&rport_ptr->quota_wait, &__wait,
- TASK_INTERRUPTIBLE);
- mutex_lock(&rport_ptr->quota_lock);
- if (rport_ptr->restart_state != RESTART_NORMAL)
- break;
- if (rport_ptr->tx_quota_cnt <
- IPC_ROUTER_DEFAULT_RX_QUOTA)
- break;
- if (signal_pending(current))
- break;
- mutex_unlock(&rport_ptr->quota_lock);
- schedule();
- }
- finish_wait(&rport_ptr->quota_wait, &__wait);
-
- if (rport_ptr->restart_state != RESTART_NORMAL) {
- mutex_unlock(&rport_ptr->quota_lock);
- return -ENETRESET;
- }
- if (signal_pending(current)) {
- mutex_unlock(&rport_ptr->quota_lock);
- return -ERESTARTSYS;
+ mutex_lock(&rport_ptr->quota_lock_lhb2);
+ if (rport_ptr->tx_quota_cnt == IPC_ROUTER_DEFAULT_RX_QUOTA) {
+ if (msm_ipc_router_lookup_resume_tx_port(
+ rport_ptr, src->this_port.port_id)) {
+ mutex_unlock(&rport_ptr->quota_lock_lhb2);
+ return -EAGAIN;
+ }
+ resume_tx_port =
+ kzalloc(sizeof(struct msm_ipc_resume_tx_port),
+ GFP_KERNEL);
+ if (!resume_tx_port) {
+ pr_err("%s: Resume_Tx port allocation failed\n",
+ __func__);
+ mutex_unlock(&rport_ptr->quota_lock_lhb2);
+ return -ENOMEM;
+ }
+ INIT_LIST_HEAD(&resume_tx_port->list);
+ resume_tx_port->port_id = src->this_port.port_id;
+ resume_tx_port->node_id = src->this_port.node_id;
+ list_add_tail(&resume_tx_port->list,
+ &rport_ptr->resume_tx_port_list);
+ mutex_unlock(&rport_ptr->quota_lock_lhb2);
+ return -EAGAIN;
}
rport_ptr->tx_quota_cnt++;
if (rport_ptr->tx_quota_cnt == IPC_ROUTER_DEFAULT_RX_QUOTA)
hdr->confirm_rx = 1;
- mutex_unlock(&rport_ptr->quota_lock);
+ mutex_unlock(&rport_ptr->quota_lock_lhb2);
- mutex_lock(&routing_table_lock);
rt_entry = lookup_routing_table(hdr->dst_node_id);
if (!rt_entry || !rt_entry->xprt_info) {
- mutex_unlock(&routing_table_lock);
pr_err("%s: Remote node %d not up\n",
__func__, hdr->dst_node_id);
return -ENODEV;
}
- mutex_lock(&rt_entry->lock);
+ down_read(&rt_entry->lock_lha4);
xprt_info = rt_entry->xprt_info;
- mutex_lock(&xprt_info->tx_lock);
+ mutex_lock(&xprt_info->tx_lock_lhb2);
ret = xprt_info->xprt->write(pkt, pkt->length, xprt_info->xprt);
- mutex_unlock(&xprt_info->tx_lock);
- mutex_unlock(&rt_entry->lock);
- mutex_unlock(&routing_table_lock);
+ mutex_unlock(&xprt_info->tx_lock_lhb2);
+ up_read(&rt_entry->lock_lha4);
if (ret < 0) {
pr_err("%s: Write on XPRT failed\n", __func__);
return ret;
}
+ update_comm_mode_info(&src->mode_info, xprt_info);
RAW_HDR("[w rr_h] "
"ver=%i,type=%s,src_nid=%08x,src_port_id=%08x,"
@@ -2063,13 +2149,13 @@
dst_node_id = dest->addr.port_addr.node_id;
dst_port_id = dest->addr.port_addr.port_id;
} else if (dest->addrtype == MSM_IPC_ADDR_NAME) {
- mutex_lock(&server_list_lock);
+ down_read(&server_list_lock_lha2);
server = msm_ipc_router_lookup_server(
dest->addr.port_name.service,
dest->addr.port_name.instance,
0, 0);
if (!server) {
- mutex_unlock(&server_list_lock);
+ up_read(&server_list_lock_lha2);
pr_err("%s: Destination not reachable\n", __func__);
return -ENODEV;
}
@@ -2078,24 +2164,26 @@
list);
dst_node_id = server_port->server_addr.node_id;
dst_port_id = server_port->server_addr.port_id;
- mutex_unlock(&server_list_lock);
+ up_read(&server_list_lock_lha2);
}
if (dst_node_id == IPC_ROUTER_NID_LOCAL) {
ret = loopback_data(src, dst_port_id, data);
return ret;
}
- /* Achieve Flow control */
+ down_read(&routing_table_lock_lha3);
rport_ptr = msm_ipc_router_lookup_remote_port(dst_node_id,
dst_port_id);
if (!rport_ptr) {
- pr_err("%s: Could not create remote port\n", __func__);
- return -ENOMEM;
+ up_read(&routing_table_lock_lha3);
+ pr_err("%s: Remote port not found\n", __func__);
+ return -ENODEV;
}
if (src->check_send_permissions) {
ret = src->check_send_permissions(rport_ptr->sec_rule);
if (ret <= 0) {
+ up_read(&routing_table_lock_lha3);
pr_err("%s: permission failure for %s\n",
__func__, current->comm);
return -EPERM;
@@ -2104,11 +2192,13 @@
pkt = create_pkt(data);
if (!pkt) {
+ up_read(&routing_table_lock_lha3);
pr_err("%s: Pkt creation failed\n", __func__);
return -ENOMEM;
}
ret = msm_ipc_router_write_pkt(src, rport_ptr, pkt);
+ up_read(&routing_table_lock_lha3);
release_pkt(pkt);
return ret;
@@ -2146,15 +2236,15 @@
if (!port_ptr || !data)
return -EINVAL;
- mutex_lock(&port_ptr->port_rx_q_lock);
+ mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
if (list_empty(&port_ptr->port_rx_q)) {
- mutex_unlock(&port_ptr->port_rx_q_lock);
+ mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
return -EAGAIN;
}
pkt = list_first_entry(&port_ptr->port_rx_q, struct rr_packet, list);
if ((buf_len) && ((pkt->length - IPC_ROUTER_HDR_SIZE) > buf_len)) {
- mutex_unlock(&port_ptr->port_rx_q_lock);
+ mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
return -ETOOSMALL;
}
list_del(&pkt->list);
@@ -2163,11 +2253,34 @@
*data = pkt->pkt_fragment_q;
ret = pkt->length;
kfree(pkt);
- mutex_unlock(&port_ptr->port_rx_q_lock);
+ mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
return ret;
}
+/**
+ * msm_ipc_router_recv_from() - Recieve messages destined to a local port.
+ * @port_ptr: Pointer to the local port
+ * @data : Pointer to the socket buffer head
+ * @src: Pointer to local port address
+ * @timeout: < 0 timeout indicates infinite wait till a message arrives.
+ * > 0 timeout indicates the wait time.
+ * 0 indicates that we do not wait.
+ * @return: = Number of bytes read(On successful read operation).
+ * = 0 (If there are no pending messages and timeout is 0).
+ * = -EINVAL (If either of the arguments, port_ptr or data is invalid)
+ * = -EFAULT (If there are no pending messages when timeout is > 0
+ * and the wait_event_interruptible_timeout has returned value > 0)
+ * = -ERESTARTSYS (If there are no pending messages when timeout
+ * is < 0 and wait_event_interruptible was interrupted by a signal)
+ *
+ * This function reads the messages that are destined for a local port. It
+ * is used by modules that exist with-in the kernel and use IPC Router for
+ * transport. The function checks if there are any messages that are already
+ * received. If yes, it reads them, else it waits as per the timeout value.
+ * On a successful read, the return value of the function indicates the number
+ * of bytes that are read.
+ */
int msm_ipc_router_recv_from(struct msm_ipc_port *port_ptr,
struct sk_buff_head **data,
struct msm_ipc_addr *src,
@@ -2183,9 +2296,9 @@
}
*data = NULL;
- mutex_lock(&port_ptr->port_rx_q_lock);
+ mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
while (list_empty(&port_ptr->port_rx_q)) {
- mutex_unlock(&port_ptr->port_rx_q_lock);
+ mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
if (timeout < 0) {
ret = wait_event_interruptible(
port_ptr->port_rx_wait_q,
@@ -2201,10 +2314,10 @@
return -EFAULT;
}
if (timeout == 0)
- return -ETIMEDOUT;
- mutex_lock(&port_ptr->port_rx_q_lock);
+ return 0;
+ mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
}
- mutex_unlock(&port_ptr->port_rx_q_lock);
+ mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
ret = msm_ipc_router_read(port_ptr, data, 0);
if (ret <= 0 || !(*data))
@@ -2236,7 +2349,11 @@
struct sk_buff_head *in_skb_head;
int ret;
- ret = msm_ipc_router_recv_from(port_ptr, &in_skb_head, src, -1);
+ ret = msm_ipc_router_recv_from(port_ptr, &in_skb_head, src, 0);
+
+ if (ret == 0)
+ return -ENOMSG;
+
if (ret < 0) {
pr_err("%s: msm_ipc_router_recv_from failed - ret: %d\n",
__func__, ret);
@@ -2282,9 +2399,9 @@
return -EINVAL;
if (port_ptr->type == SERVER_PORT || port_ptr->type == CLIENT_PORT) {
- mutex_lock(&local_ports_lock);
+ down_write(&local_ports_lock_lha2);
list_del(&port_ptr->list);
- mutex_unlock(&local_ports_lock);
+ up_write(&local_ports_lock_lha2);
if (port_ptr->type == SERVER_PORT) {
msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
@@ -2303,33 +2420,31 @@
* Server port could have been a client port earlier.
* Send REMOVE_CLIENT message in either case.
*/
- msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
- msg.cli.node_id = port_ptr->this_port.node_id;
- msg.cli.port_id = port_ptr->this_port.port_id;
RR("x REMOVE_CLIENT id=%d:%08x\n",
- msg.cli.node_id, msg.cli.port_id);
- broadcast_ctl_msg(&msg);
- broadcast_ctl_msg_locally(&msg);
+ port_ptr->this_port.node_id, port_ptr->this_port.port_id);
+ msm_ipc_router_send_remove_client(&port_ptr->mode_info,
+ port_ptr->this_port.node_id,
+ port_ptr->this_port.port_id);
} else if (port_ptr->type == CONTROL_PORT) {
- mutex_lock(&control_ports_lock);
+ down_write(&control_ports_lock_lha5);
list_del(&port_ptr->list);
- mutex_unlock(&control_ports_lock);
+ up_write(&control_ports_lock_lha5);
} else if (port_ptr->type == IRSC_PORT) {
- mutex_lock(&local_ports_lock);
+ down_write(&local_ports_lock_lha2);
list_del(&port_ptr->list);
- mutex_unlock(&local_ports_lock);
+ up_write(&local_ports_lock_lha2);
signal_irsc_completion();
}
- mutex_lock(&port_ptr->port_rx_q_lock);
+ mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
list_for_each_entry_safe(pkt, temp_pkt, &port_ptr->port_rx_q, list) {
list_del(&pkt->list);
release_pkt(pkt);
}
- mutex_unlock(&port_ptr->port_rx_q_lock);
+ mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
if (port_ptr->type == SERVER_PORT) {
- mutex_lock(&server_list_lock);
+ down_write(&server_list_lock_lha2);
server = msm_ipc_router_lookup_server(
port_ptr->port_name.service,
port_ptr->port_name.instance,
@@ -2339,7 +2454,7 @@
msm_ipc_router_destroy_server(server,
port_ptr->this_port.node_id,
port_ptr->this_port.port_id);
- mutex_unlock(&server_list_lock);
+ up_write(&server_list_lock_lha2);
}
wake_lock_destroy(&port_ptr->port_rx_wake_lock);
@@ -2355,13 +2470,13 @@
if (!port_ptr)
return -EINVAL;
- mutex_lock(&port_ptr->port_rx_q_lock);
+ mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
if (!list_empty(&port_ptr->port_rx_q)) {
pkt = list_first_entry(&port_ptr->port_rx_q,
struct rr_packet, list);
rc = pkt->length;
}
- mutex_unlock(&port_ptr->port_rx_q_lock);
+ mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
return rc;
}
@@ -2371,13 +2486,13 @@
if (!port_ptr)
return -EINVAL;
- mutex_lock(&local_ports_lock);
+ down_write(&local_ports_lock_lha2);
list_del(&port_ptr->list);
- mutex_unlock(&local_ports_lock);
+ up_write(&local_ports_lock_lha2);
port_ptr->type = CONTROL_PORT;
- mutex_lock(&control_ports_lock);
+ down_write(&control_ports_lock_lha5);
list_add_tail(&port_ptr->list, &control_ports);
- mutex_unlock(&control_ports_lock);
+ up_write(&control_ports_lock_lha5);
return 0;
}
@@ -2401,7 +2516,7 @@
return -EINVAL;
}
- mutex_lock(&server_list_lock);
+ down_read(&server_list_lock_lha2);
if (!lookup_mask)
lookup_mask = 0xFFFFFFFF;
key = (srv_name->service & (SRV_HASH_SIZE - 1));
@@ -2424,7 +2539,7 @@
i++;
}
}
- mutex_unlock(&server_list_lock);
+ up_read(&server_list_lock_lha2);
return i;
}
@@ -2433,14 +2548,14 @@
{
struct msm_ipc_router_xprt_info *xprt_info, *tmp_xprt_info;
- mutex_lock(&xprt_info_list_lock);
+ down_write(&xprt_info_list_lock_lha5);
list_for_each_entry_safe(xprt_info, tmp_xprt_info,
&xprt_info_list, list) {
xprt_info->xprt->close(xprt_info->xprt);
list_del(&xprt_info->list);
kfree(xprt_info);
}
- mutex_unlock(&xprt_info_list_lock);
+ up_write(&xprt_info_list_lock_lha5);
return 0;
}
@@ -2451,9 +2566,9 @@
struct msm_ipc_routing_table_entry *rt_entry;
for (j = 0; j < RT_HASH_SIZE; j++) {
- mutex_lock(&routing_table_lock);
+ down_read(&routing_table_lock_lha3);
list_for_each_entry(rt_entry, &routing_table[j], list) {
- mutex_lock(&rt_entry->lock);
+ down_read(&rt_entry->lock_lha4);
i += scnprintf(buf + i, max - i,
"Node Id: 0x%08x\n", rt_entry->node_id);
if (rt_entry->node_id == IPC_ROUTER_NID_LOCAL) {
@@ -2470,9 +2585,9 @@
rt_entry->xprt_info->remote_node_id);
}
i += scnprintf(buf + i, max - i, "\n");
- mutex_unlock(&rt_entry->lock);
+ up_read(&rt_entry->lock_lha4);
}
- mutex_unlock(&routing_table_lock);
+ up_read(&routing_table_lock_lha3);
}
return i;
@@ -2483,7 +2598,7 @@
int i = 0;
struct msm_ipc_router_xprt_info *xprt_info;
- mutex_lock(&xprt_info_list_lock);
+ down_read(&xprt_info_list_lock_lha5);
list_for_each_entry(xprt_info, &xprt_info_list, list) {
i += scnprintf(buf + i, max - i, "XPRT Name: %s\n",
xprt_info->xprt->name);
@@ -2495,7 +2610,7 @@
xprt_info->remote_node_id);
i += scnprintf(buf + i, max - i, "\n");
}
- mutex_unlock(&xprt_info_list_lock);
+ up_read(&xprt_info_list_lock_lha5);
return i;
}
@@ -2506,7 +2621,7 @@
struct msm_ipc_server *server;
struct msm_ipc_server_port *server_port;
- mutex_lock(&server_list_lock);
+ down_read(&server_list_lock_lha2);
for (j = 0; j < SRV_HASH_SIZE; j++) {
list_for_each_entry(server, &server_list[j], list) {
list_for_each_entry(server_port,
@@ -2526,7 +2641,7 @@
}
}
}
- mutex_unlock(&server_list_lock);
+ up_read(&server_list_lock_lha2);
return i;
}
@@ -2538,9 +2653,9 @@
struct msm_ipc_routing_table_entry *rt_entry;
for (j = 0; j < RT_HASH_SIZE; j++) {
- mutex_lock(&routing_table_lock);
+ down_read(&routing_table_lock_lha3);
list_for_each_entry(rt_entry, &routing_table[j], list) {
- mutex_lock(&rt_entry->lock);
+ down_read(&rt_entry->lock_lha4);
for (k = 0; k < RP_HASH_SIZE; k++) {
list_for_each_entry(rport_ptr,
&rt_entry->remote_port_list[k],
@@ -2557,9 +2672,9 @@
i += scnprintf(buf + i, max - i, "\n");
}
}
- mutex_unlock(&rt_entry->lock);
+ up_read(&rt_entry->lock_lha4);
}
- mutex_unlock(&routing_table_lock);
+ up_read(&routing_table_lock_lha3);
}
return i;
@@ -2570,7 +2685,7 @@
int i = 0;
struct msm_ipc_port *port_ptr;
- mutex_lock(&control_ports_lock);
+ down_read(&control_ports_lock_lha5);
list_for_each_entry(port_ptr, &control_ports, list) {
i += scnprintf(buf + i, max - i, "Node_id: 0x%08x\n",
port_ptr->this_port.node_id);
@@ -2578,7 +2693,7 @@
port_ptr->this_port.port_id);
i += scnprintf(buf + i, max - i, "\n");
}
- mutex_unlock(&control_ports_lock);
+ up_read(&control_ports_lock_lha5);
return i;
}
@@ -2589,7 +2704,7 @@
unsigned long flags;
struct msm_ipc_port *port_ptr;
- mutex_lock(&local_ports_lock);
+ down_read(&local_ports_lock_lha2);
for (j = 0; j < LP_HASH_SIZE; j++) {
list_for_each_entry(port_ptr, &local_ports[j], list) {
spin_lock_irqsave(&port_ptr->port_lock, flags);
@@ -2609,7 +2724,7 @@
i += scnprintf(buf + i, max - i, "\n");
}
}
- mutex_unlock(&local_ports_lock);
+ up_read(&local_ports_lock_lha2);
return i;
}
@@ -2683,8 +2798,8 @@
xprt_info->initialized = 0;
xprt_info->remote_node_id = -1;
INIT_LIST_HEAD(&xprt_info->pkt_list);
- mutex_init(&xprt_info->rx_lock);
- mutex_init(&xprt_info->tx_lock);
+ mutex_init(&xprt_info->rx_lock_lhb2);
+ mutex_init(&xprt_info->tx_lock_lhb2);
wake_lock_init(&xprt_info->wakelock,
WAKE_LOCK_SUSPEND, xprt->name);
xprt_info->need_len = 0;
@@ -2703,18 +2818,18 @@
xprt_info->initialized = 1;
}
- mutex_lock(&xprt_info_list_lock);
+ down_write(&xprt_info_list_lock_lha5);
list_add_tail(&xprt_info->list, &xprt_info_list);
- mutex_unlock(&xprt_info_list_lock);
+ up_write(&xprt_info_list_lock_lha5);
- mutex_lock(&routing_table_lock);
+ down_write(&routing_table_lock_lha3);
if (!routing_table_inited) {
init_routing_table();
rt_entry = alloc_routing_table_entry(IPC_ROUTER_NID_LOCAL);
add_routing_table_entry(rt_entry);
routing_table_inited = 1;
}
- mutex_unlock(&routing_table_lock);
+ up_write(&routing_table_lock_lha3);
xprt->priv = xprt_info;
@@ -2728,13 +2843,13 @@
if (xprt && xprt->priv) {
xprt_info = xprt->priv;
- mutex_lock(&xprt_info->rx_lock);
+ mutex_lock(&xprt_info->rx_lock_lhb2);
xprt_info->abort_data_read = 1;
- mutex_unlock(&xprt_info->rx_lock);
+ mutex_unlock(&xprt_info->rx_lock_lhb2);
- mutex_lock(&xprt_info_list_lock);
+ down_write(&xprt_info_list_lock_lha5);
list_del(&xprt_info->list);
- mutex_unlock(&xprt_info_list_lock);
+ up_write(&xprt_info_list_lock_lha5);
flush_workqueue(xprt_info->workqueue);
destroy_workqueue(xprt_info->workqueue);
@@ -2765,7 +2880,7 @@
struct msm_ipc_router_xprt_work *xprt_work =
container_of(work, struct msm_ipc_router_xprt_work, work);
- modem_reset_cleanup(xprt_work->xprt->priv);
+ msm_ipc_cleanup_routing_table(xprt_work->xprt->priv);
msm_ipc_router_remove_xprt(xprt_work->xprt);
if (atomic_dec_return(&pending_close_count) == 0)
@@ -2797,9 +2912,14 @@
D("open event for '%s'\n", xprt->name);
xprt_work = kmalloc(sizeof(struct msm_ipc_router_xprt_work),
GFP_ATOMIC);
- xprt_work->xprt = xprt;
- INIT_WORK(&xprt_work->work, xprt_open_worker);
- queue_work(msm_ipc_router_workqueue, &xprt_work->work);
+ if (xprt_work) {
+ xprt_work->xprt = xprt;
+ INIT_WORK(&xprt_work->work, xprt_open_worker);
+ queue_work(msm_ipc_router_workqueue, &xprt_work->work);
+ } else {
+ pr_err("%s: malloc failure - Couldn't notify OPEN event",
+ __func__);
+ }
break;
case IPC_ROUTER_XPRT_EVENT_CLOSE:
@@ -2807,9 +2927,14 @@
atomic_inc(&pending_close_count);
xprt_work = kmalloc(sizeof(struct msm_ipc_router_xprt_work),
GFP_ATOMIC);
- xprt_work->xprt = xprt;
- INIT_WORK(&xprt_work->work, xprt_close_worker);
- queue_work(msm_ipc_router_workqueue, &xprt_work->work);
+ if (xprt_work) {
+ xprt_work->xprt = xprt;
+ INIT_WORK(&xprt_work->work, xprt_close_worker);
+ queue_work(msm_ipc_router_workqueue, &xprt_work->work);
+ } else {
+ pr_err("%s: malloc failure - Couldn't notify CLOSE event",
+ __func__);
+ }
break;
}
@@ -2825,10 +2950,10 @@
if (!pkt)
return;
- mutex_lock(&xprt_info->rx_lock);
+ mutex_lock(&xprt_info->rx_lock_lhb2);
list_add_tail(&pkt->list, &xprt_info->pkt_list);
wake_lock(&xprt_info->wakelock);
- mutex_unlock(&xprt_info->rx_lock);
+ mutex_unlock(&xprt_info->rx_lock_lhb2);
queue_work(xprt_info->workqueue, &xprt_info->read_data);
}
@@ -2896,16 +3021,15 @@
for (i = 0; i < LP_HASH_SIZE; i++)
INIT_LIST_HEAD(&local_ports[i]);
- mutex_lock(&routing_table_lock);
+ down_write(&routing_table_lock_lha3);
if (!routing_table_inited) {
init_routing_table();
rt_entry = alloc_routing_table_entry(IPC_ROUTER_NID_LOCAL);
add_routing_table_entry(rt_entry);
routing_table_inited = 1;
}
- mutex_unlock(&routing_table_lock);
+ up_write(&routing_table_lock_lha3);
- init_waitqueue_head(&newserver_wait);
init_waitqueue_head(&subsystem_restart_wait);
ret = msm_ipc_router_init_sockets();
if (ret < 0)
diff --git a/arch/arm/mach-msm/ipc_router.h b/arch/arm/mach-msm/ipc_router.h
index cafcdd2..32832dd 100644
--- a/arch/arm/mach-msm/ipc_router.h
+++ b/arch/arm/mach-msm/ipc_router.h
@@ -62,6 +62,12 @@
IRSC_PORT,
};
+enum {
+ NULL_MODE,
+ SINGLE_LINK_MODE,
+ MULTI_LINK_MODE,
+};
+
union rr_control_msg {
uint32_t cmd;
struct {
diff --git a/arch/arm/mach-msm/ipc_socket.c b/arch/arm/mach-msm/ipc_socket.c
index 342663e..515dc92 100644
--- a/arch/arm/mach-msm/ipc_socket.c
+++ b/arch/arm/mach-msm/ipc_socket.c
@@ -55,6 +55,10 @@
} \
} while (0) \
+#ifndef SIZE_MAX
+#define SIZE_MAX ((size_t)-1)
+#endif
+
static int sockets_enabled;
static struct proto msm_ipc_proto;
static const struct proto_ops msm_ipc_proto_ops;
@@ -197,6 +201,7 @@
struct sockaddr_msm_ipc *addr;
struct rr_header *hdr;
struct sk_buff *temp;
+ union rr_control_msg *ctl_msg;
int offset = 0, data_len = 0, copy_len;
if (!m || !msg_head) {
@@ -207,6 +212,16 @@
temp = skb_peek(msg_head);
hdr = (struct rr_header *)(temp->data);
+ if (addr && (hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX)) {
+ skb_pull(temp, IPC_ROUTER_HDR_SIZE);
+ ctl_msg = (union rr_control_msg *)(temp->data);
+ addr->family = AF_MSM_IPC;
+ addr->address.addrtype = MSM_IPC_ADDR_ID;
+ addr->address.addr.port_addr.node_id = ctl_msg->cli.node_id;
+ addr->address.addr.port_addr.port_id = ctl_msg->cli.port_id;
+ m->msg_namelen = sizeof(struct sockaddr_msm_ipc);
+ return offset;
+ }
if (addr && (hdr->src_port_id != IPC_ROUTER_ADDRESS)) {
addr->family = AF_MSM_IPC;
addr->address.addrtype = MSM_IPC_ADDR_ID;
@@ -253,7 +268,6 @@
{
struct sock *sk;
struct msm_ipc_port *port_ptr;
- void *pil;
if (unlikely(protocol != 0)) {
pr_err("%s: Protocol not supported\n", __func__);
@@ -286,9 +300,7 @@
sock_init_data(sock, sk);
sk->sk_rcvtimeo = DEFAULT_RCV_TIMEO;
- pil = msm_ipc_load_default_node();
msm_ipc_sk(sk)->port = port_ptr;
- msm_ipc_sk(sk)->default_pil = pil;
return 0;
}
@@ -300,6 +312,7 @@
struct sock *sk = sock->sk;
struct msm_ipc_port *port_ptr;
int ret;
+ void *pil;
if (!sk)
return -EINVAL;
@@ -329,6 +342,8 @@
if (!port_ptr)
return -ENODEV;
+ pil = msm_ipc_load_default_node();
+ msm_ipc_sk(sk)->default_pil = pil;
lock_sock(sk);
ret = msm_ipc_router_register_server(port_ptr, &addr->address);
@@ -396,9 +411,9 @@
lock_sock(sk);
timeout = sk->sk_rcvtimeo;
- mutex_lock(&port_ptr->port_rx_q_lock);
+ mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
while (list_empty(&port_ptr->port_rx_q)) {
- mutex_unlock(&port_ptr->port_rx_q_lock);
+ mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
release_sock(sk);
if (timeout < 0) {
ret = wait_event_interruptible(
@@ -415,12 +430,14 @@
return -EFAULT;
}
- if (timeout == 0)
+ if (timeout == 0) {
+ m->msg_namelen = 0;
return 0;
+ }
lock_sock(sk);
- mutex_lock(&port_ptr->port_rx_q_lock);
+ mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
}
- mutex_unlock(&port_ptr->port_rx_q_lock);
+ mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
ret = msm_ipc_router_read(port_ptr, &msg, buf_len);
if (ret <= 0 || !msg) {
@@ -445,8 +462,10 @@
struct msm_ipc_port *port_ptr;
struct server_lookup_args server_arg;
struct msm_ipc_server_info *srv_info = NULL;
- unsigned int n, srv_info_sz = 0;
+ unsigned int n;
+ size_t srv_info_sz = 0;
int ret;
+ void *pil;
if (!sk)
return -EINVAL;
@@ -474,6 +493,8 @@
break;
case IPC_ROUTER_IOCTL_LOOKUP_SERVER:
+ pil = msm_ipc_load_default_node();
+ msm_ipc_sk(sk)->default_pil = pil;
ret = copy_from_user(&server_arg, (void *)arg,
sizeof(server_arg));
if (ret) {
@@ -486,16 +507,16 @@
break;
}
if (server_arg.num_entries_in_array) {
- srv_info_sz = server_arg.num_entries_in_array *
- sizeof(*srv_info);
- if ((srv_info_sz / sizeof(*srv_info)) !=
- server_arg.num_entries_in_array) {
+ if (server_arg.num_entries_in_array >
+ (SIZE_MAX / sizeof(*srv_info))) {
pr_err("%s: Integer Overflow %d * %d\n",
__func__, sizeof(*srv_info),
server_arg.num_entries_in_array);
ret = -EINVAL;
break;
}
+ srv_info_sz = server_arg.num_entries_in_array *
+ sizeof(*srv_info);
srv_info = kmalloc(srv_info_sz, GFP_KERNEL);
if (!srv_info) {
ret = -ENOMEM;
@@ -572,7 +593,8 @@
lock_sock(sk);
ret = msm_ipc_router_close_port(port_ptr);
- msm_ipc_unload_default_node(pil);
+ if (pil)
+ msm_ipc_unload_default_node(pil);
release_sock(sk);
sock_put(sk);
sock->sk = NULL;
diff --git a/arch/arm/mach-msm/krait-regulator.c b/arch/arm/mach-msm/krait-regulator.c
index 953f941d..7c1b8d6 100644
--- a/arch/arm/mach-msm/krait-regulator.c
+++ b/arch/arm/mach-msm/krait-regulator.c
@@ -61,9 +61,7 @@
#define PMIC_VOLTAGE_MAX 1355000
#define LV_RANGE_STEP 5000
-#define LOAD_PER_PHASE 3200000
-
-#define CORE_VOLTAGE_MIN 900000
+#define CORE_VOLTAGE_BOOTUP 900000
#define KRAIT_LDO_VOLTAGE_MIN 465000
#define KRAIT_LDO_VOLTAGE_OFFSET 465000
@@ -94,6 +92,16 @@
#define MDD_CONFIG_CTL 0x00000000
#define MDD_MODE 0x00000010
+#define PHASE_SCALING_REF 4
+
+/* bit definitions for phase scaling eFuses */
+#define PHASE_SCALING_EFUSE_VERSION_POS 26
+#define PHASE_SCALING_EFUSE_VERSION_MASK KRAIT_MASK(27, 26)
+#define PHASE_SCALING_EFUSE_VERSION_SET 1
+
+#define PHASE_SCALING_EFUSE_VALUE_POS 16
+#define PHASE_SCALING_EFUSE_VALUE_MASK KRAIT_MASK(18, 16)
+
/* bit definitions for APC_PWR_GATE_CTL */
#define BHS_CNT_BIT_POS 24
#define BHS_CNT_MASK KRAIT_MASK(31, 24)
@@ -135,6 +143,7 @@
#define LDO_DELTA_MIN 10000
#define LDO_DELTA_MAX 100000
+#define MSM_L2_SAW_PHYS 0xf9012000
/**
* struct pmic_gang_vreg -
* @name: the string used to represent the gang
@@ -146,7 +155,14 @@
* regulator's callback functions to prevent
* simultaneous updates to the pmic's phase
* voltage.
- * @apcs_gcc_base virtual address of the APCS GCC registers
+ * @apcs_gcc_base: virtual address of the APCS GCC registers
+ * @manage_phases: begin phase control
+ * @pfm_threshold: the sum of coefficients below which PFM can be
+ * enabled
+ * @efuse_phase_scaling_factor: Phase scaling factor read out of an eFuse. When
+ * calculating the appropriate phase count to use,
+ * coeff2 is multiplied by this factor and then
+ * divided by PHASE_SCALING_REF.
*/
struct pmic_gang_vreg {
const char *name;
@@ -159,6 +175,9 @@
bool retention_enabled;
bool use_phase_switching;
void __iomem *apcs_gcc_base;
+ bool manage_phases;
+ int pfm_threshold;
+ int efuse_phase_scaling_factor;
};
static struct pmic_gang_vreg *the_gang;
@@ -168,6 +187,9 @@
LDO_MODE = REGULATOR_MODE_IDLE,
};
+#define WAIT_FOR_LOAD 0x2
+#define WAIT_FOR_VOLTAGE 0x1
+
struct krait_power_vreg {
struct list_head link;
struct regulator_desc desc;
@@ -175,7 +197,7 @@
const char *name;
struct pmic_gang_vreg *pvreg;
int uV;
- int load_uA;
+ int load;
enum krait_supply_mode mode;
void __iomem *reg_base;
void __iomem *mdd_base;
@@ -185,13 +207,22 @@
int ldo_threshold_uV;
int ldo_delta_uV;
int cpu_num;
+ int coeff1;
+ int coeff2;
bool online;
+ int online_at_probe;
};
DEFINE_PER_CPU(struct krait_power_vreg *, krait_vregs);
static u32 version;
+static int use_efuse_phase_scaling_factor;
+module_param_named(
+ use_phase_scaling_efuse, use_efuse_phase_scaling_factor, int,
+ S_IRUSR | S_IWUSR
+);
+
static int is_between(int left, int right, int value)
{
if (left >= right && left >= value && value >= right)
@@ -293,14 +324,271 @@
return 0;
}
+#define COEFF2_UV_THRESHOLD 850000
+static int get_coeff2(int krait_uV, int phase_scaling_factor)
+{
+ int coeff2 = 0;
+ int krait_mV = krait_uV / 1000;
+
+ if (krait_uV <= COEFF2_UV_THRESHOLD)
+ coeff2 = (612229 * krait_mV) / 1000 - 211258;
+ else
+ coeff2 = (892564 * krait_mV) / 1000 - 449543;
+
+ coeff2 = coeff2 * phase_scaling_factor / PHASE_SCALING_REF;
+
+ return coeff2;
+}
+
+static int get_coeff1(int actual_uV, int requested_uV, int load)
+{
+ int ratio = actual_uV * 1000 / requested_uV;
+ int coeff1 = 330 * load + (load * 673 * ratio / 1000);
+
+ return coeff1;
+}
+
+static int get_coeff_total(struct krait_power_vreg *from)
+{
+ int coeff_total = 0;
+ struct krait_power_vreg *kvreg;
+ struct pmic_gang_vreg *pvreg = from->pvreg;
+ int phase_scaling_factor = PHASE_SCALING_REF;
+
+ if (use_efuse_phase_scaling_factor)
+ phase_scaling_factor = pvreg->efuse_phase_scaling_factor;
+
+ list_for_each_entry(kvreg, &pvreg->krait_power_vregs, link) {
+ if (!kvreg->online)
+ continue;
+
+ if (kvreg->mode == LDO_MODE) {
+ kvreg->coeff1 =
+ get_coeff1(kvreg->uV - kvreg->ldo_delta_uV,
+ kvreg->uV, kvreg->load);
+ kvreg->coeff2 =
+ get_coeff2(kvreg->uV - kvreg->ldo_delta_uV,
+ phase_scaling_factor);
+ } else {
+ kvreg->coeff1 =
+ get_coeff1(pvreg->pmic_vmax_uV,
+ kvreg->uV, kvreg->load);
+ kvreg->coeff2 = get_coeff2(pvreg->pmic_vmax_uV,
+ phase_scaling_factor);
+ }
+ coeff_total += kvreg->coeff1 + kvreg->coeff2;
+ }
+
+ return coeff_total;
+}
+
+static int set_pmic_gang_phases(struct pmic_gang_vreg *pvreg, int phase_count)
+{
+ pr_debug("programming phase_count = %d\n", phase_count);
+ if (pvreg->use_phase_switching)
+ /*
+ * note the PMIC sets the phase count to one more than
+ * the value in the register - hence subtract 1 from it
+ */
+ return msm_spm_apcs_set_phase(phase_count - 1);
+ else
+ return 0;
+}
+
+static int num_online(struct pmic_gang_vreg *pvreg)
+{
+ int online_total = 0;
+ struct krait_power_vreg *kvreg;
+
+ list_for_each_entry(kvreg, &pvreg->krait_power_vregs, link) {
+ if (kvreg->online)
+ online_total++;
+ }
+ return online_total;
+}
+
+static int get_total_load(struct krait_power_vreg *from)
+{
+ int load_total = 0;
+ struct krait_power_vreg *kvreg;
+ struct pmic_gang_vreg *pvreg = from->pvreg;
+
+ list_for_each_entry(kvreg, &pvreg->krait_power_vregs, link) {
+ if (!kvreg->online)
+ continue;
+ load_total += kvreg->load;
+ }
+
+ return load_total;
+}
+
+static bool enable_phase_management(struct pmic_gang_vreg *pvreg)
+{
+ struct krait_power_vreg *kvreg;
+
+ list_for_each_entry(kvreg, &pvreg->krait_power_vregs, link) {
+ pr_debug("%s online_at_probe:0x%x\n", kvreg->name,
+ kvreg->online_at_probe);
+ if (kvreg->online_at_probe)
+ return false;
+ }
+ return true;
+}
+
+#define PMIC_FTS_MODE_PFM 0x00
+#define PMIC_FTS_MODE_PWM 0x80
+#define ONE_PHASE_COEFF 1000000
+#define TWO_PHASE_COEFF 2000000
+
+#define PWM_SETTLING_TIME_US 50
+#define PHASE_SETTLING_TIME_US 50
+static unsigned int pmic_gang_set_phases(struct krait_power_vreg *from,
+ int coeff_total)
+{
+ struct pmic_gang_vreg *pvreg = from->pvreg;
+ int phase_count;
+ int rc = 0;
+ int n_online = num_online(pvreg);
+ int load_total;
+
+ load_total = get_total_load(from);
+
+ if (pvreg->manage_phases == false) {
+ if (enable_phase_management(pvreg))
+ pvreg->manage_phases = true;
+ else
+ return 0;
+ }
+
+ /* First check if the coeff is low for PFM mode */
+ if (load_total <= pvreg->pfm_threshold && n_online == 1) {
+ if (!pvreg->pfm_mode) {
+ rc = msm_spm_enable_fts_lpm(PMIC_FTS_MODE_PFM);
+ if (rc) {
+ pr_err("%s PFM en failed load_t %d rc = %d\n",
+ from->name, load_total, rc);
+ return rc;
+ } else {
+ pvreg->pfm_mode = true;
+ }
+ }
+ return rc;
+ }
+
+ /* coeff is high switch to PWM mode before changing phases */
+ if (pvreg->pfm_mode) {
+ rc = msm_spm_enable_fts_lpm(PMIC_FTS_MODE_PWM);
+ if (rc) {
+ pr_err("%s PFM exit failed load %d rc = %d\n",
+ from->name, coeff_total, rc);
+ return rc;
+ } else {
+ pvreg->pfm_mode = false;
+ udelay(PWM_SETTLING_TIME_US);
+ }
+ }
+
+ /* calculate phases */
+ if (coeff_total < ONE_PHASE_COEFF)
+ phase_count = 1;
+ else if (coeff_total < TWO_PHASE_COEFF)
+ phase_count = 2;
+ else
+ phase_count = 4;
+
+ /* don't increase the phase count higher than number of online cpus */
+ if (phase_count > n_online)
+ phase_count = n_online;
+
+ if (phase_count != pvreg->pmic_phase_count) {
+ rc = set_pmic_gang_phases(pvreg, phase_count);
+ if (rc < 0) {
+ pr_err("%s failed set phase %d rc = %d\n",
+ from->name, phase_count, rc);
+ return rc;
+ }
+
+ /* complete the writes before the delay */
+ mb();
+
+ /*
+ * delay until the phases are settled when
+ * the count is raised
+ */
+ if (phase_count > pvreg->pmic_phase_count)
+ udelay(PHASE_SETTLING_TIME_US);
+
+ pvreg->pmic_phase_count = phase_count;
+ }
+
+ return rc;
+}
+
+static unsigned int _get_optimum_mode(struct regulator_dev *rdev,
+ int input_uV, int output_uV, int load)
+{
+ struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
+ int coeff_total;
+ int rc;
+
+ kvreg->online_at_probe &= ~WAIT_FOR_LOAD;
+ coeff_total = get_coeff_total(kvreg);
+
+ rc = pmic_gang_set_phases(kvreg, coeff_total);
+ if (rc < 0) {
+ dev_err(&rdev->dev, "%s failed set mode %d rc = %d\n",
+ kvreg->name, coeff_total, rc);
+ }
+
+ return kvreg->mode;
+}
+
+static unsigned int krait_power_get_optimum_mode(struct regulator_dev *rdev,
+ int input_uV, int output_uV, int load_uA)
+{
+ struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
+ struct pmic_gang_vreg *pvreg = kvreg->pvreg;
+ int rc;
+
+ mutex_lock(&pvreg->krait_power_vregs_lock);
+ kvreg->load = load_uA;
+ if (!kvreg->online) {
+ mutex_unlock(&pvreg->krait_power_vregs_lock);
+ return kvreg->mode;
+ }
+
+ rc = _get_optimum_mode(rdev, input_uV, output_uV, load_uA);
+ mutex_unlock(&pvreg->krait_power_vregs_lock);
+
+ return rc;
+}
+
+static int krait_power_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ return 0;
+}
+
+static unsigned int krait_power_get_mode(struct regulator_dev *rdev)
+{
+ struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
+
+ return kvreg->mode;
+}
+
static int switch_to_using_hs(struct krait_power_vreg *kvreg)
{
if (kvreg->mode == HS_MODE)
return 0;
/* enable bhs */
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL, BHS_EN_MASK, BHS_EN_MASK);
+ /* complete the above write before the delay */
+ mb();
+ /* wait for the bhs to settle */
+ udelay(BHS_SETTLING_DELAY_US);
+
+ /* Turn on BHS segments */
krait_masked_write(kvreg, APC_PWR_GATE_CTL,
- BHS_SEG_EN_MASK | BHS_EN_MASK,
- BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS | BHS_EN_MASK);
+ BHS_SEG_EN_MASK, BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS);
/* complete the above write before the delay */
mb();
@@ -368,19 +656,6 @@
return 0;
}
-static int set_pmic_gang_phases(struct pmic_gang_vreg *pvreg, int phase_count)
-{
- pr_debug("programming phase_count = %d\n", phase_count);
- if (pvreg->use_phase_switching)
- /*
- * note the PMIC sets the phase count to one more than
- * the value in the register - hence subtract 1 from it
- */
- return msm_spm_apcs_set_phase(phase_count - 1);
- else
- return 0;
-}
-
static int set_pmic_gang_voltage(struct pmic_gang_vreg *pvreg, int uV)
{
int setpoint;
@@ -524,46 +799,6 @@
return rc;
}
-#define PHASE_SETTLING_TIME_US 10
-static unsigned int pmic_gang_set_phases(struct krait_power_vreg *from,
- int load_uA)
-{
- struct pmic_gang_vreg *pvreg = from->pvreg;
- int phase_count = DIV_ROUND_UP(load_uA, LOAD_PER_PHASE);
- int rc = 0;
-
- if (phase_count <= 0)
- phase_count = 1;
-
- /* Increase phases if it is less than the number of cpus online */
- if (phase_count < num_online_cpus()) {
- phase_count = num_online_cpus();
- }
-
- if (phase_count != pvreg->pmic_phase_count) {
- rc = set_pmic_gang_phases(pvreg, phase_count);
- if (rc < 0) {
- dev_err(&from->rdev->dev,
- "%s failed set phase %d rc = %d\n",
- pvreg->name, phase_count, rc);
- return rc;
- }
-
- /* complete the writes before the delay */
- mb();
-
- /*
- * delay until the phases are settled when
- * the count is raised
- */
- if (phase_count > pvreg->pmic_phase_count)
- udelay(PHASE_SETTLING_TIME_US);
-
- pvreg->pmic_phase_count = phase_count;
- }
- return rc;
-}
-
static int krait_power_get_voltage(struct regulator_dev *rdev)
{
struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
@@ -590,21 +825,6 @@
return vmax;
}
-static int get_total_load(struct krait_power_vreg *from)
-{
- int load_total = 0;
- struct krait_power_vreg *kvreg;
- struct pmic_gang_vreg *pvreg = from->pvreg;
-
- list_for_each_entry(kvreg, &pvreg->krait_power_vregs, link) {
- if (!kvreg->online)
- continue;
- load_total += kvreg->load_uA;
- }
-
- return load_total;
-}
-
#define ROUND_UP_VOLTAGE(v, res) (DIV_ROUND_UP(v, res) * res)
static int _set_voltage(struct regulator_dev *rdev,
int orig_krait_uV, int requested_uV)
@@ -613,6 +833,7 @@
struct pmic_gang_vreg *pvreg = kvreg->pvreg;
int rc;
int vmax;
+ int coeff_total;
pr_debug("%s: %d to %d\n", kvreg->name, orig_krait_uV, requested_uV);
/*
@@ -636,6 +857,11 @@
kvreg->name, requested_uV, orig_krait_uV, rc);
}
+ kvreg->online_at_probe &= ~WAIT_FOR_VOLTAGE;
+ coeff_total = get_coeff_total(kvreg);
+ /* adjust the phases since coeff2 would have changed */
+ rc = pmic_gang_set_phases(kvreg, coeff_total);
+
return rc;
}
@@ -670,89 +896,6 @@
return rc;
}
-#define PMIC_FTS_MODE_PFM 0x00
-#define PMIC_FTS_MODE_PWM 0x80
-#define PFM_LOAD_UA 500000
-static unsigned int _get_optimum_mode(struct regulator_dev *rdev,
- int input_uV, int output_uV, int load_uA)
-{
- struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
- struct pmic_gang_vreg *pvreg = kvreg->pvreg;
- int rc;
- int load_total_uA;
-
- load_total_uA = get_total_load(kvreg);
-
- if (load_total_uA < PFM_LOAD_UA) {
- if (!pvreg->pfm_mode) {
- rc = msm_spm_enable_fts_lpm(PMIC_FTS_MODE_PFM);
- if (rc) {
- dev_err(&rdev->dev,
- "%s enter PFM failed load %d rc = %d\n",
- kvreg->name, load_total_uA, rc);
- goto out;
- } else {
- pvreg->pfm_mode = true;
- }
- }
- return kvreg->mode;
- }
-
- if (pvreg->pfm_mode) {
- rc = msm_spm_enable_fts_lpm(PMIC_FTS_MODE_PWM);
- if (rc) {
- dev_err(&rdev->dev,
- "%s exit PFM failed load %d rc = %d\n",
- kvreg->name, load_total_uA, rc);
- goto out;
- } else {
- pvreg->pfm_mode = false;
- }
- }
-
- rc = pmic_gang_set_phases(kvreg, load_total_uA);
- if (rc < 0) {
- dev_err(&rdev->dev, "%s failed set mode %d rc = %d\n",
- kvreg->name, load_total_uA, rc);
- goto out;
- }
-
-out:
- return kvreg->mode;
-}
-
-static unsigned int krait_power_get_optimum_mode(struct regulator_dev *rdev,
- int input_uV, int output_uV, int load_uA)
-{
- struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
- struct pmic_gang_vreg *pvreg = kvreg->pvreg;
- int rc;
-
- mutex_lock(&pvreg->krait_power_vregs_lock);
- kvreg->load_uA = load_uA;
- if (!kvreg->online) {
- mutex_unlock(&pvreg->krait_power_vregs_lock);
- return kvreg->mode;
- }
-
- rc = _get_optimum_mode(rdev, input_uV, output_uV, load_uA);
- mutex_unlock(&pvreg->krait_power_vregs_lock);
-
- return rc;
-}
-
-static int krait_power_set_mode(struct regulator_dev *rdev, unsigned int mode)
-{
- return 0;
-}
-
-static unsigned int krait_power_get_mode(struct regulator_dev *rdev)
-{
- struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
-
- return kvreg->mode;
-}
-
static int krait_power_is_enabled(struct regulator_dev *rdev)
{
struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
@@ -769,7 +912,7 @@
mutex_lock(&pvreg->krait_power_vregs_lock);
__krait_power_mdd_enable(kvreg, true);
kvreg->online = true;
- rc = _get_optimum_mode(rdev, kvreg->uV, kvreg->uV, kvreg->load_uA);
+ rc = _get_optimum_mode(rdev, kvreg->uV, kvreg->uV, kvreg->load);
if (rc < 0)
goto en_err;
/*
@@ -791,8 +934,7 @@
mutex_lock(&pvreg->krait_power_vregs_lock);
kvreg->online = false;
- rc = _get_optimum_mode(rdev, kvreg->uV, kvreg->uV,
- kvreg->load_uA);
+ rc = _get_optimum_mode(rdev, kvreg->uV, kvreg->uV, kvreg->load);
if (rc < 0)
goto dis_err;
@@ -851,8 +993,10 @@
DEFINE_SIMPLE_ATTRIBUTE(retention_fops,
get_retention_dbg_uV, set_retention_dbg_uV, "%llu\n");
+#define CPU_PWR_CTL_ONLINE_MASK 0x80
static void kvreg_hw_init(struct krait_power_vreg *kvreg)
{
+ int online;
/*
* bhs_cnt value sets the ramp-up time from power collapse,
* initialize the ramp up time
@@ -865,6 +1009,10 @@
/* Enable MDD */
writel_relaxed(0x00000002, kvreg->mdd_base + MDD_MODE);
mb();
+ online = CPU_PWR_CTL_ONLINE_MASK
+ & readl_relaxed(kvreg->reg_base + CPU_PWR_CTL);
+ kvreg->online_at_probe
+ = online ? (WAIT_FOR_LOAD | WAIT_FOR_VOLTAGE) : 0x0;
}
static void glb_init(void __iomem *apcs_gcc_base)
@@ -1012,7 +1160,7 @@
kvreg->desc.ops = &krait_power_ops;
kvreg->desc.type = REGULATOR_VOLTAGE;
kvreg->desc.owner = THIS_MODULE;
- kvreg->uV = CORE_VOLTAGE_MIN;
+ kvreg->uV = CORE_VOLTAGE_BOOTUP;
kvreg->mode = HS_MODE;
kvreg->desc.ops = &krait_power_ops;
kvreg->headroom_uV = headroom_uV;
@@ -1107,10 +1255,70 @@
.resume = boot_cpu_mdd_on,
};
+static int __devinit krait_pdn_phase_scaling_init(struct pmic_gang_vreg *pvreg,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *efuse;
+ u32 efuse_data, efuse_version;
+ bool scaling_factor_valid, use_efuse;
+
+ use_efuse = of_property_read_bool(pdev->dev.of_node,
+ "qcom,use-phase-scaling-factor");
+ /*
+ * Allow usage of the eFuse phase scaling factor if it is enabled in
+ * either device tree or by module parameter.
+ */
+ use_efuse_phase_scaling_factor = use_efuse_phase_scaling_factor
+ || use_efuse;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "phase-scaling-efuse");
+ if (!res || !res->start) {
+ pr_err("phase scaling eFuse address is missing\n");
+ return -EINVAL;
+ }
+
+ efuse = ioremap(res->start, 8);
+ if (!efuse) {
+ pr_err("could not map phase scaling eFuse address\n");
+ return -EINVAL;
+ }
+
+ efuse_data = readl_relaxed(efuse);
+ efuse_version = readl_relaxed(efuse + 4);
+
+ iounmap(efuse);
+
+ scaling_factor_valid
+ = ((efuse_version & PHASE_SCALING_EFUSE_VERSION_MASK) >>
+ PHASE_SCALING_EFUSE_VERSION_POS)
+ == PHASE_SCALING_EFUSE_VERSION_SET;
+
+ if (scaling_factor_valid)
+ pvreg->efuse_phase_scaling_factor
+ = ((efuse_data & PHASE_SCALING_EFUSE_VALUE_MASK)
+ >> PHASE_SCALING_EFUSE_VALUE_POS) + 1;
+ else
+ pvreg->efuse_phase_scaling_factor = PHASE_SCALING_REF;
+
+ pr_info("eFuse phase scaling factor = %d/%d%s\n",
+ pvreg->efuse_phase_scaling_factor, PHASE_SCALING_REF,
+ scaling_factor_valid ? "" : " (eFuse not blown)");
+ pr_info("initial phase scaling factor = %d/%d%s\n",
+ use_efuse_phase_scaling_factor
+ ? pvreg->efuse_phase_scaling_factor : PHASE_SCALING_REF,
+ PHASE_SCALING_REF,
+ use_efuse_phase_scaling_factor ? "" : " (ignoring eFuse)");
+
+ return 0;
+}
+
static int __devinit krait_pdn_probe(struct platform_device *pdev)
{
int rc;
bool use_phase_switching = false;
+ int pfm_threshold;
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct pmic_gang_vreg *pvreg;
@@ -1123,6 +1331,13 @@
use_phase_switching = of_property_read_bool(node,
"qcom,use-phase-switching");
+
+ rc = of_property_read_u32(node, "qcom,pfm-threshold", &pfm_threshold);
+ if (rc < 0) {
+ dev_err(dev, "pfm-threshold missing rc=%d, pfm disabled\n", rc);
+ return -EINVAL;
+ }
+
pvreg = devm_kzalloc(&pdev->dev,
sizeof(struct pmic_gang_vreg), GFP_KERNEL);
if (!pvreg) {
@@ -1142,12 +1357,17 @@
if (pvreg->apcs_gcc_base == NULL)
return -ENOMEM;
+ rc = krait_pdn_phase_scaling_init(pvreg, pdev);
+ if (rc)
+ return rc;
+
pvreg->name = "pmic_gang";
pvreg->pmic_vmax_uV = PMIC_VOLTAGE_MIN;
pvreg->pmic_phase_count = -EINVAL;
pvreg->retention_enabled = true;
pvreg->pmic_min_uV_for_retention = INT_MAX;
pvreg->use_phase_switching = use_phase_switching;
+ pvreg->pfm_threshold = pfm_threshold;
mutex_init(&pvreg->krait_power_vregs_lock);
INIT_LIST_HEAD(&pvreg->krait_power_vregs);
@@ -1208,35 +1428,53 @@
void secondary_cpu_hs_init(void *base_ptr)
{
+ uint32_t reg_val;
+ void *l2_saw_base;
+
/* Turn on the BHS, turn off LDO Bypass and power down LDO */
- writel_relaxed(
- BHS_CNT_DEFAULT << BHS_CNT_BIT_POS
+ reg_val = BHS_CNT_DEFAULT << BHS_CNT_BIT_POS
| LDO_PWR_DWN_MASK
| CLK_SRC_DEFAULT << CLK_SRC_SEL_BIT_POS
- | BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS
- | BHS_EN_MASK,
- base_ptr + APC_PWR_GATE_CTL);
+ | BHS_EN_MASK;
+ writel_relaxed(reg_val, base_ptr + APC_PWR_GATE_CTL);
/* complete the above write before the delay */
mb();
+ /* wait for the bhs to settle */
+ udelay(BHS_SETTLING_DELAY_US);
- /*
- * wait for the bhs to settle
- */
+ /* Turn on BHS segments */
+ reg_val |= BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS;
+ writel_relaxed(reg_val, base_ptr + APC_PWR_GATE_CTL);
+
+ /* complete the above write before the delay */
+ mb();
+ /* wait for the bhs to settle */
udelay(BHS_SETTLING_DELAY_US);
/* Finally turn on the bypass so that BHS supplies power */
- writel_relaxed(
- BHS_CNT_DEFAULT << BHS_CNT_BIT_POS
- | LDO_PWR_DWN_MASK
- | CLK_SRC_DEFAULT << CLK_SRC_SEL_BIT_POS
- | LDO_BYP_MASK
- | BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS
- | BHS_EN_MASK,
- base_ptr + APC_PWR_GATE_CTL);
+ reg_val |= LDO_BYP_MASK;
+ writel_relaxed(reg_val, base_ptr + APC_PWR_GATE_CTL);
+
+ if (the_gang && the_gang->manage_phases)
+ return;
+
+ /*
+ * If the driver has not yet started to manage phases then enable
+ * max phases.
+ */
+ l2_saw_base = ioremap_nocache(MSM_L2_SAW_PHYS, SZ_4K);
+ if (!l2_saw_base) {
+ __WARN();
+ return;
+ }
+ writel_relaxed(0x10003, l2_saw_base + 0x1c);
+ mb();
+ udelay(PHASE_SETTLING_TIME_US);
+
+ iounmap(l2_saw_base);
}
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("KRAIT POWER regulator driver");
-MODULE_VERSION("1.0");
MODULE_ALIAS("platform:"KRAIT_REGULATOR_DRIVER_NAME);
diff --git a/arch/arm/mach-msm/lpm_resources.c b/arch/arm/mach-msm/lpm_resources.c
index f0e5ebd..1d9c539 100644
--- a/arch/arm/mach-msm/lpm_resources.c
+++ b/arch/arm/mach-msm/lpm_resources.c
@@ -23,11 +23,12 @@
#include <linux/tick.h>
#include <mach/mpm.h>
#include <mach/rpm-smd.h>
+#include <mach/trace_msm_low_power.h>
#include "spm.h"
#include "lpm_resources.h"
#include "rpm-notifier.h"
#include "idle.h"
-#include "trace_msm_low_power.h"
+
/*Debug Definitions*/
enum {
@@ -429,7 +430,7 @@
trace_lpm_resources(rs->sleep_value, rs->name);
}
-static void msm_lpm_set_l2_mode(int sleep_mode, int notify_rpm)
+static void msm_lpm_set_l2_mode(int sleep_mode)
{
int lpm, rc;
@@ -453,7 +454,7 @@
break;
}
- rc = msm_spm_l2_set_low_power_mode(lpm, notify_rpm);
+ rc = msm_spm_l2_set_low_power_mode(lpm, true);
if (rc < 0)
pr_err("%s: Failed to set L2 low power mode %d",
@@ -474,7 +475,7 @@
{
struct msm_lpm_resource *rs = &msm_lpm_l2;
- msm_lpm_set_l2_mode(rs->sleep_value, notify_rpm);
+ msm_lpm_set_l2_mode(rs->sleep_value);
}
int msm_lpm_get_l2_cache_value(struct device_node *node,
@@ -786,7 +787,7 @@
msm_mpm_exit_sleep(from_idle);
if (msm_lpm_l2.valid)
- msm_lpm_set_l2_mode(msm_lpm_l2.rs_data.default_value, false);
+ msm_lpm_set_l2_mode(msm_lpm_l2.rs_data.default_value);
}
static int msm_lpm_cpu_callback(struct notifier_block *cpu_nb,
diff --git a/arch/arm/mach-msm/mdm2.c b/arch/arm/mach-msm/mdm2.c
index c8d6d5a..e43a0e27 100644
--- a/arch/arm/mach-msm/mdm2.c
+++ b/arch/arm/mach-msm/mdm2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, 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
@@ -28,6 +28,7 @@
#include <linux/debugfs.h>
#include <linux/completion.h>
#include <linux/workqueue.h>
+#include <linux/clk.h>
#include <linux/mfd/pmic8058.h>
#include <asm/mach-types.h>
#include <asm/uaccess.h>
@@ -38,26 +39,24 @@
#include <linux/msm_charm.h>
#include "msm_watchdog.h"
#include "devices.h"
+#include "clock.h"
#include "mdm_private.h"
#define MDM_PBLRDY_CNT 20
static int mdm_debug_mask;
-static int power_on_count;
-static int hsic_peripheral_status;
-static DEFINE_MUTEX(hsic_status_lock);
static void mdm_peripheral_connect(struct mdm_modem_drv *mdm_drv)
{
if (!mdm_drv->pdata->peripheral_platform_device)
return;
- mutex_lock(&hsic_status_lock);
- if (hsic_peripheral_status)
+ mutex_lock(&mdm_drv->peripheral_status_lock);
+ if (mdm_drv->peripheral_status)
goto out;
platform_device_add(mdm_drv->pdata->peripheral_platform_device);
- hsic_peripheral_status = 1;
+ mdm_drv->peripheral_status = 1;
out:
- mutex_unlock(&hsic_status_lock);
+ mutex_unlock(&mdm_drv->peripheral_status_lock);
}
static void mdm_peripheral_disconnect(struct mdm_modem_drv *mdm_drv)
@@ -65,13 +64,13 @@
if (!mdm_drv->pdata->peripheral_platform_device)
return;
- mutex_lock(&hsic_status_lock);
- if (!hsic_peripheral_status)
+ mutex_lock(&mdm_drv->peripheral_status_lock);
+ if (!mdm_drv->peripheral_status)
goto out;
platform_device_del(mdm_drv->pdata->peripheral_platform_device);
- hsic_peripheral_status = 0;
+ mdm_drv->peripheral_status = 0;
out:
- mutex_unlock(&hsic_status_lock);
+ mutex_unlock(&mdm_drv->peripheral_status_lock);
}
/* This function can be called from atomic context. */
@@ -112,8 +111,8 @@
for (i = 20; i > 0; i--) {
if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
if (mdm_debug_mask & MDM_DEBUG_MASK_SHDN_LOG)
- pr_info("%s: mdm2ap_status went low, i = %d\n",
- __func__, i);
+ pr_debug("%s:id %d: mdm2ap_statuswent low, i=%d\n",
+ __func__, mdm_drv->device_id, i);
break;
}
msleep(100);
@@ -123,8 +122,10 @@
gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
soft_reset_direction);
if (i == 0) {
- pr_err("%s: MDM2AP_STATUS never went low. Doing a hard reset\n",
- __func__);
+ pr_debug("%s:id %d: MDM2AP_STATUS never went low. Doing a hard reset\n",
+ __func__, mdm_drv->device_id);
+ gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
+ soft_reset_direction);
/*
* Currently, there is a debounce timer on the charm PMIC. It is
* necessary to hold the PMIC RESET low for ~3.5 seconds
@@ -139,13 +140,14 @@
{
int i;
int pblrdy;
- if (power_on_count != 1) {
- pr_err("%s: Calling fn when power_on_count != 1\n",
- __func__);
+ if (mdm_drv->power_on_count != 1) {
+ pr_debug("%s:id %d: Calling fn when power_on_count != 1\n",
+ __func__, mdm_drv->device_id);
return;
}
- pr_err("%s: Powering on modem for the first time\n", __func__);
+ pr_debug("%s:id %d: Powering on modem for the first time\n",
+ __func__, mdm_drv->device_id);
mdm_peripheral_disconnect(mdm_drv);
/* If this is the first power-up after a panic, the modem may still
@@ -159,13 +161,15 @@
/* Pull AP2MDM_KPDPWR gpio high and wait for PS_HOLD to settle,
* then pull it back low.
*/
- pr_debug("%s: Pulling AP2MDM_KPDPWR gpio high\n", __func__);
+ pr_debug("%s:id %d: Pulling AP2MDM_KPDPWR gpio high\n",
+ __func__, mdm_drv->device_id);
gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 1);
gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
msleep(1000);
gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 0);
- } else
+ } else {
gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
+ }
if (!GPIO_IS_VALID(mdm_drv->mdm2ap_pblrdy))
goto start_mdm_peripheral;
@@ -176,7 +180,8 @@
break;
usleep_range(5000, 5000);
}
- pr_debug("%s: i:%d\n", __func__, i);
+ pr_debug("%s: id %d: pblrdy i:%d\n", __func__,
+ mdm_drv->device_id, i);
start_mdm_peripheral:
mdm_peripheral_connect(mdm_drv);
@@ -188,7 +193,8 @@
int i;
int pblrdy;
- pr_err("%s: soft resetting mdm modem\n", __func__);
+ pr_debug("%s: id %d: soft resetting mdm modem\n",
+ __func__, mdm_drv->device_id);
mdm_peripheral_disconnect(mdm_drv);
mdm_toggle_soft_reset(mdm_drv);
@@ -202,7 +208,8 @@
usleep_range(5000, 5000);
}
- pr_debug("%s: i:%d\n", __func__, i);
+ pr_debug("%s: id %d: pblrdy i:%d\n", __func__,
+ mdm_drv->device_id, i);
start_mdm_peripheral:
mdm_peripheral_connect(mdm_drv);
@@ -211,7 +218,7 @@
static void mdm_power_on_common(struct mdm_modem_drv *mdm_drv)
{
- power_on_count++;
+ mdm_drv->power_on_count++;
/* this gpio will be used to indicate apq readiness,
* de-assert it now so that it can be asserted later.
@@ -226,10 +233,10 @@
* user space but we're already powered on. Ignore it.
*/
if (mdm_drv->pdata->early_power_on &&
- (power_on_count == 2))
+ (mdm_drv->power_on_count == 2))
return;
- if (power_on_count == 1)
+ if (mdm_drv->power_on_count == 1)
mdm_do_first_power_on(mdm_drv);
else
mdm_do_soft_power_on(mdm_drv);
@@ -242,7 +249,8 @@
static void mdm_status_changed(struct mdm_modem_drv *mdm_drv, int value)
{
- pr_debug("%s: value:%d\n", __func__, value);
+ pr_debug("%s: id %d: value:%d\n", __func__,
+ value, mdm_drv->device_id);
if (value) {
mdm_peripheral_disconnect(mdm_drv);
@@ -256,13 +264,15 @@
{
switch (type) {
case APQ_CONTROLLED_UPGRADE:
- pr_debug("%s APQ controlled modem image upgrade\n", __func__);
- mdm_drv->mdm_ready = 0;
+ pr_debug("%s: id %d: APQ controlled modem image upgrade\n",
+ __func__, mdm_drv->device_id);
+ atomic_set(&mdm_drv->mdm_ready, 0);
mdm_toggle_soft_reset(mdm_drv);
break;
case MDM_CONTROLLED_UPGRADE:
- pr_debug("%s MDM controlled modem image upgrade\n", __func__);
- mdm_drv->mdm_ready = 0;
+ pr_debug("%s: id %d: MDM controlled modem image upgrade\n",
+ __func__, mdm_drv->device_id);
+ atomic_set(&mdm_drv->mdm_ready, 0);
/*
* If we have no image currently present on the modem, then we
* would be in PBL, in which case the status gpio would not go
@@ -270,15 +280,19 @@
*/
mdm_drv->disable_status_check = 1;
if (GPIO_IS_VALID(mdm_drv->usb_switch_gpio)) {
- pr_info("%s Switching usb control to MDM\n", __func__);
+ pr_debug("%s: id %d: Switching usb control to MDM\n",
+ __func__, mdm_drv->device_id);
gpio_direction_output(mdm_drv->usb_switch_gpio, 1);
} else
- pr_err("%s usb switch gpio unavailable\n", __func__);
+ pr_err("%s: id %d: usb switch gpio unavailable\n",
+ __func__, mdm_drv->device_id);
break;
default:
- pr_err("%s invalid upgrade type\n", __func__);
+ pr_err("%s: id %d: invalid upgrade type\n",
+ __func__, mdm_drv->device_id);
}
}
+
static struct mdm_ops mdm_cb = {
.power_on_mdm_cb = mdm_power_on_common,
.reset_mdm_cb = mdm_power_on_common,
@@ -289,44 +303,10 @@
.image_upgrade_cb = mdm_image_upgrade,
};
-static int __init mdm_modem_probe(struct platform_device *pdev)
+int mdm_get_ops(struct mdm_ops **mdm_ops)
{
- return mdm_common_create(pdev, &mdm_cb);
+ *mdm_ops = &mdm_cb;
+ return 0;
}
-static int __devexit mdm_modem_remove(struct platform_device *pdev)
-{
- return mdm_common_modem_remove(pdev);
-}
-static void mdm_modem_shutdown(struct platform_device *pdev)
-{
- mdm_common_modem_shutdown(pdev);
-}
-
-static struct platform_driver mdm_modem_driver = {
- .remove = mdm_modem_remove,
- .shutdown = mdm_modem_shutdown,
- .driver = {
- .name = "mdm2_modem",
- .owner = THIS_MODULE
- },
-};
-
-static int __init mdm_modem_init(void)
-{
- return platform_driver_probe(&mdm_modem_driver, mdm_modem_probe);
-}
-
-static void __exit mdm_modem_exit(void)
-{
- platform_driver_unregister(&mdm_modem_driver);
-}
-
-module_init(mdm_modem_init);
-module_exit(mdm_modem_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("mdm modem driver");
-MODULE_VERSION("2.0");
-MODULE_ALIAS("mdm_modem");
diff --git a/arch/arm/mach-msm/mdm_common.c b/arch/arm/mach-msm/mdm_common.c
index de46be8..5e12a0d 100644
--- a/arch/arm/mach-msm/mdm_common.c
+++ b/arch/arm/mach-msm/mdm_common.c
@@ -49,22 +49,15 @@
#define MDM_RDUMP_TIMEOUT 120000L
#define MDM2AP_STATUS_TIMEOUT_MS 60000L
-static unsigned int mdm_debug_mask;
-static struct workqueue_struct *mdm_queue;
-static struct workqueue_struct *mdm_sfr_queue;
-static unsigned int dump_timeout_ms;
-static int vddmin_gpios_sent;
-
+/* Allow a maximum device id of this many digits */
+#define MAX_DEVICE_DIGITS 10
#define EXTERNAL_MODEM "external_modem"
+#define SUBSYS_NAME_LENGTH \
+ (sizeof(EXTERNAL_MODEM) + MAX_DEVICE_DIGITS)
-static struct mdm_modem_drv *mdm_drv;
-static struct subsys_device *mdm_subsys_dev;
-
-DECLARE_COMPLETION(mdm_needs_reload);
-DECLARE_COMPLETION(mdm_boot);
-DECLARE_COMPLETION(mdm_ram_dumps);
-
-static int first_boot = 1;
+#define DEVICE_BASE_NAME "mdm"
+#define DEVICE_NAME_LENGTH \
+ (sizeof(DEVICE_BASE_NAME) + MAX_DEVICE_DIGITS)
#define RD_BUF_SIZE 100
#define SFR_MAX_RETRIES 10
@@ -74,58 +67,211 @@
GPIO_UPDATE_BOOTING_CONFIG = 1,
GPIO_UPDATE_RUNNING_CONFIG,
};
-static int mdm2ap_status_valid_old_config;
-static struct gpiomux_setting mdm2ap_status_old_config;
+
+struct mdm_device {
+ struct list_head link;
+ struct mdm_modem_drv mdm_data;
+
+ int mdm2ap_status_valid_old_config;
+ struct gpiomux_setting mdm2ap_status_old_config;
+ int first_boot;
+ struct workqueue_struct *mdm_queue;
+ struct workqueue_struct *mdm_sfr_queue;
+ unsigned int dump_timeout_ms;
+
+ char subsys_name[SUBSYS_NAME_LENGTH];
+ struct subsys_desc mdm_subsys;
+ struct subsys_device *mdm_subsys_dev;
+
+ char device_name[DEVICE_NAME_LENGTH];
+ struct miscdevice misc_device;
+
+ struct completion mdm_needs_reload;
+ struct completion mdm_boot;
+ struct completion mdm_ram_dumps;
+ int mdm_errfatal_irq;
+ int mdm_status_irq;
+ int mdm_pblrdy_irq;
+
+ struct delayed_work mdm2ap_status_check_work;
+ struct work_struct mdm_status_work;
+ struct work_struct sfr_reason_work;
+
+ struct notifier_block mdm_panic_blk;
+
+ int ssr_started_internally;
+};
+
+static struct list_head mdm_devices;
+static DEFINE_SPINLOCK(mdm_devices_lock);
+
+static int ssr_count;
+static DEFINE_SPINLOCK(ssr_lock);
+
+static unsigned int mdm_debug_mask;
+int vddmin_gpios_sent;
+static struct mdm_ops *mdm_ops;
+
+static void mdm_device_list_add(struct mdm_device *mdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdm_devices_lock, flags);
+ list_add_tail(&mdev->link, &mdm_devices);
+ spin_unlock_irqrestore(&mdm_devices_lock, flags);
+}
+
+static void mdm_device_list_remove(struct mdm_device *mdev)
+{
+ unsigned long flags;
+ struct mdm_device *lmdev, *tmp;
+
+ spin_lock_irqsave(&mdm_devices_lock, flags);
+ list_for_each_entry_safe(lmdev, tmp, &mdm_devices, link) {
+ if (mdev && mdev == lmdev) {
+ pr_debug("%s: removing device id %d\n",
+ __func__, mdev->mdm_data.device_id);
+ list_del(&mdev->link);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&mdm_devices_lock, flags);
+}
+
+/* If the platform's cascading_ssr flag is set, the subsystem
+ * restart module will restart the other modems so stop
+ * monitoring them as well.
+ * This function can be called from interrupt context.
+ */
+static void mdm_start_ssr(struct mdm_device *mdev)
+{
+ unsigned long flags;
+ int start_ssr = 1;
+
+ spin_lock_irqsave(&ssr_lock, flags);
+ if (mdev->mdm_data.pdata->cascading_ssr &&
+ ssr_count > 0) {
+ start_ssr = 0;
+ } else {
+ ssr_count++;
+ mdev->ssr_started_internally = 1;
+ }
+ spin_unlock_irqrestore(&ssr_lock, flags);
+
+ if (start_ssr) {
+ atomic_set(&mdev->mdm_data.mdm_ready, 0);
+ pr_debug("%s: Resetting mdm id %d due to mdm error\n",
+ __func__, mdev->mdm_data.device_id);
+ subsystem_restart_dev(mdev->mdm_subsys_dev);
+ } else {
+ pr_debug("%s: Another modem is already in SSR\n",
+ __func__);
+ }
+}
+
+/* Increment the reference count to handle the case where
+ * subsystem restart is initiated by the SSR service.
+ */
+static void mdm_ssr_started(struct mdm_device *mdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ssr_lock, flags);
+ ssr_count++;
+ atomic_set(&mdev->mdm_data.mdm_ready, 0);
+ spin_unlock_irqrestore(&ssr_lock, flags);
+}
+
+/* mdm_ssr_completed assumes that mdm_ssr_started has previously
+ * been called.
+ */
+static void mdm_ssr_completed(struct mdm_device *mdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ssr_lock, flags);
+ ssr_count--;
+ if (mdev->ssr_started_internally) {
+ mdev->ssr_started_internally = 0;
+ ssr_count--;
+ }
+
+ if (ssr_count < 0) {
+ pr_err("%s: ssr_count = %d\n",
+ __func__, ssr_count);
+ panic("%s: ssr_count = %d < 0\n",
+ __func__, ssr_count);
+ }
+ spin_unlock_irqrestore(&ssr_lock, flags);
+}
static irqreturn_t mdm_vddmin_change(int irq, void *dev_id)
{
- int value = gpio_get_value(
- mdm_drv->pdata->vddmin_resource->mdm2ap_vddmin_gpio);
+ struct mdm_device *mdev = (struct mdm_device *)dev_id;
+ struct mdm_vddmin_resource *vddmin_res;
+ int value;
+ if (!mdev)
+ goto handled;
+
+ vddmin_res = mdev->mdm_data.pdata->vddmin_resource;
+ if (!vddmin_res)
+ goto handled;
+
+ value = gpio_get_value(
+ vddmin_res->mdm2ap_vddmin_gpio);
if (value == 0)
- pr_info("External Modem entered Vddmin\n");
+ pr_debug("External Modem id %d entered Vddmin\n",
+ mdev->mdm_data.device_id);
else
- pr_info("External Modem exited Vddmin\n");
-
+ pr_debug("External Modem id %d exited Vddmin\n",
+ mdev->mdm_data.device_id);
+handled:
return IRQ_HANDLED;
}
+/* The vddmin_res resource may not be supported by some platforms. */
static void mdm_setup_vddmin_gpios(void)
{
+ unsigned long flags;
struct msm_rpm_iv_pair req;
+ struct mdm_device *mdev;
struct mdm_vddmin_resource *vddmin_res;
int irq, ret;
- /* This resource may not be supported by some platforms. */
- vddmin_res = mdm_drv->pdata->vddmin_resource;
- if (!vddmin_res)
- return;
+ spin_lock_irqsave(&mdm_devices_lock, flags);
+ list_for_each_entry(mdev, &mdm_devices, link) {
+ vddmin_res = mdev->mdm_data.pdata->vddmin_resource;
+ if (!vddmin_res)
+ continue;
- pr_info("Enabling vddmin logging\n");
- req.id = vddmin_res->rpm_id;
- req.value = ((uint32_t)vddmin_res->ap2mdm_vddmin_gpio & 0x0000FFFF)
- << 16;
- req.value |= ((uint32_t)vddmin_res->modes & 0x000000FF) << 8;
- req.value |= (uint32_t)vddmin_res->drive_strength & 0x000000FF;
+ pr_debug("Enabling vddmin logging on modem id %d\n",
+ mdev->mdm_data.device_id);
+ req.id = vddmin_res->rpm_id;
+ req.value =
+ ((uint32_t)vddmin_res->ap2mdm_vddmin_gpio & 0x0000FFFF)
+ << 16;
+ req.value |= ((uint32_t)vddmin_res->modes & 0x000000FF) << 8;
+ req.value |= (uint32_t)vddmin_res->drive_strength & 0x000000FF;
- msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
+ msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
- /* Start monitoring low power gpio from mdm */
- irq = MSM_GPIO_TO_INT(vddmin_res->mdm2ap_vddmin_gpio);
- if (irq < 0) {
- pr_err("%s: could not get LPM POWER IRQ resource.\n",
- __func__);
- goto error_end;
+ /* Start monitoring low power gpio from mdm */
+ irq = gpio_to_irq(vddmin_res->mdm2ap_vddmin_gpio);
+ if (irq < 0)
+ pr_err("%s: could not get LPM POWER IRQ resource mdm id %d.\n",
+ __func__, mdev->mdm_data.device_id);
+ else {
+ ret = request_threaded_irq(irq, NULL, mdm_vddmin_change,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "mdm lpm", mdev);
+
+ if (ret < 0)
+ pr_err("%s: MDM LPM IRQ#%d request failed with error=%d",
+ __func__, irq, ret);
+ }
}
-
- ret = request_threaded_irq(irq, NULL, mdm_vddmin_change,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "mdm lpm", NULL);
-
- if (ret < 0)
- pr_err("%s: MDM LPM IRQ#%d request failed with error=%d",
- __func__, irq, ret);
-error_end:
+ spin_unlock_irqrestore(&mdm_devices_lock, flags);
return;
}
@@ -133,48 +279,53 @@
{
int ret, ntries = 0;
char sfr_buf[RD_BUF_SIZE];
+ struct mdm_platform_data *pdata;
+ struct mdm_device *mdev = container_of(work,
+ struct mdm_device, sfr_reason_work);
- do {
- msleep(SFR_RETRY_INTERVAL);
- ret = sysmon_get_reason(SYSMON_SS_EXT_MODEM,
+ pdata = mdev->mdm_data.pdata;
+ if (pdata->sysmon_subsys_id_valid) {
+ do {
+ ret = sysmon_get_reason(pdata->sysmon_subsys_id,
sfr_buf, sizeof(sfr_buf));
- if (ret) {
- /*
- * The sysmon device may not have been probed as yet
- * after the restart.
+ if (!ret) {
+ pr_err("mdm restart reason: %s\n", sfr_buf);
+ return;
+ }
+ /* Wait for the modem to be fully booted after a
+ * subsystem restart. This may take several seconds.
*/
- pr_err("%s: Error retrieving mdm restart reason, ret = %d, "
- "%d/%d tries\n", __func__, ret,
- ntries + 1, SFR_MAX_RETRIES);
- } else {
- pr_err("mdm restart reason: %s\n", sfr_buf);
- break;
- }
- } while (++ntries < SFR_MAX_RETRIES);
+ msleep(SFR_RETRY_INTERVAL);
+ } while (++ntries < SFR_MAX_RETRIES);
+ pr_debug("%s: Error retrieving restart reason: %d\n",
+ __func__, ret);
+ }
}
-static DECLARE_WORK(sfr_reason_work, mdm_restart_reason_fn);
-
static void mdm2ap_status_check(struct work_struct *work)
{
+ struct mdm_device *mdev =
+ container_of(work, struct mdm_device,
+ mdm2ap_status_check_work.work);
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
/*
* If the mdm modem did not pull the MDM2AP_STATUS gpio
* high then call subsystem_restart.
*/
if (!mdm_drv->disable_status_check) {
if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
- pr_err("%s: MDM2AP_STATUS gpio did not go high\n",
- __func__);
- mdm_drv->mdm_ready = 0;
- subsystem_restart_dev(mdm_subsys_dev);
+ pr_debug("%s: MDM2AP_STATUS did not go high on mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
+ mdm_start_ssr(mdev);
}
}
}
-static DECLARE_DELAYED_WORK(mdm2ap_status_check_work, mdm2ap_status_check);
-
-static void mdm_update_gpio_configs(enum gpio_update_config gpio_config)
+static void mdm_update_gpio_configs(struct mdm_device *mdev,
+ enum gpio_update_config gpio_config)
{
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
+
/* Some gpio configuration may need updating after modem bootup.*/
switch (gpio_config) {
case GPIO_UPDATE_RUNNING_CONFIG:
@@ -182,20 +333,20 @@
if (msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
GPIOMUX_ACTIVE,
mdm_drv->pdata->mdm2ap_status_gpio_run_cfg,
- &mdm2ap_status_old_config))
- pr_err("%s: failed updating running gpio config\n",
- __func__);
+ &mdev->mdm2ap_status_old_config))
+ pr_err("%s: failed updating running gpio config mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
else
- mdm2ap_status_valid_old_config = 1;
+ mdev->mdm2ap_status_valid_old_config = 1;
}
break;
case GPIO_UPDATE_BOOTING_CONFIG:
- if (mdm2ap_status_valid_old_config) {
+ if (mdev->mdm2ap_status_valid_old_config) {
msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
GPIOMUX_ACTIVE,
- &mdm2ap_status_old_config,
+ &mdev->mdm2ap_status_old_config,
NULL);
- mdm2ap_status_valid_old_config = 0;
+ mdev->mdm2ap_status_valid_old_config = 0;
}
break;
default:
@@ -204,21 +355,27 @@
}
}
-long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
+static long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int status, ret = 0;
+ struct mdm_device *mdev = filp->private_data;
+ struct mdm_modem_drv *mdm_drv;
if (_IOC_TYPE(cmd) != CHARM_CODE) {
- pr_err("%s: invalid ioctl code\n", __func__);
+ pr_err("%s: invalid ioctl code to mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
return -EINVAL;
}
- pr_debug("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
+ mdm_drv = &mdev->mdm_data;
+ pr_debug("%s: Entering ioctl cmd = %d, mdm id = %d\n",
+ __func__, _IOC_NR(cmd), mdev->mdm_data.device_id);
switch (cmd) {
case WAKE_CHARM:
- pr_info("%s: Powering on mdm\n", __func__);
- mdm_drv->ops->power_on_mdm_cb(mdm_drv);
+ pr_debug("%s: Powering on mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
+ mdm_ops->power_on_mdm_cb(mdm_drv);
break;
case CHECK_FOR_BOOT:
if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
@@ -227,30 +384,33 @@
put_user(0, (unsigned long __user *) arg);
break;
case NORMAL_BOOT_DONE:
- pr_debug("%s: check if mdm is booted up\n", __func__);
+ pr_debug("%s: check if mdm id %d is booted up\n",
+ __func__, mdev->mdm_data.device_id);
get_user(status, (unsigned long __user *) arg);
if (status) {
- pr_debug("%s: normal boot failed\n", __func__);
+ pr_debug("%s: normal boot of mdm id %d failed\n",
+ __func__, mdev->mdm_data.device_id);
mdm_drv->mdm_boot_status = -EIO;
} else {
- pr_info("%s: normal boot done\n", __func__);
+ pr_debug("%s: normal boot of mdm id %d done\n",
+ __func__, mdev->mdm_data.device_id);
mdm_drv->mdm_boot_status = 0;
}
- mdm_drv->mdm_ready = 1;
+ atomic_set(&mdm_drv->mdm_ready, 1);
- if (mdm_drv->ops->normal_boot_done_cb != NULL)
- mdm_drv->ops->normal_boot_done_cb(mdm_drv);
+ if (mdm_ops->normal_boot_done_cb != NULL)
+ mdm_ops->normal_boot_done_cb(mdm_drv);
- if (!first_boot)
- complete(&mdm_boot);
+ if (!mdev->first_boot)
+ complete(&mdev->mdm_boot);
else
- first_boot = 0;
+ mdev->first_boot = 0;
/* If successful, start a timer to check that the mdm2ap_status
* gpio goes high.
*/
if (!status && gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
- schedule_delayed_work(&mdm2ap_status_check_work,
+ schedule_delayed_work(&mdev->mdm2ap_status_check_work,
msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
break;
case RAM_DUMP_DONE:
@@ -259,24 +419,26 @@
if (status)
mdm_drv->mdm_ram_dump_status = -EIO;
else {
- pr_info("%s: ramdump collection completed\n", __func__);
+ pr_debug("%s: ramdump collection completed\n",
+ __func__);
mdm_drv->mdm_ram_dump_status = 0;
}
- complete(&mdm_ram_dumps);
+ complete(&mdev->mdm_ram_dumps);
break;
case WAIT_FOR_RESTART:
pr_debug("%s: wait for mdm to need images reloaded\n",
__func__);
- ret = wait_for_completion_interruptible(&mdm_needs_reload);
+ ret = wait_for_completion_interruptible(
+ &mdev->mdm_needs_reload);
if (!ret)
put_user(mdm_drv->boot_type,
(unsigned long __user *) arg);
- INIT_COMPLETION(mdm_needs_reload);
+ init_completion(&mdev->mdm_needs_reload);
break;
case GET_DLOAD_STATUS:
pr_debug("getting status of mdm2ap_errfatal_gpio\n");
if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
- !mdm_drv->mdm_ready)
+ !atomic_read(&mdm_drv->mdm_ready))
put_user(1, (unsigned long __user *) arg);
else
put_user(0, (unsigned long __user *) arg);
@@ -284,18 +446,18 @@
case IMAGE_UPGRADE:
pr_debug("%s Image upgrade ioctl recieved\n", __func__);
if (mdm_drv->pdata->image_upgrade_supported &&
- mdm_drv->ops->image_upgrade_cb) {
+ mdm_ops->image_upgrade_cb) {
get_user(status, (unsigned long __user *) arg);
- mdm_drv->ops->image_upgrade_cb(mdm_drv, status);
+ mdm_ops->image_upgrade_cb(mdm_drv, status);
} else
pr_debug("%s Image upgrade not supported\n", __func__);
break;
case SHUTDOWN_CHARM:
if (!mdm_drv->pdata->send_shdn)
break;
- mdm_drv->mdm_ready = 0;
+ atomic_set(&mdm_drv->mdm_ready, 0);
if (mdm_debug_mask & MDM_DEBUG_MASK_SHDN_LOG)
- pr_info("Sending shutdown request to mdm\n");
+ pr_debug("Sending shutdown request to mdm\n");
ret = sysmon_send_shutdown(SYSMON_SS_EXT_MODEM);
if (ret)
pr_err("%s: Graceful shutdown of the external modem failed, ret = %d\n",
@@ -307,68 +469,76 @@
ret = -EINVAL;
break;
}
-
return ret;
}
static void mdm_status_fn(struct work_struct *work)
{
+ struct mdm_device *mdev =
+ container_of(work, struct mdm_device, mdm_status_work);
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
pr_debug("%s: status:%d\n", __func__, value);
- if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
- mdm_drv->ops->status_cb(mdm_drv, value);
+ if (atomic_read(&mdm_drv->mdm_ready) && mdm_ops->status_cb)
+ mdm_ops->status_cb(mdm_drv, value);
/* Update gpio configuration to "running" config. */
- mdm_update_gpio_configs(GPIO_UPDATE_RUNNING_CONFIG);
+ mdm_update_gpio_configs(mdev, GPIO_UPDATE_RUNNING_CONFIG);
}
-static DECLARE_WORK(mdm_status_work, mdm_status_fn);
-
-static void mdm_disable_irqs(void)
+static void mdm_disable_irqs(struct mdm_device *mdev)
{
- disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
- disable_irq_nosync(mdm_drv->mdm_status_irq);
+ if (!mdev)
+ return;
+ disable_irq_nosync(mdev->mdm_errfatal_irq);
+ disable_irq_nosync(mdev->mdm_status_irq);
+ disable_irq_nosync(mdev->mdm_pblrdy_irq);
}
static irqreturn_t mdm_errfatal(int irq, void *dev_id)
{
- pr_debug("%s: mdm got errfatal interrupt\n", __func__);
- if (mdm_drv->mdm_ready &&
+ struct mdm_modem_drv *mdm_drv;
+ struct mdm_device *mdev = (struct mdm_device *)dev_id;
+ if (!mdev)
+ return IRQ_HANDLED;
+
+ pr_debug("%s: mdm id %d sent errfatal interrupt\n",
+ __func__, mdev->mdm_data.device_id);
+ mdm_drv = &mdev->mdm_data;
+ if (atomic_read(&mdm_drv->mdm_ready) &&
(gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
- pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
- mdm_drv->mdm_ready = 0;
- subsystem_restart_dev(mdm_subsys_dev);
+ pr_debug("%s: Received err fatal from mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
+ mdm_start_ssr(mdev);
}
return IRQ_HANDLED;
}
+/* set the mdm_device as the file's private data */
static int mdm_modem_open(struct inode *inode, struct file *file)
{
+ struct miscdevice *misc = file->private_data;
+ struct mdm_device *mdev = container_of(misc,
+ struct mdm_device, misc_device);
+
+ file->private_data = mdev;
return 0;
}
-static const struct file_operations mdm_modem_fops = {
- .owner = THIS_MODULE,
- .open = mdm_modem_open,
- .unlocked_ioctl = mdm_modem_ioctl,
-};
-
-
-static struct miscdevice mdm_modem_misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "mdm",
- .fops = &mdm_modem_fops
-};
-
static int mdm_panic_prep(struct notifier_block *this,
unsigned long event, void *ptr)
{
int i;
+ struct mdm_modem_drv *mdm_drv;
+ struct mdm_device *mdev =
+ container_of(this, struct mdm_device, mdm_panic_blk);
+
+ mdm_drv = &mdev->mdm_data;
pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
__func__);
- mdm_disable_irqs();
+ mdm_disable_irqs(mdev);
gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
@@ -380,49 +550,67 @@
if (i <= 0) {
pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
/* Reset the modem so that it will go into download mode. */
- if (mdm_drv && mdm_drv->ops->atomic_reset_mdm_cb)
- mdm_drv->ops->atomic_reset_mdm_cb(mdm_drv);
+ if (mdm_drv && mdm_ops->atomic_reset_mdm_cb)
+ mdm_ops->atomic_reset_mdm_cb(mdm_drv);
}
return NOTIFY_DONE;
}
-static struct notifier_block mdm_panic_blk = {
- .notifier_call = mdm_panic_prep,
-};
-
static irqreturn_t mdm_status_change(int irq, void *dev_id)
{
- int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
+ struct mdm_modem_drv *mdm_drv;
+ struct mdm_device *mdev = (struct mdm_device *)dev_id;
+ int value;
+ if (!mdev)
+ return IRQ_HANDLED;
+
+ mdm_drv = &mdev->mdm_data;
+ value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
if ((mdm_debug_mask & MDM_DEBUG_MASK_SHDN_LOG) && (value == 0))
- pr_info("%s: mdm2ap_status went low\n", __func__);
+ pr_debug("%s: mdm2ap_status went low\n", __func__);
- pr_debug("%s: mdm sent status change interrupt\n", __func__);
- if (value == 0 && mdm_drv->mdm_ready == 1) {
- pr_info("%s: unexpected reset external modem\n", __func__);
+ pr_debug("%s: mdm id %d sent status change interrupt\n",
+ __func__, mdev->mdm_data.device_id);
+ if (value == 0 && atomic_read(&mdm_drv->mdm_ready)) {
+ pr_debug("%s: unexpected reset external modem id %d\n",
+ __func__, mdev->mdm_data.device_id);
mdm_drv->mdm_unexpected_reset_occurred = 1;
- mdm_drv->mdm_ready = 0;
- subsystem_restart_dev(mdm_subsys_dev);
+ mdm_start_ssr(mdev);
} else if (value == 1) {
- cancel_delayed_work(&mdm2ap_status_check_work);
- pr_info("%s: status = 1: mdm is now ready\n", __func__);
- queue_work(mdm_queue, &mdm_status_work);
+ cancel_delayed_work(&mdev->mdm2ap_status_check_work);
+ pr_debug("%s: status = 1: mdm id %d is now ready\n",
+ __func__, mdev->mdm_data.device_id);
+ queue_work(mdev->mdm_queue, &mdev->mdm_status_work);
}
return IRQ_HANDLED;
}
static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
{
- pr_info("%s: pbl ready:%d\n", __func__,
- gpio_get_value(mdm_drv->mdm2ap_pblrdy));
+ struct mdm_modem_drv *mdm_drv;
+ struct mdm_device *mdev = (struct mdm_device *)dev_id;
+ if (!mdev)
+ return IRQ_HANDLED;
+ mdm_drv = &mdev->mdm_data;
+ pr_debug("%s: mdm id %d: pbl ready:%d\n",
+ __func__, mdev->mdm_data.device_id,
+ gpio_get_value(mdm_drv->mdm2ap_pblrdy));
return IRQ_HANDLED;
}
static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys)
{
- mdm_drv->mdm_ready = 0;
- cancel_delayed_work(&mdm2ap_status_check_work);
+ struct mdm_device *mdev =
+ container_of(crashed_subsys, struct mdm_device, mdm_subsys);
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
+
+ pr_debug("%s: ssr on modem id %d\n", __func__,
+ mdev->mdm_data.device_id);
+
+ mdm_ssr_started(mdev);
+ cancel_delayed_work(&mdev->mdm2ap_status_check_work);
gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
if (mdm_drv->pdata->ramdump_delay_ms > 0) {
/* Wait for the external modem to complete
@@ -431,9 +619,9 @@
msleep(mdm_drv->pdata->ramdump_delay_ms);
}
if (!mdm_drv->mdm_unexpected_reset_occurred) {
- mdm_drv->ops->reset_mdm_cb(mdm_drv);
+ mdm_ops->reset_mdm_cb(mdm_drv);
/* Update gpio configuration to "booting" config. */
- mdm_update_gpio_configs(GPIO_UPDATE_BOOTING_CONFIG);
+ mdm_update_gpio_configs(mdev, GPIO_UPDATE_BOOTING_CONFIG);
} else {
mdm_drv->mdm_unexpected_reset_occurred = 0;
}
@@ -442,63 +630,76 @@
static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
{
+ struct mdm_device *mdev =
+ container_of(crashed_subsys, struct mdm_device,
+ mdm_subsys);
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
+
+ pr_debug("%s: ssr on modem id %d\n",
+ __func__, mdev->mdm_data.device_id);
+
gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
if (mdm_drv->pdata->ps_hold_delay_ms > 0)
msleep(mdm_drv->pdata->ps_hold_delay_ms);
- mdm_drv->ops->power_on_mdm_cb(mdm_drv);
+ mdm_ops->power_on_mdm_cb(mdm_drv);
mdm_drv->boot_type = CHARM_NORMAL_BOOT;
- complete(&mdm_needs_reload);
- if (!wait_for_completion_timeout(&mdm_boot,
+ mdm_ssr_completed(mdev);
+ complete(&mdev->mdm_needs_reload);
+ if (!wait_for_completion_timeout(&mdev->mdm_boot,
msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
mdm_drv->mdm_boot_status = -ETIMEDOUT;
- pr_info("%s: mdm modem restart timed out.\n", __func__);
+ pr_debug("%s: mdm modem restart timed out.\n", __func__);
} else {
- pr_info("%s: mdm modem has been restarted\n", __func__);
+ pr_debug("%s: id %d: mdm modem has been restarted\n",
+ __func__, mdm_drv->device_id);
/* Log the reason for the restart */
if (mdm_drv->pdata->sfr_query)
- queue_work(mdm_sfr_queue, &sfr_reason_work);
+ queue_work(mdev->mdm_sfr_queue, &mdev->sfr_reason_work);
}
- INIT_COMPLETION(mdm_boot);
+ init_completion(&mdev->mdm_boot);
return mdm_drv->mdm_boot_status;
}
static int mdm_subsys_ramdumps(int want_dumps,
const struct subsys_desc *crashed_subsys)
{
+ struct mdm_device *mdev =
+ container_of(crashed_subsys, struct mdm_device,
+ mdm_subsys);
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
+
+ pr_debug("%s: ssr on modem id %d\n", __func__,
+ mdev->mdm_data.device_id);
+
mdm_drv->mdm_ram_dump_status = 0;
- cancel_delayed_work(&mdm2ap_status_check_work);
+ cancel_delayed_work(&mdev->mdm2ap_status_check_work);
if (want_dumps) {
mdm_drv->boot_type = CHARM_RAM_DUMPS;
- complete(&mdm_needs_reload);
- if (!wait_for_completion_timeout(&mdm_ram_dumps,
- msecs_to_jiffies(dump_timeout_ms))) {
+ complete(&mdev->mdm_needs_reload);
+ if (!wait_for_completion_timeout(&mdev->mdm_ram_dumps,
+ msecs_to_jiffies(mdev->dump_timeout_ms))) {
mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
- pr_info("%s: mdm modem ramdumps timed out.\n",
+ mdm_ssr_completed(mdev);
+ pr_err("%s: mdm modem ramdumps timed out.\n",
__func__);
} else
- pr_info("%s: mdm modem ramdumps completed.\n",
+ pr_debug("%s: mdm modem ramdumps completed.\n",
__func__);
- INIT_COMPLETION(mdm_ram_dumps);
+ init_completion(&mdev->mdm_ram_dumps);
if (!mdm_drv->pdata->no_powerdown_after_ramdumps) {
- mdm_drv->ops->power_down_mdm_cb(mdm_drv);
+ mdm_ops->power_down_mdm_cb(mdm_drv);
/* Update gpio configuration to "booting" config. */
- mdm_update_gpio_configs(GPIO_UPDATE_BOOTING_CONFIG);
+ mdm_update_gpio_configs(mdev,
+ GPIO_UPDATE_BOOTING_CONFIG);
}
}
return mdm_drv->mdm_ram_dump_status;
}
-static struct subsys_desc mdm_subsystem = {
- .shutdown = mdm_subsys_shutdown,
- .ramdump = mdm_subsys_ramdumps,
- .powerup = mdm_subsys_powerup,
- .name = EXTERNAL_MODEM,
-};
-
/* Once the gpios are sent to RPM and debugging
* starts, there is no way to stop it without
* rebooting the device.
@@ -512,8 +713,8 @@
}
mdm_debug_mask = val;
- if (mdm_drv->ops->debug_state_changed_cb)
- mdm_drv->ops->debug_state_changed_cb(mdm_debug_mask);
+ if (mdm_ops->debug_state_changed_cb)
+ mdm_ops->debug_state_changed_cb(mdm_debug_mask);
return 0;
}
@@ -540,11 +741,55 @@
return 0;
}
-static void mdm_modem_initialize_data(struct platform_device *pdev,
- struct mdm_ops *mdm_ops)
+static const struct file_operations mdm_modem_fops = {
+ .owner = THIS_MODULE,
+ .open = mdm_modem_open,
+ .unlocked_ioctl = mdm_modem_ioctl,
+};
+
+static void mdm_modem_initialize_data(struct platform_device *pdev,
+ struct mdm_device *mdev)
{
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
struct resource *pres;
+ mdm_drv->pdata = pdev->dev.platform_data;
+ if (pdev->id < 0)
+ mdm_drv->device_id = 0;
+ else
+ mdm_drv->device_id = pdev->id;
+
+ memset((void *)&mdev->mdm_subsys, 0,
+ sizeof(struct subsys_desc));
+ if (mdev->mdm_data.device_id <= 0)
+ snprintf(mdev->subsys_name, sizeof(mdev->subsys_name),
+ "%s", EXTERNAL_MODEM);
+ else
+ snprintf(mdev->subsys_name, sizeof(mdev->subsys_name),
+ "%s.%d", EXTERNAL_MODEM, mdev->mdm_data.device_id);
+ mdev->mdm_subsys.shutdown = mdm_subsys_shutdown;
+ mdev->mdm_subsys.ramdump = mdm_subsys_ramdumps;
+ mdev->mdm_subsys.powerup = mdm_subsys_powerup;
+ mdev->mdm_subsys.name = mdev->subsys_name;
+
+ memset((void *)&mdev->misc_device, 0,
+ sizeof(struct miscdevice));
+ if (mdev->mdm_data.device_id <= 0)
+ snprintf(mdev->device_name, sizeof(mdev->device_name),
+ "%s", DEVICE_BASE_NAME);
+ else
+ snprintf(mdev->device_name, sizeof(mdev->device_name),
+ "%s%d", DEVICE_BASE_NAME, mdev->mdm_data.device_id);
+ mdev->misc_device.minor = MISC_DYNAMIC_MINOR;
+ mdev->misc_device.name = mdev->device_name;
+ mdev->misc_device.fops = &mdm_modem_fops;
+
+ memset((void *)&mdev->mdm_panic_blk, 0,
+ sizeof(struct notifier_block));
+ mdev->mdm_panic_blk.notifier_call = mdm_panic_prep;
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &mdev->mdm_panic_blk);
+
/* MDM2AP_ERRFATAL */
pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
"MDM2AP_ERRFATAL");
@@ -602,26 +847,49 @@
mdm_drv->boot_type = CHARM_NORMAL_BOOT;
- mdm_drv->ops = mdm_ops;
- mdm_drv->pdata = pdev->dev.platform_data;
- dump_timeout_ms = mdm_drv->pdata->ramdump_timeout_ms > 0 ?
+ mdm_drv->dump_timeout_ms = mdm_drv->pdata->ramdump_timeout_ms > 0 ?
mdm_drv->pdata->ramdump_timeout_ms : MDM_RDUMP_TIMEOUT;
+
+ init_completion(&mdev->mdm_needs_reload);
+ init_completion(&mdev->mdm_boot);
+ init_completion(&mdev->mdm_ram_dumps);
+
+ mdev->first_boot = 1;
+ mutex_init(&mdm_drv->peripheral_status_lock);
}
-int mdm_common_create(struct platform_device *pdev,
- struct mdm_ops *p_mdm_cb)
+static void mdm_deconfigure_ipc(struct mdm_device *mdev)
{
- int ret = -1, irq;
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
- mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
- if (mdm_drv == NULL) {
- pr_err("%s: kzalloc fail.\n", __func__);
- goto alloc_err;
+ gpio_free(mdm_drv->ap2mdm_status_gpio);
+ gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
+ if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
+ gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
+ if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
+ gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
+ gpio_free(mdm_drv->mdm2ap_status_gpio);
+ gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
+ if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
+ gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
+
+ if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
+ gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
+
+ if (mdev->mdm_queue) {
+ destroy_workqueue(mdev->mdm_queue);
+ mdev->mdm_queue = NULL;
}
+ if (mdev->mdm_sfr_queue) {
+ destroy_workqueue(mdev->mdm_sfr_queue);
+ mdev->mdm_sfr_queue = NULL;
+ }
+}
- mdm_modem_initialize_data(pdev, p_mdm_cb);
- if (mdm_drv->ops->debug_state_changed_cb)
- mdm_drv->ops->debug_state_changed_cb(mdm_debug_mask);
+static int mdm_configure_ipc(struct mdm_device *mdev)
+{
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
+ int ret = -1, irq;
gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
@@ -648,7 +916,6 @@
mdm_drv->usb_switch_gpio = -1;
}
}
-
gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 0);
gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
@@ -658,97 +925,88 @@
gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
- mdm_queue = create_singlethread_workqueue("mdm_queue");
- if (!mdm_queue) {
- pr_err("%s: could not create workqueue. All mdm "
- "functionality will be disabled\n",
- __func__);
+ mdev->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
+ if (!mdev->mdm_queue) {
+ pr_err("%s: could not create mdm_queue for mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
ret = -ENOMEM;
goto fatal_err;
}
- mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
- if (!mdm_sfr_queue) {
- pr_err("%s: could not create workqueue mdm_sfr_queue."
- " All mdm functionality will be disabled\n",
- __func__);
+ mdev->mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
+ if (!mdev->mdm_sfr_queue) {
+ pr_err("%s: could not create mdm_sfr_queue for mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
ret = -ENOMEM;
- destroy_workqueue(mdm_queue);
goto fatal_err;
}
- atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
- mdm_debugfs_init();
-
/* Register subsystem handlers */
- mdm_subsys_dev = subsys_register(&mdm_subsystem);
- if (IS_ERR(mdm_subsys_dev)) {
- ret = PTR_ERR(mdm_subsys_dev);
+ mdev->mdm_subsys_dev = subsys_register(&mdev->mdm_subsys);
+ if (IS_ERR(mdev->mdm_subsys_dev)) {
+ ret = PTR_ERR(mdev->mdm_subsys_dev);
goto fatal_err;
}
- subsys_default_online(mdm_subsys_dev);
+ subsys_default_online(mdev->mdm_subsys_dev);
/* ERR_FATAL irq. */
- irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
+ irq = gpio_to_irq(mdm_drv->mdm2ap_errfatal_gpio);
if (irq < 0) {
- pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
- "error=%d No IRQ will be generated on errfatal.",
- __func__, irq);
+ pr_err("%s: bad MDM2AP_ERRFATAL IRQ resource, err = %d\n",
+ __func__, irq);
goto errfatal_err;
}
ret = request_irq(irq, mdm_errfatal,
- IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
+ IRQF_TRIGGER_RISING , "mdm errfatal", mdev);
if (ret < 0) {
- pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
- ". No IRQ will be generated on errfatal.",
- __func__, irq, ret);
+ pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed, err=%d\n",
+ __func__, irq, ret);
goto errfatal_err;
}
- mdm_drv->mdm_errfatal_irq = irq;
+ mdev->mdm_errfatal_irq = irq;
errfatal_err:
- /* status irq */
- irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
+ /* status irq */
+ irq = gpio_to_irq(mdm_drv->mdm2ap_status_gpio);
if (irq < 0) {
- pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
- "error=%d No IRQ will be generated on status change.",
- __func__, irq);
+ pr_err("%s: bad MDM2AP_STATUS IRQ resource, err = %d\n",
+ __func__, irq);
goto status_err;
}
ret = request_threaded_irq(irq, NULL, mdm_status_change,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
- "mdm status", mdm_drv);
+ "mdm status", mdev);
if (ret < 0) {
- pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
- ". No IRQ will be generated on status change.",
- __func__, irq, ret);
+ pr_err("%s: MDM2AP_STATUS IRQ#%d request failed, err=%d",
+ __func__, irq, ret);
goto status_err;
}
- mdm_drv->mdm_status_irq = irq;
+ mdev->mdm_status_irq = irq;
status_err:
if (GPIO_IS_VALID(mdm_drv->mdm2ap_pblrdy)) {
- irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_pblrdy);
+ irq = gpio_to_irq(mdm_drv->mdm2ap_pblrdy);
if (irq < 0) {
- pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource",
- __func__);
+ pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource\n",
+ __func__);
goto pblrdy_err;
}
ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
- IRQF_SHARED,
- "mdm pbl ready", mdm_drv);
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+ IRQF_SHARED,
+ "mdm pbl ready", mdev);
if (ret < 0) {
- pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d",
+ pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d\n",
__func__, irq, ret);
goto pblrdy_err;
}
+ mdev->mdm_pblrdy_irq = irq;
}
pblrdy_err:
@@ -759,67 +1017,136 @@
if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
- /* Perform early powerup of the external modem in order to
- * allow tabla devices to be found.
- */
- if (mdm_drv->pdata->early_power_on)
- mdm_drv->ops->power_on_mdm_cb(mdm_drv);
-
- pr_info("%s: Registering mdm modem\n", __func__);
- return misc_register(&mdm_modem_misc);
+ return 0;
fatal_err:
- gpio_free(mdm_drv->ap2mdm_status_gpio);
- gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
- gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
- gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
- gpio_free(mdm_drv->mdm2ap_status_gpio);
- gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
- gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
-
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
- gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
-
- kfree(mdm_drv);
- ret = -ENODEV;
-
-alloc_err:
+ mdm_deconfigure_ipc(mdev);
return ret;
}
-int mdm_common_modem_remove(struct platform_device *pdev)
+static int __devinit mdm_modem_probe(struct platform_device *pdev)
+{
+ struct mdm_device *mdev = NULL;
+ int ret = -1;
+
+ mdev = kzalloc(sizeof(struct mdm_device), GFP_KERNEL);
+ if (!mdev) {
+ pr_err("%s: kzalloc fail.\n", __func__);
+ ret = -ENOMEM;
+ goto init_err;
+ }
+
+ platform_set_drvdata(pdev, mdev);
+ mdm_modem_initialize_data(pdev, mdev);
+
+ if (mdm_ops->debug_state_changed_cb)
+ mdm_ops->debug_state_changed_cb(mdm_debug_mask);
+
+ if (mdm_configure_ipc(mdev)) {
+ pr_err("%s: mdm_configure_ipc failed, id = %d\n",
+ __func__, mdev->mdm_data.device_id);
+ goto init_err;
+ }
+
+ pr_debug("%s: Registering mdm id %d\n", __func__,
+ mdev->mdm_data.device_id);
+ ret = misc_register(&mdev->misc_device);
+ if (ret) {
+ pr_err("%s: failed registering mdm id %d, ret = %d\n",
+ __func__, mdev->mdm_data.device_id, ret);
+ mdm_deconfigure_ipc(mdev);
+ goto init_err;
+ } else {
+ pr_err("%s: registered mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
+
+ mdm_device_list_add(mdev);
+ INIT_DELAYED_WORK(&mdev->mdm2ap_status_check_work,
+ mdm2ap_status_check);
+ INIT_WORK(&mdev->mdm_status_work, mdm_status_fn);
+ INIT_WORK(&mdev->sfr_reason_work, mdm_restart_reason_fn);
+
+ /* Perform early powerup of the external modem in order to
+ * allow tabla devices to be found.
+ */
+ if (mdev->mdm_data.pdata->early_power_on)
+ mdm_ops->power_on_mdm_cb(&mdev->mdm_data);
+ }
+
+ return ret;
+
+init_err:
+ kfree(mdev);
+ return ret;
+}
+
+static int __devexit mdm_modem_remove(struct platform_device *pdev)
{
int ret;
+ struct mdm_device *mdev = platform_get_drvdata(pdev);
- gpio_free(mdm_drv->ap2mdm_status_gpio);
- gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
- gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
- gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
- gpio_free(mdm_drv->mdm2ap_status_gpio);
- gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
- gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
-
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
- gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
-
- kfree(mdm_drv);
-
- ret = misc_deregister(&mdm_modem_misc);
+ pr_debug("%s: removing device id %d\n",
+ __func__, mdev->mdm_data.device_id);
+ mdm_deconfigure_ipc(mdev);
+ ret = misc_deregister(&mdev->misc_device);
+ mdm_device_list_remove(mdev);
+ kfree(mdev);
return ret;
}
-void mdm_common_modem_shutdown(struct platform_device *pdev)
+static void mdm_modem_shutdown(struct platform_device *pdev)
{
- mdm_disable_irqs();
+ struct mdm_modem_drv *mdm_drv;
+ struct mdm_device *mdev = platform_get_drvdata(pdev);
- mdm_drv->ops->power_down_mdm_cb(mdm_drv);
+ pr_debug("%s: shutting down device id %d\n",
+ __func__, mdev->mdm_data.device_id);
+
+ mdm_disable_irqs(mdev);
+ mdm_drv = &mdev->mdm_data;
+ mdm_ops->power_down_mdm_cb(mdm_drv);
if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
}
+static struct of_device_id mdm_match_table[] = {
+ {.compatible = "qcom,mdm2_modem,mdm2_modem.1"},
+ {},
+};
+
+static struct platform_driver mdm_modem_driver = {
+ .probe = mdm_modem_probe,
+ .remove = __devexit_p(mdm_modem_remove),
+ .shutdown = mdm_modem_shutdown,
+ .driver = {
+ .name = "mdm2_modem",
+ .owner = THIS_MODULE,
+ .of_match_table = mdm_match_table,
+ },
+};
+
+static int __init mdm_modem_init(void)
+{
+ int ret;
+
+ ret = mdm_get_ops(&mdm_ops);
+ if (ret)
+ return ret;
+
+ INIT_LIST_HEAD(&mdm_devices);
+ mdm_debugfs_init();
+ return platform_driver_register(&mdm_modem_driver);
+}
+
+static void __exit mdm_modem_exit(void)
+{
+ platform_driver_unregister(&mdm_modem_driver);
+}
+
+module_init(mdm_modem_init);
+module_exit(mdm_modem_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("mdm modem driver");
+MODULE_VERSION("2.0");
+MODULE_ALIAS("mdm_modem");
diff --git a/arch/arm/mach-msm/mdm_private.h b/arch/arm/mach-msm/mdm_private.h
index 92fb141..5a91cf9 100644
--- a/arch/arm/mach-msm/mdm_private.h
+++ b/arch/arm/mach-msm/mdm_private.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, 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
@@ -44,25 +44,22 @@
unsigned mdm2ap_pblrdy;
unsigned usb_switch_gpio;
- int mdm_errfatal_irq;
- int mdm_status_irq;
- int mdm_ready;
+ atomic_t mdm_ready;
int mdm_boot_status;
int mdm_ram_dump_status;
enum charm_boot_type boot_type;
int mdm_debug_on;
int mdm_unexpected_reset_occurred;
int disable_status_check;
+ unsigned int dump_timeout_ms;
+ int power_on_count;
+ int peripheral_status;
+ struct mutex peripheral_status_lock;
+ int device_id;
- struct mdm_ops *ops;
struct mdm_platform_data *pdata;
};
-
-int mdm_common_create(struct platform_device *pdev,
- struct mdm_ops *mdm_cb);
-int mdm_common_modem_remove(struct platform_device *pdev);
-void mdm_common_modem_shutdown(struct platform_device *pdev);
-void mdm_common_set_debug_state(int value);
+int mdm_get_ops(struct mdm_ops **mdm_ops);
#endif
diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c
index 1680993..7a7fb99 100644
--- a/arch/arm/mach-msm/memory.c
+++ b/arch/arm/mach-msm/memory.c
@@ -315,6 +315,8 @@
unsigned long memory_remove_prop_length;
unsigned long memory_size_prop_length;
unsigned int *memory_size_prop;
+ unsigned int *memory_reserve_prop;
+ unsigned long memory_reserve_prop_length;
unsigned int memory_size;
unsigned int memory_start;
int ret;
@@ -326,7 +328,11 @@
"qcom,memblock-remove",
&memory_remove_prop_length);
- if (memory_name_prop || memory_remove_prop) {
+ memory_reserve_prop = of_get_flat_dt_prop(node,
+ "qcom,memblock-reserve",
+ &memory_reserve_prop_length);
+
+ if (memory_name_prop || memory_remove_prop || memory_reserve_prop) {
if (!check_for_compat(node))
goto out;
} else {
@@ -365,7 +371,7 @@
if (memory_remove_prop) {
if (memory_remove_prop_length != (2*sizeof(unsigned int))) {
WARN(1, "Memory remove malformed\n");
- goto out;
+ goto mem_reserve;
}
memory_start = be32_to_cpu(memory_remove_prop[0]);
@@ -380,11 +386,52 @@
memory_start, memory_start+memory_size);
}
+mem_reserve:
+
+ if (memory_reserve_prop) {
+ if (memory_reserve_prop_length != (2*sizeof(unsigned int))) {
+ WARN(1, "Memory reserve malformed\n");
+ goto out;
+ }
+
+ memory_start = be32_to_cpu(memory_reserve_prop[0]);
+ memory_size = be32_to_cpu(memory_reserve_prop[1]);
+
+ ret = memblock_reserve(memory_start, memory_size);
+ if (ret)
+ WARN(1, "Failed to reserve memory %x-%x\n",
+ memory_start, memory_start+memory_size);
+ else
+ pr_info("Node %s memblock_reserve memory %x-%x\n",
+ uname, memory_start, memory_start+memory_size);
+ }
+
out:
return 0;
}
-/* This function scans the device tree to populate the memory hole table */
+/* Function to remove any meminfo blocks which are of size zero */
+static void merge_meminfo(void)
+{
+ int i = 0;
+
+ while (i < meminfo.nr_banks) {
+ struct membank *bank = &meminfo.bank[i];
+
+ if (bank->size == 0) {
+ memmove(bank, bank + 1,
+ (meminfo.nr_banks - i) * sizeof(*bank));
+ meminfo.nr_banks--;
+ continue;
+ }
+ i++;
+ }
+}
+
+/*
+ * Function to scan the device tree and adjust the meminfo table to
+ * reflect the memory holes.
+ */
int __init dt_scan_for_memory_hole(unsigned long node, const char *uname,
int depth, void *data)
{
@@ -413,16 +460,6 @@
hole_start = be32_to_cpu(memory_remove_prop[0]);
hole_size = be32_to_cpu(memory_remove_prop[1]);
- if (hole_start + hole_size <= MAX_HOLE_ADDRESS) {
- if (memory_hole_start == 0 && memory_hole_end == 0) {
- memory_hole_start = hole_start;
- memory_hole_end = hole_start + hole_size;
- } else if ((memory_hole_end - memory_hole_start)
- <= hole_size) {
- memory_hole_start = hole_start;
- memory_hole_end = hole_start + hole_size;
- }
- }
adjust_meminfo(hole_start, hole_size);
}
@@ -452,6 +489,7 @@
bank[1].start = (start + size);
bank[1].size -= (bank->size + size);
bank[1].highmem = 0;
+ merge_meminfo();
}
}
}
diff --git a/arch/arm/mach-msm/memory_topology.c b/arch/arm/mach-msm/memory_topology.c
index 781cd69..de90427 100644
--- a/arch/arm/mach-msm/memory_topology.c
+++ b/arch/arm/mach-msm/memory_topology.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013, 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
@@ -19,12 +19,45 @@
#include <linux/memory.h>
#include <mach/msm_memtypes.h>
#include <mach/socinfo.h>
-#include "smd_private.h"
+#include <mach/msm_smem.h>
#if defined(CONFIG_ARCH_MSM8960)
#include "rpm_resources.h"
#endif
+struct smem_ram_ptn {
+ char name[16];
+ unsigned start;
+ unsigned size;
+
+ /* RAM Partition attribute: READ_ONLY, READWRITE etc. */
+ unsigned attr;
+
+ /* RAM Partition category: EBI0, EBI1, IRAM, IMEM */
+ unsigned category;
+
+ /* RAM Partition domain: APPS, MODEM, APPS & MODEM (SHARED) etc. */
+ unsigned domain;
+
+ /* RAM Partition type: system, bootloader, appsboot, apps etc. */
+ unsigned type;
+
+ /* reserved for future expansion without changing version number */
+ unsigned reserved2, reserved3, reserved4, reserved5;
+} __attribute__ ((__packed__));
+
+
+struct smem_ram_ptable {
+ #define _SMEM_RAM_PTABLE_MAGIC_1 0x9DA5E0A8
+ #define _SMEM_RAM_PTABLE_MAGIC_2 0xAF9EC4E2
+ unsigned magic[2];
+ unsigned version;
+ unsigned reserved1;
+ unsigned len;
+ struct smem_ram_ptn parts[32];
+ unsigned buf;
+} __attribute__ ((__packed__));
+
static struct mem_region_t {
u64 start;
u64 size;
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_arb.c b/arch/arm/mach-msm/msm_bus/msm_bus_arb.c
index 4adfe4d..3ff7530 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_arb.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_arb.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, 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
@@ -59,6 +59,8 @@
return 1;
switch (w) {
+ case 0:
+ WARN(1, "AXI: Divide by 0 attempted\n");
case 1: return bw;
case 2: return (bw >> 1);
case 4: return (bw >> 2);
@@ -247,6 +249,11 @@
struct msm_bus_fabric_device *gwfab =
msm_bus_get_fabric_device(fabnodeinfo->
info->node_info->priv_id);
+ if (!gwfab) {
+ MSM_BUS_ERR("Err: No gateway found\n");
+ return -ENXIO;
+ }
+
if (!gwfab->visited) {
MSM_BUS_DBG("VISITED ID: %d\n",
gwfab->id);
@@ -314,12 +321,18 @@
struct msm_bus_inode_info *info;
int next_pnode;
int64_t add_bw = req_bw - curr_bw;
- unsigned bwsum = 0;
+ uint64_t bwsum = 0;
uint64_t req_clk_hz, curr_clk_hz, bwsum_hz;
int *master_tiers;
struct msm_bus_fabric_device *fabdev = msm_bus_get_fabric_device
(GET_FABID(curr));
+ if (!fabdev) {
+ MSM_BUS_ERR("Bus device for bus ID: %d not found!\n",
+ GET_FABID(curr));
+ return -ENXIO;
+ }
+
MSM_BUS_DBG("args: %d %d %d %llu %llu %llu %llu %u\n",
curr, GET_NODE(pnode), GET_INDEX(pnode), req_clk, req_bw,
curr_clk, curr_bw, ctx);
@@ -331,6 +344,15 @@
return -ENXIO;
}
+ /**
+ * If master supports dual configuration, check if
+ * the configuration needs to be changed based on
+ * incoming requests
+ */
+ if (info->node_info->dual_conf)
+ fabdev->algo->config_master(fabdev, info,
+ req_clk, req_bw);
+
info->link_info.sel_bw = &info->link_info.bw[ctx];
info->link_info.sel_clk = &info->link_info.clk[ctx];
*info->link_info.sel_bw += add_bw;
@@ -398,7 +420,7 @@
/* Update Bandwidth */
fabdev->algo->update_bw(fabdev, hop, info, add_bw,
master_tiers, ctx);
- bwsum = (uint16_t)*hop->link_info.sel_bw;
+ bwsum = *hop->link_info.sel_bw;
/* Update Fabric clocks */
curr_clk_hz = BW_TO_CLK_FREQ_HZ(hop->node_info->buswidth,
curr_clk);
@@ -406,6 +428,13 @@
req_clk);
bwsum_hz = BW_TO_CLK_FREQ_HZ(hop->node_info->buswidth,
bwsum);
+ /* Account for multiple channels if any */
+ if (hop->node_info->num_sports > 1)
+ bwsum_hz = msm_bus_div64(hop->node_info->num_sports,
+ bwsum_hz);
+ MSM_BUS_DBG("AXI: Hop: %d, ports: %d, bwsum_hz: %llu\n",
+ hop->node_info->id, hop->node_info->num_sports,
+ bwsum_hz);
MSM_BUS_DBG("up-clk: curr_hz: %llu, req_hz: %llu, bw_hz %llu\n",
curr_clk, req_clk, bwsum_hz);
ret = fabdev->algo->update_clks(fabdev, hop, index,
@@ -462,7 +491,7 @@
deffab = msm_bus_get_fabric_device(MSM_BUS_FAB_DEFAULT);
if (!deffab) {
MSM_BUS_ERR("Error finding default fabric\n");
- return -ENXIO;
+ return 0;
}
nfab = msm_bus_get_num_fab();
@@ -525,6 +554,11 @@
goto err;
}
srcfab = msm_bus_get_fabric_device(GET_FABID(src));
+ if (!srcfab) {
+ MSM_BUS_ERR("Fabric not found\n");
+ goto err;
+ }
+
srcfab->visited = true;
pnode[i] = getpath(src, dest);
bus_for_each_dev(&msm_bus_type, NULL, NULL, clearvisitedflag);
@@ -574,6 +608,10 @@
curr = client->curr;
pdata = client->pdata;
+ if (!pdata) {
+ MSM_BUS_ERR("Null pdata passed to update-request\n");
+ return -ENXIO;
+ }
if (index >= pdata->num_usecases) {
MSM_BUS_ERR("Client %u passed invalid index: %d\n",
@@ -657,6 +695,12 @@
struct msm_bus_fabric_device *fabdev;
int index, next_pnode;
fabdev = msm_bus_get_fabric_device(GET_FABID(curr));
+ if (!fabdev) {
+ MSM_BUS_ERR("Fabric not found for: %d\n",
+ (GET_FABID(curr)));
+ return -ENXIO;
+ }
+
index = GET_INDEX(pnode);
info = fabdev->algo->find_node(fabdev, curr);
if (!info) {
@@ -718,7 +762,7 @@
{
int i, src, pnode, index;
struct msm_bus_client *client = (struct msm_bus_client *)(cl);
- if (IS_ERR(client)) {
+ if (IS_ERR_OR_NULL(client)) {
MSM_BUS_ERR("msm_bus_scale_reset_pnodes error\n");
return;
}
@@ -739,7 +783,7 @@
void msm_bus_scale_unregister_client(uint32_t cl)
{
struct msm_bus_client *client = (struct msm_bus_client *)(cl);
- if (IS_ERR(client) || (!client))
+ if (IS_ERR_OR_NULL(client))
return;
if (client->curr != 0)
msm_bus_scale_client_update_request(cl, 0);
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
index 3c8348d..bc15fe8 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
@@ -26,6 +26,10 @@
SLAVE_BLOCK_SCMO,
};
+enum bke_sw {
+ BKE_OFF = 0,
+ BKE_ON = 1,
+};
/* Misc module */
@@ -477,9 +481,11 @@
#define M_MODE_ADDR(b, n) \
(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000210)
enum bimc_m_mode {
- M_MODE_RMSK = 0xf0000001,
+ M_MODE_RMSK = 0xf0000011,
M_MODE_WR_GATHER_BEATS_BMSK = 0xf0000000,
M_MODE_WR_GATHER_BEATS_SHFT = 0x1c,
+ M_MODE_NARROW_WR_BMSK = 0x10,
+ M_MODE_NARROW_WR_SHFT = 0x4,
M_MODE_ORDERING_MODEL_BMSK = 0x1,
M_MODE_ORDERING_MODEL_SHFT = 0x0,
};
@@ -1365,7 +1371,7 @@
(M_BKE_GC_GC_BMSK >> \
M_BKE_GC_GC_SHFT)
-static int bimc_div(uint64_t *a, uint32_t b)
+static int bimc_div(int64_t *a, uint32_t b)
{
if ((*a > 0) && (*a < b))
return 1;
@@ -1458,7 +1464,7 @@
* boundary in future
*/
wmb();
- set_qos_mode(binfo->base, mas_index, 0, 1, 1);
+ set_qos_mode(binfo->base, mas_index, 1, 1, 1);
break;
case BIMC_QOS_MODE_BYPASS:
@@ -1506,7 +1512,7 @@
val0 = BKE_HEALTH_VAL(qmode->rl.qhealth[index].limit_commands,
qmode->rl.qhealth[index].areq_prio,
qmode->rl.qhealth[index].prio_level);
- val = (reg_val & (~(BKE_HEALTH_MASK) | (val0 & BKE_HEALTH_MASK)));
+ val = ((reg_val & (~(BKE_HEALTH_MASK))) | (val0 & BKE_HEALTH_MASK));
writel_relaxed(val, addr);
/* Ensure that priority for regulator/limiter modes are
* set before returning
@@ -1526,10 +1532,10 @@
reg_val = readl_relaxed(M_PRIOLVL_OVERRIDE_ADDR(binfo->
base, mas_index)) & M_PRIOLVL_OVERRIDE_RMSK;
val = qmode->fixed.prio_level <<
- M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_SHFT;
+ M_PRIOLVL_OVERRIDE_SHFT;
writel_relaxed(((reg_val &
- ~(M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_BMSK)) | (val
- & M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_BMSK)),
+ ~(M_PRIOLVL_OVERRIDE_BMSK)) | (val
+ & M_PRIOLVL_OVERRIDE_BMSK)),
M_PRIOLVL_OVERRIDE_ADDR(binfo->base, mas_index));
reg_val = readl_relaxed(M_RD_CMD_OVERRIDE_ADDR(binfo->
@@ -1561,7 +1567,7 @@
mas_index), M_BKE_HEALTH_2_CONFIG_RMSK, 2, qmode);
set_qos_prio_rl(M_BKE_HEALTH_1_CONFIG_ADDR(binfo->base,
mas_index), M_BKE_HEALTH_1_CONFIG_RMSK, 1, qmode);
- set_qos_prio_rl(M_BKE_HEALTH_1_CONFIG_ADDR(binfo->base,
+ set_qos_prio_rl(M_BKE_HEALTH_0_CONFIG_ADDR(binfo->base,
mas_index), M_BKE_HEALTH_0_CONFIG_RMSK, 0 , qmode);
break;
case BIMC_QOS_MODE_BYPASS:
@@ -1571,10 +1577,11 @@
}
static void set_qos_bw_regs(void __iomem *baddr, uint32_t mas_index,
- long int th, long int tm, long int tl, uint32_t gp,
+ int32_t th, int32_t tm, int32_t tl, uint32_t gp,
uint32_t gc, bool bke_en)
{
- uint32_t reg_val, val;
+ int32_t reg_val, val;
+ int16_t val2;
/* Disable BKE before writing to registers as per spec */
reg_val = readl_relaxed(M_BKE_EN_ADDR(baddr, mas_index)) &
@@ -1603,15 +1610,15 @@
reg_val = readl_relaxed(M_BKE_THM_ADDR(baddr, mas_index)) &
M_BKE_THM_RMSK;
- val = tm << M_BKE_THM_THRESH_SHFT;
- writel_relaxed(((reg_val & ~(M_BKE_THM_THRESH_BMSK)) | (val &
+ val2 = tm << M_BKE_THM_THRESH_SHFT;
+ writel_relaxed(((reg_val & ~(M_BKE_THM_THRESH_BMSK)) | (val2 &
M_BKE_THM_THRESH_BMSK)), M_BKE_THM_ADDR(baddr, mas_index));
reg_val = readl_relaxed(M_BKE_THL_ADDR(baddr, mas_index)) &
M_BKE_THL_RMSK;
- val = tl << M_BKE_THL_THRESH_SHFT;
+ val2 = tl << M_BKE_THL_THRESH_SHFT;
writel_relaxed(((reg_val & ~(M_BKE_THL_THRESH_BMSK)) |
- (val & M_BKE_THL_THRESH_BMSK)), M_BKE_THL_ADDR(baddr,
+ (val2 & M_BKE_THL_THRESH_BMSK)), M_BKE_THL_ADDR(baddr,
mas_index));
/* Set BKE enable to the value it was */
@@ -1643,10 +1650,10 @@
/* Only calculate if there's a requested bandwidth and window */
if (qbw->bw && qbw->ws) {
- uint64_t th, tm, tl;
+ int64_t th, tm, tl;
uint32_t gp, gc, data_width;
- uint64_t gp_nominal, gp_required, gp_calc, data, temp;
- uint64_t win = qbw->ws * binfo->qos_freq;
+ int64_t gp_nominal, gp_required, gp_calc, data, temp;
+ int64_t win = qbw->ws * binfo->qos_freq;
temp = win;
/*
* Calculate nominal grant period defined by requested
@@ -1673,13 +1680,13 @@
bimc_div(&gp_required, qbw->bw);
/* User min of two grant periods */
- gp = min_t(uint64_t, gp_nominal, gp_required);
+ gp = min_t(int64_t, gp_nominal, gp_required);
/* Calculate bandwith in grants and ceil. */
temp = qbw->bw * gp;
data = data_width * binfo->qos_freq * 1000;
bimc_div(&temp, data);
- gc = min_t(uint64_t, MAX_GC, temp);
+ gc = min_t(int64_t, MAX_GC, temp);
/* Calculate thresholds */
th = qbw->bw - qbw->thh;
@@ -1767,8 +1774,13 @@
}
}
+ if (fab_pdata->virt) {
+ MSM_BUS_DBG("Don't get memory regions for virtual fabric\n");
+ goto skip_mem;
+ }
+
bimc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!bimc_mem && !fab_pdata->virt) {
+ if (!bimc_mem) {
MSM_BUS_ERR("Cannot get BIMC Base address\n");
kfree(binfo);
return NULL;
@@ -1790,6 +1802,7 @@
return NULL;
}
+skip_mem:
fab_pdata->hw_data = (void *)binfo;
return (void *)binfo;
}
@@ -1803,6 +1816,57 @@
kfree(cd);
}
+static void bke_switch(void __iomem *baddr, uint32_t mas_index, bool req)
+{
+ uint32_t reg_val, val;
+
+ val = req << M_BKE_EN_EN_SHFT;
+ reg_val = readl_relaxed(M_BKE_EN_ADDR(baddr, mas_index)) &
+ M_BKE_EN_RMSK;
+ writel_relaxed(((reg_val & ~(M_BKE_EN_EN_BMSK)) | (val &
+ M_BKE_EN_EN_BMSK)), M_BKE_EN_ADDR(baddr, mas_index));
+ wmb();
+}
+
+static void msm_bus_bimc_config_master(
+ struct msm_bus_fabric_registration *fab_pdata,
+ struct msm_bus_inode_info *info,
+ uint64_t req_clk, uint64_t req_bw)
+{
+ int mode, i, ports;
+ struct msm_bus_bimc_info *binfo;
+
+ binfo = (struct msm_bus_bimc_info *)fab_pdata->hw_data;
+ ports = info->node_info->num_mports;
+
+ /**
+ * Here check the details of dual configuration.
+ * Take actions based on different modes.
+ * Check for threshold if limiter mode, etc.
+ */
+ if (req_clk > info->node_info->th)
+ mode = info->node_info->mode_thresh;
+ else
+ mode = info->node_info->mode;
+
+ switch (mode) {
+ case BIMC_QOS_MODE_BYPASS:
+ case BIMC_QOS_MODE_FIXED:
+ for (i = 0; i < ports; i++)
+ bke_switch(binfo->base, info->node_info->qport[i],
+ BKE_OFF);
+ break;
+ case BIMC_QOS_MODE_REGULATOR:
+ case BIMC_QOS_MODE_LIMITER:
+ for (i = 0; i < ports; i++)
+ bke_switch(binfo->base, info->node_info->qport[i],
+ BKE_ON);
+ break;
+ default:
+ break;
+ }
+}
+
static void msm_bus_bimc_update_bw(struct msm_bus_inode_info *hop,
struct msm_bus_inode_info *info,
struct msm_bus_fabric_registration *fab_pdata,
@@ -1902,10 +1966,106 @@
return 0;
}
+static void bimc_set_static_qos_bw(struct msm_bus_bimc_info *binfo,
+ int mport, struct msm_bus_bimc_qos_bw *qbw)
+{
+ int32_t bw_MBps, thh = 0, thm, thl, gc;
+ int16_t gp;
+ u64 temp;
+
+ if (binfo->qos_freq == 0) {
+ MSM_BUS_DBG("Zero QoS Frequency\n");
+ return;
+ }
+
+ if (!(qbw->bw && qbw->ws)) {
+ MSM_BUS_DBG("No QoS Bandwidth or Window size\n");
+ return;
+ }
+
+ /* Convert bandwidth to MBPS */
+ temp = qbw->bw;
+ bimc_div(&temp, 1000000);
+ bw_MBps = temp;
+
+ /* Grant period in clock cycles
+ * Grant period from bandwidth structure
+ * is in micro seconds, QoS freq is in KHz.
+ * Divide by 1000 to get clock cycles */
+ gp = (binfo->qos_freq * qbw->gp) / 1000;
+
+ /* Grant count = BW in MBps * Grant period
+ * in micro seconds */
+ gc = bw_MBps * qbw->gp;
+
+ /* Medium threshold = -((Medium Threshold percentage *
+ * Grant count) / 100) */
+ thm = -((qbw->thmp * gc) / 100);
+ qbw->thm = thm;
+
+ /* Low threshold = -(Grant count) */
+ thl = -gc;
+ qbw->thl = thl;
+
+ set_qos_bw_regs(binfo->base, mport, thh, thm, thl, gp,
+ gc, 1);
+}
+
+static void bimc_init_mas_reg(struct msm_bus_bimc_info *binfo,
+ struct msm_bus_inode_info *info,
+ struct msm_bus_bimc_qos_mode *qmode, int mode)
+{
+ int i;
+
+ switch (mode) {
+ case BIMC_QOS_MODE_FIXED:
+ qmode->fixed.prio_level = info->node_info->prio_lvl;
+ qmode->fixed.areq_prio_rd = info->node_info->prio_rd;
+ qmode->fixed.areq_prio_wr = info->node_info->prio_wr;
+ break;
+ case BIMC_QOS_MODE_LIMITER:
+ qmode->rl.qhealth[0].limit_commands = 1;
+ qmode->rl.qhealth[1].limit_commands = 0;
+ qmode->rl.qhealth[2].limit_commands = 0;
+ qmode->rl.qhealth[3].limit_commands = 0;
+ break;
+ default:
+ break;
+ }
+
+ if (!info->node_info->qport) {
+ MSM_BUS_DBG("No QoS Ports to init\n");
+ return;
+ }
+
+ for (i = 0; i < info->node_info->num_mports; i++) {
+ /* If not in bypass mode, update priority */
+ if (mode != BIMC_QOS_MODE_BYPASS) {
+ msm_bus_bimc_set_qos_prio(binfo, info->node_info->
+ qport[i], mode, qmode);
+
+ /* If not in fixed mode, update bandwidth */
+ if (mode != BIMC_QOS_MODE_FIXED) {
+ struct msm_bus_bimc_qos_bw qbw;
+ qbw.ws = info->node_info->ws;
+ qbw.bw = info->node_info->bimc_bw;
+ qbw.gp = info->node_info->bimc_gp;
+ qbw.thmp = info->node_info->bimc_thmp;
+ bimc_set_static_qos_bw(binfo,
+ info->node_info->qport[i], &qbw);
+ }
+ }
+
+ /* set mode */
+ msm_bus_bimc_set_qos_mode(binfo, info->node_info->qport[i],
+ mode);
+ }
+}
+
+
static int msm_bus_bimc_mas_init(struct msm_bus_bimc_info *binfo,
struct msm_bus_inode_info *info)
{
- int i;
struct msm_bus_bimc_qos_mode *qmode;
qmode = kzalloc(sizeof(struct msm_bus_bimc_qos_mode),
GFP_KERNEL);
@@ -1915,42 +2075,17 @@
return -ENOMEM;
}
- switch (info->node_info->mode) {
- case BIMC_QOS_MODE_FIXED:
- qmode->fixed.prio_level = info->node_info->prio_lvl;
- qmode->fixed.areq_prio_rd = info->node_info->prio_rd;
- qmode->fixed.areq_prio_wr = info->node_info->prio_wr;
- break;
- default:
- break;
- }
-
info->hw_data = (void *)qmode;
- if (!info->node_info->qport) {
- MSM_BUS_DBG("No QoS Ports to init\n");
- return 0;
- }
- for (i = 0; i < info->node_info->num_mports; i++) {
- /* If not in bypass mode, update priority */
- if (info->node_info->mode != BIMC_QOS_MODE_BYPASS) {
- msm_bus_bimc_set_qos_prio(binfo, info->node_info->
- qport[i], info->node_info->mode, qmode);
+ /**
+ * If the master supports dual configuration,
+ * configure registers for both modes
+ */
+ if (info->node_info->dual_conf)
+ bimc_init_mas_reg(binfo, info, qmode,
+ info->node_info->mode_thresh);
- /* If not in fixed mode, update bandwidth */
- if (info->node_info->mode != BIMC_QOS_MODE_FIXED) {
- struct msm_bus_bimc_qos_bw qbw;
- qbw.ws = info->node_info->ws;
- msm_bus_bimc_set_qos_bw(binfo,
- info->node_info->qport[i], &qbw);
- }
- }
-
- /* set mode */
- msm_bus_bimc_set_qos_mode(binfo, info->node_info->qport[i],
- info->node_info->mode);
- }
-
+ bimc_init_mas_reg(binfo, info, qmode, info->node_info->mode);
return 0;
}
@@ -1990,6 +2125,7 @@
hw_algo->commit = msm_bus_bimc_commit;
hw_algo->port_halt = msm_bus_bimc_port_halt;
hw_algo->port_unhalt = msm_bus_bimc_port_unhalt;
+ hw_algo->config_master = msm_bus_bimc_config_master;
/* BIMC slaves are shared. Slave registers are set through RPM */
if (!pdata->ahb)
pdata->rpm_enabled = 1;
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.h b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.h
index 6df0bea..12c8325 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.h
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -93,9 +93,11 @@
struct msm_bus_bimc_qos_bw {
uint64_t bw; /* bw is in Bytes/sec */
uint32_t ws; /* Window size in nano seconds*/
- uint64_t thh; /* Threshold high, bytes per second */
- uint64_t thm; /* Threshold medium, bytes per second */
- uint64_t thl; /* Threshold low, bytes per second */
+ int64_t thh; /* Threshold high, bytes per second */
+ int64_t thm; /* Threshold medium, bytes per second */
+ int64_t thl; /* Threshold low, bytes per second */
+ u32 gp; /* Grant Period in micro seconds */
+ u32 thmp; /* Threshold medium in percentage */
};
struct msm_bus_bimc_clk_gate {
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8064.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8064.c
index b45efad..ff3dc21 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_board_8064.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8064.c
@@ -437,10 +437,7 @@
},
};
-static int mport_mdp[] = {
- MSM_BUS_MASTER_PORT_MDP_PORT0,
- MSM_BUS_MASTER_PORT_MDP_PORT1,
-};
+static int mport_mdp[] = {MSM_BUS_MASTER_PORT_MDP_PORT0,};
static int mport_mdp1[] = {MSM_BUS_MASTER_PORT_MDP_PORT1,};
static int mport_rotator[] = {MSM_BUS_MASTER_PORT_ROTATOR,};
static int mport_graphics_3d_port0[] = {MSM_BUS_MASTER_PORT_GRAPHICS_3D_PORT0,};
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8930.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8930.c
index 91d106e..af51355 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_board_8930.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8930.c
@@ -377,10 +377,7 @@
},
};
-static int mport_mdp[] = {
- MSM_BUS_MASTER_PORT_MDP_PORT0,
- MSM_BUS_MASTER_PORT_MDP_PORT1,
-};
+static int mport_mdp[] = {MSM_BUS_MASTER_PORT_MDP_PORT0,};
static int mport_mdp1[] = {MSM_BUS_MASTER_PORT_MDP_PORT1,};
static int mport_rotator[] = {MSM_BUS_MASTER_PORT_ROTATOR,};
static int mport_graphics_3d[] = {MSM_BUS_MASTER_PORT_GRAPHICS_3D,};
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8960.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8960.c
index 158dee3..295b91b 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_board_8960.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8960.c
@@ -440,10 +440,7 @@
},
};
-static int mport_mdp[] = {
- MSM_BUS_MASTER_PORT_MDP_PORT0,
- MSM_BUS_MASTER_PORT_MDP_PORT1,
-};
+static int mport_mdp[] = {MSM_BUS_MASTER_PORT_MDP_PORT0,};
static int mport_mdp1[] = {MSM_BUS_MASTER_PORT_MDP_PORT1,};
static int mport_rotator[] = {MSM_BUS_MASTER_PORT_ROTATOR,};
static int mport_graphics_3d[] = {MSM_BUS_MASTER_PORT_GRAPHICS_3D,};
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_config.c b/arch/arm/mach-msm/msm_bus/msm_bus_config.c
index c6fa250..858b15e 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_config.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_config.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, 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
@@ -39,7 +39,7 @@
MSM_BUS_DBG("master_port: %d iid: %d fabid%d\n",
master_port, priv_id, GET_FABID(priv_id));
fabdev = msm_bus_get_fabric_device(GET_FABID(priv_id));
- if (IS_ERR(fabdev)) {
+ if (IS_ERR_OR_NULL(fabdev)) {
MSM_BUS_ERR("Fabric device not found for mport: %d\n",
master_port);
return -ENODEV;
@@ -65,7 +65,7 @@
MSM_BUS_DBG("master_port: %d iid: %d fabid: %d\n",
master_port, priv_id, GET_FABID(priv_id));
fabdev = msm_bus_get_fabric_device(GET_FABID(priv_id));
- if (IS_ERR(fabdev)) {
+ if (IS_ERR_OR_NULL(fabdev)) {
MSM_BUS_ERR("Fabric device not found for mport: %d\n",
master_port);
return -ENODEV;
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_core.h b/arch/arm/mach-msm/msm_bus/msm_bus_core.h
index fd2dbb5..87719e3 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_core.h
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_core.h
@@ -36,7 +36,7 @@
(((slv >= MSM_BUS_SLAVE_FIRST) && (slv <= MSM_BUS_SLAVE_LAST)) ? 1 : 0)
#define INTERLEAVED_BW(fab_pdata, bw, ports) \
- ((fab_pdata->il_flag) ? msm_bus_div64((bw), (ports)) : (bw))
+ ((fab_pdata->il_flag) ? msm_bus_div64((ports), (bw)) : (bw))
#define INTERLEAVED_VAL(fab_pdata, n) \
((fab_pdata->il_flag) ? (n) : 1)
@@ -81,6 +81,12 @@
unsigned int prio_wr;
unsigned int prio1;
unsigned int prio0;
+ u64 th;
+ unsigned int mode_thresh;
+ bool dual_conf;
+ u64 bimc_bw;
+ u32 bimc_gp;
+ u32 bimc_thmp;
const char *name;
};
@@ -147,6 +153,9 @@
*fab_pdata, void *hw_data, void **cdata);
int (*port_unhalt)(uint32_t haltid, uint8_t mport);
int (*port_halt)(uint32_t haltid, uint8_t mport);
+ void (*config_master)(struct msm_bus_fabric_registration *fab_pdata,
+ struct msm_bus_inode_info *info,
+ uint64_t req_clk, uint64_t req_bw);
};
struct msm_bus_fabric_device {
@@ -179,6 +188,9 @@
void (*update_bw)(struct msm_bus_fabric_device *fabdev, struct
msm_bus_inode_info * hop, struct msm_bus_inode_info *info,
int64_t add_bw, int *master_tiers, int ctx);
+ void (*config_master)(struct msm_bus_fabric_device *fabdev,
+ struct msm_bus_inode_info *info, uint64_t req_clk,
+ uint64_t req_bw);
};
struct msm_bus_board_algorithm {
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
index 2c7ceab..ddf747e 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
@@ -364,7 +364,7 @@
{
struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev);
void *sel_cdata;
- int i;
+ long rounded_rate;
sel_cdata = fabric->cdata[ctx];
@@ -379,8 +379,17 @@
}
/* Enable clocks before accessing QoS registers */
- for (i = 0; i < NUM_CTX; i++)
- clk_prepare_enable(fabric->info.nodeclk[i].clk);
+ if (fabric->info.nodeclk[DUAL_CTX].clk)
+ if (fabric->info.nodeclk[DUAL_CTX].rate == 0) {
+ rounded_rate = clk_round_rate(fabric->
+ info.nodeclk[DUAL_CTX].clk, 1);
+ if (clk_set_rate(fabric->info.nodeclk[DUAL_CTX].clk,
+ rounded_rate))
+ MSM_BUS_ERR("Error: clk: en: Node: %d rate: %ld",
+ fabric->fabdev.id, rounded_rate);
+
+ clk_prepare_enable(fabric->info.nodeclk[DUAL_CTX].clk);
+ }
if (info->iface_clk.clk)
clk_prepare_enable(info->iface_clk.clk);
@@ -392,8 +401,9 @@
master_tiers, add_bw);
/* Disable clocks after accessing QoS registers */
- for (i = 0; i < NUM_CTX; i++)
- clk_disable_unprepare(fabric->info.nodeclk[i].clk);
+ if (fabric->info.nodeclk[DUAL_CTX].clk &&
+ fabric->info.nodeclk[DUAL_CTX].rate == 0)
+ clk_disable_unprepare(fabric->info.nodeclk[DUAL_CTX].clk);
if (info->iface_clk.clk) {
MSM_BUS_DBG("Commented: Will disable clock for info: %d\n",
@@ -499,6 +509,46 @@
return status;
}
+static void msm_bus_fabric_config_master(
+ struct msm_bus_fabric_device *fabdev,
+ struct msm_bus_inode_info *info, uint64_t req_clk, uint64_t req_bw)
+{
+ struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev);
+ long rounded_rate;
+
+ if (fabdev->hw_algo.config_master == NULL)
+ return;
+
+ /* Enable clocks before accessing QoS registers */
+ if (fabric->info.nodeclk[DUAL_CTX].clk)
+ if (fabric->info.nodeclk[DUAL_CTX].rate == 0) {
+ rounded_rate = clk_round_rate(fabric->
+ info.nodeclk[DUAL_CTX].clk, 1);
+ if (clk_set_rate(fabric->info.nodeclk[DUAL_CTX].clk,
+ rounded_rate))
+ MSM_BUS_ERR("Error: clk: en: Node: %d rate: %ld",
+ fabric->fabdev.id, rounded_rate);
+
+ clk_prepare_enable(fabric->info.nodeclk[DUAL_CTX].clk);
+ }
+
+ if (info->iface_clk.clk)
+ clk_prepare_enable(info->iface_clk.clk);
+
+ fabdev->hw_algo.config_master(fabric->pdata, info, req_clk, req_bw);
+
+ /* Disable clocks after accessing QoS registers */
+ if (fabric->info.nodeclk[DUAL_CTX].clk &&
+ fabric->info.nodeclk[DUAL_CTX].rate == 0)
+ clk_disable_unprepare(fabric->info.nodeclk[DUAL_CTX].clk);
+
+ if (info->iface_clk.clk) {
+ MSM_BUS_DBG("Commented: Will disable clock for info: %d\n",
+ info->node_info->priv_id);
+ clk_disable_unprepare(info->iface_clk.clk);
+ }
+}
+
/**
* msm_bus_fabric_hw_commit() - Commit the arbitration data to Hardware.
* @fabric: Fabric for which the data should be committed
@@ -654,6 +704,7 @@
.find_node = msm_bus_fabric_find_node,
.find_gw_node = msm_bus_fabric_find_gw_node,
.get_gw_list = msm_bus_fabric_get_gw_list,
+ .config_master = msm_bus_fabric_config_master,
};
static int msm_bus_fabric_hw_init(struct msm_bus_fabric_registration *pdata,
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_noc.c b/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
index 3ae37e4..d33c340 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -619,6 +619,7 @@
hw_algo->commit = msm_bus_noc_commit;
hw_algo->port_halt = msm_bus_noc_port_halt;
hw_algo->port_unhalt = msm_bus_noc_port_unhalt;
+ hw_algo->config_master = NULL;
return 0;
}
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_of.c b/arch/arm/mach-msm/msm_bus/msm_bus_of.c
index af3537c..06894c6 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_of.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_of.c
@@ -22,7 +22,7 @@
#include <mach/msm_bus_board.h>
#include "msm_bus_core.h"
-#define KBTOMB(a) (a * 1000ULL)
+#define KBTOB(a) (a * 1000ULL)
static const char * const hw_sel_name[] = {"RPM", "NoC", "BIMC", NULL};
static const char * const mode_sel_name[] = {"Fixed", "Limiter", "Bypass",
"Regulator", NULL};
@@ -103,6 +103,11 @@
}
vec_arr = of_get_property(of_node, "qcom,msm-bus,vectors-KBps", &len);
+ if (vec_arr == NULL) {
+ pr_err("Error: Vector array not found\n");
+ goto err;
+ }
+
if (len != num_usecases * num_paths * sizeof(uint32_t) * 4) {
pr_err("Error: Length-error on getting vectors\n");
goto err;
@@ -124,9 +129,9 @@
usecase[i].vectors[j].dst =
be32_to_cpu(vec_arr[index + 1]);
usecase[i].vectors[j].ab = (uint64_t)
- KBTOMB(be32_to_cpu(vec_arr[index + 2]));
+ KBTOB(be32_to_cpu(vec_arr[index + 2]));
usecase[i].vectors[j].ib = (uint64_t)
- KBTOMB(be32_to_cpu(vec_arr[index + 3]));
+ KBTOB(be32_to_cpu(vec_arr[index + 3]));
}
}
@@ -273,6 +278,7 @@
struct msm_bus_node_info *info;
struct device_node *child_node = NULL;
int i = 0, ret;
+ u32 temp;
for_each_child_of_node(of_node, child_node) {
i++;
@@ -347,6 +353,20 @@
of_property_read_u32(child_node, "qcom,buswidth",
&info[i].buswidth);
of_property_read_u32(child_node, "qcom,ws", &info[i].ws);
+ ret = of_property_read_u32(child_node, "qcom,thresh",
+ &temp);
+ if (!ret)
+ info[i].th = (uint64_t)KBTOB(temp);
+
+ ret = of_property_read_u32(child_node, "qcom,bimc,bw",
+ &temp);
+ if (!ret)
+ info[i].bimc_bw = (uint64_t)KBTOB(temp);
+
+ of_property_read_u32(child_node, "qcom,bimc,gp",
+ &info[i].bimc_gp);
+ of_property_read_u32(child_node, "qcom,bimc,thmp",
+ &info[i].bimc_thmp);
ret = of_property_read_string(child_node, "qcom,mode",
&sel_str);
if (ret)
@@ -361,6 +381,25 @@
info[i].mode = ret;
}
+ info[i].dual_conf =
+ of_property_read_bool(child_node, "qcom,dual-conf");
+
+ ret = of_property_read_string(child_node, "qcom,mode-thresh",
+ &sel_str);
+ if (ret)
+ info[i].mode_thresh = 0;
+ else {
+ ret = get_num(mode_sel_name, sel_str);
+ if (ret < 0) {
+ pr_err("Unknown mode :%s\n", sel_str);
+ goto err;
+ }
+
+ info[i].mode_thresh = ret;
+ MSM_BUS_DBG("AXI: THreshold mode set: %d\n",
+ info[i].mode_thresh);
+ }
+
ret = of_property_read_string(child_node, "qcom,perm-mode",
&sel_str);
if (ret)
@@ -432,7 +471,7 @@
struct msm_bus_fabric_registration
*msm_bus_of_get_fab_data(struct platform_device *pdev)
{
- struct device_node *of_node = pdev->dev.of_node;
+ struct device_node *of_node;
struct msm_bus_fabric_registration *pdata;
bool mem_err = false;
int ret = 0;
@@ -443,6 +482,7 @@
return NULL;
}
+ of_node = pdev->dev.of_node;
pdata = devm_kzalloc(&pdev->dev,
sizeof(struct msm_bus_fabric_registration), GFP_KERNEL);
if (!pdata) {
diff --git a/arch/arm/mach-msm/msm_dcvs.c b/arch/arm/mach-msm/msm_dcvs.c
index 9e0be63..1a919fc 100644
--- a/arch/arm/mach-msm/msm_dcvs.c
+++ b/arch/arm/mach-msm/msm_dcvs.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -1026,6 +1026,9 @@
uint32_t ret1;
uint32_t ret2;
+ if (!msm_dcvs_enabled)
+ return ret;
+
offset = get_core_offset(type, type_core_num);
if (offset < 0)
return ret;
@@ -1277,6 +1280,9 @@
struct kobject *module_kobj = NULL;
int ret = 0;
+ if (!msm_dcvs_enabled)
+ return ret;
+
module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
if (!module_kobj) {
pr_err("%s: cannot find kobject for module %s\n",
@@ -1343,6 +1349,7 @@
ret = msm_dcvs_scm_init(SZ_32K);
if (ret) {
__err("Unable to initialize DCVS err=%d\n", ret);
+ msm_dcvs_enabled = 0;
goto done;
}
diff --git a/arch/arm/mach-msm/msm_ipc_router_security.c b/arch/arm/mach-msm/msm_ipc_router_security.c
index 69efd13..5897e1f 100644
--- a/arch/arm/mach-msm/msm_ipc_router_security.c
+++ b/arch/arm/mach-msm/msm_ipc_router_security.c
@@ -22,6 +22,7 @@
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include <linux/msm_ipc.h>
+#include <linux/rwsem.h>
#include <asm/uaccess.h>
@@ -31,6 +32,11 @@
#define IRSC_COMPLETION_TIMEOUT_MS 30000
#define SEC_RULES_HASH_SZ 32
+
+#ifndef SIZE_MAX
+#define SIZE_MAX ((size_t)-1)
+#endif
+
struct security_rule {
struct list_head list;
uint32_t service_id;
@@ -40,7 +46,7 @@
gid_t *group_id;
};
-static DEFINE_MUTEX(security_rules_lock);
+static DECLARE_RWSEM(security_rules_lock_lha4);
static struct list_head security_rules[SEC_RULES_HASH_SZ];
static DECLARE_COMPLETION(irsc_completion);
@@ -98,7 +104,7 @@
struct config_sec_rules_args sec_rules_arg;
struct security_rule *rule, *temp_rule;
int key;
- int group_info_sz;
+ size_t group_info_sz;
int ret;
if (current_euid())
@@ -112,12 +118,12 @@
if (sec_rules_arg.num_group_info <= 0)
return -EINVAL;
- group_info_sz = sec_rules_arg.num_group_info * sizeof(gid_t);
- if ((group_info_sz / sizeof(gid_t)) != sec_rules_arg.num_group_info) {
+ if (sec_rules_arg.num_group_info > (SIZE_MAX / sizeof(gid_t))) {
pr_err("%s: Integer Overflow %d * %d\n", __func__,
sizeof(gid_t), sec_rules_arg.num_group_info);
return -EINVAL;
}
+ group_info_sz = sec_rules_arg.num_group_info * sizeof(gid_t);
rule = kzalloc(sizeof(struct security_rule), GFP_KERNEL);
if (!rule) {
@@ -146,7 +152,7 @@
}
key = rule->service_id & (SEC_RULES_HASH_SZ - 1);
- mutex_lock(&security_rules_lock);
+ down_write(&security_rules_lock_lha4);
if (rule->service_id == ALL_SERVICE) {
temp_rule = list_first_entry(&security_rules[key],
struct security_rule, list);
@@ -155,7 +161,7 @@
kfree(temp_rule);
}
list_add_tail(&rule->list, &security_rules[key]);
- mutex_unlock(&security_rules_lock);
+ up_write(&security_rules_lock_lha4);
if (rule->service_id == ALL_SERVICE)
msm_ipc_sync_default_sec_rule((void *)rule);
@@ -198,10 +204,10 @@
rule->instance_id = ALL_INSTANCE;
rule->num_group_info = 1;
*(rule->group_id) = AID_NET_RAW;
- mutex_lock(&security_rules_lock);
+ down_write(&security_rules_lock_lha4);
key = (ALL_SERVICE & (SEC_RULES_HASH_SZ - 1));
list_add_tail(&rule->list, &security_rules[key]);
- mutex_unlock(&security_rules_lock);
+ up_write(&security_rules_lock_lha4);
return 0;
}
@@ -222,12 +228,12 @@
struct security_rule *rule;
key = (service_id & (SEC_RULES_HASH_SZ - 1));
- mutex_lock(&security_rules_lock);
+ down_read(&security_rules_lock_lha4);
/* Return the rule for a specific <service:instance>, if found. */
list_for_each_entry(rule, &security_rules[key], list) {
if ((rule->service_id == service_id) &&
(rule->instance_id == instance_id)) {
- mutex_unlock(&security_rules_lock);
+ up_read(&security_rules_lock_lha4);
return (void *)rule;
}
}
@@ -236,7 +242,7 @@
list_for_each_entry(rule, &security_rules[key], list) {
if ((rule->service_id == service_id) &&
(rule->instance_id == ALL_INSTANCE)) {
- mutex_unlock(&security_rules_lock);
+ up_read(&security_rules_lock_lha4);
return (void *)rule;
}
}
@@ -246,10 +252,11 @@
list_for_each_entry(rule, &security_rules[key], list) {
if ((rule->service_id == ALL_SERVICE) &&
(rule->instance_id == ALL_INSTANCE)) {
- mutex_unlock(&security_rules_lock);
+ up_read(&security_rules_lock_lha4);
return (void *)rule;
}
}
+ up_read(&security_rules_lock_lha4);
return NULL;
}
EXPORT_SYMBOL(msm_ipc_get_security_rule);
diff --git a/arch/arm/mach-msm/msm_qmi_interface.c b/arch/arm/mach-msm/msm_qmi_interface.c
index 4c4635a..9b3e500 100644
--- a/arch/arm/mach-msm/msm_qmi_interface.c
+++ b/arch/arm/mach-msm/msm_qmi_interface.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -450,6 +450,11 @@
/* Read the messages */
rc = msm_ipc_router_read_msg((struct msm_ipc_port *)(handle->src_port),
&src_addr, &recv_msg, &recv_msg_len);
+ if (rc == -ENOMSG) {
+ mutex_unlock(&handle->handle_lock);
+ return rc;
+ }
+
if (rc < 0) {
pr_err("%s: Read failed %d\n", __func__, rc);
mutex_unlock(&handle->handle_lock);
diff --git a/arch/arm/mach-msm/msm_smem_iface.h b/arch/arm/mach-msm/msm_smem_iface.h
index bc3e73b..c9c56d9 100644
--- a/arch/arm/mach-msm/msm_smem_iface.h
+++ b/arch/arm/mach-msm/msm_smem_iface.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, 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
@@ -16,6 +16,7 @@
#define __ARCH_ARM_MACH_MSM_SMEM_IFACE_H
#include <mach/msm_smsm.h>
+#include <mach/msm_smem.h>
#define MAX_KEY_EVENTS 10
#define MAX_SEC_KEY_PAYLOAD 32
diff --git a/arch/arm/mach-msm/msm_watchdog.c b/arch/arm/mach-msm/msm_watchdog.c
index b1c8b30..dcfe13c 100644
--- a/arch/arm/mach-msm/msm_watchdog.c
+++ b/arch/arm/mach-msm/msm_watchdog.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013, 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
@@ -23,6 +23,7 @@
#include <linux/suspend.h>
#include <linux/percpu.h>
#include <linux/interrupt.h>
+#include <linux/reboot.h>
#include <asm/fiq.h>
#include <asm/hardware/gic.h>
#include <mach/msm_iomap.h>
@@ -64,6 +65,12 @@
module_param(enable, int, 0);
/*
+ * Watchdog bark reboot timeout in seconds.
+ * Can be specified in kernel command line.
+ */
+static int reboot_bark_timeout = 22;
+module_param(reboot_bark_timeout, int, 0644);
+/*
* If the watchdog is enabled at bootup (enable=1),
* the runtime_disable sysfs node at
* /sys/module/msm_watchdog/runtime_disable
@@ -154,6 +161,27 @@
.notifier_call = panic_wdog_handler,
};
+#define get_sclk_hz(t_ms) ((t_ms / 1000) * WDT_HZ)
+#define get_reboot_bark_timeout(t_s) ((t_s * MSEC_PER_SEC) < bark_time ? \
+ get_sclk_hz(bark_time) : get_sclk_hz(t_s * MSEC_PER_SEC))
+
+static int msm_watchdog_reboot_notifier(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+
+ u64 timeout = get_reboot_bark_timeout(reboot_bark_timeout);
+ __raw_writel(timeout, msm_wdt_base + WDT_BARK_TIME);
+ __raw_writel(timeout + 3 * WDT_HZ,
+ msm_wdt_base + WDT_BITE_TIME);
+ __raw_writel(1, msm_wdt_base + WDT_RST);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block msm_reboot_notifier = {
+ .notifier_call = msm_watchdog_reboot_notifier,
+};
+
struct wdog_disable_work_data {
struct work_struct work;
struct completion complete;
@@ -177,6 +205,7 @@
}
enable = 0;
atomic_notifier_chain_unregister(&panic_notifier_list, &panic_blk);
+ unregister_reboot_notifier(&msm_reboot_notifier);
cancel_delayed_work(&dogwork_struct);
/* may be suspended after the first write above */
__raw_writel(0, msm_wdt_base + WDT_EN);
@@ -373,6 +402,10 @@
atomic_notifier_chain_register(&panic_notifier_list,
&panic_blk);
+ ret = register_reboot_notifier(&msm_reboot_notifier);
+ if (ret)
+ pr_err("Failed to register reboot notifier\n");
+
__raw_writel(1, msm_wdt_base + WDT_EN);
__raw_writel(1, msm_wdt_base + WDT_RST);
last_pet = sched_clock();
@@ -395,6 +428,11 @@
}
bark_time = pdata->bark_time;
+ /* reboot_bark_timeout (in seconds) might have been supplied as
+ * module parameter.
+ */
+ if ((reboot_bark_timeout * MSEC_PER_SEC) < bark_time)
+ reboot_bark_timeout = (bark_time / MSEC_PER_SEC);
has_vic = pdata->has_vic;
if (!pdata->has_secure) {
appsbark = 1;
diff --git a/arch/arm/mach-msm/nand_partitions.c b/arch/arm/mach-msm/nand_partitions.c
index ea5fb9c..ad2a10e 100644
--- a/arch/arm/mach-msm/nand_partitions.c
+++ b/arch/arm/mach-msm/nand_partitions.c
@@ -4,7 +4,7 @@
* bootloader.
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2008-2009,2011 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008-2009,2011,2013 The Linux Foundation. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -34,7 +34,7 @@
#include <mach/board.h>
#ifdef CONFIG_MSM_SMD
-#include "smd_private.h"
+#include <mach/msm_smem.h>
#endif
/* configuration tags specific to msm */
diff --git a/arch/arm/mach-msm/nohlt.c b/arch/arm/mach-msm/nohlt.c
index e598ed0..94cbc4b 100644
--- a/arch/arm/mach-msm/nohlt.c
+++ b/arch/arm/mach-msm/nohlt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2009, 2013, 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
@@ -28,11 +28,18 @@
return 0;
}
-DEFINE_SIMPLE_ATTRIBUTE(nohalt_ops, NULL, set_nohalt, "%llu\n");
+static int get_nohalt(void *data, u64 *val)
+{
+ *val = (unsigned int)get_hlt();
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(nohalt_ops, get_nohalt, set_nohalt, "%llu\n");
static int __init init_hlt_debug(void)
{
- debugfs_create_file("nohlt", 0200, NULL, NULL, &nohalt_ops);
+ debugfs_create_file("nohlt", 0600, NULL, NULL, &nohalt_ops);
return 0;
}
diff --git a/arch/arm/mach-msm/ocmem_allocator.c b/arch/arm/mach-msm/ocmem_allocator.c
index 203bb60..a0ff9f9 100644
--- a/arch/arm/mach-msm/ocmem_allocator.c
+++ b/arch/arm/mach-msm/ocmem_allocator.c
@@ -36,24 +36,22 @@
reserve: Enable libgenpool to simulate tail allocations
*/
-unsigned long allocate_head(struct ocmem_zone *z, unsigned long size)
+int allocate_head(struct ocmem_zone *z, unsigned long size,
+ unsigned long *offset)
{
+ *offset = gen_pool_alloc(z->z_pool, size);
- unsigned long offset;
-
- offset = gen_pool_alloc(z->z_pool, size);
-
- if (!offset)
+ if (!(*offset))
return -ENOMEM;
z->z_head += size;
z->z_free -= size;
- return offset;
+ return 0;
}
-unsigned long allocate_tail(struct ocmem_zone *z, unsigned long size)
+int allocate_tail(struct ocmem_zone *z, unsigned long size,
+ unsigned long *offset)
{
- unsigned long offset;
unsigned long reserve;
unsigned long head;
@@ -63,17 +61,17 @@
reserve = z->z_tail - z->z_head - size;
if (reserve) {
head = gen_pool_alloc(z->z_pool, reserve);
- offset = gen_pool_alloc(z->z_pool, size);
+ *offset = gen_pool_alloc(z->z_pool, size);
gen_pool_free(z->z_pool, head, reserve);
} else
- offset = gen_pool_alloc(z->z_pool, size);
+ *offset = gen_pool_alloc(z->z_pool, size);
- if (!offset)
+ if (!(*offset))
return -ENOMEM;
z->z_tail -= size;
z->z_free -= size;
- return offset;
+ return 0;
}
int free_head(struct ocmem_zone *z, unsigned long offset,
diff --git a/arch/arm/mach-msm/ocmem_core.c b/arch/arm/mach-msm/ocmem_core.c
index 4ea1de9..153864d 100644
--- a/arch/arm/mach-msm/ocmem_core.c
+++ b/arch/arm/mach-msm/ocmem_core.c
@@ -66,7 +66,7 @@
#define NUM_PORTS_SHIFT (0)
#define GFX_MPU_SHIFT (12)
-#define NUM_MACROS_MASK (0xF << 8)
+#define NUM_MACROS_MASK (0x3F << 8)
#define NUM_MACROS_SHIFT (8)
#define INTERLEAVING_MASK (0x1 << 17)
@@ -97,9 +97,12 @@
#define REGION_SLEEP_PERI_ON 0x00007777
#define REGION_DEFAULT_OFF REGION_SLEEP_NO_RETENTION
-#define REGION_DEFAULT_ON REGION_NORMAL_PASSTHROUGH
+#define REGION_DEFAULT_ON REGION_FORCE_CORE_ON
#define REGION_DEFAULT_RETENTION REGION_SLEEP_PERI_OFF
+#define REGION_STAGING_SET(x) (REGION_SLEEP_PERI_OFF & (0xF << (x * 0x4)))
+#define REGION_ON_SET(x) (REGION_DEFAULT_OFF & (0xF << (x * 0x4)))
+
enum rpm_macro_state {
rpm_macro_off = 0x0,
rpm_macro_retain,
@@ -195,6 +198,51 @@
return state;
}
+#ifndef CONFIG_MSM_OCMEM_POWER_DISABLE
+static int commit_region_staging(unsigned region_num, unsigned start_m,
+ unsigned new_state)
+{
+ int rc = -1;
+ unsigned state = 0x0;
+ unsigned read_state = 0x0;
+ unsigned curr_state = 0x0;
+
+ if (region_num >= num_regions)
+ return -EINVAL;
+
+ if (rpm_power_control)
+ return 0;
+ else {
+ if (new_state != REGION_DEFAULT_OFF) {
+ curr_state = read_region_state(region_num);
+ state = curr_state | REGION_STAGING_SET(start_m);
+ rc = ocmem_write(state,
+ ocmem_base + PSCGC_CTL_n(region_num));
+ /* Barrier to commit the region state */
+ mb();
+ read_state = read_region_state(region_num);
+ if (new_state == REGION_DEFAULT_ON) {
+ curr_state = read_region_state(region_num);
+ state = curr_state ^ REGION_ON_SET(start_m);
+ rc = ocmem_write(state,
+ ocmem_base + PSCGC_CTL_n(region_num));
+ /* Barrier to commit the region state */
+ mb();
+ read_state = read_region_state(region_num);
+ }
+ } else {
+ curr_state = read_region_state(region_num);
+ state = curr_state ^ REGION_STAGING_SET(start_m);
+ rc = ocmem_write(state,
+ ocmem_base + PSCGC_CTL_n(region_num));
+ /* Barrier to commit the region state */
+ mb();
+ read_state = read_region_state(region_num);
+ }
+ }
+ return 0;
+}
+#endif
/* Returns the current state of a OCMEM macro that belongs to a region */
static int read_macro_state(unsigned region_num, unsigned macro_num)
@@ -711,7 +759,7 @@
return -EINVAL;
}
-#if defined(CONFIG_MSM_OCMEM_POWER_DISABLE)
+#if defined(CONFIG_MSM_OCMEM_DEBUG_ALWAYS_ON)
static int ocmem_core_set_default_state(void)
{
int rc = 0;
@@ -727,7 +775,14 @@
return 0;
}
+#else
+static int ocmem_core_set_default_state(void)
+{
+ return 0;
+}
+#endif
+#if defined(CONFIG_MSM_OCMEM_POWER_DISABLE)
/* Initializes a region to be turned ON in wide mode */
static int ocmem_region_set_default_state(unsigned int r_num)
{
@@ -752,15 +807,9 @@
{
return 0;
}
-
-static int ocmem_core_set_default_state(void)
-{
- return 0;
-}
#endif
#if defined(CONFIG_MSM_OCMEM_POWER_DEBUG)
-
static int read_hw_region_state(unsigned region_num)
{
int state;
@@ -948,9 +997,11 @@
apply_macro_vote(id, i, j,
hw_macro_state(new_state));
aggregate_macro_state(i, j);
+ commit_region_staging(i, j, new_state);
}
aggregate_region_state(i);
- commit_region_state(i);
+ if (rpm_power_control)
+ commit_region_state(i);
len -= region_size;
/* If we voted ON/retain the banks must never be OFF */
diff --git a/arch/arm/mach-msm/ocmem_sched.c b/arch/arm/mach-msm/ocmem_sched.c
index 8afe695..a3fd6b2 100644
--- a/arch/arm/mach-msm/ocmem_sched.c
+++ b/arch/arm/mach-msm/ocmem_sched.c
@@ -724,6 +724,7 @@
bool retry;
struct ocmem_region *spanned_r = NULL;
struct ocmem_region *overlap_r = NULL;
+ int rc = 0;
struct ocmem_req *matched_req = NULL;
struct ocmem_region *matched_region = NULL;
@@ -767,9 +768,10 @@
if (overlap_r == NULL) {
/* no conflicting regions, schedule this region */
zone->z_ops->free(zone, curr_start, curr_sz);
- alloc_addr = zone->z_ops->allocate(zone, curr_sz + growth_sz);
+ rc = zone->z_ops->allocate(zone, curr_sz + growth_sz,
+ &alloc_addr);
- if (alloc_addr < 0) {
+ if (rc) {
pr_err("ocmem: zone allocation operation failed\n");
goto internal_error;
}
@@ -933,6 +935,7 @@
struct ocmem_region *matched_region = NULL;
struct ocmem_region *region = NULL;
unsigned long alloc_addr = 0x0;
+ int rc = 0;
struct ocmem_zone *zone = get_zone(owner);
@@ -957,9 +960,9 @@
goto internal_error;
}
- alloc_addr = zone->z_ops->allocate(zone, new_sz);
+ rc = zone->z_ops->allocate(zone, new_sz, &alloc_addr);
- if (alloc_addr < 0) {
+ if (rc) {
pr_err("Zone Allocation operation failed\n");
goto internal_error;
}
@@ -1032,6 +1035,7 @@
enum client_prio prio = req->prio;
unsigned long alloc_addr = 0x0;
bool retry;
+ int rc = 0;
struct ocmem_region *spanned_r = NULL;
struct ocmem_region *overlap_r = NULL;
@@ -1056,13 +1060,6 @@
goto invalid_op_error;
}
- region = create_region();
-
- if (!region) {
- pr_err("ocmem: Unable to create region\n");
- goto invalid_op_error;
- }
-
retry = false;
pr_debug("ocmem: do_allocate: %s request %p size %lx\n",
@@ -1077,10 +1074,18 @@
overlap_r = find_region_intersection(zone->z_head, zone->z_head + sz);
if (overlap_r == NULL) {
- /* no conflicting regions, schedule this region */
- alloc_addr = zone->z_ops->allocate(zone, sz);
- if (alloc_addr < 0) {
+ region = create_region();
+
+ if (!region) {
+ pr_err("ocmem: Unable to create region\n");
+ goto invalid_op_error;
+ }
+
+ /* no conflicting regions, schedule this region */
+ rc = zone->z_ops->allocate(zone, sz, &alloc_addr);
+
+ if (rc) {
pr_err("Zone Allocation operation failed\n");
goto internal_error;
}
@@ -1172,7 +1177,6 @@
trigger_eviction:
pr_debug("Trigger eviction of region %p\n", overlap_r);
- destroy_region(region);
return OP_EVICT;
err_not_supported:
@@ -1515,6 +1519,18 @@
pr_err("ocmem: Failed to unmap %p\n", req);
goto free_fail;
}
+ /* Turn off the memory */
+ if (req->req_sz != 0) {
+
+ offset = phys_to_offset(req->req_start);
+ rc = ocmem_memory_off(req->owner, offset,
+ req->req_sz);
+
+ if (rc < 0) {
+ pr_err("Failed to switch OFF memory macros\n");
+ goto free_fail;
+ }
+ }
rc = do_free(req);
if (rc < 0) {
@@ -1525,21 +1541,19 @@
pr_debug("request %p was already shrunk to 0\n", req);
}
- /* Turn off the memory */
- if (req->req_sz != 0) {
+ if (!TEST_STATE(req, R_FREE)) {
+ /* Turn off the memory */
+ if (req->req_sz != 0) {
- offset = phys_to_offset(req->req_start);
+ offset = phys_to_offset(req->req_start);
+ rc = ocmem_memory_off(req->owner, offset, req->req_sz);
- rc = ocmem_memory_off(req->owner, offset, req->req_sz);
-
- if (rc < 0) {
- pr_err("Failed to switch OFF memory macros\n");
- goto free_fail;
+ if (rc < 0) {
+ pr_err("Failed to switch OFF memory macros\n");
+ goto free_fail;
+ }
}
- }
-
- if (!TEST_STATE(req, R_FREE)) {
/* free the allocation */
rc = do_free(req);
if (rc < 0)
diff --git a/arch/arm/mach-msm/pcie.c b/arch/arm/mach-msm/pcie.c
index c2ba6c1..c09b759 100644
--- a/arch/arm/mach-msm/pcie.c
+++ b/arch/arm/mach-msm/pcie.c
@@ -46,6 +46,7 @@
#define PCIE20_PARF_PHY_REFCLK 0x4C
#define PCIE20_PARF_CONFIG_BITS 0x50
+#define PCIE20_ELBI_VERSION 0x00
#define PCIE20_ELBI_SYS_CTRL 0x04
#define PCIE20_CAP 0x70
@@ -55,6 +56,8 @@
#define PCIE20_BUSNUMBERS 0x18
#define PCIE20_MEMORY_BASE_LIMIT 0x20
+#define PCIE20_PLR_AXI_MSTR_RESP_COMP_CTRL0 0x818
+#define PCIE20_PLR_AXI_MSTR_RESP_COMP_CTRL1 0x81c
#define PCIE20_PLR_IATU_VIEWPORT 0x900
#define PCIE20_PLR_IATU_CTRL1 0x904
#define PCIE20_PLR_IATU_CTRL2 0x908
@@ -478,6 +481,27 @@
msm_pcie_dev.axi_conf = NULL;
}
+static void msm_pcie_adjust_tlp_size(struct msm_pcie_dev_t *dev)
+{
+ /*
+ * Apply this fix only for device such as APQ8064 version 1.
+ * Set the Max TLP size to 2K, instead of using default of 4K
+ * to avoid a RAM problem in PCIE20 core of that version.
+ */
+ if (readl_relaxed(dev->elbi + PCIE20_ELBI_VERSION) == 0x01002107) {
+
+ /*
+ * CFG_REMOTE_RD_REQ_BRIDGE_SIZE:
+ * 5=4KB/4=2KB/3=1KB/2=512B/1=256B/0=128B
+ */
+ writel_relaxed(4, dev->pcie20 +
+ PCIE20_PLR_AXI_MSTR_RESP_COMP_CTRL0);
+
+ writel_relaxed(1, dev->pcie20 +
+ PCIE20_PLR_AXI_MSTR_RESP_COMP_CTRL1);
+ }
+};
+
static int __init msm_pcie_setup(int nr, struct pci_sys_data *sys)
{
int rc;
@@ -555,6 +579,12 @@
gpio_set_value_cansleep(dev->gpio[MSM_PCIE_GPIO_RST_N].num,
!dev->gpio[MSM_PCIE_GPIO_RST_N].on);
+ /*
+ * adjust tlp size before link comes up
+ * so there will be no transactions.
+ */
+ msm_pcie_adjust_tlp_size(dev);
+
/* enable link training */
msm_pcie_write_mask(dev->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0));
diff --git a/arch/arm/mach-msm/perf_debug.c b/arch/arm/mach-msm/perf_debug.c
index 5bf88a2..70420db 100644
--- a/arch/arm/mach-msm/perf_debug.c
+++ b/arch/arm/mach-msm/perf_debug.c
@@ -30,6 +30,8 @@
"5 Perf: Add DT support for L1 and L2 PMU\n"
"6 Perf: Add cortex A5 device tree support\n"
"7 Perf: Add L1 counters to tracepoints\n"
+ "8 Perf: Add cortex A7 perf support\n"
+ "9 ARM: dts: msm: add perf-events support for msm8226\n"
;
static ssize_t desc_read(struct file *fp, char __user *buf,
diff --git a/arch/arm/mach-msm/perf_trace_counters.h b/arch/arm/mach-msm/perf_trace_counters.h
index ce7e336..8f77bad 100644
--- a/arch/arm/mach-msm/perf_trace_counters.h
+++ b/arch/arm/mach-msm/perf_trace_counters.h
@@ -121,7 +121,6 @@
#endif
#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_PATH ../../arch/arm/mach-msm
#define TRACE_INCLUDE_FILE perf_trace_counters
#include <trace/define_trace.h>
-
diff --git a/arch/arm/mach-msm/peripheral-loader.c b/arch/arm/mach-msm/peripheral-loader.c
index 056da7d..475e8a1 100644
--- a/arch/arm/mach-msm/peripheral-loader.c
+++ b/arch/arm/mach-msm/peripheral-loader.c
@@ -290,10 +290,12 @@
static void pil_dump_segs(const struct pil_priv *priv)
{
struct pil_seg *seg;
+ phys_addr_t seg_h_paddr;
list_for_each_entry(seg, &priv->segs, list) {
- pil_info(priv->desc, "%d: %#08zx %#08lx\n", seg->num,
- seg->paddr, seg->paddr + seg->sz);
+ seg_h_paddr = seg->paddr + seg->sz;
+ pil_info(priv->desc, "%d: %pa %pa\n", seg->num,
+ &seg->paddr, &seg_h_paddr);
}
}
@@ -322,7 +324,7 @@
return 0;
}
}
- pil_err(priv->desc, "entry address %08zx not within range\n", entry);
+ pil_err(priv->desc, "entry address %pa not within range\n", &entry);
pil_dump_segs(priv);
return -EADDRNOTAVAIL;
}
@@ -489,7 +491,8 @@
static int pil_load_seg(struct pil_desc *desc, struct pil_seg *seg)
{
- int ret = 0, count, paddr;
+ int ret = 0, count;
+ phys_addr_t paddr;
char fw_name[30];
const struct firmware *fw = NULL;
const u8 *data;
diff --git a/arch/arm/mach-msm/peripheral-loader.h b/arch/arm/mach-msm/peripheral-loader.h
index ff10fe5..5aeeaf3 100644
--- a/arch/arm/mach-msm/peripheral-loader.h
+++ b/arch/arm/mach-msm/peripheral-loader.h
@@ -55,7 +55,8 @@
int (*init_image)(struct pil_desc *pil, const u8 *metadata,
size_t size);
int (*mem_setup)(struct pil_desc *pil, phys_addr_t addr, size_t size);
- int (*verify_blob)(struct pil_desc *pil, u32 phy_addr, size_t size);
+ int (*verify_blob)(struct pil_desc *pil, phys_addr_t phy_addr,
+ size_t size);
int (*proxy_vote)(struct pil_desc *pil);
int (*auth_and_reset)(struct pil_desc *pil);
void (*proxy_unvote)(struct pil_desc *pil);
diff --git a/arch/arm/mach-msm/pil-dsps.c b/arch/arm/mach-msm/pil-dsps.c
index df5ea35..73b58ab 100644
--- a/arch/arm/mach-msm/pil-dsps.c
+++ b/arch/arm/mach-msm/pil-dsps.c
@@ -22,6 +22,7 @@
#include <mach/subsystem_restart.h>
#include <mach/msm_smsm.h>
#include <mach/ramdump.h>
+#include <mach/msm_smem.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
diff --git a/arch/arm/mach-msm/pil-gss.c b/arch/arm/mach-msm/pil-gss.c
index d44add6..840c90f 100644
--- a/arch/arm/mach-msm/pil-gss.c
+++ b/arch/arm/mach-msm/pil-gss.c
@@ -30,6 +30,7 @@
#include <mach/msm_bus.h>
#include <mach/subsystem_restart.h>
#include <mach/ramdump.h>
+#include <mach/msm_smem.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -203,7 +204,7 @@
{
struct gss_data *drv = dev_get_drvdata(pil->dev);
void __iomem *base = drv->base;
- unsigned long start_addr = pil_get_entry_addr(pil);
+ phys_addr_t start_addr = pil_get_entry_addr(pil);
void __iomem *cbase = drv->cbase;
int ret;
diff --git a/arch/arm/mach-msm/pil-modem.c b/arch/arm/mach-msm/pil-modem.c
index 30f480a..8398206 100644
--- a/arch/arm/mach-msm/pil-modem.c
+++ b/arch/arm/mach-msm/pil-modem.c
@@ -93,7 +93,7 @@
{
u32 reg;
const struct modem_data *drv = dev_get_drvdata(pil->dev);
- unsigned long start_addr = pil_get_entry_addr(pil);
+ phys_addr_t start_addr = pil_get_entry_addr(pil);
/* Put modem AHB0,1,2 clocks into reset */
writel_relaxed(BIT(0) | BIT(1), drv->cbase + MAHB0_SFAB_PORT_RESET);
diff --git a/arch/arm/mach-msm/pil-pronto.c b/arch/arm/mach-msm/pil-pronto.c
index 0df8739..098cbd5 100644
--- a/arch/arm/mach-msm/pil-pronto.c
+++ b/arch/arm/mach-msm/pil-pronto.c
@@ -30,6 +30,7 @@
#include <mach/subsystem_restart.h>
#include <mach/msm_smsm.h>
#include <mach/ramdump.h>
+#include <mach/msm_smem.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -123,7 +124,7 @@
int rc;
struct pronto_data *drv = dev_get_drvdata(pil->dev);
void __iomem *base = drv->base;
- unsigned long start_addr = pil_get_entry_addr(pil);
+ phys_addr_t start_addr = pil_get_entry_addr(pil);
/* Deassert reset to subsystem and wait for propagation */
reg = readl_relaxed(drv->reset_base);
@@ -329,12 +330,13 @@
drv->crash = true;
+ disable_irq_nosync(drv->irq);
+
if (drv->restart_inprogress) {
pr_err("Ignoring wcnss bite irq, restart in progress\n");
return IRQ_HANDLED;
}
- disable_irq_nosync(drv->irq);
drv->restart_inprogress = true;
restart_wcnss(drv);
@@ -515,6 +517,17 @@
drv->subsys_desc.start = pronto_start;
drv->subsys_desc.stop = pronto_stop;
+ ret = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,gpio-err-ready", 0);
+ if (ret < 0)
+ return ret;
+
+ ret = gpio_to_irq(ret);
+ if (ret < 0)
+ return ret;
+
+ drv->subsys_desc.err_ready_irq = ret;
+
INIT_DELAYED_WORK(&drv->cancel_vote_work, wcnss_post_bootup);
drv->subsys = subsys_register(&drv->subsys_desc);
diff --git a/arch/arm/mach-msm/pil-q6v3.c b/arch/arm/mach-msm/pil-q6v3.c
index 0575a3d..a369878 100644
--- a/arch/arm/mach-msm/pil-q6v3.c
+++ b/arch/arm/mach-msm/pil-q6v3.c
@@ -116,7 +116,7 @@
{
u32 reg;
struct q6v3_data *drv = dev_get_drvdata(pil->dev);
- unsigned long start_addr = pil_get_entry_addr(pil);
+ phys_addr_t start_addr = pil_get_entry_addr(pil);
/* Put Q6 into reset */
reg = readl_relaxed(drv->cbase + LCC_Q6_FUNC);
diff --git a/arch/arm/mach-msm/pil-q6v4-lpass.c b/arch/arm/mach-msm/pil-q6v4-lpass.c
index f05bcdb..7acb599 100644
--- a/arch/arm/mach-msm/pil-q6v4-lpass.c
+++ b/arch/arm/mach-msm/pil-q6v4-lpass.c
@@ -25,6 +25,7 @@
#include <mach/subsystem_restart.h>
#include <mach/subsystem_notif.h>
#include <mach/ramdump.h>
+#include <mach/msm_smem.h>
#include "smd_private.h"
#include "sysmon.h"
diff --git a/arch/arm/mach-msm/pil-q6v4-mss.c b/arch/arm/mach-msm/pil-q6v4-mss.c
index 1821ab1..c4b6038 100644
--- a/arch/arm/mach-msm/pil-q6v4-mss.c
+++ b/arch/arm/mach-msm/pil-q6v4-mss.c
@@ -23,6 +23,7 @@
#include <mach/subsystem_restart.h>
#include <mach/msm_smsm.h>
#include <mach/ramdump.h>
+#include <mach/msm_smem.h>
#include "smd_private.h"
#include "peripheral-loader.h"
diff --git a/arch/arm/mach-msm/pil-q6v4.c b/arch/arm/mach-msm/pil-q6v4.c
index 29d14dd..51f7aa2 100644
--- a/arch/arm/mach-msm/pil-q6v4.c
+++ b/arch/arm/mach-msm/pil-q6v4.c
@@ -130,7 +130,7 @@
{
u32 reg, err;
const struct q6v4_data *drv = pil_to_q6v4_data(pil);
- unsigned long start_addr = pil_get_entry_addr(pil);
+ phys_addr_t start_addr = pil_get_entry_addr(pil);
/* Enable Q6 ACLK */
writel_relaxed(0x10, drv->aclk_reg);
diff --git a/arch/arm/mach-msm/pil-q6v5-lpass.c b/arch/arm/mach-msm/pil-q6v5-lpass.c
index 608c5e0..6cd6ffe 100644
--- a/arch/arm/mach-msm/pil-q6v5-lpass.c
+++ b/arch/arm/mach-msm/pil-q6v5-lpass.c
@@ -29,6 +29,7 @@
#include <mach/subsystem_notif.h>
#include <mach/scm.h>
#include <mach/ramdump.h>
+#include <mach/msm_smem.h>
#include "peripheral-loader.h"
#include "pil-q6v5.h"
@@ -127,7 +128,7 @@
static int pil_lpass_reset(struct pil_desc *pil)
{
struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
- unsigned long start_addr = pil_get_entry_addr(pil);
+ phys_addr_t start_addr = pil_get_entry_addr(pil);
int ret;
/* Deassert reset to subsystem and wait for propagation */
@@ -175,12 +176,25 @@
static int pil_lpass_reset_trusted(struct pil_desc *pil)
{
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
+ int ret;
+
+ ret = clk_prepare_enable(drv->axi_clk);
+ if (ret)
+ return ret;
return pas_auth_and_reset(PAS_Q6);
}
static int pil_lpass_shutdown_trusted(struct pil_desc *pil)
{
- return pas_shutdown(PAS_Q6);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
+ int ret;
+
+ ret = pas_shutdown(PAS_Q6);
+ if (ret)
+ return ret;
+ clk_disable_unprepare(drv->axi_clk);
+ return 0;
}
static struct pil_reset_ops pil_lpass_ops_trusted = {
@@ -391,7 +405,7 @@
struct q6v5_data *q6;
struct pil_desc *desc;
struct resource *res;
- int ret;
+ int ret, gpio_clk_ready;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
@@ -408,6 +422,12 @@
return ret;
drv->err_fatal_irq = ret;
+ ret = gpio_to_irq(of_get_named_gpio(pdev->dev.of_node,
+ "qcom,gpio-proxy-unvote", 0));
+ if (ret < 0)
+ return ret;
+ gpio_clk_ready = ret;
+
drv->force_stop_gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,gpio-force-stop", 0);
if (drv->force_stop_gpio < 0)
@@ -421,6 +441,7 @@
desc = &q6->desc;
desc->owner = THIS_MODULE;
desc->proxy_timeout = PROXY_TIMEOUT_MS;
+ desc->proxy_unvote_irq = gpio_clk_ready;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "restart_reg");
q6->restart_reg = devm_request_and_ioremap(&pdev->dev, res);
diff --git a/arch/arm/mach-msm/pil-q6v5-mss.c b/arch/arm/mach-msm/pil-q6v5-mss.c
index c3a0d32..8cf6011 100644
--- a/arch/arm/mach-msm/pil-q6v5-mss.c
+++ b/arch/arm/mach-msm/pil-q6v5-mss.c
@@ -31,6 +31,7 @@
#include <mach/clk.h>
#include <mach/msm_smsm.h>
#include <mach/ramdump.h>
+#include <mach/msm_smem.h>
#include "peripheral-loader.h"
#include "pil-q6v5.h"
@@ -78,6 +79,8 @@
#define EXTERNAL_BHS_STATUS BIT(4)
#define BHS_TIMEOUT_US 50
+#define STOP_ACK_TIMEOUT_MS 1000
+
struct mba_data {
void __iomem *rmb_base;
void __iomem *io_clamp_reg;
@@ -92,12 +95,13 @@
void *smem_ramdump_dev;
bool crash_shutdown;
bool ignore_errors;
- int is_loadable;
int err_fatal_irq;
+ unsigned int stop_ack_irq;
int force_stop_gpio;
+ struct completion stop_ack;
};
-static int pbl_mba_boot_timeout_ms = 100;
+static int pbl_mba_boot_timeout_ms = 1000;
module_param(pbl_mba_boot_timeout_ms, int, S_IRUGO | S_IWUSR);
static int modem_auth_timeout_ms = 10000;
@@ -105,13 +109,15 @@
static int pil_mss_power_up(struct q6v5_data *drv)
{
- int ret;
+ int ret = 0;
struct device *dev = drv->desc.dev;
u32 regval;
- ret = regulator_enable(drv->vreg);
- if (ret)
- dev_err(dev, "Failed to enable modem regulator.\n");
+ if (drv->vreg) {
+ ret = regulator_enable(drv->vreg);
+ if (ret)
+ dev_err(dev, "Failed to enable modem regulator.\n");
+ }
if (drv->cxrail_bhs) {
regval = readl_relaxed(drv->cxrail_bhs);
@@ -135,7 +141,10 @@
writel_relaxed(regval, drv->cxrail_bhs);
}
- return regulator_disable(drv->vreg);
+ if (drv->vreg)
+ return regulator_disable(drv->vreg);
+
+ return 0;
}
static int pil_mss_enable_clks(struct q6v5_data *drv)
@@ -212,28 +221,13 @@
pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_MODEM_HALT_BASE);
pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_NC_HALT_BASE);
- /*
- * If the shutdown function is called before the reset function, clocks
- * and power will not be enabled yet. Enable them here so that register
- * writes performed during the shutdown succeed.
- */
- if (drv->is_booted == false) {
- pil_mss_power_up(drv);
- pil_mss_enable_clks(drv);
- }
- pil_q6v5_shutdown(pil);
-
- pil_mss_disable_clks(drv);
-
writel_relaxed(1, drv->restart_reg);
- /*
- * access to the cx_rail_bhs is restricted until after the gcc_mss
- * reset is asserted once the PBL starts executing.
- */
- pil_mss_power_down(drv);
-
- drv->is_booted = false;
+ if (drv->is_booted) {
+ pil_mss_disable_clks(drv);
+ pil_mss_power_down(drv);
+ drv->is_booted = false;
+ }
return 0;
}
@@ -243,7 +237,7 @@
struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
struct platform_device *pdev = to_platform_device(pil->dev);
struct mba_data *mba = platform_get_drvdata(pdev);
- unsigned long start_addr = pil_get_entry_addr(pil);
+ phys_addr_t start_addr = pil_get_entry_addr(pil);
int ret;
/*
@@ -402,7 +396,7 @@
return ret;
}
-static int pil_mba_verify_blob(struct pil_desc *pil, u32 phy_addr,
+static int pil_mba_verify_blob(struct pil_desc *pil, phys_addr_t phy_addr,
size_t size)
{
struct mba_data *drv = dev_get_drvdata(pil->dev);
@@ -494,16 +488,36 @@
return IRQ_HANDLED;
pr_err("Fatal error on the modem.\n");
+ subsys_set_crash_status(drv->subsys, true);
restart_modem(drv);
return IRQ_HANDLED;
}
+static irqreturn_t modem_stop_ack_intr_handler(int irq, void *dev_id)
+{
+ struct mba_data *drv = dev_id;
+ pr_info("Received stop ack interrupt from modem\n");
+ complete(&drv->stop_ack);
+ return IRQ_HANDLED;
+}
+
static int modem_shutdown(const struct subsys_desc *subsys)
{
struct mba_data *drv = subsys_to_drv(subsys);
+ unsigned long ret;
- if (!drv->is_loadable)
+ if (subsys->is_not_loadable)
return 0;
+
+ if (!subsys_get_crash_status(drv->subsys)) {
+ gpio_set_value(drv->force_stop_gpio, 1);
+ ret = wait_for_completion_timeout(&drv->stop_ack,
+ msecs_to_jiffies(STOP_ACK_TIMEOUT_MS));
+ if (!ret)
+ pr_warn("Timed out on stop ack from modem.\n");
+ gpio_set_value(drv->force_stop_gpio, 0);
+ }
+
pil_shutdown(&drv->desc);
pil_shutdown(&drv->q6->desc);
return 0;
@@ -514,13 +528,14 @@
struct mba_data *drv = subsys_to_drv(subsys);
int ret;
- if (!drv->is_loadable)
+ if (subsys->is_not_loadable)
return 0;
/*
* At this time, the modem is shutdown. Therefore this function cannot
* run concurrently with either the watchdog bite error handler or the
* SMSM callback, making it safe to unset the flag below.
*/
+ init_completion(&drv->stop_ack);
drv->ignore_errors = false;
ret = pil_boot(&drv->q6->desc);
if (ret)
@@ -535,7 +550,10 @@
{
struct mba_data *drv = subsys_to_drv(subsys);
drv->crash_shutdown = true;
- gpio_set_value(drv->force_stop_gpio, 1);
+ if (!subsys_get_crash_status(drv->subsys)) {
+ gpio_set_value(drv->force_stop_gpio, 1);
+ mdelay(STOP_ACK_TIMEOUT_MS);
+ }
}
static struct ramdump_segment smem_segments[] = {
@@ -592,6 +610,7 @@
if (drv->ignore_errors)
return IRQ_HANDLED;
pr_err("Watchdog bite received from modem software!\n");
+ subsys_set_crash_status(drv->subsys, true);
restart_modem(drv);
return IRQ_HANDLED;
}
@@ -601,9 +620,10 @@
int ret;
struct mba_data *drv = subsys_to_drv(desc);
- if (!drv->is_loadable)
+ if (desc->is_not_loadable)
return 0;
+ init_completion(&drv->stop_ack);
ret = pil_boot(&drv->q6->desc);
if (ret)
return ret;
@@ -624,7 +644,7 @@
{
struct mba_data *drv = subsys_to_drv(desc);
- if (!drv->is_loadable)
+ if (desc->is_not_loadable)
return;
pil_shutdown(&drv->desc);
@@ -650,6 +670,17 @@
drv->subsys_desc.start = mss_start;
drv->subsys_desc.stop = mss_stop;
+ ret = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,gpio-err-ready", 0);
+ if (ret < 0)
+ return ret;
+
+ ret = gpio_to_irq(ret);
+ if (ret < 0)
+ return ret;
+
+ drv->subsys_desc.err_ready_irq = ret;
+
drv->subsys = subsys_register(&drv->subsys_desc);
if (IS_ERR(drv->subsys)) {
ret = PTR_ERR(drv->subsys);
@@ -687,6 +718,14 @@
goto err_irq;
}
+ ret = devm_request_irq(&pdev->dev, drv->stop_ack_irq,
+ modem_stop_ack_intr_handler,
+ IRQF_TRIGGER_RISING, "pil-mss", drv);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Unable to register SMP2P stop ack handler!\n");
+ goto err_irq;
+ }
+
drv->adsp_state_notifier = subsys_notif_register_notifier("adsp",
&adsp_state_notifier_block);
if (IS_ERR(drv->adsp_state_notifier)) {
@@ -714,6 +753,7 @@
struct q6v5_data *q6;
struct pil_desc *q6_desc, *mba_desc;
struct resource *res;
+ struct property *prop;
int ret;
int clk_ready = of_get_named_gpio(pdev->dev.of_node,
@@ -751,31 +791,36 @@
if (!q6->restart_reg)
return -ENOMEM;
- q6->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
- if (IS_ERR(q6->vreg))
- return PTR_ERR(q6->vreg);
+ q6->vreg = NULL;
+
+ prop = of_find_property(pdev->dev.of_node, "vdd_mss-supply", NULL);
+ if (prop) {
+ q6->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
+ if (IS_ERR(q6->vreg))
+ return PTR_ERR(q6->vreg);
+
+ ret = regulator_set_voltage(q6->vreg, VDD_MSS_UV,
+ MAX_VDD_MSS_UV);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to set vreg voltage.\n");
+
+ ret = regulator_set_optimum_mode(q6->vreg, 100000);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to set vreg mode.\n");
+ return ret;
+ }
+ }
q6->vreg_mx = devm_regulator_get(&pdev->dev, "vdd_mx");
if (IS_ERR(q6->vreg_mx))
return PTR_ERR(q6->vreg_mx);
- ret = regulator_set_voltage(q6->vreg, VDD_MSS_UV, MAX_VDD_MSS_UV);
- if (ret)
- dev_err(&pdev->dev, "Failed to set regulator's voltage.\n");
-
- ret = regulator_set_optimum_mode(q6->vreg, 100000);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to set regulator's mode.\n");
- return ret;
- }
-
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"cxrail_bhs_reg");
if (res)
q6->cxrail_bhs = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
-
q6->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
if (IS_ERR(q6->ahb_clk))
return PTR_ERR(q6->ahb_clk);
@@ -815,16 +860,18 @@
static int __devinit pil_mss_driver_probe(struct platform_device *pdev)
{
struct mba_data *drv;
- int ret, err_fatal_gpio;
+ int ret, err_fatal_gpio, is_not_loadable, stop_ack_gpio;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
return -ENOMEM;
platform_set_drvdata(pdev, drv);
- drv->is_loadable = of_property_read_bool(pdev->dev.of_node,
- "qcom,is-loadable");
- if (drv->is_loadable) {
+ is_not_loadable = of_property_read_bool(pdev->dev.of_node,
+ "qcom,is-not-loadable");
+ if (is_not_loadable) {
+ drv->subsys_desc.is_not_loadable = 1;
+ } else {
ret = pil_mss_loadable_init(drv, pdev);
if (ret)
return ret;
@@ -840,6 +887,16 @@
if (drv->err_fatal_irq < 0)
return drv->err_fatal_irq;
+ stop_ack_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,gpio-stop-ack", 0);
+ if (stop_ack_gpio < 0)
+ return stop_ack_gpio;
+
+ ret = gpio_to_irq(stop_ack_gpio);
+ if (ret < 0)
+ return ret;
+ drv->stop_ack_irq = ret;
+
/* Get the GPIO pin for writing the outbound bits: add more as needed */
drv->force_stop_gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,gpio-force-stop", 0);
diff --git a/arch/arm/mach-msm/pil-riva.c b/arch/arm/mach-msm/pil-riva.c
index a2665b4..7fd76ab 100644
--- a/arch/arm/mach-msm/pil-riva.c
+++ b/arch/arm/mach-msm/pil-riva.c
@@ -24,6 +24,7 @@
#include <mach/subsystem_restart.h>
#include <mach/ramdump.h>
+#include <mach/msm_smem.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -134,7 +135,7 @@
u32 reg, sel;
struct riva_data *drv = dev_get_drvdata(pil->dev);
void __iomem *base = drv->base;
- unsigned long start_addr = pil_get_entry_addr(pil);
+ phys_addr_t start_addr = pil_get_entry_addr(pil);
void __iomem *cbase = drv->cbase;
bool use_cxo = cxo_is_needed(drv);
@@ -312,6 +313,7 @@
}
pr_err("riva: smsm state changed to smsm reset\n");
+ wcnss_riva_dump_pmic_regs();
smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_WCNSS0,
&smem_reset_size);
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index 5a6e66a..a054077 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -30,12 +30,16 @@
#include <linux/of_platform.h>
#include <linux/regulator/krait-regulator.h>
#include <linux/cpu.h>
+#include <linux/clk.h>
#include <mach/msm_iomap.h>
#include <mach/socinfo.h>
#include <mach/system.h>
#include <mach/scm.h>
#include <mach/socinfo.h>
+#define CREATE_TRACE_POINTS
+#include <mach/trace_msm_low_power.h>
#include <mach/msm-krait-l2-accessors.h>
+#include <mach/msm_bus.h>
#include <asm/cacheflush.h>
#include <asm/hardware/gic.h>
#include <asm/pgtable.h>
@@ -55,8 +59,8 @@
#include "timer.h"
#include "pm-boot.h"
#include <mach/event_timer.h>
-#define CREATE_TRACE_POINTS
-#include "trace_msm_low_power.h"
+#include <linux/cpu_pm.h>
+
#define SCM_L2_RETENTION (0x2)
#define SCM_CMD_TERMINATE_PC (0x2)
@@ -64,7 +68,6 @@
(container_of(attr, struct msm_pm_kobj_attribute, ka)->cpu)
#define SCLK_HZ (32768)
-#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000)
#define NUM_OF_COUNTERS 3
#define MAX_BUF_SIZE 512
@@ -127,10 +130,10 @@
static bool msm_pm_use_sync_timer;
static struct msm_pm_cp15_save_data cp15_data;
static bool msm_pm_retention_calls_tz;
-static uint32_t msm_pm_max_sleep_time;
static bool msm_no_ramp_down_pc;
static struct msm_pm_sleep_status_data *msm_pm_slp_sts;
static bool msm_pm_pc_reset_timer;
+static struct clk *pnoc_clk;
static int msm_pm_get_pc_mode(struct device_node *node,
const char *key, uint32_t *pc_mode_val)
@@ -405,39 +408,6 @@
return;
}
-/*
- * Convert time from nanoseconds to slow clock ticks, then cap it to the
- * specified limit
- */
-static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit)
-{
- do_div(time_ns, NSEC_PER_SEC / SCLK_HZ);
- return (time_ns > limit) ? limit : time_ns;
-}
-
-/*
- * Set the sleep time for suspend. 0 means infinite sleep time.
- */
-void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns)
-{
- if (max_sleep_time_ns == 0) {
- msm_pm_max_sleep_time = 0;
- } else {
- msm_pm_max_sleep_time =
- (uint32_t)msm_pm_convert_and_cap_time(
- max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT);
-
- if (msm_pm_max_sleep_time == 0)
- msm_pm_max_sleep_time = 1;
- }
-
- if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
- pr_info("%s: Requested %lld ns Giving %u sclk ticks\n",
- __func__, max_sleep_time_ns,
- msm_pm_max_sleep_time);
-}
-EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
-
static void msm_pm_save_cpu_reg(void)
{
int i;
@@ -515,6 +485,9 @@
pr_info("CPU%u: %s: notify_rpm %d\n",
cpu, __func__, (int) notify_rpm);
+ if (from_idle == true)
+ cpu_pm_enter();
+
ret = msm_spm_set_low_power_mode(
MSM_SPM_MODE_POWER_COLLAPSE, notify_rpm);
WARN_ON(ret);
@@ -547,6 +520,10 @@
ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
WARN_ON(ret);
+
+ if (from_idle == true)
+ cpu_pm_exit();
+
return collapsed;
}
@@ -706,7 +683,7 @@
u64 modified_time_ns = modified_time_us * NSEC_PER_USEC;
ktime_t modified_ktime = ns_to_ktime(modified_time_ns);
pm_hrtimer.function = pm_hrtimer_cb;
- hrtimer_start(&pm_hrtimer, modified_ktime, HRTIMER_MODE_ABS);
+ hrtimer_start(&pm_hrtimer, modified_ktime, HRTIMER_MODE_REL);
}
/******************************************************************************
@@ -869,9 +846,8 @@
int exit_stat = -1;
enum msm_pm_sleep_mode sleep_mode;
void *msm_pm_idle_rs_limits = NULL;
- int sleep_delay = 1;
+ uint32_t sleep_delay = 1;
int ret = -ENODEV;
- int64_t timer_expiration = 0;
int notify_rpm = false;
bool timer_halted = false;
@@ -890,11 +866,11 @@
time = ktime_to_ns(ktime_get());
if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) {
+ int64_t ns = msm_pm_timer_enter_idle();
notify_rpm = true;
- timer_expiration = msm_pm_timer_enter_idle();
+ do_div(ns, NSEC_PER_SEC / SCLK_HZ);
+ sleep_delay = (uint32_t)ns;
- sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
- timer_expiration, MSM_PM_SLEEP_TICK_LIMIT);
if (sleep_delay == 0) /* 0 would mean infinite time */
sleep_delay = 1;
}
@@ -918,7 +894,11 @@
case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
collapsed = msm_pm_power_collapse_standalone(true);
- exit_stat = MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
+ if (collapsed)
+ exit_stat = MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
+ else
+ exit_stat
+ = MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE;
break;
case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
@@ -928,7 +908,11 @@
collapsed = msm_pm_power_collapse(true);
timer_halted = true;
- exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
+ if (collapsed)
+ exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
+ else
+ exit_stat = MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE;
+
msm_pm_timer_exit_idle(timer_halted);
break;
@@ -964,13 +948,13 @@
int msm_pm_wait_cpu_shutdown(unsigned int cpu)
{
- int timeout = 10;
+ int timeout = 0;
if (!msm_pm_slp_sts)
return 0;
if (!msm_pm_slp_sts[cpu].base_addr)
return 0;
- while (timeout--) {
+ while (1) {
/*
* Check for the SPM of the core being hotplugged to set
* its sleep state.The SPM sleep state indicates that the
@@ -981,10 +965,10 @@
if (acc_sts & msm_pm_slp_sts[cpu].mask)
return 0;
udelay(100);
+ WARN(++timeout == 10, "CPU%u didn't collape within 1ms\n",
+ cpu);
}
- pr_info("%s(): Timed out waiting for CPU %u SPM to enter sleep state",
- __func__, cpu);
return -EBUSY;
}
@@ -1081,6 +1065,8 @@
void *rs_limits = NULL;
int ret = -ENODEV;
uint32_t power;
+ uint32_t msm_pm_max_sleep_time = 0;
+ int collapsed = 0;
if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
pr_info("%s: power collapse\n", __func__);
@@ -1090,8 +1076,8 @@
if (msm_pm_sleep_time_override > 0) {
int64_t ns = NSEC_PER_SEC *
(int64_t) msm_pm_sleep_time_override;
- msm_pm_set_max_sleep_time(ns);
- msm_pm_sleep_time_override = 0;
+ do_div(ns, NSEC_PER_SEC / SCLK_HZ);
+ msm_pm_max_sleep_time = (uint32_t) ns;
}
if (pm_sleep_ops.lowest_limits)
@@ -1104,7 +1090,7 @@
msm_pm_max_sleep_time,
rs_limits, false, true);
if (!ret) {
- int collapsed = msm_pm_power_collapse(false);
+ collapsed = msm_pm_power_collapse(false);
if (pm_sleep_ops.exit_sleep) {
pm_sleep_ops.exit_sleep(rs_limits,
false, true, collapsed);
@@ -1115,7 +1101,10 @@
__func__);
}
time = msm_pm_timer_exit_suspend(time, period);
- msm_pm_add_stat(MSM_PM_STAT_SUSPEND, time);
+ if (collapsed)
+ msm_pm_add_stat(MSM_PM_STAT_SUSPEND, time);
+ else
+ msm_pm_add_stat(MSM_PM_STAT_FAILED_SUSPEND, time);
} else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
pr_info("%s: standalone power collapse\n", __func__);
@@ -1143,10 +1132,55 @@
pm_sleep_ops = *ops;
}
+int msm_suspend_prepare(void)
+{
+ if (pnoc_clk != NULL)
+ clk_disable_unprepare(pnoc_clk);
+ return 0;
+}
+
+void msm_suspend_wake(void)
+{
+ if (pnoc_clk != NULL)
+ clk_prepare_enable(pnoc_clk);
+}
+
static const struct platform_suspend_ops msm_pm_ops = {
.enter = msm_pm_enter,
.valid = suspend_valid_only_mem,
+ .prepare_late = msm_suspend_prepare,
+ .wake = msm_suspend_wake,
};
+
+static int __devinit msm_pm_snoc_client_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ static struct msm_bus_scale_pdata *msm_pm_bus_pdata;
+ static uint32_t msm_pm_bus_client;
+
+ msm_pm_bus_pdata = msm_bus_cl_get_pdata(pdev);
+
+ if (msm_pm_bus_pdata) {
+ msm_pm_bus_client =
+ msm_bus_scale_register_client(msm_pm_bus_pdata);
+
+ if (!msm_pm_bus_client) {
+ pr_err("%s: Failed to register SNOC client",
+ __func__);
+ rc = -ENXIO;
+ goto snoc_cl_probe_done;
+ }
+
+ rc = msm_bus_scale_client_update_request(msm_pm_bus_client, 1);
+
+ if (rc)
+ pr_err("%s: Error setting bus rate", __func__);
+ }
+
+snoc_cl_probe_done:
+ return rc;
+}
+
static int __devinit msm_cpu_status_probe(struct platform_device *pdev)
{
struct msm_pm_sleep_status_data *pdata;
@@ -1235,6 +1269,21 @@
},
};
+static struct of_device_id msm_snoc_clnt_match_tbl[] = {
+ {.compatible = "qcom,pm-snoc-client"},
+ {},
+};
+
+static struct platform_driver msm_cpu_pm_snoc_client_driver = {
+ .probe = msm_pm_snoc_client_probe,
+ .driver = {
+ .name = "pm_snoc_client",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_snoc_clnt_match_tbl,
+ },
+};
+
+
static int __init msm_pm_setup_saved_state(void)
{
pgd_t *pc_pgd;
@@ -1285,24 +1334,19 @@
static void setup_broadcast_timer(void *arg)
{
- unsigned long reason = (unsigned long)arg;
int cpu = smp_processor_id();
- reason = reason ?
- CLOCK_EVT_NOTIFY_BROADCAST_ON : CLOCK_EVT_NOTIFY_BROADCAST_OFF;
-
- clockevents_notify(reason, &cpu);
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu);
}
static int setup_broadcast_cpuhp_notify(struct notifier_block *n,
unsigned long action, void *hcpu)
{
- int hotcpu = (unsigned long)hcpu;
+ int cpu = (unsigned long)hcpu;
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_ONLINE:
- smp_call_function_single(hotcpu, setup_broadcast_timer,
- (void *)true, 1);
+ smp_call_function_single(cpu, setup_broadcast_timer, NULL, 1);
break;
}
@@ -1325,14 +1369,11 @@
msm_pm_mode_sysfs_add();
msm_pm_add_stats(enable_stats, ARRAY_SIZE(enable_stats));
suspend_set_ops(&msm_pm_ops);
- hrtimer_init(&pm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ hrtimer_init(&pm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
msm_cpuidle_init();
if (msm_pm_pc_reset_timer) {
- get_cpu();
- smp_call_function_many(cpu_online_mask, setup_broadcast_timer,
- (void *)true, 1);
- put_cpu();
+ on_each_cpu(setup_broadcast_timer, NULL, 1);
register_cpu_notifier(&setup_broadcast_notifier);
}
@@ -1405,7 +1446,7 @@
if (!data)
return -EINVAL;
- if (!bufu || count < 0)
+ if (!bufu)
return -EINVAL;
if (!access_ok(VERIFY_WRITE, bufu, count))
@@ -1575,14 +1616,31 @@
{
int rc;
- rc = platform_driver_register(&msm_cpu_status_driver);
+ rc = platform_driver_register(&msm_cpu_pm_snoc_client_driver);
if (rc) {
pr_err("%s(): failed to register driver %s\n", __func__,
- msm_cpu_status_driver.driver.name);
+ msm_cpu_pm_snoc_client_driver.driver.name);
return rc;
}
+ pnoc_clk = clk_get_sys("pm_8x60", "bus_clk");
+
+ if (IS_ERR(pnoc_clk))
+ pnoc_clk = NULL;
+ else {
+ clk_set_rate(pnoc_clk, 19200000);
+ rc = clk_prepare_enable(pnoc_clk);
+
+ if (rc)
+ pr_err("%s: PNOC clock enable failed\n", __func__);
+ }
+
return platform_driver_register(&msm_pm_8x60_driver);
}
device_initcall(msm_pm_8x60_init);
+
+void __init msm_pm_sleep_status_init(void)
+{
+ platform_driver_register(&msm_cpu_status_driver);
+}
diff --git a/arch/arm/mach-msm/pm-data.c b/arch/arm/mach-msm/pm-data.c
index 249032f..f41c569 100644
--- a/arch/arm/mach-msm/pm-data.c
+++ b/arch/arm/mach-msm/pm-data.c
@@ -125,4 +125,11 @@
.idle_enabled = 1,
.suspend_enabled = 0,
},
+
+ [MSM_PM_MODE(3, MSM_PM_SLEEP_MODE_NR)] = {
+ .idle_supported = 0,
+ .suspend_supported = 0,
+ .idle_enabled = 0,
+ .suspend_enabled = 0,
+ },
};
diff --git a/arch/arm/mach-msm/pm.h b/arch/arm/mach-msm/pm.h
index 8a043d8..f2fc80b 100644
--- a/arch/arm/mach-msm/pm.h
+++ b/arch/arm/mach-msm/pm.h
@@ -136,10 +136,12 @@
void msm_pm_set_rpm_wakeup_irq(unsigned int irq);
void msm_pm_set_sleep_ops(struct msm_pm_sleep_ops *ops);
int msm_pm_wait_cpu_shutdown(unsigned int cpu);
+void __init msm_pm_sleep_status_init(void);
#else
static inline void msm_pm_set_rpm_wakeup_irq(unsigned int irq) {}
static inline void msm_pm_set_sleep_ops(struct msm_pm_sleep_ops *ops) {}
static inline int msm_pm_wait_cpu_shutdown(unsigned int cpu) { return 0; }
+static inline void msm_pm_sleep_status_init(void) {};
#endif
#ifdef CONFIG_HOTPLUG_CPU
int msm_platform_secondary_init(unsigned int cpu);
diff --git a/arch/arm/mach-msm/pm2.c b/arch/arm/mach-msm/pm2.c
index a2da8b0..9f97a59 100644
--- a/arch/arm/mach-msm/pm2.c
+++ b/arch/arm/mach-msm/pm2.c
@@ -3,7 +3,7 @@
* MSM Power Management Routines
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2008-2012 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008-2013 The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -44,9 +44,10 @@
#endif
#include <mach/socinfo.h>
#include <mach/proc_comm.h>
+#include <mach/msm_smem.h>
+#include <mach/msm_smsm.h>
#include <asm/smp_scu.h>
-#include "smd_private.h"
#include "smd_rpcrouter.h"
#include "acpuclock.h"
#include "clock.h"
diff --git a/arch/arm/mach-msm/qdsp6v2/apr.c b/arch/arm/mach-msm/qdsp6v2/apr.c
index 6e60db1..8d9ad29 100644
--- a/arch/arm/mach-msm/qdsp6v2/apr.c
+++ b/arch/arm/mach-msm/qdsp6v2/apr.c
@@ -118,7 +118,7 @@
},
{
.name = "LSM",
- .idx = 9,
+ .idx = 10,
.id = APR_SVC_LSM,
.client_id = APR_CLIENT_AUDIO,
},
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
index ea22c12..b5ccc31 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
@@ -786,7 +786,7 @@
goto done;
}
- if (size <= 0) {
+ if ((size <= 0) || (size > sizeof(data))) {
pr_err("%s: Invalid size sent to driver: %d\n",
__func__, size);
result = -EFAULT;
diff --git a/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c b/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
index c9bc3d7..5303009 100644
--- a/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
+++ b/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
@@ -22,10 +22,15 @@
#include <linux/of_device.h>
#include <linux/msm_audio_ion.h>
+#include <linux/iommu.h>
+#include <mach/iommu_domains.h>
+
struct msm_audio_ion_private {
bool smmu_enabled;
- /*u32 group_id;*/
- /*u32 domain_id;*/
+ bool audioheap_enabled;
+ struct iommu_group *group;
+ u32 domain_id;
+ struct iommu_domain *domain;
};
static struct msm_audio_ion_private msm_audio_ion_data = {0,};
@@ -49,10 +54,22 @@
goto err;
}
- *handle = ion_alloc(*client, bufsz, SZ_4K, (0x1<<ION_AUDIO_HEAP_ID), 0);
+ *handle = ion_alloc(*client, bufsz, SZ_4K,
+ ION_HEAP(ION_AUDIO_HEAP_ID), 0);
if (IS_ERR_OR_NULL((void *) (*handle))) {
- pr_err("%s: ION memory allocation for AUDIO failed\n",
- __func__);
+ pr_debug("system heap is used");
+ msm_audio_ion_data.audioheap_enabled = 0;
+ *handle = ion_alloc(*client, bufsz, SZ_4K,
+ ION_HEAP(ION_SYSTEM_HEAP_ID), 0);
+
+ } else {
+ pr_debug("audio heap is used");
+ msm_audio_ion_data.audioheap_enabled = 1;
+ }
+
+ if (IS_ERR_OR_NULL((void *) (*handle))) {
+ pr_err("%s: ION memory allocation for AUDIO failed rc=%d, smmu_enabled=%d\n",
+ __func__, rc, msm_audio_ion_data.smmu_enabled);
goto err_ion_client;
}
@@ -63,15 +80,17 @@
goto err_ion_handle;
}
- /*Need to add condition SMMU enable or not */
*vaddr = ion_map_kernel(*client, *handle);
if (IS_ERR_OR_NULL((void *)*vaddr)) {
pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
goto err_ion_handle;
}
+ pr_debug("%s: mapped address = %p, size=%d\n", __func__, *vaddr, bufsz);
- if (bufsz != 0)
+ if (bufsz != 0) {
+ pr_debug("%s: memset to 0 %p %d\n", __func__, *vaddr, bufsz);
memset((void *)*vaddr, 0, bufsz);
+ }
return 0;
@@ -81,7 +100,6 @@
msm_audio_ion_client_destroy(*client);
err:
return -EINVAL;
-
}
int msm_audio_ion_import(const char *name, struct ion_client **client,
@@ -125,13 +143,6 @@
goto err_ion_handle;
}
- /*Need to add condition SMMU enable or not */
- *vaddr = ion_map_kernel(*client, *handle);
- if (IS_ERR_OR_NULL((void *)*vaddr)) {
- pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
- goto err_ion_handle;
- }
-
if (bufsz != 0)
memset((void *)*vaddr, 0, bufsz);
@@ -142,12 +153,20 @@
msm_audio_ion_client_destroy(*client);
err:
return -EINVAL;
-
}
int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle)
{
- /* add condition for SMMU enabled */
+ if (msm_audio_ion_data.smmu_enabled) {
+ /* Need to populate book kept infomation */
+ pr_debug("client=%p, domain=%p, domain_id=%d, group=%p",
+ client, msm_audio_ion_data.domain,
+ msm_audio_ion_data.domain_id, msm_audio_ion_data.group);
+
+ ion_unmap_iommu(client, handle,
+ msm_audio_ion_data.domain_id, 0);
+ }
+
ion_unmap_kernel(client, handle);
ion_free(client, handle);
@@ -155,6 +174,91 @@
return 0;
}
+int msm_audio_ion_mmap(struct audio_buffer *ab,
+ struct vm_area_struct *vma)
+{
+ struct sg_table *table;
+ unsigned long addr = vma->vm_start;
+ unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
+ struct scatterlist *sg;
+ unsigned int i;
+ struct page *page;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+
+ table = ion_sg_table(ab->client, ab->handle);
+
+ if (IS_ERR(table)) {
+ pr_err("%s: Unable to get sg_table from ion: %ld\n",
+ __func__, PTR_ERR(table));
+ return PTR_ERR(table);
+ } else if (!table) {
+ pr_err("%s: sg_list is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ /* uncached */
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ /* We need to check if a page is associated with this sg list because:
+ * If the allocation came from a carveout we currently don't have
+ * pages associated with carved out memory. This might change in the
+ * future and we can remove this check and the else statement.
+ */
+ page = sg_page(table->sgl);
+ if (page) {
+ pr_debug("%s: page is NOT null\n", __func__);
+ for_each_sg(table->sgl, sg, table->nents, i) {
+ unsigned long remainder = vma->vm_end - addr;
+ unsigned long len = sg_dma_len(sg);
+
+ page = sg_page(sg);
+
+ if (offset >= sg_dma_len(sg)) {
+ offset -= sg_dma_len(sg);
+ continue;
+ } else if (offset) {
+ page += offset / PAGE_SIZE;
+ len = sg_dma_len(sg) - offset;
+ offset = 0;
+ }
+ len = min(len, remainder);
+ pr_debug("vma=%p, addr=%x len=%ld vm_start=%x vm_end=%x vm_page_prot=%ld\n",
+ vma, (unsigned int)addr, len,
+ (unsigned int)vma->vm_start,
+ (unsigned int)vma->vm_end,
+ (unsigned long int)vma->vm_page_prot);
+ remap_pfn_range(vma, addr, page_to_pfn(page), len,
+ vma->vm_page_prot);
+ addr += len;
+ if (addr >= vma->vm_end)
+ return 0;
+ }
+ } else {
+ ion_phys_addr_t phys_addr;
+ size_t phys_len;
+ pr_debug("%s: page is NULL\n", __func__);
+
+ ret = ion_phys(ab->client, ab->handle, &phys_addr, &phys_len);
+ if (ret) {
+ pr_err("%s: Unable to get phys address from ION buffer: %d\n"
+ , __func__ , ret);
+ return ret;
+ }
+ pr_debug("phys=%x len=%d\n", (unsigned int)phys_addr, phys_len);
+ pr_debug("vma=%p, vm_start=%x vm_end=%x vm_pgoff=%ld vm_page_prot=%ld\n",
+ vma, (unsigned int)vma->vm_start,
+ (unsigned int)vma->vm_end, vma->vm_pgoff,
+ (unsigned long int)vma->vm_page_prot);
+ ret = remap_pfn_range(vma, vma->vm_start,
+ __phys_to_pfn(phys_addr) + vma->vm_pgoff,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot);
+ }
+ return 0;
+}
+
bool msm_audio_ion_is_smmu_available(void)
{
@@ -165,18 +269,17 @@
struct ion_client *msm_audio_ion_client_create(unsigned int heap_mask,
const char *name)
{
- pr_debug("%s: smmu_enabled = %d\n", __func__,
- msm_audio_ion_data.smmu_enabled);
-
-
- return msm_ion_client_create(heap_mask, name);
+ struct ion_client *pclient = NULL;
+ /*IOMMU group and domain are moved to probe()*/
+ pclient = msm_ion_client_create(heap_mask, name);
+ return pclient;
}
void msm_audio_ion_client_destroy(struct ion_client *client)
{
- pr_debug("%s: smmu_enabled = %d\n", __func__,
- msm_audio_ion_data.smmu_enabled);
+ pr_debug("%s: client = %p smmu_enabled = %d\n", __func__,
+ client, msm_audio_ion_data.smmu_enabled);
ion_client_destroy(client);
}
@@ -192,9 +295,9 @@
bufsz should be 0 and fd shouldn't be 0 as of now
*/
*handle = ion_import_dma_buf(client, fd);
- pr_err("%s: DMA Buf name=%s, fd=%d handle=%p\n", __func__,
+ pr_debug("%s: DMA Buf name=%s, fd=%d handle=%p\n", __func__,
name, fd, *handle);
- if (IS_ERR_OR_NULL((void *) (*handle))) {
+ if (IS_ERR_OR_NULL((void *)(*handle))) {
pr_err("%s: ion import dma buffer failed\n",
__func__);
goto err_ion_handle;
@@ -245,6 +348,41 @@
return 0;
}
+int msm_audio_ion_cache_operations(struct audio_buffer *abuff, int cache_op)
+{
+ unsigned long ionflag = 0;
+ int rc = 0;
+ int msm_cache_ops = 0;
+
+ if (!abuff) {
+ pr_err("Invalid params: %p, %p\n", __func__, abuff);
+ return -EINVAL;
+ }
+ rc = ion_handle_get_flags(abuff->client, abuff->handle,
+ &ionflag);
+ if (rc) {
+ pr_err("ion_handle_get_flags failed: %d\n", rc);
+ goto cache_op_failed;
+ }
+
+ /* has to be CACHED */
+ if (ION_IS_CACHED(ionflag)) {
+ /* ION_IOC_INV_CACHES or ION_IOC_CLEAN_CACHES */
+ msm_cache_ops = cache_op;
+ rc = msm_ion_do_cache_op(abuff->client,
+ abuff->handle,
+ (unsigned long *) abuff->data,
+ (unsigned long)abuff->size,
+ msm_cache_ops);
+ if (rc) {
+ pr_err("cache operation failed %d\n", rc);
+ goto cache_op_failed;
+ }
+ }
+cache_op_failed:
+ return rc;
+}
+
static int msm_audio_ion_get_phys(struct ion_client *client,
struct ion_handle *handle,
@@ -255,18 +393,25 @@
msm_audio_ion_data.smmu_enabled);
if (msm_audio_ion_data.smmu_enabled) {
- /* SMMU enabled case ion_map_iommu()*/
+ rc = ion_map_iommu(client, handle, msm_audio_ion_data.domain_id,
+ 0 /*partition_num*/, SZ_4K /*align*/, 0/*iova_length*/,
+ addr, (unsigned long *)len,
+ 0, 0);
+ if (rc) {
+ pr_err("%s: ION map iommu failed %d\n", __func__, rc);
+ return rc;
+ }
+ pr_debug("client=%p, domain=%p, domain_id=%d, group=%p",
+ client, msm_audio_ion_data.domain,
+ msm_audio_ion_data.domain_id, msm_audio_ion_data.group);
} else {
/* SMMU is disabled*/
rc = ion_phys(client, handle, addr, len);
}
- pr_debug("%s: addr= 0x%p, len= %d\n", __func__, addr, *len);
+ pr_debug("phys=%x, len=%d, rc=%d\n", (unsigned int)*addr, *len, rc);
return rc;
}
-
-
-
static int msm_audio_ion_probe(struct platform_device *pdev)
{
int rc = 0;
@@ -283,13 +428,53 @@
msm_audio_ion_dt);
msm_audio_ion_data.smmu_enabled = smmu_enabled;
+ if (smmu_enabled) {
+ msm_audio_ion_data.group = iommu_group_find("lpass_audio");
+ if (!msm_audio_ion_data.group) {
+ pr_debug("Failed to find group lpass_audio deferred\n");
+ goto fail_group;
+ }
+ msm_audio_ion_data.domain =
+ iommu_group_get_iommudata(msm_audio_ion_data.group);
+ if (IS_ERR_OR_NULL(msm_audio_ion_data.domain)) {
+ pr_err("Failed to get domain data for group %p",
+ msm_audio_ion_data.group);
+ goto fail_group;
+ }
+ msm_audio_ion_data.domain_id =
+ msm_find_domain_no(msm_audio_ion_data.domain);
+ if (msm_audio_ion_data.domain_id < 0) {
+ pr_err("Failed to get domain index for domain %p",
+ msm_audio_ion_data.domain);
+ goto fail_group;
+ }
+ pr_debug("domain=%p, domain_id=%d, group=%p",
+ msm_audio_ion_data.domain,
+ msm_audio_ion_data.domain_id, msm_audio_ion_data.group);
+
+ /* iommu_attach_group() will make AXI clock ON. For future PL
+ this will require to be called in once per session */
+ rc = iommu_attach_group(msm_audio_ion_data.domain,
+ msm_audio_ion_data.group);
+ if (rc) {
+ pr_err("%s:ION attach group failed %d\n", __func__, rc);
+ return rc;
+ }
+
+ }
+
pr_debug("%s: SMMU-Enabled = %d\n", __func__, smmu_enabled);
return rc;
+
+fail_group:
+ return -EPROBE_DEFER;
}
static int msm_audio_ion_remove(struct platform_device *pdev)
{
- pr_debug("%s: msm audio ion is unloaded\n", __func__);
+ pr_debug("%s: msm audio ion is unloaded, domain=%p, group=%p\n",
+ __func__, msm_audio_ion_data.domain, msm_audio_ion_data.group);
+ iommu_detach_group(msm_audio_ion_data.domain, msm_audio_ion_data.group);
return 0;
}
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
index ff7ba33..a3af3e78 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
@@ -17,7 +17,7 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/msm_audio.h>
-#include <sound/apr_audio.h>
+#include <sound/apr_audio-v2.h>
#include <mach/qdsp6v2/apr_us_b.h>
#include "q6usm.h"
diff --git a/arch/arm/mach-msm/remote_spinlock.c b/arch/arm/mach-msm/remote_spinlock.c
index 94923a0..a9ebd7c 100644
--- a/arch/arm/mach-msm/remote_spinlock.c
+++ b/arch/arm/mach-msm/remote_spinlock.c
@@ -25,6 +25,7 @@
#include <mach/msm_iomap.h>
#include <mach/remote_spinlock.h>
#include <mach/dal.h>
+#include <mach/msm_smem.h>
#include "smd_private.h"
@@ -143,6 +144,7 @@
}
/* end dekkers implementation ----------------------------------------------- */
+#ifndef CONFIG_THUMB2_KERNEL
/* swp implementation ------------------------------------------------------- */
static void __raw_remote_swp_spin_lock(raw_remote_spinlock_t *lock)
{
@@ -194,6 +196,7 @@
: "cc");
}
/* end swp implementation --------------------------------------------------- */
+#endif
/* ldrex implementation ----------------------------------------------------- */
static char *ldrex_compatible_string = "qcom,ipc-spinlock-ldrex";
@@ -431,6 +434,7 @@
current_ops.owner = __raw_remote_dek_spin_owner;
is_hw_lock_type = 0;
break;
+#ifndef CONFIG_THUMB2_KERNEL
case SWP_MODE:
current_ops.lock = __raw_remote_swp_spin_lock;
current_ops.unlock = __raw_remote_swp_spin_unlock;
@@ -439,6 +443,7 @@
current_ops.owner = __raw_remote_gen_spin_owner;
is_hw_lock_type = 0;
break;
+#endif
case LDREX_MODE:
current_ops.lock = __raw_remote_ex_spin_lock;
current_ops.unlock = __raw_remote_ex_spin_unlock;
diff --git a/arch/arm/mach-msm/rmt_storage_client.c b/arch/arm/mach-msm/rmt_storage_client.c
index a4562e9..550624c 100644
--- a/arch/arm/mach-msm/rmt_storage_client.c
+++ b/arch/arm/mach-msm/rmt_storage_client.c
@@ -35,7 +35,7 @@
#ifdef CONFIG_MSM_SDIO_SMEM
#include <mach/sdio_smem.h>
#endif
-#include "smd_private.h"
+#include <mach/msm_smem.h>
enum {
RMT_STORAGE_EVNT_OPEN = 0,
diff --git a/arch/arm/mach-msm/rpm-smd.c b/arch/arm/mach-msm/rpm-smd.c
index 6ed80f6..38ed867 100644
--- a/arch/arm/mach-msm/rpm-smd.c
+++ b/arch/arm/mach-msm/rpm-smd.c
@@ -36,9 +36,9 @@
#include <mach/socinfo.h>
#include <mach/msm_smd.h>
#include <mach/rpm-smd.h>
-#include "rpm-notifier.h"
#define CREATE_TRACE_POINTS
-#include "trace_rpm_smd.h"
+#include <mach/trace_rpm_smd.h>
+#include "rpm-notifier.h"
/* Debug Definitions */
enum {
@@ -1349,7 +1349,8 @@
smd_disable_read_intr(msm_rpm_data.ch_info);
if (!standalone) {
- msm_rpm_smd_wq = create_singlethread_workqueue("rpm-smd");
+ msm_rpm_smd_wq = alloc_workqueue("rpm-smd",
+ WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1);
if (!msm_rpm_smd_wq)
return -EINVAL;
queue_work(msm_rpm_smd_wq, &msm_rpm_data.work);
diff --git a/arch/arm/mach-msm/rpm_log.c b/arch/arm/mach-msm/rpm_log.c
index 53d5752..58e8588 100644
--- a/arch/arm/mach-msm/rpm_log.c
+++ b/arch/arm/mach-msm/rpm_log.c
@@ -213,7 +213,7 @@
return -EINVAL;
if (!buf->data)
return -ENOMEM;
- if (!bufu || count < 0)
+ if (!bufu || count == 0)
return -EINVAL;
if (!access_ok(VERIFY_WRITE, bufu, count))
return -EFAULT;
diff --git a/arch/arm/mach-msm/sdio_al_dloader.c b/arch/arm/mach-msm/sdio_al_dloader.c
index b0cb88f..f3effa8 100644
--- a/arch/arm/mach-msm/sdio_al_dloader.c
+++ b/arch/arm/mach-msm/sdio_al_dloader.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, 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
@@ -228,6 +228,7 @@
static unsigned long lock_flags2;
static atomic_t sdio_dld_in_use = ATOMIC_INIT(0);
+static atomic_t sdio_dld_setup_done = ATOMIC_INIT(0);
/*
* sdio_op_mode sets the operation mode of the sdio_dloader -
@@ -2388,6 +2389,14 @@
if (atomic_read(&sdio_dld_in_use) == 1)
return -EBUSY;
+ /*
+ * If the setup is already complete tear down the existing
+ * one and reinitialize. This might happen during modem restarts
+ * in boot phase.
+ */
+ if (atomic_read(&sdio_dld_setup_done) == 1)
+ sdio_dld_tear_down(NULL);
+
if (num_of_devices == 0 || num_of_devices > MAX_NUM_DEVICES) {
pr_err(MODULE_NAME ": %s - invalid number of devices\n",
__func__);
@@ -2478,6 +2487,7 @@
pr_err(MODULE_NAME ": %s - tty_register_device() "
"failed\n", __func__);
tty_unregister_driver(sdio_dld->tty_drv);
+ put_tty_driver(sdio_dld->tty_drv);
kfree(sdio_dld);
return PTR_ERR(tty_dev);
}
@@ -2518,6 +2528,7 @@
goto exit_err;
}
+ atomic_set(&sdio_dld_setup_done, 1);
return 0;
exit_err:
@@ -2526,7 +2537,9 @@
if (result)
pr_err(MODULE_NAME ": %s - tty_unregister_driver() "
"failed. result=%d\n", __func__, -result);
+ put_tty_driver(sdio_dld->tty_drv);
kfree(sdio_dld);
+ atomic_set(&sdio_dld_setup_done, 0);
return status;
}
@@ -2534,22 +2547,24 @@
{
int status = 0;
- del_timer_sync(&sdio_dld->timer);
- del_timer_sync(&sdio_dld->push_timer);
-
- sdio_dld_dealloc_local_buffers();
+ if (atomic_read(&sdio_dld_in_use) == 1) {
+ del_timer_sync(&sdio_dld->timer);
+ del_timer_sync(&sdio_dld->push_timer);
+ sdio_dld_dealloc_local_buffers();
+ }
tty_unregister_device(sdio_dld->tty_drv, 0);
status = tty_unregister_driver(sdio_dld->tty_drv);
-
if (status) {
pr_err(MODULE_NAME ": %s - tty_unregister_driver() failed\n",
__func__);
}
+ put_tty_driver(sdio_dld->tty_drv);
kfree(sdio_dld);
atomic_set(&sdio_dld_in_use, 0);
+ atomic_set(&sdio_dld_setup_done, 0);
}
MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index 3590e6b..4649390 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -47,11 +47,13 @@
#include <mach/msm_ipc_logging.h>
#include <mach/ramdump.h>
#include <mach/board.h>
+#include <mach/msm_smem.h>
#include <asm/cacheflush.h>
#include "smd_private.h"
#include "modem_notifier.h"
+#include "smem_private.h"
#if defined(CONFIG_ARCH_QSD8X50) || defined(CONFIG_ARCH_MSM8X60) \
|| defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_FSM9XXX) \
@@ -71,7 +73,6 @@
#endif
#define MODULE_NAME "msm_smd"
-#define SMEM_VERSION 0x000B
#define SMD_VERSION 0x00020000
#define SMSM_SNAPSHOT_CNT 64
#define SMSM_SNAPSHOT_SIZE ((SMSM_NUM_ENTRIES + 1) * 4)
@@ -175,16 +176,6 @@
},
};
-struct smem_area {
- phys_addr_t phys_addr;
- resource_size_t size;
- void __iomem *virt_addr;
-};
-static uint32_t num_smem_areas;
-static struct smem_area *smem_areas;
-static struct ramdump_segment *smem_ramdump_segments;
-static void *smem_ramdump_dev;
-static void *smem_phys_to_virt(phys_addr_t base, unsigned offset);
static void *smd_dev;
struct interrupt_stat interrupt_stats[NUM_SMD_SUBSYSTEMS];
@@ -380,9 +371,6 @@
#define SMD_LOOPBACK_CID 100
-#define SMEM_SPINLOCK_SMEM_ALLOC "S:3"
-static remote_spinlock_t remote_spinlock;
-
static LIST_HEAD(smd_ch_list_loopback);
static void smd_fake_irq_handler(unsigned long arg);
static void smsm_cb_snapshot(uint32_t use_wakelock);
@@ -392,7 +380,6 @@
static DECLARE_WORK(smsm_cb_work, notify_smsm_cb_clients_worker);
static DEFINE_MUTEX(smsm_lock);
static struct smsm_state_info *smsm_states;
-static int spinlocks_initialized;
/**
* Variables to indicate smd module initialization.
@@ -2408,222 +2395,6 @@
}
EXPORT_SYMBOL(smd_is_pkt_avail);
-
-/* -------------------------------------------------------------------------- */
-
-/**
- * smem_phys_to_virt() - Convert a physical base and offset to virtual address
- *
- * @base: physical base address to check
- * @offset: offset from the base to get the final address
- * @returns: virtual SMEM address; NULL for failure
- *
- * Takes a physical address and an offset and checks if the resulting physical
- * address would fit into one of the smem regions. If so, returns the
- * corresponding virtual address. Otherwise returns NULL.
- */
-static void *smem_phys_to_virt(phys_addr_t base, unsigned offset)
-{
- int i;
- phys_addr_t phys_addr;
- resource_size_t size;
-
- if (OVERFLOW_ADD_UNSIGNED(phys_addr_t, base, offset))
- return NULL;
-
- if (!smem_areas) {
- /*
- * Early boot - no area configuration yet, so default
- * to using the main memory region.
- *
- * To remove the MSM_SHARED_RAM_BASE and the static
- * mapping of SMEM in the future, add dump_stack()
- * to identify the early callers of smem_get_entry()
- * (which calls this function) and replace those calls
- * with a new function that knows how to lookup the
- * SMEM base address before SMEM has been probed.
- */
- phys_addr = msm_shared_ram_phys;
- size = MSM_SHARED_RAM_SIZE;
-
- if (base >= phys_addr && base + offset < phys_addr + size) {
- if (OVERFLOW_ADD_UNSIGNED(uintptr_t,
- (uintptr_t)MSM_SHARED_RAM_BASE, offset)) {
- pr_err("%s: overflow %p %x\n", __func__,
- MSM_SHARED_RAM_BASE, offset);
- return NULL;
- }
-
- return MSM_SHARED_RAM_BASE + offset;
- } else {
- return NULL;
- }
- }
- for (i = 0; i < num_smem_areas; ++i) {
- phys_addr = smem_areas[i].phys_addr;
- size = smem_areas[i].size;
-
- if (base < phys_addr || base + offset >= phys_addr + size)
- continue;
-
- if (OVERFLOW_ADD_UNSIGNED(uintptr_t,
- (uintptr_t)smem_areas[i].virt_addr, offset)) {
- pr_err("%s: overflow %p %x\n", __func__,
- smem_areas[i].virt_addr, offset);
- return NULL;
- }
-
- return smem_areas[i].virt_addr + offset;
- }
-
- return NULL;
-}
-
-/**
- * smem_virt_to_phys() - Convert SMEM address to physical address.
- *
- * @smem_address: Address of SMEM item (returned by smem_alloc(), etc)
- * @returns: Physical address (or NULL if there is a failure)
- *
- * This function should only be used if an SMEM item needs to be handed
- * off to a DMA engine.
- */
-phys_addr_t smem_virt_to_phys(void *smem_address)
-{
- phys_addr_t phys_addr = 0;
- int i;
- void *vend;
-
- if (!smem_areas)
- return phys_addr;
-
- for (i = 0; i < num_smem_areas; ++i) {
- vend = (void *)(smem_areas[i].virt_addr + smem_areas[i].size);
-
- if (smem_address >= smem_areas[i].virt_addr &&
- smem_address < vend) {
- phys_addr = smem_address - smem_areas[i].virt_addr;
- phys_addr += smem_areas[i].phys_addr;
- break;
- }
- }
-
- return phys_addr;
-}
-EXPORT_SYMBOL(smem_virt_to_phys);
-
-/* smem_alloc returns the pointer to smem item if it is already allocated.
- * Otherwise, it returns NULL.
- */
-void *smem_alloc(unsigned id, unsigned size)
-{
- return smem_find(id, size);
-}
-EXPORT_SYMBOL(smem_alloc);
-
-/* smem_alloc2 returns the pointer to smem item. If it is not allocated,
- * it allocates it and then returns the pointer to it.
- */
-void *smem_alloc2(unsigned id, unsigned size_in)
-{
- struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
- struct smem_heap_entry *toc = shared->heap_toc;
- unsigned long flags;
- void *ret = NULL;
-
- if (!shared->heap_info.initialized) {
- pr_err("%s: smem heap info not initialized\n", __func__);
- return NULL;
- }
-
- if (id >= SMEM_NUM_ITEMS)
- return NULL;
-
- size_in = ALIGN(size_in, 8);
- remote_spin_lock_irqsave(&remote_spinlock, flags);
- if (toc[id].allocated) {
- SMD_DBG("%s: %u already allocated\n", __func__, id);
- if (size_in != toc[id].size)
- pr_err("%s: wrong size %u (expected %u)\n",
- __func__, toc[id].size, size_in);
- else
- ret = (void *)(MSM_SHARED_RAM_BASE + toc[id].offset);
- } else if (id > SMEM_FIXED_ITEM_LAST) {
- SMD_DBG("%s: allocating %u\n", __func__, id);
- if (shared->heap_info.heap_remaining >= size_in) {
- toc[id].offset = shared->heap_info.free_offset;
- toc[id].size = size_in;
- wmb();
- toc[id].allocated = 1;
-
- shared->heap_info.free_offset += size_in;
- shared->heap_info.heap_remaining -= size_in;
- ret = (void *)(MSM_SHARED_RAM_BASE + toc[id].offset);
- } else
- pr_err("%s: not enough memory %u (required %u)\n",
- __func__, shared->heap_info.heap_remaining,
- size_in);
- }
- wmb();
- remote_spin_unlock_irqrestore(&remote_spinlock, flags);
- return ret;
-}
-EXPORT_SYMBOL(smem_alloc2);
-
-void *smem_get_entry(unsigned id, unsigned *size)
-{
- struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
- struct smem_heap_entry *toc = shared->heap_toc;
- int use_spinlocks = spinlocks_initialized;
- void *ret = 0;
- unsigned long flags = 0;
-
- if (id >= SMEM_NUM_ITEMS)
- return ret;
-
- if (use_spinlocks)
- remote_spin_lock_irqsave(&remote_spinlock, flags);
- /* toc is in device memory and cannot be speculatively accessed */
- if (toc[id].allocated) {
- phys_addr_t phys_base;
-
- *size = toc[id].size;
- barrier();
-
- phys_base = toc[id].reserved & BASE_ADDR_MASK;
- if (!phys_base)
- phys_base = (phys_addr_t)msm_shared_ram_phys;
- ret = smem_phys_to_virt(phys_base, toc[id].offset);
- } else {
- *size = 0;
- }
- if (use_spinlocks)
- remote_spin_unlock_irqrestore(&remote_spinlock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(smem_get_entry);
-
-void *smem_find(unsigned id, unsigned size_in)
-{
- unsigned size;
- void *ptr;
-
- ptr = smem_get_entry(id, &size);
- if (!ptr)
- return 0;
-
- size_in = ALIGN(size_in, 8);
- if (size_in != size) {
- pr_err("smem_find(%d, %d): wrong size %d\n",
- id, size_in, size);
- return 0;
- }
-
- return ptr;
-}
-EXPORT_SYMBOL(smem_find);
-
static int smsm_cb_init(void)
{
struct smsm_state_info *state_info;
@@ -3313,17 +3084,6 @@
}
EXPORT_SYMBOL(smsm_state_cb_deregister);
-/**
- * smem_get_remote_spinlock - Remote spinlock pointer for unit testing.
- *
- * @returns: pointer to SMEM remote spinlock
- */
-remote_spinlock_t *smem_get_remote_spinlock(void)
-{
- return &remote_spinlock;
-}
-EXPORT_SYMBOL(smem_get_remote_spinlock);
-
int smd_module_init_notifier_register(struct notifier_block *nb)
{
int ret;
@@ -4041,6 +3801,9 @@
{
int ret;
+ if (!smem_initialized_check())
+ return -ENODEV;
+
SMD_INFO("smd probe\n");
INIT_WORK(&probe_work, smd_channel_probe_worker);
@@ -4126,23 +3889,6 @@
remote_spin_release(&remote_spinlock, notifier->processor);
remote_spin_release_all(notifier->processor);
- if (smem_ramdump_dev) {
- int ret;
-
- SMD_INFO("%s: saving ramdump\n", __func__);
- /*
- * XPU protection does not currently allow the
- * auxiliary memory regions to be dumped. If this
- * changes, then num_smem_areas + 1 should be passed
- * into do_elf_ramdump() to dump all regions.
- */
- ret = do_elf_ramdump(smem_ramdump_dev,
- smem_ramdump_segments, 1);
- if (ret < 0)
- pr_err("%s: unable to dump smem %d\n", __func__,
- ret);
- }
-
smd_channel_reset(notifier->processor);
}
@@ -4155,13 +3901,6 @@
void *handle;
struct restart_notifier_block *nb;
- smem_ramdump_dev = create_ramdump_device("smem-smd", smd_dev);
- if (IS_ERR_OR_NULL(smem_ramdump_dev)) {
- pr_err("%s: Unable to create smem ramdump device.\n",
- __func__);
- smem_ramdump_dev = NULL;
- }
-
for (i = 0; i < ARRAY_SIZE(restart_notifiers); i++) {
nb = &restart_notifiers[i];
handle = subsys_notif_register_notifier(nb->name, &nb->nb);
@@ -4202,12 +3941,11 @@
}
registered = true;
- rc = remote_spin_lock_init(&remote_spinlock, SMEM_SPINLOCK_SMEM_ALLOC);
+ rc = init_smem_remote_spinlock();
if (rc) {
pr_err("%s: remote spinlock init failed %d\n", __func__, rc);
return rc;
}
- spinlocks_initialized = 1;
rc = platform_driver_register(&msm_smd_driver);
if (rc) {
diff --git a/arch/arm/mach-msm/smd_debug.c b/arch/arm/mach-msm/smd_debug.c
index 4dcf72f..b66e258 100644
--- a/arch/arm/mach-msm/smd_debug.c
+++ b/arch/arm/mach-msm/smd_debug.c
@@ -21,8 +21,10 @@
#include <linux/jiffies.h>
#include <mach/msm_iomap.h>
+#include <mach/msm_smem.h>
#include "smd_private.h"
+#include "smem_private.h"
#if defined(CONFIG_DEBUG_FS)
diff --git a/arch/arm/mach-msm/smd_pkt.c b/arch/arm/mach-msm/smd_pkt.c
index 424d310..20a6165 100644
--- a/arch/arm/mach-msm/smd_pkt.c
+++ b/arch/arm/mach-msm/smd_pkt.c
@@ -41,7 +41,7 @@
#ifdef CONFIG_ARCH_FSM9XXX
#define NUM_SMD_PKT_PORTS 4
#else
-#define NUM_SMD_PKT_PORTS 27
+#define NUM_SMD_PKT_PORTS 28
#endif
#define PDRIVER_NAME_MAX_SIZE 32
@@ -729,6 +729,7 @@
"smd_test_framework",
"smd_logging_0",
"smd_data_0",
+ "apr",
"smd_pkt_loopback",
};
@@ -759,6 +760,7 @@
"TESTFRAMEWORK",
"LOGGING",
"DATA",
+ "apr",
"LOOPBACK",
};
@@ -789,6 +791,7 @@
SMD_APPS_QDSP,
SMD_APPS_QDSP,
SMD_APPS_QDSP,
+ SMD_APPS_QDSP,
SMD_APPS_MODEM,
};
#endif
diff --git a/arch/arm/mach-msm/smd_private.h b/arch/arm/mach-msm/smd_private.h
index 4a6a509..2096063 100644
--- a/arch/arm/mach-msm/smd_private.h
+++ b/arch/arm/mach-msm/smd_private.h
@@ -34,37 +34,6 @@
#define VERSION_MODEM 9
#define VERSION_DSPS 10
-#define SMD_HEAP_SIZE 512
-
-struct smem_heap_info {
- unsigned initialized;
- unsigned free_offset;
- unsigned heap_remaining;
- unsigned reserved;
-};
-
-struct smem_heap_entry {
- unsigned allocated;
- unsigned offset;
- unsigned size;
- unsigned reserved; /* bits 1:0 reserved, bits 31:2 aux smem base addr */
-};
-#define BASE_ADDR_MASK 0xfffffffc
-
-struct smem_proc_comm {
- unsigned command;
- unsigned status;
- unsigned data1;
- unsigned data2;
-};
-
-struct smem_shared {
- struct smem_proc_comm proc_comm[4];
- unsigned version[32];
- struct smem_heap_info heap_info;
- struct smem_heap_entry heap_toc[SMD_HEAP_SIZE];
-};
-
#if defined(CONFIG_MSM_SMD_PKG4)
struct smsm_interrupt_info {
uint32_t aArm_en_mask;
@@ -186,27 +155,6 @@
struct smd_half_channel_access *get_half_ch_funcs(unsigned ch_type);
-struct smem_ram_ptn {
- char name[16];
- unsigned start;
- unsigned size;
-
- /* RAM Partition attribute: READ_ONLY, READWRITE etc. */
- unsigned attr;
-
- /* RAM Partition category: EBI0, EBI1, IRAM, IMEM */
- unsigned category;
-
- /* RAM Partition domain: APPS, MODEM, APPS & MODEM (SHARED) etc. */
- unsigned domain;
-
- /* RAM Partition type: system, bootloader, appsboot, apps etc. */
- unsigned type;
-
- /* reserved for future expansion without changing version number */
- unsigned reserved2, reserved3, reserved4, reserved5;
-} __attribute__ ((__packed__));
-
struct smd_channel {
volatile void __iomem *send; /* some variant of smd_half_channel */
volatile void __iomem *recv; /* some variant of smd_half_channel */
@@ -248,54 +196,6 @@
struct smd_half_channel_access *half_ch;
};
-struct smem_ram_ptable {
- #define _SMEM_RAM_PTABLE_MAGIC_1 0x9DA5E0A8
- #define _SMEM_RAM_PTABLE_MAGIC_2 0xAF9EC4E2
- unsigned magic[2];
- unsigned version;
- unsigned reserved1;
- unsigned len;
- struct smem_ram_ptn parts[32];
- unsigned buf;
-} __attribute__ ((__packed__));
-
-/* SMEM RAM Partition */
-enum {
- DEFAULT_ATTRB = ~0x0,
- READ_ONLY = 0x0,
- READWRITE,
-};
-
-enum {
- DEFAULT_CATEGORY = ~0x0,
- SMI = 0x0,
- EBI1,
- EBI2,
- QDSP6,
- IRAM,
- IMEM,
- EBI0_CS0,
- EBI0_CS1,
- EBI1_CS0,
- EBI1_CS1,
- SDRAM = 0xE,
-};
-
-enum {
- DEFAULT_DOMAIN = 0x0,
- APPS_DOMAIN,
- MODEM_DOMAIN,
- SHARED_DOMAIN,
-};
-
-enum {
- SYS_MEMORY = 1, /* system memory*/
- BOOT_REGION_MEMORY1, /* boot loader memory 1*/
- BOOT_REGION_MEMORY2, /* boot loader memory 2,reserved*/
- APPSBL_MEMORY, /* apps boot loader memory*/
- APPS_MEMORY, /* apps usage memory*/
-};
-
extern spinlock_t smem_lock;
@@ -313,7 +213,4 @@
uint32_t smsm_interrupt_id;
};
extern struct interrupt_stat interrupt_stats[NUM_SMD_SUBSYSTEMS];
-
-/* used for unit testing spinlocks */
-remote_spinlock_t *smem_get_remote_spinlock(void);
#endif
diff --git a/arch/arm/mach-msm/smem.c b/arch/arm/mach-msm/smem.c
new file mode 100644
index 0000000..bbb6ce0
--- /dev/null
+++ b/arch/arm/mach-msm/smem.c
@@ -0,0 +1,475 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/printk.h>
+
+#include <mach/board.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_smem.h>
+#include <mach/ramdump.h>
+#include <mach/subsystem_notif.h>
+
+#include "smem_private.h"
+
+/**
+ * OVERFLOW_ADD_UNSIGNED() - check for unsigned overflow
+ *
+ * @type: type to check for overflow
+ * @a: left value to use
+ * @b: right value to use
+ * @returns: true if a + b will result in overflow; false otherwise
+ */
+#define OVERFLOW_ADD_UNSIGNED(type, a, b) \
+ (((type)~0 - (a)) < (b) ? true : false)
+
+#define MODEM_SBL_VERSION_INDEX 7
+#define SMEM_VERSION_INFO_SIZE (32 * 4)
+#define SMEM_VERSION 0x000B
+
+enum {
+ MSM_SMEM_DEBUG = 1U << 0,
+ MSM_SMEM_INFO = 1U << 1,
+};
+
+static int msm_smem_debug_mask;
+module_param_named(debug_mask, msm_smem_debug_mask,
+ int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define SMEM_DBG(x...) do { \
+ if (msm_smem_debug_mask & MSM_SMEM_DEBUG) \
+ pr_debug(x); \
+ } while (0)
+
+remote_spinlock_t remote_spinlock;
+int spinlocks_initialized;
+uint32_t num_smem_areas;
+struct smem_area *smem_areas;
+struct ramdump_segment *smem_ramdump_segments;
+
+static void *smem_ramdump_dev;
+static DEFINE_MUTEX(spinlock_init_lock);
+static DEFINE_SPINLOCK(smem_init_check_lock);
+
+struct restart_notifier_block {
+ unsigned processor;
+ char *name;
+ struct notifier_block nb;
+};
+
+static int restart_notifier_cb(struct notifier_block *this,
+ unsigned long code,
+ void *data);
+
+static struct restart_notifier_block restart_notifiers[] = {
+ {SMEM_MODEM, "modem", .nb.notifier_call = restart_notifier_cb},
+ {SMEM_Q6, "lpass", .nb.notifier_call = restart_notifier_cb},
+ {SMEM_WCNSS, "wcnss", .nb.notifier_call = restart_notifier_cb},
+ {SMEM_DSPS, "dsps", .nb.notifier_call = restart_notifier_cb},
+ {SMEM_MODEM, "gss", .nb.notifier_call = restart_notifier_cb},
+ {SMEM_Q6, "adsp", .nb.notifier_call = restart_notifier_cb},
+};
+
+/**
+ * smem_phys_to_virt() - Convert a physical base and offset to virtual address
+ *
+ * @base: physical base address to check
+ * @offset: offset from the base to get the final address
+ * @returns: virtual SMEM address; NULL for failure
+ *
+ * Takes a physical address and an offset and checks if the resulting physical
+ * address would fit into one of the smem regions. If so, returns the
+ * corresponding virtual address. Otherwise returns NULL.
+ */
+static void *smem_phys_to_virt(phys_addr_t base, unsigned offset)
+{
+ int i;
+ phys_addr_t phys_addr;
+ resource_size_t size;
+
+ if (OVERFLOW_ADD_UNSIGNED(phys_addr_t, base, offset))
+ return NULL;
+
+ if (!smem_areas) {
+ /*
+ * Early boot - no area configuration yet, so default
+ * to using the main memory region.
+ *
+ * To remove the MSM_SHARED_RAM_BASE and the static
+ * mapping of SMEM in the future, add dump_stack()
+ * to identify the early callers of smem_get_entry()
+ * (which calls this function) and replace those calls
+ * with a new function that knows how to lookup the
+ * SMEM base address before SMEM has been probed.
+ */
+ phys_addr = msm_shared_ram_phys;
+ size = MSM_SHARED_RAM_SIZE;
+
+ if (base >= phys_addr && base + offset < phys_addr + size) {
+ if (OVERFLOW_ADD_UNSIGNED(uintptr_t,
+ (uintptr_t)MSM_SHARED_RAM_BASE, offset)) {
+ pr_err("%s: overflow %p %x\n", __func__,
+ MSM_SHARED_RAM_BASE, offset);
+ return NULL;
+ }
+
+ return MSM_SHARED_RAM_BASE + offset;
+ } else {
+ return NULL;
+ }
+ }
+ for (i = 0; i < num_smem_areas; ++i) {
+ phys_addr = smem_areas[i].phys_addr;
+ size = smem_areas[i].size;
+
+ if (base < phys_addr || base + offset >= phys_addr + size)
+ continue;
+
+ if (OVERFLOW_ADD_UNSIGNED(uintptr_t,
+ (uintptr_t)smem_areas[i].virt_addr, offset)) {
+ pr_err("%s: overflow %p %x\n", __func__,
+ smem_areas[i].virt_addr, offset);
+ return NULL;
+ }
+
+ return smem_areas[i].virt_addr + offset;
+ }
+
+ return NULL;
+}
+
+/**
+ * smem_virt_to_phys() - Convert SMEM address to physical address.
+ *
+ * @smem_address: Address of SMEM item (returned by smem_alloc(), etc)
+ * @returns: Physical address (or NULL if there is a failure)
+ *
+ * This function should only be used if an SMEM item needs to be handed
+ * off to a DMA engine.
+ */
+phys_addr_t smem_virt_to_phys(void *smem_address)
+{
+ phys_addr_t phys_addr = 0;
+ int i;
+ void *vend;
+
+ if (!smem_areas)
+ return phys_addr;
+
+ for (i = 0; i < num_smem_areas; ++i) {
+ vend = (void *)(smem_areas[i].virt_addr + smem_areas[i].size);
+
+ if (smem_address >= smem_areas[i].virt_addr &&
+ smem_address < vend) {
+ phys_addr = smem_address - smem_areas[i].virt_addr;
+ phys_addr += smem_areas[i].phys_addr;
+ break;
+ }
+ }
+
+ return phys_addr;
+}
+EXPORT_SYMBOL(smem_virt_to_phys);
+
+/* smem_alloc returns the pointer to smem item if it is already allocated.
+ * Otherwise, it returns NULL.
+ */
+void *smem_alloc(unsigned id, unsigned size)
+{
+ return smem_find(id, size);
+}
+EXPORT_SYMBOL(smem_alloc);
+
+static void *__smem_get_entry(unsigned id, unsigned *size, bool skip_init_check)
+{
+ struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
+ struct smem_heap_entry *toc = shared->heap_toc;
+ int use_spinlocks = spinlocks_initialized;
+ void *ret = 0;
+ unsigned long flags = 0;
+
+ if (!skip_init_check && !smem_initialized_check())
+ return ret;
+
+ if (id >= SMEM_NUM_ITEMS)
+ return ret;
+
+ if (use_spinlocks)
+ remote_spin_lock_irqsave(&remote_spinlock, flags);
+ /* toc is in device memory and cannot be speculatively accessed */
+ if (toc[id].allocated) {
+ phys_addr_t phys_base;
+
+ *size = toc[id].size;
+ barrier();
+
+ phys_base = toc[id].reserved & BASE_ADDR_MASK;
+ if (!phys_base)
+ phys_base = (phys_addr_t)msm_shared_ram_phys;
+ ret = smem_phys_to_virt(phys_base, toc[id].offset);
+ } else {
+ *size = 0;
+ }
+ if (use_spinlocks)
+ remote_spin_unlock_irqrestore(&remote_spinlock, flags);
+
+ return ret;
+}
+
+static void *__smem_find(unsigned id, unsigned size_in, bool skip_init_check)
+{
+ unsigned size;
+ void *ptr;
+
+ ptr = __smem_get_entry(id, &size, skip_init_check);
+ if (!ptr)
+ return 0;
+
+ size_in = ALIGN(size_in, 8);
+ if (size_in != size) {
+ pr_err("smem_find(%d, %d): wrong size %d\n",
+ id, size_in, size);
+ return 0;
+ }
+
+ return ptr;
+}
+
+void *smem_find(unsigned id, unsigned size_in)
+{
+ return __smem_find(id, size_in, false);
+}
+EXPORT_SYMBOL(smem_find);
+
+/* smem_alloc2 returns the pointer to smem item. If it is not allocated,
+ * it allocates it and then returns the pointer to it.
+ */
+void *smem_alloc2(unsigned id, unsigned size_in)
+{
+ struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
+ struct smem_heap_entry *toc = shared->heap_toc;
+ unsigned long flags;
+ void *ret = NULL;
+ int rc;
+
+ if (!smem_initialized_check())
+ return NULL;
+
+ if (id >= SMEM_NUM_ITEMS)
+ return NULL;
+
+ if (unlikely(!spinlocks_initialized)) {
+ rc = init_smem_remote_spinlock();
+ if (unlikely(rc)) {
+ pr_err("%s: remote spinlock init failed %d\n",
+ __func__, rc);
+ return NULL;
+ }
+ }
+
+ size_in = ALIGN(size_in, 8);
+ remote_spin_lock_irqsave(&remote_spinlock, flags);
+ if (toc[id].allocated) {
+ SMEM_DBG("%s: %u already allocated\n", __func__, id);
+ if (size_in != toc[id].size)
+ pr_err("%s: wrong size %u (expected %u)\n",
+ __func__, toc[id].size, size_in);
+ else
+ ret = (void *)(MSM_SHARED_RAM_BASE + toc[id].offset);
+ } else if (id > SMEM_FIXED_ITEM_LAST) {
+ SMEM_DBG("%s: allocating %u\n", __func__, id);
+ if (shared->heap_info.heap_remaining >= size_in) {
+ toc[id].offset = shared->heap_info.free_offset;
+ toc[id].size = size_in;
+ wmb();
+ toc[id].allocated = 1;
+
+ shared->heap_info.free_offset += size_in;
+ shared->heap_info.heap_remaining -= size_in;
+ ret = (void *)(MSM_SHARED_RAM_BASE + toc[id].offset);
+ } else
+ pr_err("%s: not enough memory %u (required %u)\n",
+ __func__, shared->heap_info.heap_remaining,
+ size_in);
+ }
+ wmb();
+ remote_spin_unlock_irqrestore(&remote_spinlock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(smem_alloc2);
+
+void *smem_get_entry(unsigned id, unsigned *size)
+{
+ return __smem_get_entry(id, size, false);
+}
+EXPORT_SYMBOL(smem_get_entry);
+
+
+/**
+ * smem_get_remote_spinlock - Remote spinlock pointer for unit testing.
+ *
+ * @returns: pointer to SMEM remote spinlock
+ */
+remote_spinlock_t *smem_get_remote_spinlock(void)
+{
+ return &remote_spinlock;
+}
+EXPORT_SYMBOL(smem_get_remote_spinlock);
+
+/**
+ * init_smem_remote_spinlock - Reentrant remote spinlock initialization
+ *
+ * @returns: sucess or error code for failure
+ */
+int init_smem_remote_spinlock(void)
+{
+ int rc = 0;
+
+ /*
+ * Optimistic locking. Init only needs to be done once by the first
+ * caller. After that, serializing inits between different callers
+ * is unnecessary. The second check after the lock ensures init
+ * wasn't previously completed by someone else before the lock could
+ * be grabbed.
+ */
+ if (!spinlocks_initialized) {
+ mutex_lock(&spinlock_init_lock);
+ if (!spinlocks_initialized) {
+ rc = remote_spin_lock_init(&remote_spinlock,
+ SMEM_SPINLOCK_SMEM_ALLOC);
+ if (!rc)
+ spinlocks_initialized = 1;
+ }
+ mutex_unlock(&spinlock_init_lock);
+ }
+ return rc;
+}
+
+/**
+ * smem_initialized_check - Reentrant check that smem has been initialized
+ *
+ * @returns: true if initialized, false if not.
+ */
+bool smem_initialized_check(void)
+{
+ static int checked;
+ static int is_inited;
+ unsigned long flags;
+ struct smem_shared *smem;
+ int *version_array;
+
+ if (likely(checked)) {
+ if (unlikely(!is_inited))
+ pr_err("%s: smem not initialized\n", __func__);
+ return is_inited;
+ }
+
+ spin_lock_irqsave(&smem_init_check_lock, flags);
+ if (checked) {
+ spin_unlock_irqrestore(&smem_init_check_lock, flags);
+ if (unlikely(!is_inited))
+ pr_err("%s: smem not initialized\n", __func__);
+ return is_inited;
+ }
+
+ smem = (void *)MSM_SHARED_RAM_BASE;
+
+ if (smem->heap_info.initialized != 1)
+ goto failed;
+ if (smem->heap_info.reserved != 0)
+ goto failed;
+
+ version_array = __smem_find(SMEM_VERSION_INFO, SMEM_VERSION_INFO_SIZE,
+ true);
+ if (version_array == NULL)
+ goto failed;
+ if (version_array[MODEM_SBL_VERSION_INDEX] != SMEM_VERSION << 16)
+ goto failed;
+
+ is_inited = 1;
+ checked = 1;
+ spin_unlock_irqrestore(&smem_init_check_lock, flags);
+ return is_inited;
+
+failed:
+ is_inited = 0;
+ checked = 1;
+ spin_unlock_irqrestore(&smem_init_check_lock, flags);
+ pr_err("%s: bootloader failure detected, shared memory not inited\n",
+ __func__);
+ return is_inited;
+}
+EXPORT_SYMBOL(smem_initialized_check);
+
+static int restart_notifier_cb(struct notifier_block *this,
+ unsigned long code,
+ void *data)
+{
+ if (code == SUBSYS_AFTER_SHUTDOWN) {
+ struct restart_notifier_block *notifier;
+
+ notifier = container_of(this,
+ struct restart_notifier_block, nb);
+ SMEM_DBG("%s: ssrestart for processor %d ('%s')\n",
+ __func__, notifier->processor,
+ notifier->name);
+
+ remote_spin_release(&remote_spinlock, notifier->processor);
+ remote_spin_release_all(notifier->processor);
+
+ if (smem_ramdump_dev) {
+ int ret;
+
+ SMEM_DBG("%s: saving ramdump\n", __func__);
+ /*
+ * XPU protection does not currently allow the
+ * auxiliary memory regions to be dumped. If this
+ * changes, then num_smem_areas + 1 should be passed
+ * into do_elf_ramdump() to dump all regions.
+ */
+ ret = do_elf_ramdump(smem_ramdump_dev,
+ smem_ramdump_segments, 1);
+ if (ret < 0)
+ pr_err("%s: unable to dump smem %d\n", __func__,
+ ret);
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+static __init int modem_restart_late_init(void)
+{
+ int i;
+ void *handle;
+ struct restart_notifier_block *nb;
+
+ smem_ramdump_dev = create_ramdump_device("smem", NULL);
+ if (IS_ERR_OR_NULL(smem_ramdump_dev)) {
+ pr_err("%s: Unable to create smem ramdump device.\n",
+ __func__);
+ smem_ramdump_dev = NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(restart_notifiers); i++) {
+ nb = &restart_notifiers[i];
+ handle = subsys_notif_register_notifier(nb->name, &nb->nb);
+ SMEM_DBG("%s: registering notif for '%s', handle=%p\n",
+ __func__, nb->name, handle);
+ }
+
+ return 0;
+}
+late_initcall(modem_restart_late_init);
diff --git a/arch/arm/mach-msm/smem_log.c b/arch/arm/mach-msm/smem_log.c
index 361df33..87f141d2 100644
--- a/arch/arm/mach-msm/smem_log.c
+++ b/arch/arm/mach-msm/smem_log.c
@@ -32,6 +32,7 @@
#include <mach/msm_iomap.h>
#include <mach/smem_log.h>
+#include <mach/msm_smem.h>
#include <asm/arch_timer.h>
diff --git a/arch/arm/mach-msm/smem_private.h b/arch/arm/mach-msm/smem_private.h
new file mode 100644
index 0000000..c4f9a77
--- /dev/null
+++ b/arch/arm/mach-msm/smem_private.h
@@ -0,0 +1,76 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_SMEM_PRIVATE_H_
+#define _ARCH_ARM_MACH_MSM_SMEM_PRIVATE_H_
+
+#include <linux/remote_spinlock.h>
+
+#include <mach/ramdump.h>
+
+#define SMEM_SPINLOCK_SMEM_ALLOC "S:3"
+extern remote_spinlock_t remote_spinlock;
+extern int spinlocks_initialized; /* only modify in init_smem_remote_spinlock */
+
+#define SMD_HEAP_SIZE 512
+
+struct smem_heap_info {
+ unsigned initialized;
+ unsigned free_offset;
+ unsigned heap_remaining;
+ unsigned reserved;
+};
+
+struct smem_heap_entry {
+ unsigned allocated;
+ unsigned offset;
+ unsigned size;
+ unsigned reserved; /* bits 1:0 reserved, bits 31:2 aux smem base addr */
+};
+#define BASE_ADDR_MASK 0xfffffffc
+
+struct smem_proc_comm {
+ unsigned command;
+ unsigned status;
+ unsigned data1;
+ unsigned data2;
+};
+
+struct smem_shared {
+ struct smem_proc_comm proc_comm[4];
+ unsigned version[32];
+ struct smem_heap_info heap_info;
+ struct smem_heap_entry heap_toc[SMD_HEAP_SIZE];
+};
+
+struct smem_area {
+ phys_addr_t phys_addr;
+ resource_size_t size;
+ void __iomem *virt_addr;
+};
+
+extern uint32_t num_smem_areas;
+extern struct smem_area *smem_areas;
+
+extern struct ramdump_segment *smem_ramdump_segments;
+
+/* used for unit testing spinlocks */
+remote_spinlock_t *smem_get_remote_spinlock(void);
+
+/*
+ * used to ensure the remote spinlock is only inited once since local
+ * spinlock init code appears non-reentrant
+ */
+int init_smem_remote_spinlock(void);
+
+bool smem_initialized_check(void);
+#endif /* _ARCH_ARM_MACH_MSM_SMEM_PRIVATE_H_ */
diff --git a/arch/arm/mach-msm/smp2p.c b/arch/arm/mach-msm/smp2p.c
index 7bdcce9..ee262b0 100644
--- a/arch/arm/mach-msm/smp2p.c
+++ b/arch/arm/mach-msm/smp2p.c
@@ -19,7 +19,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
-#include <mach/msm_smsm.h>
+#include <mach/msm_smem.h>
#include <mach/msm_ipc_logging.h>
#include "smp2p_private_api.h"
#include "smp2p_private.h"
diff --git a/arch/arm/mach-msm/smp2p_loopback.c b/arch/arm/mach-msm/smp2p_loopback.c
index d95c93f..5df3d70 100644
--- a/arch/arm/mach-msm/smp2p_loopback.c
+++ b/arch/arm/mach-msm/smp2p_loopback.c
@@ -21,7 +21,7 @@
#include <linux/termios.h>
#include <linux/module.h>
#include <linux/remote_spinlock.h>
-#include "smd_private.h"
+#include "smem_private.h"
#include "smp2p_private.h"
/**
diff --git a/arch/arm/mach-msm/smp2p_spinlock_test.c b/arch/arm/mach-msm/smp2p_spinlock_test.c
index 09d7c0d..c14bac0 100644
--- a/arch/arm/mach-msm/smp2p_spinlock_test.c
+++ b/arch/arm/mach-msm/smp2p_spinlock_test.c
@@ -17,8 +17,8 @@
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/remote_spinlock.h>
-#include <mach/msm_smsm.h>
-#include "smd_private.h"
+#include <mach/msm_smem.h>
+#include "smem_private.h"
#include "smp2p_private.h"
#include "smp2p_test_common.h"
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index 83f7a1d..575cb49 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -27,8 +27,8 @@
#include <asm/mach-types.h>
#include <mach/socinfo.h>
+#include <mach/msm_smem.h>
-#include "smd_private.h"
#include "boot_stats.h"
#define BUILD_ID_LENGTH 32
@@ -279,6 +279,8 @@
/* 8974 IDs */
[126] = MSM_CPU_8974,
[184] = MSM_CPU_8974,
+ [185] = MSM_CPU_8974,
+ [186] = MSM_CPU_8974,
/* 8625 IDs */
[127] = MSM_CPU_8625,
@@ -322,13 +324,24 @@
/* 8226 IDs */
[145] = MSM_CPU_8226,
+ [158] = MSM_CPU_8226,
+ [159] = MSM_CPU_8226,
+ [198] = MSM_CPU_8226,
+ [199] = MSM_CPU_8226,
+ [200] = MSM_CPU_8226,
+ [205] = MSM_CPU_8226,
/* 8092 IDs */
[146] = MSM_CPU_8092,
/* 8610 IDs */
[147] = MSM_CPU_8610,
+ [161] = MSM_CPU_8610,
+ [162] = MSM_CPU_8610,
+ [163] = MSM_CPU_8610,
+ [164] = MSM_CPU_8610,
[165] = MSM_CPU_8610,
+ [166] = MSM_CPU_8610,
/* 8064AB IDs */
[153] = MSM_CPU_8064AB,
@@ -348,12 +361,18 @@
/* 8064AA IDs */
[172] = MSM_CPU_8064AA,
- /* zinc IDs */
- [178] = MSM_CPU_ZINC,
+ /* 8084 IDs */
+ [178] = MSM_CPU_8084,
/* krypton IDs */
[187] = MSM_CPU_KRYPTON,
+ /* FSM9900 ID */
+ [188] = FSM_CPU_9900,
+
+ /* Samarium IDs */
+ [195] = MSM_CPU_SAMARIUM,
+
/* Uninitialized IDs are not known to run Linux.
MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are
considered as unknown CPU. */
@@ -853,14 +872,18 @@
dummy_socinfo.id = 146;
strlcpy(dummy_socinfo.build_id, "mpq8092 - ",
sizeof(dummy_socinfo.build_id));
- } else if (early_machine_is_msmzinc()) {
+ } else if (early_machine_is_apq8084()) {
dummy_socinfo.id = 178;
- strlcpy(dummy_socinfo.build_id, "msmzinc - ",
+ strlcpy(dummy_socinfo.build_id, "apq8084 - ",
sizeof(dummy_socinfo.build_id));
} else if (early_machine_is_msmkrypton()) {
dummy_socinfo.id = 187;
strlcpy(dummy_socinfo.build_id, "msmkrypton - ",
sizeof(dummy_socinfo.build_id));
+ } else if (early_machine_is_msmsamarium()) {
+ dummy_socinfo.id = 195;
+ strlcpy(dummy_socinfo.build_id, "msmsamarium - ",
+ sizeof(dummy_socinfo.build_id));
}
strlcat(dummy_socinfo.build_id, "Dummy socinfo",
sizeof(dummy_socinfo.build_id));
diff --git a/arch/arm/mach-msm/spm-regulator.c b/arch/arm/mach-msm/spm-regulator.c
index 00817c0..244a779 100644
--- a/arch/arm/mach-msm/spm-regulator.c
+++ b/arch/arm/mach-msm/spm-regulator.c
@@ -42,17 +42,40 @@
static const struct voltage_range fts2_range0 = {0, 350000, 1275000, 5000};
static const struct voltage_range fts2_range1 = {0, 700000, 2040000, 10000};
-/* Specifies the PMIC internal slew rate in uV/us. */
-#define QPNP_FTS2_SLEW_RATE 6000
-
#define QPNP_FTS2_REG_TYPE 0x04
#define QPNP_FTS2_REG_SUBTYPE 0x05
#define QPNP_FTS2_REG_VOLTAGE_RANGE 0x40
#define QPNP_FTS2_REG_VOLTAGE_SETPOINT 0x41
+#define QPNP_FTS2_REG_MODE 0x45
+#define QPNP_FTS2_REG_STEP_CTRL 0x61
#define QPNP_FTS2_TYPE 0x1C
#define QPNP_FTS2_SUBTYPE 0x08
+#define QPNP_FTS2_MODE_PWM 0x80
+#define QPNP_FTS2_MODE_AUTO 0x40
+
+#define QPNP_FTS2_STEP_CTRL_STEP_MASK 0x18
+#define QPNP_FTS2_STEP_CTRL_STEP_SHIFT 3
+#define QPNP_FTS2_STEP_CTRL_DELAY_MASK 0x07
+#define QPNP_FTS2_STEP_CTRL_DELAY_SHIFT 0
+
+/* Clock rate in kHz of the FTS2 regulator reference clock. */
+#define QPNP_FTS2_CLOCK_RATE 19200
+
+/* Time to delay in us to ensure that a mode change has completed. */
+#define QPNP_FTS2_MODE_CHANGE_DELAY 50
+
+/* Minimum time in us that it takes to complete a single SPMI write. */
+#define QPNP_SPMI_WRITE_MIN_DELAY 8
+
+/*
+ * The ratio QPNP_FTS2_STEP_MARGIN_NUM/QPNP_FTS2_STEP_MARGIN_DEN is use to
+ * adjust the step rate in order to account for oscillator variance.
+ */
+#define QPNP_FTS2_STEP_MARGIN_NUM 4
+#define QPNP_FTS2_STEP_MARGIN_DEN 5
+
struct spm_vreg {
struct regulator_desc rdesc;
struct regulator_dev *rdev;
@@ -64,8 +87,23 @@
unsigned last_set_vlevel;
bool online;
u16 spmi_base_addr;
+ u8 init_mode;
+ int step_rate;
};
+static int qpnp_fts2_set_mode(struct spm_vreg *vreg, u8 mode)
+{
+ int rc;
+
+ rc = spmi_ext_register_writel(vreg->spmi_dev->ctrl, vreg->spmi_dev->sid,
+ vreg->spmi_base_addr + QPNP_FTS2_REG_MODE, &mode, 1);
+ if (rc)
+ dev_err(&vreg->spmi_dev->dev, "%s: could not write to mode register, rc=%d\n",
+ __func__, rc);
+
+ return rc;
+}
+
static int _spm_regulator_set_voltage(struct regulator_dev *rdev)
{
struct spm_vreg *vreg = rdev_get_drvdata(rdev);
@@ -74,6 +112,14 @@
if (vreg->vlevel == vreg->last_set_vlevel)
return 0;
+ if (!(vreg->init_mode & QPNP_FTS2_MODE_PWM)
+ && vreg->uV > vreg->last_set_uV) {
+ /* Switch to PWM mode so that voltage ramping is fast. */
+ rc = qpnp_fts2_set_mode(vreg, QPNP_FTS2_MODE_PWM);
+ if (rc)
+ return rc;
+ }
+
rc = msm_spm_apcs_set_vdd(vreg->vlevel);
if (rc) {
pr_err("%s: msm_spm_set_vdd failed %d\n", vreg->rdesc.name, rc);
@@ -81,10 +127,21 @@
}
if (vreg->uV > vreg->last_set_uV) {
- /* Wait for voltage to stabalize. */
+ /* Wait for voltage stepping to complete. */
udelay(DIV_ROUND_UP(vreg->uV - vreg->last_set_uV,
- QPNP_FTS2_SLEW_RATE));
+ vreg->step_rate));
}
+
+ if (!(vreg->init_mode & QPNP_FTS2_MODE_PWM)
+ && vreg->uV > vreg->last_set_uV) {
+ /* Wait for mode transition to complete. */
+ udelay(QPNP_FTS2_MODE_CHANGE_DELAY - QPNP_SPMI_WRITE_MIN_DELAY);
+ /* Switch to AUTO mode so that power consumption is lowered. */
+ rc = qpnp_fts2_set_mode(vreg, QPNP_FTS2_MODE_AUTO);
+ if (rc)
+ return rc;
+ }
+
vreg->last_set_uV = vreg->uV;
vreg->last_set_vlevel = vreg->vlevel;
@@ -254,6 +311,51 @@
return rc;
}
+static int qpnp_fts2_init_mode(struct spm_vreg *vreg)
+{
+ int rc;
+
+ rc = spmi_ext_register_readl(vreg->spmi_dev->ctrl, vreg->spmi_dev->sid,
+ vreg->spmi_base_addr + QPNP_FTS2_REG_MODE, &vreg->init_mode, 1);
+ if (rc)
+ dev_err(&vreg->spmi_dev->dev, "%s: could not read mode register, rc=%d\n",
+ __func__, rc);
+
+ return rc;
+}
+
+static int qpnp_fts2_init_step_rate(struct spm_vreg *vreg)
+{
+ int rc;
+ u8 reg = 0;
+ int step, delay;
+
+ rc = spmi_ext_register_readl(vreg->spmi_dev->ctrl, vreg->spmi_dev->sid,
+ vreg->spmi_base_addr + QPNP_FTS2_REG_STEP_CTRL, ®, 1);
+ if (rc) {
+ dev_err(&vreg->spmi_dev->dev, "%s: could not read stepping control register, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ step = (reg & QPNP_FTS2_STEP_CTRL_STEP_MASK)
+ >> QPNP_FTS2_STEP_CTRL_STEP_SHIFT;
+ delay = (reg & QPNP_FTS2_STEP_CTRL_DELAY_MASK)
+ >> QPNP_FTS2_STEP_CTRL_DELAY_SHIFT;
+
+ /* step_rate has units of uV/us. */
+ vreg->step_rate = QPNP_FTS2_CLOCK_RATE * vreg->range->step_uV
+ * (1 << step);
+ vreg->step_rate /= 1000 * (8 << delay);
+ vreg->step_rate = vreg->step_rate * QPNP_FTS2_STEP_MARGIN_NUM
+ / QPNP_FTS2_STEP_MARGIN_DEN;
+
+ /* Ensure that the stepping rate is greater than 0. */
+ vreg->step_rate = max(vreg->step_rate, 1);
+
+ return rc;
+}
+
static int __devinit spm_regulator_probe(struct spmi_device *spmi)
{
struct device_node *node = spmi->dev.of_node;
@@ -299,6 +401,14 @@
if (rc)
return rc;
+ rc = qpnp_fts2_init_mode(vreg);
+ if (rc)
+ return rc;
+
+ rc = qpnp_fts2_init_step_rate(vreg);
+ if (rc)
+ return rc;
+
init_data = of_get_regulator_init_data(&spmi->dev, node);
if (!init_data) {
dev_err(&spmi->dev, "%s: unable to allocate memory\n",
@@ -334,8 +444,12 @@
dev_set_drvdata(&spmi->dev, vreg);
- pr_info("name=%s, range=%d\n", vreg->rdesc.name,
- (vreg->range == &fts2_range0) ? 0 : 1);
+ pr_info("name=%s, range=%s, voltage=%d uV, mode=%s, step rate=%d uV/us\n",
+ vreg->rdesc.name, vreg->range == &fts2_range0 ? "LV" : "MV",
+ vreg->uV,
+ vreg->init_mode & QPNP_FTS2_MODE_PWM ? "PWM" :
+ (vreg->init_mode & QPNP_FTS2_MODE_AUTO ? "AUTO" : "PFM"),
+ vreg->step_rate);
return rc;
}
diff --git a/arch/arm/mach-msm/spm_devices.c b/arch/arm/mach-msm/spm_devices.c
index 174d444..fc05fce 100644
--- a/arch/arm/mach-msm/spm_devices.c
+++ b/arch/arm/mach-msm/spm_devices.c
@@ -76,7 +76,7 @@
info.vlevel = vlevel;
info.err = -ENODEV;
- if (cpu_online(cpu)) {
+ if ((smp_processor_id() != cpu) && cpu_online(cpu)) {
/**
* We do not want to set the voltage of another core from
* this core, as its possible that we may race the vdd change
@@ -392,7 +392,7 @@
};
struct mode_of of_l2_modes[] = {
- {"qcom,saw2-spm-cmd-ret", MSM_SPM_L2_MODE_RETENTION, 0},
+ {"qcom,saw2-spm-cmd-ret", MSM_SPM_L2_MODE_RETENTION, 1},
{"qcom,saw2-spm-cmd-gdhs", MSM_SPM_L2_MODE_GDHS, 1},
{"qcom,saw2-spm-cmd-pc", MSM_SPM_L2_MODE_POWER_COLLAPSE, 1},
};
@@ -404,21 +404,28 @@
memset(&modes, 0,
(MSM_SPM_MODE_NR - 2) * sizeof(struct msm_spm_seq_entry));
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- goto fail;
-
- spm_data.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!spm_data.reg_base_addr)
- return -ENOMEM;
-
key = "qcom,core-id";
ret = of_property_read_u32(node, key, &val);
if (ret)
goto fail;
cpu = val;
+ /*
+ * Device with id 0..NR_CPUS are SPM for apps cores
+ * Device with id 0xFFFF is for L2 SPM.
+ */
+ if (cpu >= 0 && cpu < num_possible_cpus()) {
+ mode_of_data = of_cpu_modes;
+ num_modes = ARRAY_SIZE(of_cpu_modes);
+ dev = &per_cpu(msm_cpu_spm_device, cpu);
+
+ } else if (cpu == 0xffff) {
+ mode_of_data = of_l2_modes;
+ num_modes = ARRAY_SIZE(of_l2_modes);
+ dev = &msm_spm_l2_device;
+ } else
+ return ret;
+
key = "qcom,saw2-ver-reg";
ret = of_property_read_u32(node, key, &val);
if (ret)
@@ -429,21 +436,17 @@
ret = of_property_read_u32(node, key, &val);
if (!ret)
spm_data.vctl_timeout_us = val;
+ else if (cpu == 0xffff)
+ goto fail;
- /*
- * Device with id 0..NR_CPUS are SPM for apps cores
- * Device with id 0xFFFF is for L2 SPM.
- */
- if (cpu >= 0 && cpu < num_possible_cpus()) {
- mode_of_data = of_cpu_modes;
- num_modes = ARRAY_SIZE(of_cpu_modes);
- dev = &per_cpu(msm_cpu_spm_device, cpu);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ goto fail;
- } else {
- mode_of_data = of_l2_modes;
- num_modes = ARRAY_SIZE(of_l2_modes);
- dev = &msm_spm_l2_device;
- }
+ spm_data.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!spm_data.reg_base_addr)
+ return -ENOMEM;
spm_data.vctl_port = -1;
spm_data.phase_port = -1;
diff --git a/arch/arm/mach-msm/subsystem_restart.c b/arch/arm/mach-msm/subsystem_restart.c
index 5fe7a29..7b1a4c3 100644
--- a/arch/arm/mach-msm/subsystem_restart.c
+++ b/arch/arm/mach-msm/subsystem_restart.c
@@ -31,6 +31,7 @@
#include <linux/idr.h>
#include <linux/debugfs.h>
#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
#include <asm/current.h>
@@ -40,6 +41,9 @@
#include "smd_private.h"
+static int enable_debug;
+module_param(enable_debug, int, S_IRUGO | S_IWUSR);
+
/**
* enum p_subsys_state - state of a subsystem (private)
* @SUBSYS_NORMAL: subsystem is operating normally
@@ -132,6 +136,8 @@
* @restart_order: order of other devices this devices restarts with
* @dentry: debugfs directory for this device
* @do_ramdump_on_put: ramdump on subsystem_put() if true
+ * @err_ready: completion variable to record error ready from subsystem
+ * @crashed: indicates if subsystem has crashed
*/
struct subsys_device {
struct subsys_desc *desc;
@@ -154,6 +160,8 @@
bool do_ramdump_on_put;
struct miscdevice misc_dev;
char miscdevice_name[32];
+ struct completion err_ready;
+ bool crashed;
};
static struct subsys_device *to_subsys(struct device *d)
@@ -412,6 +420,23 @@
}
}
+static int wait_for_err_ready(struct subsys_device *subsys)
+{
+ int ret;
+
+ if (!subsys->desc->err_ready_irq || enable_debug == 1)
+ return 0;
+
+ ret = wait_for_completion_timeout(&subsys->err_ready,
+ msecs_to_jiffies(10000));
+ if (!ret) {
+ pr_err("[%s]: Error ready timed out\n", subsys->desc->name);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
static void subsystem_shutdown(struct subsys_device *dev, void *data)
{
const char *name = dev->desc->name;
@@ -436,10 +461,17 @@
static void subsystem_powerup(struct subsys_device *dev, void *data)
{
const char *name = dev->desc->name;
+ int ret;
pr_info("[%p]: Powering up %s\n", current, name);
+ init_completion(&dev->err_ready);
if (dev->desc->powerup(dev->desc) < 0)
- panic("[%p]: Failed to powerup %s!", current, name);
+ panic("[%p]: Powerup error: %s!", current, name);
+
+ ret = wait_for_err_ready(dev);
+ if (ret)
+ panic("[%p]: Timed out waiting for error ready: %s!",
+ current, name);
subsys_set_state(dev, SUBSYS_ONLINE);
}
@@ -465,8 +497,21 @@
{
int ret;
+ init_completion(&subsys->err_ready);
ret = subsys->desc->start(subsys->desc);
- if (!ret)
+ if (ret)
+ return ret;
+
+ if (subsys->desc->is_not_loadable)
+ return 0;
+
+ ret = wait_for_err_ready(subsys);
+ if (ret)
+ /* pil-boot succeeded but we need to shutdown
+ * the device because error ready timed out.
+ */
+ subsys->desc->stop(subsys->desc);
+ else
subsys_set_state(subsys, SUBSYS_ONLINE);
return ret;
@@ -770,6 +815,15 @@
}
EXPORT_SYMBOL(subsystem_crashed);
+void subsys_set_crash_status(struct subsys_device *dev, bool crashed)
+{
+ dev->crashed = true;
+}
+
+bool subsys_get_crash_status(struct subsys_device *dev)
+{
+ return dev->crashed;
+}
#ifdef CONFIG_DEBUG_FS
static ssize_t subsys_debugfs_read(struct file *filp, char __user *ubuf,
size_t cnt, loff_t *ppos)
@@ -895,6 +949,18 @@
ida_simple_remove(&subsys_ida, subsys->id);
kfree(subsys);
}
+static irqreturn_t subsys_err_ready_intr_handler(int irq, void *subsys)
+{
+ struct subsys_device *subsys_dev = subsys;
+ dev_info(subsys_dev->desc->dev,
+ "Subsystem error monitoring/handling services are up\n");
+
+ if (subsys_dev->desc->is_not_loadable)
+ return IRQ_HANDLED;
+
+ complete(&subsys_dev->err_ready);
+ return IRQ_HANDLED;
+}
static int subsys_misc_device_add(struct subsys_device *subsys_dev)
{
@@ -971,8 +1037,24 @@
goto err_register;
}
+ if (subsys->desc->err_ready_irq) {
+ ret = devm_request_irq(&subsys->dev,
+ subsys->desc->err_ready_irq,
+ subsys_err_ready_intr_handler,
+ IRQF_TRIGGER_RISING,
+ "error_ready_interrupt", subsys);
+ if (ret < 0) {
+ dev_err(&subsys->dev,
+ "[%s]: Unable to register err ready handler\n",
+ subsys->desc->name);
+ goto err_misc_device;
+ }
+ }
+
return subsys;
+err_misc_device:
+ subsys_misc_device_remove(subsys);
err_register:
subsys_debugfs_remove(subsys);
err_debugfs:
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index 06a4c29..1ea0f2d 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -36,7 +36,8 @@
#include <mach/socinfo.h>
#if defined(CONFIG_MSM_SMD)
-#include "smd_private.h"
+#include <mach/msm_smem.h>
+#include <mach/msm_smsm.h>
#endif
#include "timer.h"
diff --git a/arch/arm/mach-msm/wallclk.c b/arch/arm/mach-msm/wallclk.c
new file mode 100644
index 0000000..9624795
--- /dev/null
+++ b/arch/arm/mach-msm/wallclk.c
@@ -0,0 +1,475 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/semaphore.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/hrtimer.h>
+#include <linux/timer.h>
+#include <linux/io.h>
+#include <linux/ctype.h>
+#include <linux/uaccess.h>
+#include <linux/errno.h>
+
+#include <mach/msm_iomap.h>
+
+#include "wallclk.h"
+
+#define WALLCLK_MODULE_NAME "wallclk"
+#define WALLCLK_MODULE_NAME_LEN 10
+
+#define FLAG_WALLCLK_INITED 0x1
+#define FLAG_WALLCLK_SFN_REF_SET 0x2
+#define FLAG_WALLCLK_ENABLED 0x4
+
+#define WALLCLK_TIMER_INTERVAL_NS 100000
+
+#define WALLCLK_SHARED_MEM_SIZE 1024
+#define ALIGN_64(addr) (((addr) + 7) & ~0x7)
+
+#define GPS_EPOCH_DIFF 315964800
+
+struct wallclk_cnt {
+ u32 pulse;
+ u32 clk;
+};
+
+struct wallclk_reg {
+ u32 ctrl;
+ u32 pulse_cnt;
+ u32 snapshot_clock_cnt;
+ u32 clock_cnt;
+ u32 __unused__[5];
+ u32 base_time0;
+ u32 base_time1;
+};
+
+struct wallclk_sm {
+ struct wallclk_reg reg;
+ u32 sfn_ref;
+};
+
+struct wallclk_cfg {
+ u32 ppns;
+ u32 clk_rate;
+ u32 clk_rate_v; /* clk_rate = clk_rate_v x clk_rate_p */
+ u32 clk_rate_p; /* power of 10 */
+ u32 ns_per_clk_rate_v;
+};
+
+struct wallclk {
+ struct wallclk_sm *shm;
+
+ struct wallclk_cfg cfg;
+
+ struct timespec tv;
+
+ struct hrtimer timer;
+ ktime_t interval;
+
+ spinlock_t lock;
+ u32 flags;
+
+ char name[WALLCLK_MODULE_NAME_LEN];
+};
+
+static struct wallclk wall_clk;
+
+static inline int is_valid_register(u32 offset)
+{
+ int rc = 0;
+
+ switch (offset) {
+ case CTRL_REG_OFFSET:
+ case PULSE_CNT_REG_OFFSET:
+ case CLK_CNT_SNAPSHOT_REG_OFFSET:
+ case CLK_CNT_REG_OFFSET:
+ case CLK_BASE_TIME0_OFFSET:
+ case CLK_BASE_TIME1_OFFSET:
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+ return rc;
+}
+
+static inline void wallclk_ctrl_reg_set(struct wallclk *wclk, u32 v)
+{
+ struct wallclk_reg *reg = &wclk->shm->reg;
+
+ if (v & CTRL_ENABLE_MASK) {
+ if (!(wclk->flags & FLAG_WALLCLK_ENABLED)) {
+ getnstimeofday(&wclk->tv);
+ __raw_writel(0, ®->snapshot_clock_cnt);
+ __raw_writel(0, ®->clock_cnt);
+ __raw_writel(0, ®->pulse_cnt);
+ hrtimer_start(&wclk->timer,
+ wclk->interval,
+ HRTIMER_MODE_REL);
+ wclk->flags |= FLAG_WALLCLK_ENABLED;
+ }
+ } else {
+ if (wclk->flags & FLAG_WALLCLK_ENABLED) {
+ hrtimer_cancel(&wclk->timer);
+ wclk->flags &= ~FLAG_WALLCLK_ENABLED;
+ }
+ }
+
+ __raw_writel(v, ®->ctrl);
+}
+
+static inline void wallclk_cfg_init(struct wallclk_cfg *cfg,
+ u32 ppns,
+ u32 clk_rate)
+{
+ cfg->ppns = ppns;
+ cfg->clk_rate = clk_rate;
+ cfg->clk_rate_v = clk_rate;
+ cfg->clk_rate_p = 1;
+ cfg->ns_per_clk_rate_v = 1000000000;
+
+ while (!(cfg->clk_rate_v % 10)) {
+ cfg->clk_rate_v /= 10;
+ cfg->clk_rate_p *= 10;
+ cfg->ns_per_clk_rate_v /= 10;
+ }
+}
+
+static inline struct timespec timestamp_convert(const struct timespec *tv)
+{
+ struct timespec rc;
+
+ rc.tv_sec = tv->tv_sec - GPS_EPOCH_DIFF;
+ rc.tv_nsec = tv->tv_nsec;
+
+ return rc;
+}
+
+static inline void timespec_delta_to_wclk_cnt(const struct timespec *tv,
+ const struct wallclk_cfg *cfg,
+ struct wallclk_cnt *wclk_cnt)
+{
+ long ns;
+
+ wclk_cnt->pulse = tv->tv_sec / cfg->ppns;
+ wclk_cnt->clk = (tv->tv_sec % cfg->ppns) * cfg->clk_rate;
+
+ ns = tv->tv_nsec;
+ while (ns >= cfg->ns_per_clk_rate_v) {
+ ns -= cfg->ns_per_clk_rate_v;
+ wclk_cnt->clk += cfg->clk_rate_v;
+ }
+
+ wclk_cnt->clk += (ns * cfg->clk_rate_v)/cfg->ns_per_clk_rate_v;
+}
+
+static inline u32 wallclk_cnt_to_sfn(const struct wallclk_cnt *cnt,
+ const struct wallclk_cfg *cfg)
+{
+ u32 sfn;
+ u32 delta, p;
+
+ sfn = SFN_PER_SECOND * cnt->pulse * cfg->ppns;
+ if (cfg->clk_rate_p > 100) {
+ p = cfg->clk_rate_p/100;
+ delta = cnt->clk/(cfg->clk_rate_v * p);
+ } else {
+ p = 100/cfg->clk_rate_p;
+ delta = (cnt->clk * p)/cfg->clk_rate_v;
+ }
+ sfn += delta;
+
+ return sfn;
+}
+
+static void update_wallclk(struct wallclk *wclk)
+{
+ struct timespec tv;
+ struct timespec delta_tv;
+ struct wallclk_cnt cnt;
+ struct wallclk_reg *reg = &wclk->shm->reg;
+
+ spin_lock(&wclk->lock);
+ getnstimeofday(&tv);
+ delta_tv = timespec_sub(tv, wclk->tv);
+ timespec_delta_to_wclk_cnt(&delta_tv, &wclk->cfg, &cnt);
+ __raw_writel(cnt.pulse, ®->pulse_cnt);
+ __raw_writel(cnt.clk, ®->clock_cnt);
+ __raw_writel(cnt.clk, ®->snapshot_clock_cnt);
+
+ spin_unlock(&wclk->lock);
+}
+
+static int set_sfn(struct wallclk *wclk, u16 sfn)
+{
+ int rc = 0;
+ struct wallclk_reg *reg = &wclk->shm->reg;
+ u32 v;
+ struct timespec ts;
+
+ if (sfn > MAX_SFN) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (!(wclk->flags & FLAG_WALLCLK_INITED)) {
+ rc = -EIO;
+ goto out;
+ }
+
+ spin_lock_bh(&wclk->lock);
+
+ v = __raw_readl(®->ctrl);
+ wallclk_ctrl_reg_set(wclk, v & ~CTRL_ENABLE_MASK);
+
+ getnstimeofday(&wclk->tv);
+ ts = timestamp_convert(&wclk->tv);
+ __raw_writel(ts.tv_sec, ®->base_time0);
+ __raw_writel(ts.tv_nsec, ®->base_time1);
+
+ wclk->shm->sfn_ref = sfn;
+ wclk->flags |= FLAG_WALLCLK_SFN_REF_SET;
+
+ __raw_writel(0, ®->pulse_cnt);
+ __raw_writel(0, ®->clock_cnt);
+ __raw_writel(0, ®->snapshot_clock_cnt);
+ hrtimer_start(&wclk->timer, wclk->interval, HRTIMER_MODE_REL);
+ wclk->flags |= FLAG_WALLCLK_ENABLED;
+ __raw_writel(v | CTRL_ENABLE_MASK, ®->ctrl);
+
+ spin_unlock_bh(&wclk->lock);
+
+out:
+ return rc;
+}
+
+static int get_sfn(struct wallclk *wclk)
+{
+ struct wallclk_cnt cnt;
+ int rc = 0;
+ u32 sfn;
+
+ if (!(wclk->flags & FLAG_WALLCLK_INITED)) {
+ rc = -EIO;
+ goto out;
+ }
+
+ spin_lock_bh(&wclk->lock);
+
+ if (!(wclk->flags & FLAG_WALLCLK_ENABLED) ||
+ !(wclk->flags & FLAG_WALLCLK_SFN_REF_SET)) {
+ rc = -EIO;
+ goto unlock;
+ }
+
+ cnt.pulse = __raw_readl(&(wclk->shm->reg.pulse_cnt));
+ cnt.clk = __raw_readl(&(wclk->shm->reg.clock_cnt));
+ sfn = wallclk_cnt_to_sfn(&cnt, &wclk->cfg);
+
+ sfn += wclk->shm->sfn_ref;
+ rc = sfn & MAX_SFN;
+
+unlock:
+ spin_unlock_bh(&wclk->lock);
+out:
+ return rc;
+}
+
+enum hrtimer_restart wallclk_timer_cb(struct hrtimer *timer)
+{
+ update_wallclk(&wall_clk);
+ hrtimer_forward_now(timer, wall_clk.interval);
+ return HRTIMER_RESTART;
+}
+
+int wallclk_set_sfn(u16 sfn)
+{
+ return set_sfn(&wall_clk, sfn);
+}
+EXPORT_SYMBOL_GPL(wallclk_set_sfn);
+
+int wallclk_get_sfn(void)
+{
+ return get_sfn(&wall_clk);
+}
+EXPORT_SYMBOL_GPL(wallclk_get_sfn);
+
+int wallclk_set_sfn_ref(u16 sfn)
+{
+ int rc = 0;
+
+ if (sfn > MAX_SFN) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (!(wall_clk.flags & FLAG_WALLCLK_INITED)) {
+ rc = -EIO;
+ goto out;
+ }
+
+ spin_lock_bh(&wall_clk.lock);
+
+ wall_clk.shm->sfn_ref = sfn;
+ wall_clk.flags |= FLAG_WALLCLK_SFN_REF_SET;
+
+ spin_unlock_bh(&wall_clk.lock);
+
+out:
+ return rc;
+}
+EXPORT_SYMBOL_GPL(wallclk_set_sfn_ref);
+
+int wallclk_get_sfn_ref(void)
+{
+ int rc = 0;
+
+ if (!(wall_clk.flags & FLAG_WALLCLK_INITED)) {
+ rc = -EIO;
+ goto out;
+ }
+
+ spin_lock_bh(&wall_clk.lock);
+
+ if (!(wall_clk.flags & FLAG_WALLCLK_SFN_REF_SET)) {
+ rc = -EAGAIN;
+ goto unlock;
+ }
+ rc = wall_clk.shm->sfn_ref;
+
+unlock:
+ spin_unlock_bh(&wall_clk.lock);
+out:
+ return rc;
+}
+EXPORT_SYMBOL_GPL(wallclk_get_sfn_ref);
+
+int wallclk_reg_read(u32 offset, u32 *p)
+{
+ int rc = 0;
+
+ if (!(wall_clk.flags & FLAG_WALLCLK_INITED)) {
+ rc = -EIO;
+ goto out;
+ }
+
+ if (!is_valid_register(offset)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ spin_lock_bh(&wall_clk.lock);
+ *p = __raw_readl((char *)&wall_clk.shm->reg + offset);
+ spin_unlock_bh(&wall_clk.lock);
+out:
+ return rc;
+}
+EXPORT_SYMBOL_GPL(wallclk_reg_read);
+
+int wallclk_reg_write(u32 offset, u32 val)
+{
+ int rc = 0;
+ char *p;
+
+ if (!(wall_clk.flags & FLAG_WALLCLK_INITED)) {
+ rc = -EIO;
+ goto out;
+ }
+
+ p = (char *)&wall_clk.shm->reg;
+
+ spin_lock_bh(&wall_clk.lock);
+ switch (offset) {
+ case CTRL_REG_OFFSET:
+ wallclk_ctrl_reg_set(&wall_clk, val);
+ break;
+ case PULSE_CNT_REG_OFFSET:
+ case CLK_BASE_TIME0_OFFSET:
+ case CLK_BASE_TIME1_OFFSET:
+ __raw_writel(val, p + offset);
+ break;
+ case CLK_CNT_REG_OFFSET:
+ __raw_writel(val, p + CLK_CNT_REG_OFFSET);
+ __raw_writel(val, p + CLK_CNT_SNAPSHOT_REG_OFFSET);
+ break;
+ case CLK_CNT_SNAPSHOT_REG_OFFSET:
+ rc = -EIO;
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ spin_unlock_bh(&wall_clk.lock);
+out:
+ return rc;
+}
+EXPORT_SYMBOL_GPL(wallclk_reg_write);
+
+static int __init wallclk_init(void)
+{
+ int rc = 0;
+ u32 addr;
+
+ memset(&wall_clk, 0, sizeof(wall_clk));
+
+ addr = (u32)MSM_SHARED_RAM_BASE + MSM_SHARED_RAM_SIZE -
+ WALLCLK_SHARED_MEM_SIZE;
+ wall_clk.shm = (struct wallclk_sm *)ALIGN_64(addr);
+
+ __raw_writel(0, &(wall_clk.shm->reg.ctrl));
+ __raw_writel(0, &(wall_clk.shm->reg.pulse_cnt));
+ __raw_writel(0, &(wall_clk.shm->reg.snapshot_clock_cnt));
+ __raw_writel(0, &(wall_clk.shm->reg.clock_cnt));
+ __raw_writel(0, &(wall_clk.shm->reg.clock_cnt));
+ __raw_writel(0, &(wall_clk.shm->reg.base_time0));
+ __raw_writel(0, &(wall_clk.shm->reg.base_time1));
+
+ wall_clk.shm->sfn_ref = 0;
+
+ wallclk_cfg_init(&wall_clk.cfg, PPNS_PULSE, CLK_RATE);
+
+ strlcpy(wall_clk.name, WALLCLK_MODULE_NAME, WALLCLK_MODULE_NAME_LEN);
+
+ hrtimer_init(&wall_clk.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ wall_clk.timer.function = wallclk_timer_cb;
+ wall_clk.interval = ns_to_ktime(WALLCLK_TIMER_INTERVAL_NS);
+ spin_lock_init(&wall_clk.lock);
+
+ wall_clk.flags |= FLAG_WALLCLK_INITED;
+
+ printk(KERN_INFO "%s: clk_rate=%u ppns=%u clk_reg_addr=0x%x\n",
+ wall_clk.name, wall_clk.cfg.clk_rate, wall_clk.cfg.ppns,
+ (int)(&wall_clk.shm->reg));
+ return rc;
+}
+
+static void __exit wallclk_exit(void)
+{
+ if (wall_clk.flags & FLAG_WALLCLK_INITED) {
+ spin_lock_bh(&wall_clk.lock);
+ wallclk_ctrl_reg_set(&wall_clk, 0);
+ wall_clk.flags = 0;
+ spin_unlock_bh(&wall_clk.lock);
+ }
+}
+
+module_init(wallclk_init);
+module_exit(wallclk_exit);
+
+MODULE_DESCRIPTION("Wall clock");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/wallclk.h b/arch/arm/mach-msm/wallclk.h
new file mode 100644
index 0000000..1794395
--- /dev/null
+++ b/arch/arm/mach-msm/wallclk.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2013, 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.
+ *
+ */
+#ifndef _WALLCLK_H
+#define _WALLCLK_H
+
+/* wallclock register offset */
+#define CTRL_REG_OFFSET 0x0
+#define PULSE_CNT_REG_OFFSET 0x4
+#define CLK_CNT_SNAPSHOT_REG_OFFSET 0x8
+#define CLK_CNT_REG_OFFSET 0xC
+#define CLK_BASE_TIME0_OFFSET 0x24
+#define CLK_BASE_TIME1_OFFSET 0x28
+
+/* ctrl register bitmap */
+#define CTRL_TIME_SRC_POS 0
+#define CTRL_TIME_SRC_MASK 0x0000000F
+#define CTRL_SW_BITS_POS 4
+#define CTRL_SW_BITS_MASK 0x7FFFFFF0
+#define CTRL_ENA_DIS_POS 31
+#define CTRL_ENABLE_MASK 0x80000000
+
+/* clock rate from time source */
+#define CLK_RATE 122880000 /* 122.88 Mhz */
+#define PPNS_PULSE 2 /* PP2S */
+
+#define MAX_SFN 1023
+#define SFN_PER_SECOND 100
+
+extern int wallclk_set_sfn(u16 sfn);
+extern int wallclk_get_sfn(void);
+extern int wallclk_set_sfn_ref(u16 sfn);
+extern int wallclk_get_sfn_ref(void);
+extern int wallclk_reg_read(u32 offset, u32 *p);
+extern int wallclk_reg_write(u32 offset, u32 value);
+
+#endif /* _WALLCLK_H */
diff --git a/arch/arm/mach-msm/wallclk_sysfs.c b/arch/arm/mach-msm/wallclk_sysfs.c
new file mode 100644
index 0000000..9274137
--- /dev/null
+++ b/arch/arm/mach-msm/wallclk_sysfs.c
@@ -0,0 +1,308 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/semaphore.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/ctype.h>
+#include <linux/uaccess.h>
+#include <linux/errno.h>
+
+#include "wallclk.h"
+
+#define WALLCLK_SYSFS_MODULE_NAME "wallclk_sysfs"
+
+static struct kobject *wallclk_kobj;
+
+static ssize_t sfn_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ int rc;
+
+ rc = wallclk_get_sfn();
+ if (rc < 0)
+ return rc;
+ return snprintf(buf, 10, "%d\n", rc);
+}
+
+static ssize_t sfn_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ u16 sfn;
+ int rc;
+
+ if (kstrtou16(buf, 0, &sfn)) {
+ printk(KERN_ERR "%s: sfn input is not a valid u16 value\n",
+ WALLCLK_SYSFS_MODULE_NAME);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = wallclk_set_sfn(sfn);
+
+ if (rc) {
+ printk(KERN_ERR "%s: fail to set sfn\n",
+ WALLCLK_SYSFS_MODULE_NAME);
+ goto out;
+ }
+ rc = count;
+
+out:
+ return rc;
+}
+
+static struct kobj_attribute sfn_attribute =
+ __ATTR(sfn, 0666, sfn_show, sfn_store);
+
+static ssize_t sfn_ref_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ int rc;
+
+ rc = wallclk_get_sfn_ref();
+ if (rc < 0)
+ return rc;
+ return snprintf(buf, 10, "%d\n", rc);
+}
+
+static ssize_t sfn_ref_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ u16 sfn_ref;
+ int rc;
+
+ if (kstrtou16(buf, 0, &sfn_ref)) {
+ printk(KERN_ERR "%s: sfn_ref input is not a valid u16 value\n",
+ WALLCLK_SYSFS_MODULE_NAME);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = wallclk_set_sfn_ref(sfn_ref);
+
+ if (rc) {
+ printk(KERN_ERR "%s: fail to set sfn_ref\n",
+ WALLCLK_SYSFS_MODULE_NAME);
+ goto out;
+ }
+ rc = count;
+
+out:
+ return rc;
+}
+
+static struct kobj_attribute sfn_ref_attribute =
+ __ATTR(sfn_ref, 0666, sfn_ref_show, sfn_ref_store);
+
+static ssize_t reg_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf,
+ u32 offset)
+{
+ int rc;
+ u32 val;
+
+ rc = wallclk_reg_read(offset, &val);
+ if (rc)
+ return rc;
+
+ return snprintf(buf, 20, "%08x\n", val);
+}
+
+static ssize_t reg_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ const size_t count,
+ u32 offset)
+{
+ u32 v;
+ int rc;
+
+ if (kstrtou32(buf, 0, &v)) {
+ printk(KERN_ERR "%s: input is not a valid u32 value\n",
+ WALLCLK_SYSFS_MODULE_NAME);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = wallclk_reg_write(offset, v);
+
+ if (rc) {
+ printk(KERN_ERR "%s: fail to set register(offset=0x%x)\n",
+ WALLCLK_SYSFS_MODULE_NAME, offset);
+ goto out;
+ }
+ rc = count;
+
+out:
+ return rc;
+}
+
+static ssize_t ctrl_reg_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return reg_show(kobj, attr, buf, CTRL_REG_OFFSET);
+}
+
+static ssize_t ctrl_reg_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ const size_t count)
+{
+ return reg_store(kobj, attr, buf, count, CTRL_REG_OFFSET);
+}
+
+static struct kobj_attribute ctrl_reg_attribute =
+ __ATTR(ctrl_reg, 0666, ctrl_reg_show, ctrl_reg_store);
+
+static ssize_t basetime0_reg_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return reg_show(kobj, attr, buf, CLK_BASE_TIME0_OFFSET);
+}
+
+static ssize_t basetime0_reg_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ return reg_store(kobj, attr, buf, count, CLK_BASE_TIME0_OFFSET);
+}
+
+static struct kobj_attribute basetime0_reg_attribute =
+ __ATTR(base_time0_reg, 0666, basetime0_reg_show, basetime0_reg_store);
+
+static ssize_t basetime1_reg_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return reg_show(kobj, attr, buf, CLK_BASE_TIME1_OFFSET);
+}
+
+static ssize_t basetime1_reg_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ return reg_store(kobj, attr, buf, count, CLK_BASE_TIME1_OFFSET);
+}
+
+static struct kobj_attribute basetime1_reg_attribute =
+ __ATTR(base_time1_reg, 0666, basetime1_reg_show, basetime1_reg_store);
+
+static ssize_t pulse_cnt_reg_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return reg_show(kobj, attr, buf, PULSE_CNT_REG_OFFSET);
+}
+
+static ssize_t pulse_cnt_reg_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ return reg_store(kobj, attr, buf, count, PULSE_CNT_REG_OFFSET);
+}
+
+static struct kobj_attribute pulse_cnt_reg_attribute =
+ __ATTR(pulse_cnt_reg, 0666, pulse_cnt_reg_show, pulse_cnt_reg_store);
+
+static ssize_t clk_cnt_reg_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return reg_show(kobj, attr, buf, CLK_CNT_REG_OFFSET);
+}
+
+static ssize_t clk_cnt_reg_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ return reg_store(kobj, attr, buf, count, CLK_CNT_REG_OFFSET);
+}
+
+static struct kobj_attribute clk_cnt_reg_attribute =
+ __ATTR(clock_cnt_reg, 0666, clk_cnt_reg_show, clk_cnt_reg_store);
+
+static ssize_t clk_cnt_snapshot_reg_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return reg_show(kobj, attr, buf, CLK_CNT_SNAPSHOT_REG_OFFSET);
+}
+
+static struct kobj_attribute clk_cnt_snapshot_reg_attribute =
+ __ATTR(clock_cnt_snapshot_reg, 0444, clk_cnt_snapshot_reg_show, NULL);
+
+static struct attribute *wallclk_attrs[] = {
+ &sfn_attribute.attr,
+ &sfn_ref_attribute.attr,
+ &ctrl_reg_attribute.attr,
+ &pulse_cnt_reg_attribute.attr,
+ &clk_cnt_snapshot_reg_attribute.attr,
+ &clk_cnt_reg_attribute.attr,
+ &basetime0_reg_attribute.attr,
+ &basetime1_reg_attribute.attr,
+ NULL
+};
+
+static struct attribute_group wallclk_attr_group = {
+ .attrs = wallclk_attrs,
+};
+
+static int __init wallclk_sysfs_init(void)
+{
+ int rc;
+
+ wallclk_kobj = kobject_create_and_add("wallclk", kernel_kobj);
+ if (!wallclk_kobj) {
+ printk(KERN_ERR "%s: failed to create kobject\n",
+ WALLCLK_SYSFS_MODULE_NAME);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = sysfs_create_group(wallclk_kobj, &wallclk_attr_group);
+ if (rc) {
+ kobject_put(wallclk_kobj);
+ printk(KERN_ERR "%s: failed to create sysfs group\n",
+ WALLCLK_SYSFS_MODULE_NAME);
+ }
+
+out:
+ return rc;
+}
+
+static void __exit wallclk_sysfs_exit(void)
+{
+ kobject_put(wallclk_kobj);
+}
+
+module_init(wallclk_sysfs_init);
+module_exit(wallclk_sysfs_exit);
+
+MODULE_DESCRIPTION("Wall clock SysFS");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 1c8a25d..4ff4b3e 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -747,16 +747,17 @@
if (PageHighMem(page)) {
if (len + offset > PAGE_SIZE)
len = PAGE_SIZE - offset;
- vaddr = kmap_high_get(page);
- if (vaddr) {
- vaddr += offset;
- op(vaddr, len, dir);
- kunmap_high(page);
- } else if (cache_is_vipt()) {
- /* unmapped pages might still be cached */
+
+ if (cache_is_vipt_nonaliasing()) {
vaddr = kmap_atomic(page);
op(vaddr + offset, len, dir);
kunmap_atomic(vaddr);
+ } else {
+ vaddr = kmap_high_get(page);
+ if (vaddr) {
+ op(vaddr + offset, len, dir);
+ kunmap_high(page);
+ }
}
} else {
vaddr = page_address(page) + offset;
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c
index 7745854..839a18e 100644
--- a/arch/arm/mm/flush.c
+++ b/arch/arm/mm/flush.c
@@ -170,15 +170,18 @@
if (!PageHighMem(page)) {
__cpuc_flush_dcache_area(page_address(page), PAGE_SIZE);
} else {
- void *addr = kmap_high_get(page);
- if (addr) {
- __cpuc_flush_dcache_area(addr, PAGE_SIZE);
- kunmap_high(page);
- } else if (cache_is_vipt()) {
- /* unmapped pages might still be cached */
+ void *addr;
+
+ if (cache_is_vipt_nonaliasing()) {
addr = kmap_atomic(page);
__cpuc_flush_dcache_area(addr, PAGE_SIZE);
kunmap_atomic(addr);
+ } else {
+ addr = kmap_high_get(page);
+ if (addr) {
+ __cpuc_flush_dcache_area(addr, PAGE_SIZE);
+ kunmap_high(page);
+ }
}
}
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 26b92d4..34cb153 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -395,32 +395,29 @@
unsigned long hole_end_virt;
/*
- * Find the start and end of the hole, using meminfo
- * if it hasnt been found already.
+ * Find the start and end of the hole, using meminfo.
*/
- if (memory_hole_start == 0 && memory_hole_end == 0) {
- for (i = 0; i < (meminfo.nr_banks - 1); i++) {
- if ((meminfo.bank[i].start + meminfo.bank[i].size) !=
+ for (i = 0; i < (meminfo.nr_banks - 1); i++) {
+ if ((meminfo.bank[i].start + meminfo.bank[i].size) !=
meminfo.bank[i+1].start) {
- if (meminfo.bank[i].start + meminfo.bank[i].size
+ if (meminfo.bank[i].start + meminfo.bank[i].size
<= MAX_HOLE_ADDRESS) {
- hole_start = meminfo.bank[i].start +
+ hole_start = meminfo.bank[i].start +
meminfo.bank[i].size;
- hole_size = meminfo.bank[i+1].start -
+ hole_size = meminfo.bank[i+1].start -
hole_start;
- if (memory_hole_start == 0 &&
+ if (memory_hole_start == 0 &&
memory_hole_end == 0) {
- memory_hole_start = hole_start;
- memory_hole_end = hole_start +
+ memory_hole_start = hole_start;
+ memory_hole_end = hole_start +
hole_size;
- } else if ((memory_hole_end -
+ } else if ((memory_hole_end -
memory_hole_start) <= hole_size) {
- memory_hole_start = hole_start;
- memory_hole_end = hole_start +
+ memory_hole_start = hole_start;
+ memory_hole_end = hole_start +
hole_size;
- }
}
}
}
diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c
index 8df41e2..07b6f6c 100644
--- a/arch/arm/mm/ioremap.c
+++ b/arch/arm/mm/ioremap.c
@@ -280,11 +280,11 @@
return (void __iomem *) (offset + addr);
}
-void __iomem *__arm_ioremap_caller(unsigned long phys_addr, size_t size,
+void __iomem *__arm_ioremap_caller(phys_addr_t phys_addr, size_t size,
unsigned int mtype, void *caller)
{
- unsigned long last_addr;
- unsigned long offset = phys_addr & ~PAGE_MASK;
+ phys_addr_t last_addr;
+ phys_addr_t offset = phys_addr & ~PAGE_MASK;
unsigned long pfn = __phys_to_pfn(phys_addr);
/*
@@ -316,12 +316,12 @@
}
EXPORT_SYMBOL(__arm_ioremap_pfn);
-void __iomem * (*arch_ioremap_caller)(unsigned long, size_t,
+void __iomem * (*arch_ioremap_caller)(phys_addr_t, size_t,
unsigned int, void *) =
__arm_ioremap_caller;
void __iomem *
-__arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype)
+__arm_ioremap(phys_addr_t phys_addr, size_t size, unsigned int mtype)
{
return arch_ioremap_caller(phys_addr, size, mtype,
__builtin_return_address(0));
@@ -336,7 +336,7 @@
* CONFIG_GENERIC_ALLOCATOR for allocating external memory.
*/
void __iomem *
-__arm_ioremap_exec(unsigned long phys_addr, size_t size, bool cached)
+__arm_ioremap_exec(phys_addr_t phys_addr, size_t size, bool cached)
{
unsigned int mtype;
diff --git a/arch/arm/mm/mmap.c b/arch/arm/mm/mmap.c
index 06262c5..ce8cb19 100644
--- a/arch/arm/mm/mmap.c
+++ b/arch/arm/mm/mmap.c
@@ -11,10 +11,49 @@
#include <linux/random.h>
#include <asm/cachetype.h>
+static inline unsigned long COLOUR_ALIGN_DOWN(unsigned long addr,
+ unsigned long pgoff)
+{
+ unsigned long base = addr & ~(SHMLBA-1);
+ unsigned long off = (pgoff << PAGE_SHIFT) & (SHMLBA-1);
+
+ if (base + off <= addr)
+ return base + off;
+
+ return base - off;
+}
+
#define COLOUR_ALIGN(addr,pgoff) \
((((addr)+SHMLBA-1)&~(SHMLBA-1)) + \
(((pgoff)<<PAGE_SHIFT) & (SHMLBA-1)))
+/* gap between mmap and stack */
+#define MIN_GAP (128*1024*1024UL)
+#define MAX_GAP ((TASK_SIZE)/6*5)
+
+static int mmap_is_legacy(void)
+{
+ if (current->personality & ADDR_COMPAT_LAYOUT)
+ return 1;
+
+ if (rlimit(RLIMIT_STACK) == RLIM_INFINITY)
+ return 1;
+
+ return sysctl_legacy_va_layout;
+}
+
+static unsigned long mmap_base(unsigned long rnd)
+{
+ unsigned long gap = rlimit(RLIMIT_STACK);
+
+ if (gap < MIN_GAP)
+ gap = MIN_GAP;
+ else if (gap > MAX_GAP)
+ gap = MAX_GAP;
+
+ return PAGE_ALIGN(TASK_SIZE - gap - rnd);
+}
+
/*
* We need to ensure that shared mappings are correctly aligned to
* avoid aliasing issues with VIPT caches. We need to ensure that
@@ -68,13 +107,9 @@
if (len > mm->cached_hole_size) {
start_addr = addr = mm->free_area_cache;
} else {
- start_addr = addr = TASK_UNMAPPED_BASE;
+ start_addr = addr = mm->mmap_base;
mm->cached_hole_size = 0;
}
- /* 8 bits of randomness in 20 address space bits */
- if ((current->flags & PF_RANDOMIZE) &&
- !(current->personality & ADDR_NO_RANDOMIZE))
- addr += (get_random_int() % (1 << 8)) << PAGE_SHIFT;
full_search:
if (do_align)
@@ -111,6 +146,134 @@
}
}
+unsigned long
+arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
+ const unsigned long len, const unsigned long pgoff,
+ const unsigned long flags)
+{
+ struct vm_area_struct *vma;
+ struct mm_struct *mm = current->mm;
+ unsigned long addr = addr0;
+ int do_align = 0;
+ int aliasing = cache_is_vipt_aliasing();
+
+ /*
+ * We only need to do colour alignment if either the I or D
+ * caches alias.
+ */
+ if (aliasing)
+ do_align = filp || (flags & MAP_SHARED);
+
+ /* requested length too big for entire address space */
+ if (len > TASK_SIZE)
+ return -ENOMEM;
+
+ if (flags & MAP_FIXED) {
+ if (aliasing && flags & MAP_SHARED &&
+ (addr - (pgoff << PAGE_SHIFT)) & (SHMLBA - 1))
+ return -EINVAL;
+ return addr;
+ }
+
+ /* requesting a specific address */
+ if (addr) {
+ if (do_align)
+ addr = COLOUR_ALIGN(addr, pgoff);
+ else
+ addr = PAGE_ALIGN(addr);
+ vma = find_vma(mm, addr);
+ if (TASK_SIZE - len >= addr &&
+ (!vma || addr + len <= vma->vm_start))
+ return addr;
+ }
+
+ /* check if free_area_cache is useful for us */
+ if (len <= mm->cached_hole_size) {
+ mm->cached_hole_size = 0;
+ mm->free_area_cache = mm->mmap_base;
+ }
+
+ /* either no address requested or can't fit in requested address hole */
+ addr = mm->free_area_cache;
+ if (do_align) {
+ unsigned long base = COLOUR_ALIGN_DOWN(addr - len, pgoff);
+ addr = base + len;
+ }
+
+ /* make sure it can fit in the remaining address space */
+ if (addr > len) {
+ vma = find_vma(mm, addr-len);
+ if (!vma || addr <= vma->vm_start)
+ /* remember the address as a hint for next time */
+ return (mm->free_area_cache = addr-len);
+ }
+
+ if (mm->mmap_base < len)
+ goto bottomup;
+
+ addr = mm->mmap_base - len;
+ if (do_align)
+ addr = COLOUR_ALIGN_DOWN(addr, pgoff);
+
+ do {
+ /*
+ * Lookup failure means no vma is above this address,
+ * else if new region fits below vma->vm_start,
+ * return with success:
+ */
+ vma = find_vma(mm, addr);
+ if (!vma || addr+len <= vma->vm_start)
+ /* remember the address as a hint for next time */
+ return (mm->free_area_cache = addr);
+
+ /* remember the largest hole we saw so far */
+ if (addr + mm->cached_hole_size < vma->vm_start)
+ mm->cached_hole_size = vma->vm_start - addr;
+
+ /* try just below the current vma->vm_start */
+ addr = vma->vm_start - len;
+ if (do_align)
+ addr = COLOUR_ALIGN_DOWN(addr, pgoff);
+ } while (len < vma->vm_start);
+
+bottomup:
+ /*
+ * A failed mmap() very likely causes application failure,
+ * so fall back to the bottom-up function here. This scenario
+ * can happen with large stack limits and large mmap()
+ * allocations.
+ */
+ mm->cached_hole_size = ~0UL;
+ mm->free_area_cache = TASK_UNMAPPED_BASE;
+ addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags);
+ /*
+ * Restore the topdown base:
+ */
+ mm->free_area_cache = mm->mmap_base;
+ mm->cached_hole_size = ~0UL;
+
+ return addr;
+}
+
+void arch_pick_mmap_layout(struct mm_struct *mm)
+{
+ unsigned long random_factor = 0UL;
+
+ /* 8 bits of randomness in 20 address space bits */
+ if ((current->flags & PF_RANDOMIZE) &&
+ !(current->personality & ADDR_NO_RANDOMIZE))
+ random_factor = (get_random_int() % (1 << 8)) << PAGE_SHIFT;
+
+ if (mmap_is_legacy()) {
+ mm->mmap_base = TASK_UNMAPPED_BASE + random_factor;
+ mm->get_unmapped_area = arch_get_unmapped_area;
+ mm->unmap_area = arch_unmap_area;
+ } else {
+ mm->mmap_base = mmap_base(random_factor);
+ mm->get_unmapped_area = arch_get_unmapped_area_topdown;
+ mm->unmap_area = arch_unmap_area_topdown;
+ }
+}
/*
* You really shouldn't be using read() or write() on /dev/mem. This
diff --git a/block/blk-core.c b/block/blk-core.c
index 69764df..153240e 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1086,7 +1086,7 @@
* urgent requests. We want to be able to track this
* down.
*/
- pr_err("%s(): requeueing an URGENT request", __func__);
+ pr_debug("%s(): requeueing an URGENT request", __func__);
WARN_ON(!q->dispatched_urgent);
q->dispatched_urgent = false;
}
@@ -1123,7 +1123,7 @@
* urgent requests. We want to be able to track this
* down.
*/
- pr_err("%s(): requeueing an URGENT request", __func__);
+ pr_debug("%s(): reinserting an URGENT request", __func__);
WARN_ON(!q->dispatched_urgent);
q->dispatched_urgent = false;
}
@@ -2018,6 +2018,10 @@
* not be passed by new incoming requests
*/
rq->cmd_flags |= REQ_STARTED;
+ if (rq->cmd_flags & REQ_URGENT) {
+ WARN_ON(q->dispatched_urgent);
+ q->dispatched_urgent = true;
+ }
trace_block_rq_issue(q, rq);
}
@@ -2151,13 +2155,8 @@
struct request *rq;
rq = blk_peek_request(q);
- if (rq) {
- if (rq->cmd_flags & REQ_URGENT) {
- WARN_ON(q->dispatched_urgent);
- q->dispatched_urgent = true;
- }
+ if (rq)
blk_start_request(rq);
- }
return rq;
}
EXPORT_SYMBOL(blk_fetch_request);
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 26a06b8..770e1d6 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -715,12 +715,12 @@
if (error)
goto out_unregister;
+ klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
- klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
diff --git a/drivers/bluetooth/bluetooth-power.c b/drivers/bluetooth/bluetooth-power.c
index d7c69db..13c9080 100644
--- a/drivers/bluetooth/bluetooth-power.c
+++ b/drivers/bluetooth/bluetooth-power.c
@@ -23,38 +23,227 @@
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
+#include <linux/bluetooth-power.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
-static struct of_device_id ar3002_match_table[] = {
+#define BT_PWR_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg)
+#define BT_PWR_INFO(fmt, arg...) pr_info("%s: " fmt "\n" , __func__ , ## arg)
+#define BT_PWR_ERR(fmt, arg...) pr_err("%s: " fmt "\n" , __func__ , ## arg)
+
+
+static struct of_device_id bt_power_match_table[] = {
{ .compatible = "qca,ar3002" },
{}
};
-static int bt_reset_gpio;
-
+static struct bluetooth_power_platform_data *bt_power_pdata;
+static struct platform_device *btpdev;
static bool previous;
-static int bluetooth_power(int on)
+static int bt_vreg_init(struct bt_power_vreg_data *vreg)
{
- int rc;
+ int rc = 0;
+ struct device *dev = &btpdev->dev;
- pr_debug("%s bt_gpio= %d\n", __func__, bt_reset_gpio);
+ BT_PWR_DBG("vreg_get for : %s", vreg->name);
+
+ /* Get the regulator handle */
+ vreg->reg = regulator_get(dev, vreg->name);
+ if (IS_ERR(vreg->reg)) {
+ rc = PTR_ERR(vreg->reg);
+ pr_err("%s: regulator_get(%s) failed. rc=%d\n",
+ __func__, vreg->name, rc);
+ goto out;
+ }
+
+ if ((regulator_count_voltages(vreg->reg) > 0)
+ && (vreg->low_vol_level) && (vreg->high_vol_level))
+ vreg->set_voltage_sup = 1;
+
+out:
+ return rc;
+}
+
+static int bt_vreg_enable(struct bt_power_vreg_data *vreg)
+{
+ int rc = 0;
+
+ BT_PWR_DBG("vreg_en for : %s", vreg->name);
+
+ if (!vreg->is_enabled) {
+ if (vreg->set_voltage_sup) {
+ rc = regulator_set_voltage(vreg->reg,
+ vreg->low_vol_level,
+ vreg->high_vol_level);
+ if (rc < 0) {
+ BT_PWR_ERR("vreg_set_vol(%s) failed rc=%d\n",
+ vreg->name, rc);
+ goto out;
+ }
+ }
+
+ rc = regulator_enable(vreg->reg);
+ if (rc < 0) {
+ BT_PWR_ERR("regulator_enable(%s) failed. rc=%d\n",
+ vreg->name, rc);
+ goto out;
+ }
+ vreg->is_enabled = true;
+ }
+out:
+ return rc;
+}
+
+static int bt_vreg_disable(struct bt_power_vreg_data *vreg)
+{
+ int rc = 0;
+
+ if (!vreg)
+ return rc;
+
+ BT_PWR_DBG("vreg_disable for : %s", vreg->name);
+
+ if (vreg->is_enabled) {
+ rc = regulator_disable(vreg->reg);
+ if (rc < 0) {
+ BT_PWR_ERR("regulator_disable(%s) failed. rc=%d\n",
+ vreg->name, rc);
+ goto out;
+ }
+ vreg->is_enabled = false;
+
+ if (vreg->set_voltage_sup) {
+ /* Set the min voltage to 0 */
+ rc = regulator_set_voltage(vreg->reg,
+ 0,
+ vreg->high_vol_level);
+ if (rc < 0) {
+ BT_PWR_ERR("vreg_set_vol(%s) failed rc=%d\n",
+ vreg->name, rc);
+ goto out;
+
+ }
+ }
+ }
+out:
+ return rc;
+}
+
+static int bt_configure_vreg(struct bt_power_vreg_data *vreg)
+{
+ int rc = 0;
+
+ BT_PWR_DBG("config %s", vreg->name);
+
+ /* Get the regulator handle for vreg */
+ if (!(vreg->reg)) {
+ rc = bt_vreg_init(vreg);
+ if (rc < 0)
+ return rc;
+ }
+ rc = bt_vreg_enable(vreg);
+
+ return rc;
+}
+
+static int bt_configure_gpios(int on)
+{
+ int rc = 0;
+ int bt_reset_gpio = bt_power_pdata->bt_gpio_sys_rst;
+
+ BT_PWR_DBG("%s bt_gpio= %d on: %d", __func__, bt_reset_gpio, on);
+
if (on) {
+ rc = gpio_request(bt_reset_gpio, "bt_sys_rst_n");
+ if (rc) {
+ BT_PWR_ERR("unable to request gpio %d (%d)\n",
+ bt_reset_gpio, rc);
+ return rc;
+ }
+
+ rc = gpio_direction_output(bt_reset_gpio, 0);
+ if (rc) {
+ BT_PWR_ERR("Unable to set direction\n");
+ return rc;
+ }
+
rc = gpio_direction_output(bt_reset_gpio, 1);
if (rc) {
- pr_err("%s: Unable to set direction\n", __func__);
+ BT_PWR_ERR("Unable to set direction\n");
return rc;
}
msleep(100);
} else {
gpio_set_value(bt_reset_gpio, 0);
+
rc = gpio_direction_input(bt_reset_gpio);
- if (rc) {
- pr_err("%s: Unable to set direction\n", __func__);
- return rc;
- }
+ if (rc)
+ BT_PWR_ERR("Unable to set direction\n");
+
msleep(100);
}
- return 0;
+ return rc;
+}
+
+static int bluetooth_power(int on)
+{
+ int rc = 0;
+
+ BT_PWR_DBG("on: %d", on);
+
+ if (on) {
+ if (bt_power_pdata->bt_vdd_io) {
+ rc = bt_configure_vreg(bt_power_pdata->bt_vdd_io);
+ if (rc < 0) {
+ BT_PWR_ERR("bt_power vddio config failed");
+ goto out;
+ }
+ }
+ if (bt_power_pdata->bt_vdd_ldo) {
+ rc = bt_configure_vreg(bt_power_pdata->bt_vdd_ldo);
+ if (rc < 0) {
+ BT_PWR_ERR("bt_power vddldo config failed");
+ goto vdd_ldo_fail;
+ }
+ }
+ if (bt_power_pdata->bt_vdd_pa) {
+ rc = bt_configure_vreg(bt_power_pdata->bt_vdd_pa);
+ if (rc < 0) {
+ BT_PWR_ERR("bt_power vddpa config failed");
+ goto vdd_pa_fail;
+ }
+ }
+ if (bt_power_pdata->bt_chip_pwd) {
+ rc = bt_configure_vreg(bt_power_pdata->bt_chip_pwd);
+ if (rc < 0) {
+ BT_PWR_ERR("bt_power vddldo config failed");
+ goto chip_pwd_fail;
+ }
+ }
+ if (bt_power_pdata->bt_gpio_sys_rst) {
+ rc = bt_configure_gpios(on);
+ if (rc < 0) {
+ BT_PWR_ERR("bt_power gpio config failed");
+ goto gpio_fail;
+ }
+ }
+ } else {
+ bt_configure_gpios(on);
+gpio_fail:
+ if (bt_power_pdata->bt_gpio_sys_rst)
+ gpio_free(bt_power_pdata->bt_gpio_sys_rst);
+ bt_vreg_disable(bt_power_pdata->bt_chip_pwd);
+chip_pwd_fail:
+ bt_vreg_disable(bt_power_pdata->bt_vdd_pa);
+vdd_pa_fail:
+ bt_vreg_disable(bt_power_pdata->bt_vdd_ldo);
+vdd_ldo_fail:
+ bt_vreg_disable(bt_power_pdata->bt_vdd_io);
+ }
+
+out:
+ return rc;
}
static int bluetooth_toggle_radio(void *data, bool blocked)
@@ -62,7 +251,9 @@
int ret = 0;
int (*power_control)(int enable);
- power_control = data;
+ power_control =
+ ((struct bluetooth_power_platform_data *)data)->bt_power_setup;
+
if (previous != blocked)
ret = (*power_control)(!blocked);
if (!ret)
@@ -117,47 +308,146 @@
platform_set_drvdata(pdev, NULL);
}
+#define MAX_PROP_SIZE 32
+static int bt_dt_parse_vreg_info(struct device *dev,
+ struct bt_power_vreg_data **vreg_data, const char *vreg_name)
+{
+ int len, ret = 0;
+ const __be32 *prop;
+ char prop_name[MAX_PROP_SIZE];
+ struct bt_power_vreg_data *vreg;
+ struct device_node *np = dev->of_node;
+
+ BT_PWR_DBG("vreg dev tree parse for %s", vreg_name);
+
+ snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", vreg_name);
+ if (of_parse_phandle(np, prop_name, 0)) {
+ vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+ if (!vreg) {
+ dev_err(dev, "No memory for vreg: %s\n", vreg_name);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ vreg->name = vreg_name;
+
+ snprintf(prop_name, MAX_PROP_SIZE,
+ "qcom,%s-voltage-level", vreg_name);
+ prop = of_get_property(np, prop_name, &len);
+ if (!prop || (len != (2 * sizeof(__be32)))) {
+ dev_warn(dev, "%s %s property\n",
+ prop ? "invalid format" : "no", prop_name);
+ } else {
+ vreg->low_vol_level = be32_to_cpup(&prop[0]);
+ vreg->high_vol_level = be32_to_cpup(&prop[1]);
+ }
+
+ *vreg_data = vreg;
+ BT_PWR_DBG("%s: vol=[%d %d]uV\n",
+ vreg->name, vreg->low_vol_level,
+ vreg->high_vol_level);
+ } else
+ BT_PWR_INFO("%s: is not provided in device tree", vreg_name);
+
+err:
+ return ret;
+}
+
+static int bt_power_populate_dt_pinfo(struct platform_device *pdev)
+{
+ int rc;
+
+ BT_PWR_DBG("");
+
+ if (!bt_power_pdata)
+ return -ENOMEM;
+
+ if (pdev->dev.of_node) {
+ bt_power_pdata->bt_gpio_sys_rst =
+ of_get_named_gpio(pdev->dev.of_node,
+ "qca,bt-reset-gpio", 0);
+ if (bt_power_pdata->bt_gpio_sys_rst < 0) {
+ BT_PWR_ERR("bt-reset-gpio not provided in device tree");
+ return bt_power_pdata->bt_gpio_sys_rst;
+ }
+
+ rc = bt_dt_parse_vreg_info(&pdev->dev,
+ &bt_power_pdata->bt_vdd_io,
+ "qca,bt-vdd-io");
+ if (rc < 0)
+ return rc;
+
+ rc = bt_dt_parse_vreg_info(&pdev->dev,
+ &bt_power_pdata->bt_vdd_pa,
+ "qca,bt-vdd-pa");
+ if (rc < 0)
+ return rc;
+
+ rc = bt_dt_parse_vreg_info(&pdev->dev,
+ &bt_power_pdata->bt_vdd_ldo,
+ "qca,bt-vdd-ldo");
+ if (rc < 0)
+ return rc;
+
+ rc = bt_dt_parse_vreg_info(&pdev->dev,
+ &bt_power_pdata->bt_chip_pwd,
+ "qca,bt-chip-pwd");
+ if (rc < 0)
+ return rc;
+
+ }
+
+ bt_power_pdata->bt_power_setup = bluetooth_power;
+
+ return 0;
+}
+
static int __devinit bt_power_probe(struct platform_device *pdev)
{
int ret = 0;
dev_dbg(&pdev->dev, "%s\n", __func__);
- if (!pdev->dev.platform_data) {
- /* Update the platform data if the
- device node exists as part of device tree.*/
- if (pdev->dev.of_node) {
- pdev->dev.platform_data = bluetooth_power;
- } else {
- dev_err(&pdev->dev, "device node not set\n");
- return -ENOSYS;
- }
+ bt_power_pdata =
+ kzalloc(sizeof(struct bluetooth_power_platform_data),
+ GFP_KERNEL);
+
+ if (!bt_power_pdata) {
+ BT_PWR_ERR("Failed to allocate memory");
+ return -ENOMEM;
}
+
if (pdev->dev.of_node) {
- bt_reset_gpio = of_get_named_gpio(pdev->dev.of_node,
- "qca,bt-reset-gpio", 0);
- if (bt_reset_gpio < 0) {
- pr_err("bt-reset-gpio not available");
- return bt_reset_gpio;
+ ret = bt_power_populate_dt_pinfo(pdev);
+ if (ret < 0) {
+ BT_PWR_ERR("Failed to populate device tree info");
+ goto free_pdata;
}
+ pdev->dev.platform_data = bt_power_pdata;
+ } else if (pdev->dev.platform_data) {
+ /* Optional data set to default if not provided */
+ if (!((struct bluetooth_power_platform_data *)
+ (pdev->dev.platform_data))->bt_power_setup)
+ ((struct bluetooth_power_platform_data *)
+ (pdev->dev.platform_data))->bt_power_setup =
+ bluetooth_power;
+
+ memcpy(bt_power_pdata, pdev->dev.platform_data,
+ sizeof(struct bluetooth_power_platform_data));
+ } else {
+ BT_PWR_ERR("Failed to get platform data");
+ goto free_pdata;
}
- ret = gpio_request(bt_reset_gpio, "bt sys_rst_n");
- if (ret) {
- pr_err("%s: unable to request gpio %d (%d)\n",
- __func__, bt_reset_gpio, ret);
- return ret;
- }
+ if (bluetooth_power_rfkill_probe(pdev) < 0)
+ goto free_pdata;
- /* When booting up, de-assert BT reset pin */
- ret = gpio_direction_output(bt_reset_gpio, 0);
- if (ret) {
- pr_err("%s: Unable to set direction\n", __func__);
- return ret;
- }
+ btpdev = pdev;
- ret = bluetooth_power_rfkill_probe(pdev);
+ return 0;
+free_pdata:
+ kfree(bt_power_pdata);
return ret;
}
@@ -167,6 +457,11 @@
bluetooth_power_rfkill_remove(pdev);
+ if (bt_power_pdata->bt_chip_pwd->reg)
+ regulator_put(bt_power_pdata->bt_chip_pwd->reg);
+
+ kfree(bt_power_pdata);
+
return 0;
}
@@ -176,7 +471,7 @@
.driver = {
.name = "bt_power",
.owner = THIS_MODULE,
- .of_match_table = ar3002_match_table,
+ .of_match_table = bt_power_match_table,
},
};
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index d78327f..e98fff2 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -26,10 +26,13 @@
#include <linux/msm_ion.h>
#include <mach/msm_smd.h>
#include <mach/ion.h>
+#include <mach/iommu_domains.h>
#include <linux/scatterlist.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/iommu.h>
#ifndef ION_ADSPRPC_HEAP_ID
#define ION_ADSPRPC_HEAP_ID ION_AUDIO_HEAP_ID
@@ -53,6 +56,8 @@
} while (0)
+#define IS_CACHE_ALIGNED(x) (((x) & ((L1_CACHE_BYTES)-1)) == 0)
+
static inline uint32_t buf_page_start(void *buf)
{
uint32_t start = (uint32_t) buf & PAGE_MASK;
@@ -84,8 +89,9 @@
{
struct vm_area_struct *vma;
uint32_t start = buf_page_start(addr);
+ uint32_t end = buf_page_start((void *)((uint32_t)addr + sz - 1));
uint32_t len = nr_pages << PAGE_SHIFT;
- unsigned long pfn;
+ unsigned long pfn, pfnend;
int n = -1, err = 0;
VERIFY(err, 0 != access_ok(access ? VERIFY_WRITE : VERIFY_READ,
@@ -102,6 +108,12 @@
VERIFY(err, 0 == follow_pfn(vma, start, &pfn));
if (err)
goto bail;
+ VERIFY(err, 0 == follow_pfn(vma, end, &pfnend));
+ if (err)
+ goto bail;
+ VERIFY(err, (pfn + nr_pages - 1) == pfnend);
+ if (err)
+ goto bail;
VERIFY(err, nr_elems > 0);
if (err)
goto bail;
@@ -124,6 +136,13 @@
int last;
};
+struct fastrpc_smmu {
+ struct iommu_group *group;
+ struct iommu_domain *domain;
+ int domain_id;
+ bool enabled;
+};
+
struct fastrpc_apps {
smd_channel_t *chan;
struct smq_context_list clst;
@@ -132,6 +151,7 @@
struct cdev cdev;
struct class *class;
struct device *dev;
+ struct fastrpc_smmu smmu;
dev_t dev_no;
spinlock_t wrlock;
spinlock_t hlock;
@@ -172,7 +192,12 @@
{
struct fastrpc_apps *me = &gfa;
- if (buf->handle) {
+ if (!IS_ERR_OR_NULL(buf->handle)) {
+ if (me->smmu.enabled && buf->phys) {
+ ion_unmap_iommu(me->iclient, buf->handle,
+ me->smmu.domain_id, 0);
+ buf->phys = 0;
+ }
if (buf->virt) {
ion_unmap_kernel(me->iclient, buf->handle);
buf->virt = 0;
@@ -185,7 +210,7 @@
static void free_map(struct fastrpc_mmap *map)
{
struct fastrpc_apps *me = &gfa;
- if (map->handle) {
+ if (!IS_ERR_OR_NULL(map->handle)) {
if (map->virt) {
ion_unmap_kernel(me->iclient, map->handle);
map->virt = 0;
@@ -197,26 +222,39 @@
static int alloc_mem(struct fastrpc_buf *buf)
{
+ struct fastrpc_apps *me = &gfa;
struct ion_client *clnt = gfa.iclient;
struct sg_table *sg;
int err = 0;
+ unsigned int heap;
+ unsigned long len;
buf->handle = 0;
buf->virt = 0;
- buf->handle = ion_alloc(clnt, buf->size, SZ_4K,
- ION_HEAP(ION_AUDIO_HEAP_ID), 0);
+ heap = me->smmu.enabled ? ION_HEAP(ION_IOMMU_HEAP_ID) :
+ ION_HEAP(ION_ADSP_HEAP_ID) | ION_HEAP(ION_AUDIO_HEAP_ID);
+ buf->handle = ion_alloc(clnt, buf->size, SZ_4K, heap, 0);
VERIFY(err, 0 == IS_ERR_OR_NULL(buf->handle));
if (err)
goto bail;
VERIFY(err, 0 != (buf->virt = ion_map_kernel(clnt, buf->handle)));
if (err)
goto bail;
- VERIFY(err, 0 != (sg = ion_sg_table(clnt, buf->handle)));
- if (err)
- goto bail;
- VERIFY(err, 1 == sg->nents);
- if (err)
- goto bail;
- buf->phys = sg_dma_address(sg->sgl);
+ if (me->smmu.enabled) {
+ len = buf->size;
+ VERIFY(err, 0 == ion_map_iommu(clnt, buf->handle,
+ me->smmu.domain_id, 0, SZ_4K, 0,
+ &buf->phys, &len, 0, 0));
+ if (err)
+ goto bail;
+ } else {
+ VERIFY(err, 0 != (sg = ion_sg_table(clnt, buf->handle)));
+ if (err)
+ goto bail;
+ VERIFY(err, 1 == sg->nents);
+ if (err)
+ goto bail;
+ buf->phys = sg_dma_address(sg->sgl);
+ }
bail:
if (err && !IS_ERR_OR_NULL(buf->handle))
free_mem(buf);
@@ -481,6 +519,28 @@
return err;
}
+static void inv_args_pre(uint32_t sc, remote_arg_t *rpra)
+{
+ int i, inbufs, outbufs;
+ uint32_t end;
+
+ inbufs = REMOTE_SCALARS_INBUFS(sc);
+ outbufs = REMOTE_SCALARS_OUTBUFS(sc);
+ for (i = inbufs; i < inbufs + outbufs; ++i) {
+ if (!rpra[i].buf.len)
+ continue;
+ if (buf_page_start(rpra) == buf_page_start(rpra[i].buf.pv))
+ continue;
+ if (!IS_CACHE_ALIGNED((uint32_t)rpra[i].buf.pv))
+ dmac_flush_range(rpra[i].buf.pv,
+ (char *)rpra[i].buf.pv + 1);
+ end = (uint32_t)rpra[i].buf.pv + rpra[i].buf.len;
+ if (!IS_CACHE_ALIGNED(end))
+ dmac_flush_range((char *)end,
+ (char *)end + 1);
+ }
+}
+
static void inv_args(uint32_t sc, remote_arg_t *rpra, int used)
{
int i, inbufs, outbufs;
@@ -527,11 +587,9 @@
{
struct fastrpc_apps *me = &gfa;
- if (me->chan)
- (void)smd_close(me->chan);
+ smd_close(me->chan);
context_list_dtor(&me->clst);
- if (me->iclient)
- ion_client_destroy(me->iclient);
+ ion_client_destroy(me->iclient);
me->iclient = 0;
me->chan = 0;
}
@@ -574,6 +632,8 @@
{
int err = 0;
struct fastrpc_apps *me = &gfa;
+ struct device_node *node;
+ bool enabled = 0;
if (me->chan == 0) {
int i;
@@ -584,25 +644,48 @@
INIT_HLIST_HEAD(&me->htbl[i]);
VERIFY(err, 0 == context_list_ctor(&me->clst, SZ_4K));
if (err)
- goto bail;
+ goto context_list_bail;
me->iclient = msm_ion_client_create(ION_HEAP_CARVEOUT_MASK,
DEVICE_NAME);
VERIFY(err, 0 == IS_ERR_OR_NULL(me->iclient));
if (err)
- goto bail;
+ goto ion_bail;
+ node = of_find_compatible_node(NULL, NULL,
+ "qcom,msm-audio-ion");
+ if (node)
+ enabled = of_property_read_bool(node,
+ "qcom,smmu-enabled");
+ if (enabled)
+ me->smmu.group = iommu_group_find("lpass_audio");
+ if (me->smmu.group)
+ me->smmu.domain = iommu_group_get_iommudata(
+ me->smmu.group);
+ if (!IS_ERR_OR_NULL(me->smmu.domain)) {
+ me->smmu.domain_id = msm_find_domain_no(
+ me->smmu.domain);
+ if (me->smmu.domain_id >= 0)
+ me->smmu.enabled = enabled;
+ }
VERIFY(err, 0 == smd_named_open_on_edge(FASTRPC_SMD_GUID,
SMD_APPS_QDSP, &me->chan,
me, smd_event_handler));
if (err)
- goto bail;
+ goto smd_bail;
VERIFY(err, 0 != wait_for_completion_timeout(&me->work,
RPC_TIMEOUT));
if (err)
- goto bail;
+ goto completion_bail;
}
- bail:
- if (err)
- fastrpc_deinit();
+
+ return 0;
+
+completion_bail:
+ smd_close(me->chan);
+smd_bail:
+ ion_client_destroy(me->iclient);
+ion_bail:
+ context_list_dtor(&me->clst);
+context_list_bail:
return err;
}
@@ -699,6 +782,12 @@
sc = invoke->sc;
obuf.handle = 0;
+ if (me->smmu.enabled) {
+ VERIFY(err, 0 == iommu_attach_group(me->smmu.domain,
+ me->smmu.group));
+ if (err)
+ return err;
+ }
if (REMOTE_SCALARS_LENGTH(sc)) {
VERIFY(err, 0 == get_dev(me, &dev));
if (err)
@@ -715,6 +804,7 @@
}
context_list_alloc_ctx(&me->clst, &ctx);
+ inv_args_pre(sc, rpra);
VERIFY(err, 0 == fastrpc_invoke_send(me, kernel, invoke->handle, sc,
ctx, &obuf));
if (err)
@@ -738,6 +828,8 @@
}
context_free(ctx);
+ if (me->smmu.enabled)
+ iommu_detach_group(me->smmu.domain, me->smmu.group);
for (i = 0, b = abufs; i < nbufs; ++i, ++b)
free_mem(b);
@@ -1088,37 +1180,40 @@
struct fastrpc_apps *me = &gfa;
int err = 0;
+ memset(me, 0, sizeof(*me));
VERIFY(err, 0 == fastrpc_init());
if (err)
- goto bail;
+ goto fastrpc_bail;
VERIFY(err, 0 == alloc_chrdev_region(&me->dev_no, 0, 1, DEVICE_NAME));
if (err)
- goto bail;
+ goto alloc_chrdev_bail;
cdev_init(&me->cdev, &fops);
me->cdev.owner = THIS_MODULE;
VERIFY(err, 0 == cdev_add(&me->cdev, MKDEV(MAJOR(me->dev_no), 0), 1));
if (err)
- goto bail;
+ goto cdev_init_bail;
me->class = class_create(THIS_MODULE, "chardrv");
VERIFY(err, !IS_ERR(me->class));
if (err)
- goto bail;
+ goto class_create_bail;
me->dev = device_create(me->class, NULL, MKDEV(MAJOR(me->dev_no), 0),
NULL, DEVICE_NAME);
VERIFY(err, !IS_ERR(me->dev));
if (err)
- goto bail;
+ goto device_create_bail;
pr_info("'created /dev/%s c %d 0'\n", DEVICE_NAME, MAJOR(me->dev_no));
- bail:
- if (err) {
- if (me->dev_no)
- unregister_chrdev_region(me->dev_no, 1);
- if (me->class)
- class_destroy(me->class);
- if (me->cdev.owner)
- cdev_del(&me->cdev);
- fastrpc_deinit();
- }
+
+ return 0;
+
+device_create_bail:
+ class_destroy(me->class);
+class_create_bail:
+ cdev_del(&me->cdev);
+cdev_init_bail:
+ unregister_chrdev_region(me->dev_no, 1);
+alloc_chrdev_bail:
+ fastrpc_deinit();
+fastrpc_bail:
return err;
}
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 682d876..fa0e9d7 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -58,14 +58,14 @@
pr_debug("diag: bytes read = %d, single dci pkt len = %d\n",
read_bytes, dci_pkt_len);
/* print_hex_dump(KERN_DEBUG, "Single DCI packet :",
- DUMP_PREFIX_ADDRESS, 16, 1, buf, 5 + dci_pkt_len, 1);*/
+ DUMP_PREFIX_ADDRESS, 16, 1, buf, 5 + dci_pkt_len, 1); */
recv_pkt_cmd_code = *(uint8_t *)(buf+4);
if (recv_pkt_cmd_code == LOG_CMD_CODE)
extract_dci_log(buf+4);
else if (recv_pkt_cmd_code == EVENT_CMD_CODE)
extract_dci_events(buf+4);
else
- extract_dci_pkt_rsp(buf); /* pkt response */
+ extract_dci_pkt_rsp(smd_info, buf); /* pkt response */
read_bytes += 5 + dci_pkt_len;
buf += 5 + dci_pkt_len; /* advance to next DCI pkt */
}
@@ -83,7 +83,7 @@
return 0;
}
-void extract_dci_pkt_rsp(unsigned char *buf)
+void extract_dci_pkt_rsp(struct diag_smd_info *smd_info, unsigned char *buf)
{
int i = 0, index = -1, cmd_code_len = 1;
int curr_client_pid = 0, write_len;
@@ -95,6 +95,7 @@
if (recv_pkt_cmd_code != DCI_PKT_RSP_CODE)
cmd_code_len = 4; /* delayed response */
write_len = (int)(*(uint16_t *)(buf+2)) - cmd_code_len;
+
pr_debug("diag: len = %d\n", write_len);
/* look up DCI client with tag */
for (i = 0; i < dci_max_reg; i++) {
@@ -139,8 +140,7 @@
buf+4+cmd_code_len, write_len);
entry->data_len += write_len;
/* delete immediate response entry */
- if (driver->smd_dci[MODEM_DATA].
- buf_in_1[8+cmd_code_len] != 0x80)
+ if (smd_info->buf_in_1[8+cmd_code_len] != 0x80)
driver->req_tracking_tbl[index].pid = 0;
}
}
@@ -372,39 +372,26 @@
} /* end of loop for all DCI clients */
}
-static int diag_dci_probe(struct platform_device *pdev)
-{
- int err = 0;
- int index;
-
- if (pdev->id == SMD_APPS_MODEM) {
- index = MODEM_DATA;
- err = smd_open("DIAG_2", &driver->smd_dci[index].ch,
- &driver->smd_dci[index],
- diag_smd_notify);
- driver->smd_dci[index].ch_save =
- driver->smd_dci[index].ch;
- if (err)
- pr_err("diag: In %s, cannot open DCI port, Id = %d, err: %d\n",
- __func__, pdev->id, err);
- }
-
- return err;
-}
-
int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
int len, int index)
{
- int i;
- int status = 0;
+ int i, status = 0;
+ unsigned int read_len = 0;
- /* remove UID from user space pkt before sending to peripheral */
- buf = buf + 4;
- if (len > APPS_BUF_SIZE - 10) {
- pr_err("diag: dci: buffer overwrite possible since payload bigger than buf size\n");
+ /* The first 4 bytes is the uid tag and the next four bytes is
+ the minmum packet length of a request packet */
+ if (len < DCI_PKT_REQ_MIN_LEN) {
+ pr_err("diag: dci: Invalid pkt len %d in %s\n", len, __func__);
return -EIO;
}
- len = len - 4;
+ if (len > APPS_BUF_SIZE - 10) {
+ pr_err("diag: dci: Invalid payload length in %s\n", __func__);
+ return -EIO;
+ }
+ /* remove UID from user space pkt before sending to peripheral*/
+ buf = buf + sizeof(int);
+ read_len += sizeof(int);
+ len = len - sizeof(int);
mutex_lock(&driver->dci_mutex);
/* prepare DCI packet */
driver->apps_dci_buf[0] = CONTROL_CHAR; /* start */
@@ -415,12 +402,21 @@
driver->req_tracking_tbl[index].tag;
for (i = 0; i < len; i++)
driver->apps_dci_buf[i+9] = *(buf+i);
+ read_len += len;
driver->apps_dci_buf[9+len] = CONTROL_CHAR; /* end */
+ if ((read_len + 9) >= USER_SPACE_DATA) {
+ pr_err("diag: dci: Invalid length while forming dci pkt in %s",
+ __func__);
+ return -EIO;
+ }
for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++) {
- if (entry.client_id == driver->smd_dci[i].peripheral) {
- if (driver->smd_dci[i].ch) {
- smd_write(driver->smd_dci[i].ch,
+ struct diag_smd_info *smd_info = driver->separate_cmdrsp[i] ?
+ &driver->smd_dci_cmd[i] :
+ &driver->smd_dci[i];
+ if (entry.client_id == smd_info->peripheral) {
+ if (smd_info->ch) {
+ smd_write(smd_info->ch,
driver->apps_dci_buf, len + 10);
status = DIAG_DCI_NO_ERROR;
}
@@ -466,10 +462,10 @@
{
unsigned char *temp = buf;
uint16_t subsys_cmd_code, log_code, item_num;
- int subsys_id, cmd_code, ret = -1, index = -1, found = 0, read_len = 0;
+ int subsys_id, cmd_code, ret = -1, index = -1, found = 0;
struct diag_master_table entry;
int count, set_mask, num_codes, bit_index, event_id, offset = 0, i;
- unsigned int byte_index;
+ unsigned int byte_index, read_len = 0;
uint8_t equip_id, *log_mask_ptr, *head_log_mask_ptr, byte_mask;
uint8_t *event_mask_ptr;
@@ -479,15 +475,24 @@
return DIAG_DCI_SEND_DATA_FAIL;
}
+ if (!temp) {
+ pr_err("diag: Invalid buffer in %s\n", __func__);
+ }
+
/* This is Pkt request/response transaction */
if (*(int *)temp > 0) {
+ if (len < DCI_PKT_REQ_MIN_LEN || len > USER_SPACE_DATA) {
+ pr_err("diag: dci: Invalid length %d len in %s", len,
+ __func__);
+ return -EIO;
+ }
/* enter this UID into kernel table and return index */
index = diag_register_dci_transaction(*(int *)temp);
if (index < 0) {
pr_alert("diag: registering new DCI transaction failed\n");
return DIAG_DCI_NO_REG;
}
- temp += 4;
+ temp += sizeof(int);
/*
* Check for registered peripheral and fwd pkt to
* appropriate proc
@@ -497,7 +502,12 @@
subsys_id = (int)(*(char *)temp);
temp++;
subsys_cmd_code = *(uint16_t *)temp;
- temp += 2;
+ temp += sizeof(uint16_t);
+ read_len += sizeof(int) + 2 + sizeof(uint16_t);
+ if (read_len >= USER_SPACE_DATA) {
+ pr_err("diag: dci: Invalid length in %s\n", __func__);
+ return -EIO;
+ }
pr_debug("diag: %d %d %d", cmd_code, subsys_id,
subsys_cmd_code);
for (i = 0; i < diag_max_reg; i++) {
@@ -531,6 +541,12 @@
}
}
} else if (*(int *)temp == DCI_LOG_TYPE) {
+ /* Minimum length of a log mask config is 12 + 2 bytes for
+ atleast one log code to be set or reset */
+ if (len < DCI_LOG_CON_MIN_LEN || len > USER_SPACE_DATA) {
+ pr_err("diag: dci: Invalid length in %s\n", __func__);
+ return -EIO;
+ }
/* find client id and table */
i = diag_dci_find_client_index(current->tgid);
if (i == DCI_CLIENT_INDEX_INVALID) {
@@ -538,21 +554,33 @@
return ret;
}
/* Extract each log code and put in client table */
- temp += 4;
- read_len += 4;
+ temp += sizeof(int);
+ read_len += sizeof(int);
set_mask = *(int *)temp;
- temp += 4;
- read_len += 4;
+ temp += sizeof(int);
+ read_len += sizeof(int);
num_codes = *(int *)temp;
- temp += 4;
- read_len += 4;
+ temp += sizeof(int);
+ read_len += sizeof(int);
+
+ if (num_codes == 0 || (num_codes >= (USER_SPACE_DATA - 8)/2)) {
+ pr_err("diag: dci: Invalid number of log codes %d\n",
+ num_codes);
+ return -EIO;
+ }
head_log_mask_ptr = driver->dci_client_tbl[i].dci_log_mask;
+ if (!head_log_mask_ptr) {
+ pr_err("diag: dci: Invalid Log mask pointer in %s\n",
+ __func__);
+ return -ENOMEM;
+ }
pr_debug("diag: head of dci log mask %p\n", head_log_mask_ptr);
count = 0; /* iterator for extracting log codes */
while (count < num_codes) {
if (read_len >= USER_SPACE_DATA) {
- pr_err("diag: dci: Log type, possible buffer overflow\n");
+ pr_err("diag: dci: Invalid length for log type in %s",
+ __func__);
return -EIO;
}
log_code = *(uint16_t *)temp;
@@ -606,6 +634,12 @@
/* send updated mask to peripherals */
ret = diag_send_dci_log_mask(driver->smd_cntl[MODEM_DATA].ch);
} else if (*(int *)temp == DCI_EVENT_TYPE) {
+ /* Minimum length of a event mask config is 12 + 4 bytes for
+ atleast one event id to be set or reset. */
+ if (len < DCI_EVENT_CON_MIN_LEN || len > USER_SPACE_DATA) {
+ pr_err("diag: dci: Invalid length in %s\n", __func__);
+ return -EIO;
+ }
/* find client id and table */
i = diag_dci_find_client_index(current->tgid);
if (i == DCI_CLIENT_INDEX_INVALID) {
@@ -613,21 +647,36 @@
return ret;
}
/* Extract each log code and put in client table */
- temp += 4;
- read_len += 4;
+ temp += sizeof(int);
+ read_len += sizeof(int);
set_mask = *(int *)temp;
- temp += 4;
- read_len += 4;
+ temp += sizeof(int);
+ read_len += sizeof(int);
num_codes = *(int *)temp;
- temp += 4;
- read_len += 4;
+ temp += sizeof(int);
+ read_len += sizeof(int);
+
+ /* Check for positive number of event ids. Also, the number of
+ event ids should fit in the buffer along with set_mask and
+ num_codes which are 4 bytes each */
+ if (num_codes == 0 || (num_codes >= (USER_SPACE_DATA - 8)/2)) {
+ pr_err("diag: dci: Invalid number of event ids %d\n",
+ num_codes);
+ return -EIO;
+ }
event_mask_ptr = driver->dci_client_tbl[i].dci_event_mask;
+ if (!event_mask_ptr) {
+ pr_err("diag: dci: Invalid event mask pointer in %s\n",
+ __func__);
+ return -ENOMEM;
+ }
pr_debug("diag: head of dci event mask %p\n", event_mask_ptr);
count = 0; /* iterator for extracting log codes */
while (count < num_codes) {
if (read_len >= USER_SPACE_DATA) {
- pr_err("diag: dci: Event type, possible buffer overflow\n");
+ pr_err("diag: dci: Invalid length for event type in %s",
+ __func__);
return -EIO;
}
event_id = *(int *)temp;
@@ -1000,6 +1049,49 @@
memset(tbl_buf, 0, 512);
}
+static int diag_dci_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ int index;
+
+ if (pdev->id == SMD_APPS_MODEM) {
+ index = MODEM_DATA;
+ err = smd_open("DIAG_2",
+ &driver->smd_dci[index].ch,
+ &driver->smd_dci[index],
+ diag_smd_notify);
+ driver->smd_dci[index].ch_save =
+ driver->smd_dci[index].ch;
+ if (err)
+ pr_err("diag: In %s, cannot open DCI port, Id = %d, err: %d\n",
+ __func__, pdev->id, err);
+ }
+
+ return err;
+}
+
+static int diag_dci_cmd_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ int index;
+
+ if (pdev->id == SMD_APPS_MODEM) {
+ index = MODEM_DATA;
+ err = smd_named_open_on_edge("DIAG_2_CMD",
+ pdev->id,
+ &driver->smd_dci_cmd[index].ch,
+ &driver->smd_dci_cmd[index],
+ diag_smd_notify);
+ driver->smd_dci_cmd[index].ch_save =
+ driver->smd_dci_cmd[index].ch;
+ if (err)
+ pr_err("diag: In %s, cannot open DCI port, Id = %d, err: %d\n",
+ __func__, pdev->id, err);
+ }
+
+ return err;
+}
+
static int diag_dci_runtime_suspend(struct device *dev)
{
dev_dbg(dev, "pm_runtime: suspending...\n");
@@ -1020,9 +1112,18 @@
struct platform_driver msm_diag_dci_driver = {
.probe = diag_dci_probe,
.driver = {
- .name = "DIAG_2",
- .owner = THIS_MODULE,
- .pm = &diag_dci_dev_pm_ops,
+ .name = "DIAG_2",
+ .owner = THIS_MODULE,
+ .pm = &diag_dci_dev_pm_ops,
+ },
+};
+
+struct platform_driver msm_diag_dci_cmd_driver = {
+ .probe = diag_dci_cmd_probe,
+ .driver = {
+ .name = "DIAG_2_CMD",
+ .owner = THIS_MODULE,
+ .pm = &diag_dci_dev_pm_ops,
},
};
@@ -1040,12 +1141,21 @@
mutex_init(&dci_health_mutex);
for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++) {
- success = diag_smd_constructor(&driver->smd_dci[i],
- i, SMD_DCI_TYPE);
+ success = diag_smd_constructor(&driver->smd_dci[i], i,
+ SMD_DCI_TYPE);
if (!success)
goto err;
}
+ if (driver->supports_separate_cmdrsp) {
+ for (i = 0; i < NUM_SMD_DCI_CMD_CHANNELS; i++) {
+ success = diag_smd_constructor(&driver->smd_dci_cmd[i],
+ i, SMD_DCI_CMD_TYPE);
+ if (!success)
+ goto err;
+ }
+ }
+
if (driver->req_tracking_tbl == NULL) {
driver->req_tracking_tbl = kzalloc(dci_max_reg *
sizeof(struct dci_pkt_req_tracking_tbl), GFP_KERNEL);
@@ -1069,6 +1179,13 @@
pr_err("diag: Could not register DCI driver\n");
goto err;
}
+ if (driver->supports_separate_cmdrsp) {
+ success = platform_driver_register(&msm_diag_dci_cmd_driver);
+ if (success) {
+ pr_err("diag: Could not register DCI cmd driver\n");
+ goto err;
+ }
+ }
return DIAG_DCI_NO_ERROR;
err:
pr_err("diag: Could not initialize diag DCI buffers");
@@ -1077,6 +1194,11 @@
kfree(driver->apps_dci_buf);
for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++)
diag_smd_destructor(&driver->smd_dci[i]);
+
+ if (driver->supports_separate_cmdrsp)
+ for (i = 0; i < NUM_SMD_DCI_CMD_CHANNELS; i++)
+ diag_smd_destructor(&driver->smd_dci_cmd[i]);
+
if (driver->diag_dci_wq)
destroy_workqueue(driver->diag_dci_wq);
return DIAG_DCI_NO_REG;
@@ -1096,6 +1218,12 @@
kfree(driver->dci_client_tbl[i].dci_data);
}
+ if (driver->supports_separate_cmdrsp) {
+ for (i = 0; i < NUM_SMD_DCI_CMD_CHANNELS; i++)
+ diag_smd_destructor(&driver->smd_dci_cmd[i]);
+
+ platform_driver_unregister(&msm_diag_dci_cmd_driver);
+ }
kfree(driver->req_tracking_tbl);
kfree(driver->dci_client_tbl);
kfree(driver->apps_dci_buf);
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index 260cdf3..d530de9 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -24,6 +24,9 @@
#define DISABLE_LOG_MASK 0
#define MAX_EVENT_SIZE 512
#define DCI_CLIENT_INDEX_INVALID -1
+#define DCI_PKT_REQ_MIN_LEN 8
+#define DCI_LOG_CON_MIN_LEN 14
+#define DCI_EVENT_CON_MIN_LEN 16
/* 16 log code categories, each has:
@@ -95,7 +98,7 @@
int diag_process_dci_transaction(unsigned char *buf, int len);
int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
int len, int index);
-void extract_dci_pkt_rsp(unsigned char *buf);
+void extract_dci_pkt_rsp(struct diag_smd_info *smd_info, unsigned char *buf);
int diag_dci_find_client_index(int client_id);
/* DCI Log streaming functions */
void create_dci_log_mask_tbl(unsigned char *tbl_buf);
diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c
index 76490c8..71eda68 100644
--- a/drivers/char/diag/diag_debugfs.c
+++ b/drivers/char/diag/diag_debugfs.c
@@ -44,12 +44,18 @@
"modem cntl_ch: 0x%x\n"
"lpass cntl_ch: 0x%x\n"
"riva cntl_ch: 0x%x\n"
+ "modem cmd ch: 0x%x\n"
+ "dci cmd ch: 0x%x\n"
"CPU Tools id: %d\n"
"Apps only: %d\n"
"Apps master: %d\n"
"Check Polling Response: %d\n"
"polling_reg_flag: %d\n"
"uses device tree: %d\n"
+ "supports separate cmdrsp: %d\n"
+ "Modem separate cmdrsp: %d\n"
+ "LPASS separate cmdrsp: %d\n"
+ "RIVA separate cmdrsp: %d\n"
"Modem in_busy_1: %d\n"
"Modem in_busy_2: %d\n"
"LPASS in_busy_1: %d\n"
@@ -57,6 +63,9 @@
"RIVA in_busy_1: %d\n"
"RIVA in_busy_2: %d\n"
"DCI Modem in_busy_1: %d\n"
+ "Modem CMD in_busy_1: %d\n"
+ "Modem CMD in_busy_2: %d\n"
+ "DCI CMD Modem in_busy_1: %d\n"
"logging_mode: %d\n",
(unsigned int)driver->smd_data[MODEM_DATA].ch,
(unsigned int)driver->smd_data[LPASS_DATA].ch,
@@ -65,12 +74,18 @@
(unsigned int)driver->smd_cntl[MODEM_DATA].ch,
(unsigned int)driver->smd_cntl[LPASS_DATA].ch,
(unsigned int)driver->smd_cntl[WCNSS_DATA].ch,
+ (unsigned int)driver->smd_cmd[MODEM_DATA].ch,
+ (unsigned int)driver->smd_dci_cmd[MODEM_DATA].ch,
chk_config_get_id(),
chk_apps_only(),
chk_apps_master(),
chk_polling_response(),
driver->polling_reg_flag,
driver->use_device_tree,
+ driver->supports_separate_cmdrsp,
+ driver->separate_cmdrsp[MODEM_DATA],
+ driver->separate_cmdrsp[LPASS_DATA],
+ driver->separate_cmdrsp[WCNSS_DATA],
driver->smd_data[MODEM_DATA].in_busy_1,
driver->smd_data[MODEM_DATA].in_busy_2,
driver->smd_data[LPASS_DATA].in_busy_1,
@@ -78,6 +93,9 @@
driver->smd_data[WCNSS_DATA].in_busy_1,
driver->smd_data[WCNSS_DATA].in_busy_2,
driver->smd_dci[MODEM_DATA].in_busy_1,
+ driver->smd_cmd[MODEM_DATA].in_busy_1,
+ driver->smd_cmd[MODEM_DATA].in_busy_2,
+ driver->smd_dci_cmd[MODEM_DATA].in_busy_1,
driver->logging_mode);
#ifdef CONFIG_DIAG_OVER_USB
@@ -181,7 +199,7 @@
}
buf = kzalloc(sizeof(char) * buf_size, GFP_KERNEL);
- if (!buf) {
+ if (ZERO_OR_NULL_PTR(buf)) {
pr_err("diag: %s, Error allocating memory\n", __func__);
return -ENOMEM;
}
@@ -249,7 +267,7 @@
}
buf = kzalloc(sizeof(char) * buf_size, GFP_KERNEL);
- if (!buf) {
+ if (ZERO_OR_NULL_PTR(buf)) {
pr_err("diag: %s, Error allocating memory\n", __func__);
return -ENOMEM;
}
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index 9e36e1e..5aed793 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -27,7 +27,6 @@
#define MAX_SSID_PER_RANGE 100
#define FEATURE_MASK_LEN_BYTES 1
-#define APPS_RESPOND_LOG_ON_DEMAND 0x04
struct mask_info {
int equip_id;
@@ -48,13 +47,6 @@
msg_mask_tbl_ptr += MAX_SSID_PER_RANGE * sizeof(int); \
} while (0)
-#define WAIT_FOR_SMD(num_delays, delay_time) \
-do { \
- int count; \
- for (count = 0; count < (num_delays); count++) \
- udelay((delay_time)); \
-} while (0)
-
static void diag_print_mask_table(void)
{
/* Enable this to print mask table when updated */
@@ -312,7 +304,7 @@
smd_info->peripheral);
diag_send_log_mask_update(smd_info->ch, ALL_EQUIP_ID);
diag_send_event_mask_update(smd_info->ch, diag_event_num_bytes);
- diag_send_feature_mask_update(smd_info->ch, smd_info->peripheral);
+ diag_send_feature_mask_update(smd_info);
if (smd_info->notify_context == SMD_EVENT_OPEN)
diag_send_diag_mode_update_by_smd(smd_info, MODE_REALTIME);
@@ -352,7 +344,7 @@
header_size + size);
if (wr_size == -ENOMEM) {
retry_count++;
- WAIT_FOR_SMD(5, 2000);
+ usleep_range(10000, 10100);
} else
break;
}
@@ -397,7 +389,7 @@
wr_size = smd_write(ch, buf, header_size + num_bytes);
if (wr_size == -ENOMEM) {
retry_count++;
- WAIT_FOR_SMD(5, 2000);
+ usleep_range(10000, 10100);
} else
break;
}
@@ -447,7 +439,7 @@
4*(driver->msg_mask->msg_mask_size));
if (size == -ENOMEM) {
retry_count++;
- WAIT_FOR_SMD(5, 2000);
+ usleep_range(10000, 10100);
} else
break;
}
@@ -468,12 +460,25 @@
mutex_unlock(&driver->diag_cntl_mutex);
}
-void diag_send_feature_mask_update(smd_channel_t *ch, int proc)
+void diag_send_feature_mask_update(struct diag_smd_info *smd_info)
{
void *buf = driver->buf_feature_mask_update;
int header_size = sizeof(struct diag_ctrl_feature_mask);
- int wr_size = -ENOMEM, retry_count = 0, timer;
+ int wr_size = -ENOMEM, retry_count = 0;
uint8_t feature_byte = 0;
+ int total_len = 0;
+
+ if (!smd_info) {
+ pr_err("diag: In %s, null smd info pointer\n",
+ __func__);
+ return;
+ }
+
+ if (!smd_info->ch) {
+ pr_err("diag: In %s, smd channel not open for peripheral: %d, type: %d\n",
+ __func__, smd_info->peripheral, smd_info->type);
+ return;
+ }
mutex_lock(&driver->diag_cntl_mutex);
/* send feature mask update */
@@ -481,27 +486,33 @@
driver->feature_mask->ctrl_pkt_data_len = 4 + FEATURE_MASK_LEN_BYTES;
driver->feature_mask->feature_mask_len = FEATURE_MASK_LEN_BYTES;
memcpy(buf, driver->feature_mask, header_size);
- feature_byte |= APPS_RESPOND_LOG_ON_DEMAND;
+ feature_byte |= F_DIAG_INT_FEATURE_MASK;
+ feature_byte |= F_DIAG_LOG_ON_DEMAND_RSP_ON_MASTER;
+ feature_byte |= driver->supports_separate_cmdrsp ?
+ F_DIAG_REQ_RSP_CHANNEL : 0;
memcpy(buf+header_size, &feature_byte, FEATURE_MASK_LEN_BYTES);
+ total_len = header_size + FEATURE_MASK_LEN_BYTES;
- if (ch) {
- while (retry_count < 3) {
- wr_size = smd_write(ch, buf, header_size +
- FEATURE_MASK_LEN_BYTES);
- if (wr_size == -ENOMEM) {
- retry_count++;
- for (timer = 0; timer < 5; timer++)
- udelay(2000);
- } else
- break;
- }
- if (wr_size != header_size + FEATURE_MASK_LEN_BYTES)
- pr_err("diag: proc %d fail feature update %d, tried %d",
- proc, wr_size, header_size + FEATURE_MASK_LEN_BYTES);
- } else
- pr_err("diag: ch invalid, feature update on proc %d\n", proc);
+ while (retry_count < 3) {
+ wr_size = smd_write(smd_info->ch, buf, total_len);
+ if (wr_size == -ENOMEM) {
+ retry_count++;
+ /*
+ * The smd channel is full. Delay while
+ * smd processes existing data and smd
+ * has memory become available. The delay
+ * of 10000 was determined empirically as
+ * best value to use.
+ */
+ usleep_range(10000, 10100);
+ } else
+ break;
+ }
+ if (wr_size != total_len)
+ pr_err("diag: In %s, peripheral %d fail feature update, size: %d, tried: %d",
+ __func__, smd_info->peripheral, wr_size, total_len);
+
mutex_unlock(&driver->diag_cntl_mutex);
-
}
int diag_process_apps_masks(unsigned char *buf, int len)
diff --git a/drivers/char/diag/diag_masks.h b/drivers/char/diag/diag_masks.h
index 53f72e8..c66a4b6 100644
--- a/drivers/char/diag/diag_masks.h
+++ b/drivers/char/diag/diag_masks.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013, 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
@@ -21,7 +21,7 @@
int ssid_last, int proc);
void diag_send_log_mask_update(smd_channel_t *, int);
void diag_mask_update_fn(struct work_struct *work);
-void diag_send_feature_mask_update(smd_channel_t *ch, int proc);
+void diag_send_feature_mask_update(struct diag_smd_info *smd_info);
int diag_process_apps_masks(unsigned char *buf, int len);
void diag_masks_init(void);
void diag_masks_exit(void);
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 684f11d..b05bcef 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -82,12 +82,16 @@
#define MODE_NONREALTIME 0
#define NUM_SMD_DATA_CHANNELS 3
-#define NUM_SMD_CONTROL_CHANNELS 3
+#define NUM_SMD_CONTROL_CHANNELS NUM_SMD_DATA_CHANNELS
#define NUM_SMD_DCI_CHANNELS 1
+#define NUM_SMD_CMD_CHANNELS 1
+#define NUM_SMD_DCI_CMD_CHANNELS 1
#define SMD_DATA_TYPE 0
#define SMD_CNTL_TYPE 1
#define SMD_DCI_TYPE 2
+#define SMD_CMD_TYPE 3
+#define SMD_DCI_CMD_TYPE 4
/* Maximum number of pkt reg supported at initialization*/
extern int diag_max_reg;
@@ -219,7 +223,9 @@
int num_clients;
int polling_reg_flag;
struct diag_write_device *buf_tbl;
+ unsigned int buf_tbl_size;
int use_device_tree;
+ int supports_separate_cmdrsp;
/* DCI related variables */
struct dci_pkt_req_tracking_tbl *req_tracking_tbl;
struct diag_dci_client_tbl *dci_client_tbl;
@@ -262,6 +268,9 @@
struct diag_smd_info smd_data[NUM_SMD_DATA_CHANNELS];
struct diag_smd_info smd_cntl[NUM_SMD_CONTROL_CHANNELS];
struct diag_smd_info smd_dci[NUM_SMD_DCI_CHANNELS];
+ struct diag_smd_info smd_cmd[NUM_SMD_CMD_CHANNELS];
+ struct diag_smd_info smd_dci_cmd[NUM_SMD_DCI_CMD_CHANNELS];
+ int separate_cmdrsp[NUM_SMD_CONTROL_CHANNELS];
unsigned char *usb_buf_out;
unsigned char *apps_rsp_buf;
/* buffer for updating mask to peripherals */
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 2ebae71..90a4154 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -595,7 +595,7 @@
}
head_params = kzalloc(pkt_params.count*sizeof(
struct bindpkt_params), GFP_KERNEL);
- if (!head_params) {
+ if (ZERO_OR_NULL_PTR(head_params)) {
pr_err("diag: unable to alloc memory\n");
return -ENOMEM;
} else
@@ -782,7 +782,7 @@
int diag_switch_logging(unsigned long ioarg)
{
- int i, temp, success = -EINVAL, status;
+ int temp = 0, success = -EINVAL, status = 0;
int temp_realtime_mode = driver->real_time_mode;
int requested_mode = (int)ioarg;
@@ -857,24 +857,13 @@
if (temp == MEMORY_DEVICE_MODE && driver->logging_mode
== NO_LOGGING_MODE) {
- for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
- driver->smd_data[i].in_busy_1 = 0;
- driver->smd_data[i].in_busy_2 = 0;
- }
+ diag_reset_smd_data(RESET_AND_NO_QUEUE);
diag_cmp_logging_modes_sdio_pipe(temp, driver->logging_mode);
diag_cmp_logging_modes_diagfwd_bridge(temp,
driver->logging_mode);
} else if (temp == NO_LOGGING_MODE && driver->logging_mode
== MEMORY_DEVICE_MODE) {
- for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
- driver->smd_data[i].in_busy_1 = 0;
- driver->smd_data[i].in_busy_2 = 0;
- /* Poll SMD channels to check for data*/
- if (driver->smd_data[i].ch)
- queue_work(driver->diag_wq,
- &(driver->smd_data[i].
- diag_read_smd_work));
- }
+ diag_reset_smd_data(RESET_AND_QUEUE);
diag_cmp_logging_modes_sdio_pipe(temp,
driver->logging_mode);
diag_cmp_logging_modes_diagfwd_bridge(temp,
@@ -892,15 +881,7 @@
} else if (temp == USB_MODE && driver->logging_mode
== MEMORY_DEVICE_MODE) {
diagfwd_disconnect();
- for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
- driver->smd_data[i].in_busy_1 = 0;
- driver->smd_data[i].in_busy_2 = 0;
- /* Poll SMD channels to check for data*/
- if (driver->smd_data[i].ch)
- queue_work(driver->diag_wq,
- &(driver->smd_data[i].
- diag_read_smd_work));
- }
+ diag_reset_smd_data(RESET_AND_QUEUE);
diag_cmp_logging_modes_sdio_pipe(temp, driver->logging_mode);
diag_cmp_logging_modes_diagfwd_bridge(temp,
driver->logging_mode);
@@ -964,9 +945,13 @@
return -EFAULT;
}
mutex_lock(&driver->dci_mutex);
- if (!(driver->num_dci_client))
+ if (!(driver->num_dci_client)) {
for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++)
driver->smd_dci[i].in_busy_1 = 0;
+ if (driver->supports_separate_cmdrsp)
+ for (i = 0; i < NUM_SMD_DCI_CMD_CHANNELS; i++)
+ driver->smd_dci_cmd[i].in_busy_1 = 0;
+ }
driver->num_dci_client++;
pr_debug("diag: In %s, id = %d\n",
__func__, driver->dci_client_id);
@@ -1167,7 +1152,7 @@
/* place holder for number of data field */
ret += 4;
- for (i = 0; i < driver->poolsize_write_struct; i++) {
+ for (i = 0; i < driver->buf_tbl_size; i++) {
if (driver->buf_tbl[i].length > 0) {
#ifdef DIAG_DEBUG
pr_debug("diag: WRITING the buf address "
@@ -1206,7 +1191,7 @@
}
}
- /* copy modem data */
+ /* Copy peripheral data */
for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
struct diag_smd_info *data = &driver->smd_data[i];
if (data->in_busy_1 == 1) {
@@ -1240,6 +1225,37 @@
data->in_busy_2 = 0;
}
}
+ if (driver->supports_separate_cmdrsp) {
+ for (i = 0; i < NUM_SMD_CMD_CHANNELS; i++) {
+ struct diag_smd_info *data =
+ &driver->smd_cmd[i];
+ if (!driver->separate_cmdrsp[i])
+ continue;
+
+ if (data->in_busy_1 == 1) {
+ num_data++;
+ /*Copy the length of data being passed*/
+ COPY_USER_SPACE_OR_EXIT(buf+ret,
+ (data->write_ptr_1->length), 4);
+ /*Copy the actual data being passed*/
+ COPY_USER_SPACE_OR_EXIT(buf+ret,
+ *(data->buf_in_1),
+ data->write_ptr_1->length);
+ data->in_busy_1 = 0;
+ }
+ if (data->in_busy_2 == 1) {
+ num_data++;
+ /*Copy the length of data being passed*/
+ COPY_USER_SPACE_OR_EXIT(buf+ret,
+ (data->write_ptr_2->length), 4);
+ /*Copy the actual data being passed*/
+ COPY_USER_SPACE_OR_EXIT(buf+ret,
+ *(data->buf_in_2),
+ data->write_ptr_2->length);
+ data->in_busy_2 = 0;
+ }
+ }
+ }
#ifdef CONFIG_DIAG_SDIO_PIPE
/* copy 9K data over SDIO */
if (driver->in_busy_sdio == 1) {
@@ -1360,6 +1376,17 @@
queue_work(driver->diag_dci_wq,
&(driver->smd_dci[i].diag_read_smd_work));
}
+ if (driver->supports_separate_cmdrsp) {
+ for (i = 0; i < NUM_SMD_DCI_CMD_CHANNELS; i++) {
+ if (!driver->separate_cmdrsp[i])
+ continue;
+ driver->smd_dci_cmd[i].in_busy_1 = 0;
+ if (driver->smd_dci_cmd[i].ch)
+ queue_work(driver->diag_dci_wq,
+ &(driver->smd_dci_cmd[i].
+ diag_read_smd_work));
+ }
+ }
goto exit;
}
exit:
@@ -2071,6 +2098,7 @@
diagchar_cleanup();
diagfwd_exit();
diagfwd_cntl_exit();
+ diag_dci_exit();
diag_masks_exit();
diag_sdio_fn(EXIT);
diagfwd_bridge_fn(EXIT);
@@ -2085,6 +2113,7 @@
diagmem_exit(driver, POOL_TYPE_ALL);
diagfwd_exit();
diagfwd_cntl_exit();
+ diag_dci_exit();
diag_masks_exit();
diag_sdio_fn(EXIT);
diagfwd_bridge_fn(EXIT);
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 151e304..6851fd8 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -48,7 +48,8 @@
int diag_debug_buf_idx;
unsigned char diag_debug_buf[1024];
-static unsigned int buf_tbl_size = 8; /*Number of entries in table of buffers */
+/* Number of entries in table of buffers */
+static unsigned int buf_tbl_size = 10;
struct diag_master_table entry;
struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
@@ -320,6 +321,19 @@
{
struct diag_request *write_ptr_modem = NULL;
int *in_busy_ptr = 0;
+ int err = 0;
+
+ /*
+ * Do not process data on command channel if the
+ * channel is not designated to do so
+ */
+ if ((smd_info->type == SMD_CMD_TYPE) &&
+ !driver->separate_cmdrsp[smd_info->peripheral]) {
+ /* This print is for debugging */
+ pr_err("diag, In %s, received data on non-designated command channel: %d\n",
+ __func__, smd_info->peripheral);
+ return 0;
+ }
if (smd_info->buf_in_1 == buf) {
write_ptr_modem = smd_info->write_ptr_1;
@@ -334,7 +348,14 @@
if (write_ptr_modem) {
write_ptr_modem->length = total_recd;
*in_busy_ptr = 1;
- diag_device_write(buf, smd_info->peripheral, write_ptr_modem);
+ err = diag_device_write(buf, smd_info->peripheral,
+ write_ptr_modem);
+ if (err) {
+ /* Free up the buffer for future use */
+ *in_busy_ptr = 0;
+ pr_err_ratelimited("diag: In %s, diag_device_write error: %d\n",
+ __func__, err);
+ }
}
return 0;
@@ -355,7 +376,8 @@
if (!smd_info->in_busy_1)
buf = smd_info->buf_in_1;
- else if ((smd_info->type == SMD_DATA_TYPE) && !smd_info->in_busy_2)
+ else if (!smd_info->in_busy_2 &&
+ (smd_info->type == SMD_DATA_TYPE))
buf = smd_info->buf_in_2;
if (smd_info->ch && buf) {
@@ -467,7 +489,7 @@
if (driver->logging_mode == MEMORY_DEVICE_MODE) {
int hsic_updated = 0;
if (data_type == APPS_DATA) {
- for (i = 0; i < driver->poolsize_write_struct; i++)
+ for (i = 0; i < driver->buf_tbl_size; i++)
if (driver->buf_tbl[i].length == 0) {
driver->buf_tbl[i].buf = buf;
driver->buf_tbl[i].length =
@@ -525,12 +547,20 @@
} else
return -EINVAL;
} else if (driver->logging_mode == NO_LOGGING_MODE) {
- if ((data_type >= 0) && (data_type < NUM_SMD_DATA_CHANNELS)) {
+ if ((data_type >= MODEM_DATA) && (data_type <= WCNSS_DATA)) {
driver->smd_data[data_type].in_busy_1 = 0;
driver->smd_data[data_type].in_busy_2 = 0;
queue_work(driver->diag_wq,
&(driver->smd_data[data_type].
diag_read_smd_work));
+ if (data_type == MODEM_DATA &&
+ driver->separate_cmdrsp[data_type]) {
+ driver->smd_cmd[data_type].in_busy_1 = 0;
+ driver->smd_cmd[data_type].in_busy_2 = 0;
+ queue_work(driver->diag_wq,
+ &(driver->smd_cmd[data_type].
+ diag_read_smd_work));
+ }
}
#ifdef CONFIG_DIAG_SDIO_PIPE
else if (data_type == SDIO_DATA) {
@@ -563,8 +593,8 @@
driver->write_ptr_svc);
} else
err = -1;
- } else if ((data_type >= 0) &&
- (data_type < NUM_SMD_DATA_CHANNELS)) {
+ } else if ((data_type >= MODEM_DATA) &&
+ (data_type <= WCNSS_DATA)) {
write_ptr->buf = buf;
#ifdef DIAG_DEBUG
printk(KERN_INFO "writing data to USB,"
@@ -688,27 +718,42 @@
int len, int type)
{
driver->pkt_length = len;
- if (entry.process_id != NON_APPS_PROC && type != MODEM_DATA) {
- diag_update_pkt_buffer(buf);
- diag_update_sleeping_process(entry.process_id, PKT_TYPE);
+
+ /* If the process_id corresponds to an apps process */
+ if (entry.process_id != NON_APPS_PROC) {
+ /* If the message is to be sent to the apps process */
+ if (type != MODEM_DATA) {
+ diag_update_pkt_buffer(buf);
+ diag_update_sleeping_process(entry.process_id,
+ PKT_TYPE);
+ }
} else {
if (len > 0) {
if (entry.client_id < NUM_SMD_DATA_CHANNELS) {
+ struct diag_smd_info *smd_info;
int index = entry.client_id;
- if (driver->smd_data[index].ch) {
- if ((index == MODEM_DATA) &&
- diag_check_mode_reset(buf)) {
- return;
- }
- mutex_lock(&driver->smd_data[index].
- smd_ch_mutex);
- smd_write(driver->smd_data[index].ch,
- buf, len);
- mutex_unlock(&driver->smd_data[index].
- smd_ch_mutex);
+ /*
+ * Mode reset should work even if
+ * modem is down
+ */
+ if ((index == MODEM_DATA) &&
+ diag_check_mode_reset(buf)) {
+ return;
+ }
+ smd_info = (driver->separate_cmdrsp[index] &&
+ index < NUM_SMD_CMD_CHANNELS) ?
+ &driver->smd_cmd[index] :
+ &driver->smd_data[index];
+
+ if (smd_info->ch) {
+ mutex_lock(&smd_info->smd_ch_mutex);
+ smd_write(smd_info->ch, buf, len);
+ mutex_unlock(&smd_info->smd_ch_mutex);
} else {
- pr_err("diag: In %s, smd channel %d not open\n",
- __func__, index);
+ pr_err("diag: In %s, smd channel %d not open, peripheral: %d, type: %d\n",
+ __func__, index,
+ smd_info->peripheral,
+ smd_info->type);
}
} else {
pr_alert("diag: In %s, incorrect channel: %d",
@@ -1277,9 +1322,37 @@
mutex_unlock(&driver->diag_hdlc_mutex);
}
+void diag_reset_smd_data(int queue)
+{
+ int i;
+
+ for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
+ driver->smd_data[i].in_busy_1 = 0;
+ driver->smd_data[i].in_busy_2 = 0;
+ if (queue)
+ /* Poll SMD data channels to check for data */
+ queue_work(driver->diag_wq,
+ &(driver->smd_data[i].diag_read_smd_work));
+ }
+
+ if (driver->supports_separate_cmdrsp) {
+ for (i = 0; i < NUM_SMD_CMD_CHANNELS; i++) {
+ driver->smd_cmd[i].in_busy_1 = 0;
+ driver->smd_cmd[i].in_busy_2 = 0;
+ if (queue)
+ /* Poll SMD data channels to check for data */
+ queue_work(driver->diag_wq,
+ &(driver->smd_cmd[i].
+ diag_read_smd_work));
+ }
+ }
+}
+
#ifdef CONFIG_DIAG_OVER_USB
/* 2+1 for modem ; 2 for LPASS ; 1 for WCNSS */
#define N_LEGACY_WRITE (driver->poolsize + 6)
+/* Additionally support number of command data and dci channels */
+#define N_LEGACY_WRITE_CMD ((N_LEGACY_WRITE) + 4)
#define N_LEGACY_READ 1
static void diag_usb_connect_work_fn(struct work_struct *w)
@@ -1298,18 +1371,16 @@
int i;
printk(KERN_DEBUG "diag: USB connected\n");
- err = usb_diag_alloc_req(driver->legacy_ch, N_LEGACY_WRITE,
+ err = usb_diag_alloc_req(driver->legacy_ch,
+ (driver->supports_separate_cmdrsp ?
+ N_LEGACY_WRITE_CMD : N_LEGACY_WRITE),
N_LEGACY_READ);
if (err)
printk(KERN_ERR "diag: unable to alloc USB req on legacy ch");
driver->usb_connected = 1;
+ diag_reset_smd_data(RESET_AND_QUEUE);
for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
- driver->smd_data[i].in_busy_1 = 0;
- driver->smd_data[i].in_busy_2 = 0;
- /* Poll SMD data channels to check for data */
- queue_work(driver->diag_wq,
- &(driver->smd_data[i].diag_read_smd_work));
/* Poll SMD CNTL channels to check for data */
diag_smd_notify(&(driver->smd_cntl[i]), SMD_EVENT_DATA);
}
@@ -1340,6 +1411,13 @@
driver->smd_data[i].in_busy_1 = 1;
driver->smd_data[i].in_busy_2 = 1;
}
+
+ if (driver->supports_separate_cmdrsp) {
+ for (i = 0; i < NUM_SMD_CMD_CHANNELS; i++) {
+ driver->smd_cmd[i].in_busy_1 = 1;
+ driver->smd_cmd[i].in_busy_2 = 1;
+ }
+ }
}
#ifdef CONFIG_DIAG_SDIO_PIPE
if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa())
@@ -1350,30 +1428,44 @@
return 0;
}
-int diagfwd_write_complete(struct diag_request *diag_write_ptr)
+static int diagfwd_check_buf_match(int num_channels,
+ struct diag_smd_info *data, unsigned char *buf)
{
- unsigned char *buf = diag_write_ptr->buf;
- int found_it = 0;
int i;
+ int found_it = 0;
- /* Determine if the write complete is for data from modem/apps/q6 */
- /* Need a context variable here instead */
- for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
- struct diag_smd_info *data = &(driver->smd_data[i]);
- if (buf == (void *)data->buf_in_1) {
- data->in_busy_1 = 0;
+ for (i = 0; i < num_channels; i++) {
+ if (buf == (void *)data[i].buf_in_1) {
+ data[i].in_busy_1 = 0;
queue_work(driver->diag_wq,
- &(data->diag_read_smd_work));
+ &(data[i].diag_read_smd_work));
found_it = 1;
break;
- } else if (buf == (void *)data->buf_in_2) {
- data->in_busy_2 = 0;
+ } else if (buf == (void *)data[i].buf_in_2) {
+ data[i].in_busy_2 = 0;
queue_work(driver->diag_wq,
- &(data->diag_read_smd_work));
+ &(data[i].diag_read_smd_work));
found_it = 1;
break;
}
}
+
+ return found_it;
+}
+
+int diagfwd_write_complete(struct diag_request *diag_write_ptr)
+{
+ unsigned char *buf = diag_write_ptr->buf;
+ int found_it = 0;
+
+ /* Determine if the write complete is for data from modem/apps/q6 */
+ found_it = diagfwd_check_buf_match(NUM_SMD_DATA_CHANNELS,
+ driver->smd_data, buf);
+
+ if (!found_it && driver->supports_separate_cmdrsp)
+ found_it = diagfwd_check_buf_match(NUM_SMD_CMD_CHANNELS,
+ driver->smd_cmd, buf);
+
#ifdef CONFIG_DIAG_SDIO_PIPE
if (!found_it) {
if (buf == (void *)driver->buf_in_sdio) {
@@ -1521,7 +1613,8 @@
wake_up(&driver->smd_wait_q);
- if (smd_info->type == SMD_DCI_TYPE)
+ if (smd_info->type == SMD_DCI_TYPE ||
+ smd_info->type == SMD_DCI_CMD_TYPE)
queue_work(driver->diag_dci_wq,
&(smd_info->diag_read_smd_work));
else
@@ -1532,40 +1625,66 @@
{
int r = 0;
int index = -1;
+ const char *channel_name = NULL;
if (pdev->id == SMD_APPS_MODEM) {
index = MODEM_DATA;
- r = smd_open("DIAG", &driver->smd_data[index].ch,
- &driver->smd_data[index],
- diag_smd_notify);
- driver->smd_data[index].ch_save =
- driver->smd_data[index].ch;
+ channel_name = "DIAG";
}
#if defined(CONFIG_MSM_N_WAY_SMD)
- if (pdev->id == SMD_APPS_QDSP) {
+ else if (pdev->id == SMD_APPS_QDSP) {
index = LPASS_DATA;
- r = smd_named_open_on_edge("DIAG", SMD_APPS_QDSP,
- &driver->smd_data[index].ch,
- &driver->smd_data[index],
- diag_smd_notify);
- driver->smd_data[index].ch_save =
- driver->smd_data[index].ch;
+ channel_name = "DIAG";
}
#endif
- if (pdev->id == SMD_APPS_WCNSS) {
+ else if (pdev->id == SMD_APPS_WCNSS) {
index = WCNSS_DATA;
- r = smd_named_open_on_edge("APPS_RIVA_DATA",
- SMD_APPS_WCNSS,
+ channel_name = "APPS_RIVA_DATA";
+ }
+
+ if (index != -1) {
+ r = smd_named_open_on_edge(channel_name,
+ pdev->id,
&driver->smd_data[index].ch,
&driver->smd_data[index],
diag_smd_notify);
- driver->smd_data[index].ch_save =
- driver->smd_data[index].ch;
+ driver->smd_data[index].ch_save = driver->smd_data[index].ch;
}
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
- pr_debug("diag: open SMD port, Id = %d, r = %d\n", pdev->id, r);
+ pr_debug("diag: In %s, open SMD port, Id = %d, r = %d\n",
+ __func__, pdev->id, r);
+
+ return 0;
+}
+
+static int diag_smd_cmd_probe(struct platform_device *pdev)
+{
+ int r = 0;
+ int index = -1;
+ const char *channel_name = NULL;
+
+ if (!driver->supports_separate_cmdrsp)
+ return 0;
+
+ if (pdev->id == SMD_APPS_MODEM) {
+ index = MODEM_DATA;
+ channel_name = "DIAG_CMD";
+ }
+
+ if (index != -1) {
+ r = smd_named_open_on_edge(channel_name,
+ pdev->id,
+ &driver->smd_cmd[index].ch,
+ &driver->smd_cmd[index],
+ diag_smd_notify);
+ driver->smd_cmd[index].ch_save =
+ driver->smd_cmd[index].ch;
+ }
+
+ pr_debug("diag: In %s, open SMD CMD port, Id = %d, r = %d\n",
+ __func__, pdev->id, r);
return 0;
}
@@ -1607,6 +1726,24 @@
},
};
+static struct platform_driver
+ smd_lite_data_cmd_drivers[NUM_SMD_CMD_CHANNELS] = {
+ {
+ /* Modem data */
+ .probe = diag_smd_cmd_probe,
+ .driver = {
+ .name = "DIAG_CMD",
+ .owner = THIS_MODULE,
+ .pm = &diag_smd_dev_pm_ops,
+ },
+ }
+};
+
+int device_supports_separate_cmdrsp(void)
+{
+ return driver->use_device_tree;
+}
+
void diag_smd_destructor(struct diag_smd_info *smd_info)
{
if (smd_info->type == SMD_DATA_TYPE)
@@ -1692,16 +1829,22 @@
* information to the update function.
*/
smd_info->notify_context = 0;
- if (type == SMD_DATA_TYPE)
+ switch (type) {
+ case SMD_DATA_TYPE:
+ case SMD_CMD_TYPE:
INIT_WORK(&(smd_info->diag_notify_update_smd_work),
- diag_clean_reg_fn);
- else if (type == SMD_CNTL_TYPE)
+ diag_clean_reg_fn);
+ break;
+ case SMD_CNTL_TYPE:
INIT_WORK(&(smd_info->diag_notify_update_smd_work),
- diag_mask_update_fn);
- else if (type == SMD_DCI_TYPE)
+ diag_mask_update_fn);
+ break;
+ case SMD_DCI_TYPE:
+ case SMD_DCI_CMD_TYPE:
INIT_WORK(&(smd_info->diag_notify_update_smd_work),
- diag_update_smd_dci_work_fn);
- else {
+ diag_update_smd_dci_work_fn);
+ break;
+ default:
pr_err("diag: In %s, unknown type, type: %d\n", __func__, type);
goto err;
}
@@ -1710,15 +1853,21 @@
* Set function ptr for function to call to process the data that
* was just read from the smd channel
*/
- if (type == SMD_DATA_TYPE)
+ switch (type) {
+ case SMD_DATA_TYPE:
+ case SMD_CMD_TYPE:
smd_info->process_smd_read_data = diag_process_smd_read_data;
- else if (type == SMD_CNTL_TYPE)
+ break;
+ case SMD_CNTL_TYPE:
smd_info->process_smd_read_data =
diag_process_smd_cntl_read_data;
- else if (type == SMD_DCI_TYPE)
+ break;
+ case SMD_DCI_TYPE:
+ case SMD_DCI_CMD_TYPE:
smd_info->process_smd_read_data =
diag_process_smd_dci_read_data;
- else {
+ break;
+ default:
pr_err("diag: In %s, unknown type, type: %d\n", __func__, type);
goto err;
}
@@ -1768,23 +1917,34 @@
driver->read_len_legacy = 0;
driver->use_device_tree = has_device_tree();
driver->real_time_mode = 1;
+ /*
+ * The number of entries in table of buffers
+ * should not be any smaller than hdlc poolsize.
+ */
+ driver->buf_tbl_size = (buf_tbl_size < driver->poolsize_hdlc) ?
+ driver->poolsize_hdlc : buf_tbl_size;
+ driver->supports_separate_cmdrsp = device_supports_separate_cmdrsp();
mutex_init(&driver->diag_hdlc_mutex);
mutex_init(&driver->diag_cntl_mutex);
- success = diag_smd_constructor(&driver->smd_data[MODEM_DATA],
- MODEM_DATA, SMD_DATA_TYPE);
- if (!success)
- goto err;
+ for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++)
+ driver->separate_cmdrsp[i] = 0;
- success = diag_smd_constructor(&driver->smd_data[LPASS_DATA],
- LPASS_DATA, SMD_DATA_TYPE);
- if (!success)
- goto err;
+ for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
+ success = diag_smd_constructor(&driver->smd_data[i], i,
+ SMD_DATA_TYPE);
+ if (!success)
+ goto err;
+ }
- success = diag_smd_constructor(&driver->smd_data[WCNSS_DATA],
- WCNSS_DATA, SMD_DATA_TYPE);
- if (!success)
- goto err;
+ if (driver->supports_separate_cmdrsp) {
+ for (i = 0; i < NUM_SMD_CMD_CHANNELS; i++) {
+ success = diag_smd_constructor(&driver->smd_cmd[i], i,
+ SMD_CMD_TYPE);
+ if (!success)
+ goto err;
+ }
+ }
if (driver->usb_buf_out == NULL &&
(driver->usb_buf_out = kzalloc(USB_MAX_OUT_BUF,
@@ -1802,7 +1962,7 @@
goto err;
kmemleak_not_leak(driver->client_map);
if (driver->buf_tbl == NULL)
- driver->buf_tbl = kzalloc(buf_tbl_size *
+ driver->buf_tbl = kzalloc(driver->buf_tbl_size *
sizeof(struct diag_write_device), GFP_KERNEL);
if (driver->buf_tbl == NULL)
goto err;
@@ -1854,6 +2014,12 @@
#endif
platform_driver_register(&msm_smd_ch1_driver);
platform_driver_register(&diag_smd_lite_driver);
+
+ if (driver->supports_separate_cmdrsp) {
+ for (i = 0; i < NUM_SMD_CMD_CHANNELS; i++)
+ platform_driver_register(&smd_lite_data_cmd_drivers[i]);
+ }
+
return;
err:
pr_err("diag: Could not initialize diag buffers");
@@ -1861,6 +2027,9 @@
for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++)
diag_smd_destructor(&driver->smd_data[i]);
+ for (i = 0; i < NUM_SMD_CMD_CHANNELS; i++)
+ diag_smd_destructor(&driver->smd_cmd[i]);
+
kfree(driver->buf_msg_mask_update);
kfree(driver->buf_log_mask_update);
kfree(driver->buf_event_mask_update);
@@ -1890,9 +2059,16 @@
usb_diag_close(driver->legacy_ch);
#endif
platform_driver_unregister(&msm_smd_ch1_driver);
- platform_driver_unregister(&msm_diag_dci_driver);
platform_driver_unregister(&diag_smd_lite_driver);
+ if (driver->supports_separate_cmdrsp) {
+ for (i = 0; i < NUM_SMD_CMD_CHANNELS; i++) {
+ diag_smd_destructor(&driver->smd_cmd[i]);
+ platform_driver_unregister(
+ &smd_lite_data_cmd_drivers[i]);
+ }
+ }
+
kfree(driver->buf_msg_mask_update);
kfree(driver->buf_log_mask_update);
kfree(driver->buf_event_mask_update);
diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h
index 09f2f5e..c6e1273 100644
--- a/drivers/char/diag/diagfwd.h
+++ b/drivers/char/diag/diagfwd.h
@@ -16,6 +16,9 @@
#define NO_PROCESS 0
#define NON_APPS_PROC -1
+#define RESET_AND_NO_QUEUE 0
+#define RESET_AND_QUEUE 1
+
#define CHK_OVERFLOW(bufStart, start, end, length) \
((((bufStart) <= (start)) && ((end) - (start) >= (length))) ? 1 : 0)
@@ -49,6 +52,7 @@
void diag_cmp_logging_modes_sdio_pipe(int old_mode, int new_mode);
void diag_cmp_logging_modes_diagfwd_bridge(int old_mode, int new_mode);
int diag_process_apps_pkt(unsigned char *buf, int len);
+void diag_reset_smd_data(int queue);
/* State for diag forwarding */
#ifdef CONFIG_DIAG_OVER_USB
int diagfwd_connect(void);
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index c3ff7dc..13cbc8f 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -45,7 +45,6 @@
int total_recd)
{
int data_len = 0, type = -1, count_bytes = 0, j, flag = 0;
- int feature_mask_len;
struct bindpkt_params_per_process *pkt_params =
kzalloc(sizeof(struct bindpkt_params_per_process), GFP_KERNEL);
struct diag_ctrl_msg *msg;
@@ -69,7 +68,7 @@
type = *(uint32_t *)(buf);
data_len = *(uint32_t *)(buf + 4);
if (type < DIAG_CTRL_MSG_REG ||
- type > DIAG_CTRL_MSG_F3_MASK_V2) {
+ type > DIAG_CTRL_MSG_LAST) {
pr_alert("diag: In %s, Invalid Msg type %d proc %d",
__func__, type, smd_info->peripheral);
break;
@@ -88,7 +87,7 @@
pkt_params->count = msg->count_entries;
pkt_params->params = kzalloc(pkt_params->count *
sizeof(struct bindpkt_params), GFP_KERNEL);
- if (pkt_params->params == NULL) {
+ if (ZERO_OR_NULL_PTR(pkt_params->params)) {
pr_alert("diag: In %s, Memory alloc fail\n",
__func__);
kfree(pkt_params);
@@ -116,11 +115,32 @@
pr_err("diag: drop reg proc %d\n",
smd_info->peripheral);
kfree(pkt_params->params);
- } else if ((type == DIAG_CTRL_MSG_FEATURE) &&
- (smd_info->peripheral == MODEM_DATA)) {
- feature_mask_len = *(int *)(buf + 8);
- driver->log_on_demand_support = (*(uint8_t *)
- (buf + 12)) & 0x04;
+ } else if (type == DIAG_CTRL_MSG_FEATURE &&
+ total_recd >= count_bytes) {
+ uint8_t feature_mask = 0;
+ int feature_mask_len = *(int *)(buf+8);
+ if (feature_mask_len > 0) {
+ feature_mask = *(uint8_t *)(buf+12);
+ if (smd_info->peripheral == MODEM_DATA)
+ driver->log_on_demand_support =
+ feature_mask &
+ F_DIAG_LOG_ON_DEMAND_RSP_ON_MASTER;
+ /*
+ * If apps supports separate cmd/rsp channels
+ * and the peripheral supports separate cmd/rsp
+ * channels
+ */
+ if (driver->supports_separate_cmdrsp &&
+ (feature_mask & F_DIAG_REQ_RSP_CHANNEL))
+ driver->separate_cmdrsp
+ [smd_info->peripheral] =
+ ENABLE_SEPARATE_CMDRSP;
+ else
+ driver->separate_cmdrsp
+ [smd_info->peripheral] =
+ DISABLE_SEPARATE_CMDRSP;
+ }
+ flag = 1;
} else if (type != DIAG_CTRL_MSG_REG) {
flag = 1;
}
@@ -212,39 +232,38 @@
{
int r = 0;
int index = -1;
+ const char *channel_name = NULL;
/* open control ports only on 8960 & newer targets */
if (chk_apps_only()) {
if (pdev->id == SMD_APPS_MODEM) {
index = MODEM_DATA;
- r = smd_open("DIAG_CNTL",
- &driver->smd_cntl[index].ch,
- &driver->smd_cntl[index],
- diag_smd_notify);
- driver->smd_cntl[index].ch_save =
- driver->smd_cntl[index].ch;
- } else if (pdev->id == SMD_APPS_QDSP) {
+ channel_name = "DIAG_CNTL";
+ }
+#if defined(CONFIG_MSM_N_WAY_SMD)
+ else if (pdev->id == SMD_APPS_QDSP) {
index = LPASS_DATA;
- r = smd_named_open_on_edge("DIAG_CNTL",
- SMD_APPS_QDSP,
- &driver->smd_cntl[index].ch,
- &driver->smd_cntl[index],
- diag_smd_notify);
- driver->smd_cntl[index].ch_save =
- driver->smd_cntl[index].ch;
- } else if (pdev->id == SMD_APPS_WCNSS) {
+ channel_name = "DIAG_CNTL";
+ }
+#endif
+ else if (pdev->id == SMD_APPS_WCNSS) {
index = WCNSS_DATA;
- r = smd_named_open_on_edge("APPS_RIVA_CTRL",
- SMD_APPS_WCNSS,
- &driver->smd_cntl[index].ch,
- &driver->smd_cntl[index],
- diag_smd_notify);
- driver->smd_cntl[index].ch_save =
- driver->smd_cntl[index].ch;
+ channel_name = "APPS_RIVA_CTRL";
}
- pr_debug("diag: open CNTL port, ID = %d,r = %d\n", pdev->id, r);
+ if (index != -1) {
+ r = smd_named_open_on_edge(channel_name,
+ pdev->id,
+ &driver->smd_cntl[index].ch,
+ &driver->smd_cntl[index],
+ diag_smd_notify);
+ driver->smd_cntl[index].ch_save =
+ driver->smd_cntl[index].ch;
+ }
+ pr_debug("diag: In %s, open SMD CNTL port, Id = %d, r = %d\n",
+ __func__, pdev->id, r);
}
+
return 0;
}
@@ -269,20 +288,20 @@
.probe = diag_smd_cntl_probe,
.driver = {
- .name = "DIAG_CNTL",
- .owner = THIS_MODULE,
- .pm = &diagfwd_cntl_dev_pm_ops,
- },
+ .name = "DIAG_CNTL",
+ .owner = THIS_MODULE,
+ .pm = &diagfwd_cntl_dev_pm_ops,
+ },
};
static struct platform_driver diag_smd_lite_cntl_driver = {
.probe = diag_smd_cntl_probe,
.driver = {
- .name = "APPS_RIVA_CTRL",
- .owner = THIS_MODULE,
- .pm = &diagfwd_cntl_dev_pm_ops,
- },
+ .name = "APPS_RIVA_CTRL",
+ .owner = THIS_MODULE,
+ .pm = &diagfwd_cntl_dev_pm_ops,
+ },
};
void diagfwd_cntl_init(void)
@@ -295,20 +314,12 @@
driver->log_on_demand_support = 1;
driver->diag_cntl_wq = create_singlethread_workqueue("diag_cntl_wq");
- success = diag_smd_constructor(&driver->smd_cntl[MODEM_DATA],
- MODEM_DATA, SMD_CNTL_TYPE);
- if (!success)
- goto err;
-
- success = diag_smd_constructor(&driver->smd_cntl[LPASS_DATA],
- LPASS_DATA, SMD_CNTL_TYPE);
- if (!success)
- goto err;
-
- success = diag_smd_constructor(&driver->smd_cntl[WCNSS_DATA],
- WCNSS_DATA, SMD_CNTL_TYPE);
- if (!success)
- goto err;
+ for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++) {
+ success = diag_smd_constructor(&driver->smd_cntl[i], i,
+ SMD_CNTL_TYPE);
+ if (!success)
+ goto err;
+ }
platform_driver_register(&msm_smd_ch1_cntl_driver);
platform_driver_register(&diag_smd_lite_cntl_driver);
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index 7cd1866..02b9757 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -29,6 +29,25 @@
#define DIAG_CTRL_MSG_EVENT_MASK_V2 10
/* Send Diag F3 mask */
#define DIAG_CTRL_MSG_F3_MASK_V2 11
+#define DIAG_CTRL_MSG_NUM_PRESETS 12
+#define DIAG_CTRL_MSG_SET_PRESET_ID 13
+#define DIAG_CTRL_MSG_LOG_MASK_WITH_PRESET_ID 14
+#define DIAG_CTRL_MSG_EVENT_MASK_WITH_PRESET_ID 15
+#define DIAG_CTRL_MSG_F3_MASK_WITH_PRESET_ID 16
+#define DIAG_CTRL_MSG_LAST DIAG_CTRL_MSG_F3_MASK_WITH_PRESET_ID
+
+/* Denotes that we support sending/receiving the feature mask */
+#define F_DIAG_INT_FEATURE_MASK 0x01
+/* Denotes that we support responding to "Log on Demand" */
+#define F_DIAG_LOG_ON_DEMAND_RSP_ON_MASTER 0x04
+/*
+ * Supports dedicated main request/response on
+ * new Data Rx and DCI Rx channels
+ */
+#define F_DIAG_REQ_RSP_CHANNEL 0x10
+
+#define ENABLE_SEPARATE_CMDRSP 1
+#define DISABLE_SEPARATE_CMDRSP 0
struct cmd_code_range {
uint16_t cmd_code_lo;
@@ -101,6 +120,7 @@
void diagfwd_cntl_init(void);
void diagfwd_cntl_exit(void);
void diag_read_smd_cntl_work_fn(struct work_struct *);
+void diag_notify_ctrl_update_fn(struct work_struct *work);
void diag_clean_reg_fn(struct work_struct *work);
int diag_process_smd_cntl_read_data(struct diag_smd_info *smd_info, void *buf,
int total_recd);
diff --git a/drivers/char/msm_rotator.c b/drivers/char/msm_rotator.c
index 9a43ea4..156a2a5 100644
--- a/drivers/char/msm_rotator.c
+++ b/drivers/char/msm_rotator.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2009-2013, 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
@@ -351,6 +351,7 @@
case MDP_RGBA_8888:
case MDP_BGRA_8888:
case MDP_RGBX_8888:
+ case MDP_BGRX_8888:
return 4;
case MDP_Y_CBCR_H2V2:
@@ -407,6 +408,7 @@
case MDP_RGBA_8888:
case MDP_BGRA_8888:
case MDP_RGBX_8888:
+ case MDP_BGRX_8888:
case MDP_RGB_888:
case MDP_RGB_565:
case MDP_BGR_565:
@@ -811,6 +813,7 @@
break;
case MDP_BGRA_8888:
+ case MDP_BGRX_8888:
iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G,
CLR_R, 8),
MSM_ROTATOR_SRC_UNPACK_PATTERN1);
@@ -1118,6 +1121,7 @@
case MDP_XRGB_8888:
case MDP_BGRA_8888:
case MDP_RGBX_8888:
+ case MDP_BGRX_8888:
case MDP_YCBCR_H1V1:
case MDP_YCRCB_H1V1:
rc = msm_rotator_rgb_types(msm_rotator_dev->img_info[s],
@@ -1275,6 +1279,7 @@
case MDP_XRGB_8888:
case MDP_RGBX_8888:
case MDP_BGRA_8888:
+ case MDP_BGRX_8888:
is_rgb = 1;
info.dst.format = info.src.format;
break;
diff --git a/drivers/coresight/Kconfig b/drivers/coresight/Kconfig
index 5e00570..7ec83dd 100644
--- a/drivers/coresight/Kconfig
+++ b/drivers/coresight/Kconfig
@@ -105,6 +105,16 @@
If unsure, say 'N' here to avoid potential power and performance
penalty.
+config CORESIGHT_HWEVENT
+ bool "CoreSight Hardware Event driver"
+ depends on CORESIGHT_STM
+ select CORESIGHT_CSR
+ help
+ This driver provides support for monitoring and tracing CoreSight
+ Hardware Event across STM interface. It configures Coresight
+ Hardware Event mux control registers to select hardware events
+ based on user input.
+
config CORESIGHT_ETM
bool "CoreSight Embedded Trace Macrocell driver"
help
diff --git a/drivers/coresight/Makefile b/drivers/coresight/Makefile
index 0595064..2b14f86 100644
--- a/drivers/coresight/Makefile
+++ b/drivers/coresight/Makefile
@@ -2,7 +2,6 @@
# Makefile for CoreSight drivers.
#
obj-$(CONFIG_CORESIGHT) += coresight.o
-obj-$(CONFIG_OF) += of_coresight.o
obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o
obj-$(CONFIG_CORESIGHT_TMC) += coresight-tmc.o
@@ -11,5 +10,6 @@
obj-$(CONFIG_CORESIGHT_FUNNEL) += coresight-funnel.o
obj-$(CONFIG_CORESIGHT_REPLICATOR) += coresight-replicator.o
obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o
+obj-$(CONFIG_CORESIGHT_HWEVENT) += coresight-hwevent.o
obj-$(CONFIG_CORESIGHT_ETM) += coresight-etm.o coresight-etm-cp14.o
obj-$(CONFIG_CORESIGHT_EVENT) += coresight-event.o
diff --git a/drivers/coresight/coresight-csr.c b/drivers/coresight/coresight-csr.c
index 988d1c9..8195184 100644
--- a/drivers/coresight/coresight-csr.c
+++ b/drivers/coresight/coresight-csr.c
@@ -73,6 +73,7 @@
struct csr_drvdata {
void __iomem *base;
+ phys_addr_t pbase;
struct device *dev;
struct coresight_device *csdev;
uint32_t blksize;
@@ -102,7 +103,7 @@
CSR_LOCK(drvdata);
}
-EXPORT_SYMBOL_GPL(msm_qdss_csr_enable_bam_to_usb);
+EXPORT_SYMBOL(msm_qdss_csr_enable_bam_to_usb);
void msm_qdss_csr_disable_bam_to_usb(void)
{
@@ -117,7 +118,7 @@
CSR_LOCK(drvdata);
}
-EXPORT_SYMBOL_GPL(msm_qdss_csr_disable_bam_to_usb);
+EXPORT_SYMBOL(msm_qdss_csr_disable_bam_to_usb);
void msm_qdss_csr_disable_flush(void)
{
@@ -132,7 +133,31 @@
CSR_LOCK(drvdata);
}
-EXPORT_SYMBOL_GPL(msm_qdss_csr_disable_flush);
+EXPORT_SYMBOL(msm_qdss_csr_disable_flush);
+
+int coresight_csr_hwctrl_set(phys_addr_t addr, uint32_t val)
+{
+ struct csr_drvdata *drvdata = csrdrvdata;
+ int ret = 0;
+
+ CSR_UNLOCK(drvdata);
+
+ if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL0))
+ csr_writel(drvdata, val, CSR_STMEXTHWCTRL0);
+ else if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL1))
+ csr_writel(drvdata, val, CSR_STMEXTHWCTRL1);
+ else if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL2))
+ csr_writel(drvdata, val, CSR_STMEXTHWCTRL2);
+ else if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL3))
+ csr_writel(drvdata, val, CSR_STMEXTHWCTRL3);
+ else
+ ret = -EINVAL;
+
+ CSR_LOCK(drvdata);
+
+ return ret;
+}
+EXPORT_SYMBOL(coresight_csr_hwctrl_set);
static int __devinit csr_probe(struct platform_device *pdev)
{
@@ -161,6 +186,7 @@
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr-base");
if (!res)
return -ENODEV;
+ drvdata->pbase = res->start;
drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
if (!drvdata->base)
diff --git a/drivers/coresight/coresight-hwevent.c b/drivers/coresight/coresight-hwevent.c
new file mode 100644
index 0000000..777484d
--- /dev/null
+++ b/drivers/coresight/coresight-hwevent.c
@@ -0,0 +1,341 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/of_coresight.h>
+#include <linux/coresight.h>
+#include <linux/of.h>
+
+#include "coresight-priv.h"
+
+struct hwevent_mux {
+ phys_addr_t start;
+ phys_addr_t end;
+};
+
+struct hwevent_drvdata {
+ struct device *dev;
+ struct coresight_device *csdev;
+ struct clk *clk;
+ struct mutex mutex;
+ int nr_hclk;
+ struct clk **hclk;
+ int nr_hmux;
+ struct hwevent_mux *hmux;
+ bool enable;
+};
+
+static int hwevent_enable(struct hwevent_drvdata *drvdata)
+{
+ int ret, i;
+
+ mutex_lock(&drvdata->mutex);
+
+ if (drvdata->enable)
+ goto out;
+
+ ret = clk_prepare_enable(drvdata->clk);
+ if (ret)
+ goto err0;
+ for (i = 0; i < drvdata->nr_hclk; i++) {
+ ret = clk_prepare_enable(drvdata->hclk[i]);
+ if (ret)
+ goto err1;
+ }
+ drvdata->enable = true;
+ dev_info(drvdata->dev, "Hardware Event driver enabled\n");
+out:
+ mutex_unlock(&drvdata->mutex);
+ return 0;
+err1:
+ clk_disable_unprepare(drvdata->clk);
+ for (i--; i >= 0; i--)
+ clk_disable_unprepare(drvdata->hclk[i]);
+err0:
+ mutex_unlock(&drvdata->mutex);
+ return ret;
+}
+
+static void hwevent_disable(struct hwevent_drvdata *drvdata)
+{
+ int i;
+
+ mutex_lock(&drvdata->mutex);
+
+ if (!drvdata->enable)
+ goto out;
+
+ drvdata->enable = false;
+ clk_disable_unprepare(drvdata->clk);
+ for (i = 0; i < drvdata->nr_hclk; i++)
+ clk_disable_unprepare(drvdata->hclk[i]);
+ dev_info(drvdata->dev, "Hardware Event driver disabled\n");
+out:
+ mutex_unlock(&drvdata->mutex);
+}
+
+static ssize_t hwevent_show_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hwevent_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val = drvdata->enable;
+
+ return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+
+static ssize_t hwevent_store_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct hwevent_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+ int ret = 0;
+
+ if (sscanf(buf, "%lx", &val) != 1)
+ return -EINVAL;
+
+ if (val)
+ ret = hwevent_enable(drvdata);
+ else
+ hwevent_disable(drvdata);
+
+ if (ret)
+ return ret;
+ return size;
+}
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, hwevent_show_enable,
+ hwevent_store_enable);
+
+static ssize_t hwevent_store_setreg(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct hwevent_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ void *hwereg;
+ phys_addr_t addr;
+ uint32_t val;
+ int ret, i;
+
+ if (sscanf(buf, "%x %x", &addr, &val) != 2)
+ return -EINVAL;
+
+ mutex_lock(&drvdata->mutex);
+
+ if (!drvdata->enable) {
+ dev_err(dev, "Hardware Event driver not enabled\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ for (i = 0; i < drvdata->nr_hmux; i++) {
+ if ((addr >= drvdata->hmux[i].start) &&
+ (addr < drvdata->hmux[i].end)) {
+ hwereg = devm_ioremap(dev,
+ drvdata->hmux[i].start,
+ drvdata->hmux[i].end -
+ drvdata->hmux[i].start);
+ if (!hwereg) {
+ dev_err(dev, "unable to map address 0x%x\n",
+ addr);
+ ret = -ENOMEM;
+ goto err;
+ }
+ writel_relaxed(val, hwereg + addr -
+ drvdata->hmux[i].start);
+ /* Ensure writes to hwevent control registers
+ are completed before unmapping the address
+ */
+ mb();
+ devm_iounmap(dev, hwereg);
+ break;
+ }
+ }
+
+ if (i == drvdata->nr_hmux) {
+ ret = coresight_csr_hwctrl_set(addr, val);
+ if (ret) {
+ dev_err(dev, "invalid mux control register address\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ mutex_unlock(&drvdata->mutex);
+ return size;
+err:
+ mutex_unlock(&drvdata->mutex);
+ return ret;
+}
+static DEVICE_ATTR(setreg, S_IWUSR, NULL, hwevent_store_setreg);
+
+static struct attribute *hwevent_attrs[] = {
+ &dev_attr_enable.attr,
+ &dev_attr_setreg.attr,
+ NULL,
+};
+
+static struct attribute_group hwevent_attr_grp = {
+ .attrs = hwevent_attrs,
+};
+
+static const struct attribute_group *hwevent_attr_grps[] = {
+ &hwevent_attr_grp,
+ NULL,
+};
+
+static int __devinit hwevent_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct hwevent_drvdata *drvdata;
+ struct coresight_desc *desc;
+ struct coresight_platform_data *pdata;
+ struct resource *res;
+ int ret, i;
+ const char *hmux_name, *hclk_name;
+
+ if (pdev->dev.of_node) {
+ pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ pdev->dev.platform_data = pdata;
+ }
+
+ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+ drvdata->dev = &pdev->dev;
+ platform_set_drvdata(pdev, drvdata);
+
+ if (pdev->dev.of_node)
+ drvdata->nr_hmux = of_property_count_strings(pdev->dev.of_node,
+ "reg-names");
+
+ if (drvdata->nr_hmux > 0) {
+ drvdata->hmux = devm_kzalloc(dev, drvdata->nr_hmux *
+ sizeof(*drvdata->hmux),
+ GFP_KERNEL);
+ if (!drvdata->hmux)
+ return -ENOMEM;
+ for (i = 0; i < drvdata->nr_hmux; i++) {
+ ret = of_property_read_string_index(pdev->dev.of_node,
+ "reg-names", i,
+ &hmux_name);
+ if (ret)
+ return ret;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ hmux_name);
+ if (!res)
+ return -ENODEV;
+ drvdata->hmux[i].start = res->start;
+ drvdata->hmux[i].end = res->end;
+ }
+ } else if (drvdata->nr_hmux < 0) {
+ return drvdata->nr_hmux;
+ } else {
+ /* return error if reg-names in dt node is empty string */
+ return -ENODEV;
+ }
+
+ mutex_init(&drvdata->mutex);
+
+ drvdata->clk = devm_clk_get(dev, "core_clk");
+ if (IS_ERR(drvdata->clk))
+ return PTR_ERR(drvdata->clk);
+
+ ret = clk_set_rate(drvdata->clk, CORESIGHT_CLK_RATE_TRACE);
+ if (ret)
+ return ret;
+
+ if (pdev->dev.of_node)
+ drvdata->nr_hclk = of_property_count_strings(pdev->dev.of_node,
+ "qcom,hwevent-clks");
+ if (drvdata->nr_hclk > 0) {
+ drvdata->hclk = devm_kzalloc(dev, drvdata->nr_hclk *
+ sizeof(*drvdata->hclk),
+ GFP_KERNEL);
+ if (!drvdata->hclk)
+ return -ENOMEM;
+ for (i = 0; i < drvdata->nr_hclk; i++) {
+ ret = of_property_read_string_index(pdev->dev.of_node,
+ "qcom,hwevent-clks",
+ i, &hclk_name);
+ if (ret)
+ return ret;
+ drvdata->hclk[i] = devm_clk_get(dev, hclk_name);
+ if (IS_ERR(drvdata->hclk[i]))
+ return PTR_ERR(drvdata->hclk[i]);
+ }
+ }
+
+ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ desc->type = CORESIGHT_DEV_TYPE_NONE;
+ desc->pdata = pdev->dev.platform_data;
+ desc->dev = &pdev->dev;
+ desc->groups = hwevent_attr_grps;
+ desc->owner = THIS_MODULE;
+ drvdata->csdev = coresight_register(desc);
+ if (IS_ERR(drvdata->csdev))
+ return PTR_ERR(drvdata->csdev);
+
+ dev_info(dev, "Hardware Event driver initialized\n");
+ return 0;
+}
+
+static int __devexit hwevent_remove(struct platform_device *pdev)
+{
+ struct hwevent_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ coresight_unregister(drvdata->csdev);
+ return 0;
+}
+
+static struct of_device_id hwevent_match[] = {
+ {.compatible = "qcom,coresight-hwevent"},
+ {}
+};
+
+static struct platform_driver hwevent_driver = {
+ .probe = hwevent_probe,
+ .remove = __devexit_p(hwevent_remove),
+ .driver = {
+ .name = "coresight-hwevent",
+ .owner = THIS_MODULE,
+ .of_match_table = hwevent_match,
+ },
+};
+
+static int __init hwevent_init(void)
+{
+ return platform_driver_register(&hwevent_driver);
+}
+module_init(hwevent_init);
+
+static void __exit hwevent_exit(void)
+{
+ platform_driver_unregister(&hwevent_driver);
+}
+module_exit(hwevent_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CoreSight Hardware Event driver");
diff --git a/drivers/coresight/coresight-priv.h b/drivers/coresight/coresight-priv.h
index 258ff09..f208185 100644
--- a/drivers/coresight/coresight-priv.h
+++ b/drivers/coresight/coresight-priv.h
@@ -40,10 +40,13 @@
extern void msm_qdss_csr_enable_bam_to_usb(void);
extern void msm_qdss_csr_disable_bam_to_usb(void);
extern void msm_qdss_csr_disable_flush(void);
+extern int coresight_csr_hwctrl_set(phys_addr_t addr, uint32_t val);
#else
static inline void msm_qdss_csr_enable_bam_to_usb(void) {}
static inline void msm_qdss_csr_disable_bam_to_usb(void) {}
static inline void msm_qdss_csr_disable_flush(void) {}
+static inline int coresight_csr_hwctrl_set(phys_addr_t addr,
+ uint32_t val) { return -ENOSYS; }
#endif
#ifdef CONFIG_CORESIGHT_ETM
extern unsigned int etm_readl_cp14(uint32_t off);
diff --git a/drivers/coresight/coresight-stm.c b/drivers/coresight/coresight-stm.c
index 87cf63a..7d4dabe 100644
--- a/drivers/coresight/coresight-stm.c
+++ b/drivers/coresight/coresight-stm.c
@@ -595,7 +595,7 @@
return __stm_trace(options, entity_id, proto_id, data, size);
}
-EXPORT_SYMBOL_GPL(stm_trace);
+EXPORT_SYMBOL(stm_trace);
static ssize_t stm_write(struct file *file, const char __user *data,
size_t size, loff_t *ppos)
diff --git a/drivers/coresight/coresight-tmc.c b/drivers/coresight/coresight-tmc.c
index 4a9a97a..f39334a 100644
--- a/drivers/coresight/coresight-tmc.c
+++ b/drivers/coresight/coresight-tmc.c
@@ -441,7 +441,7 @@
if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM &&
!drvdata->reset_flush_race) {
coresight_cti_map_trigout(drvdata->cti_flush, 3, 0);
- coresight_cti_map_trigin(drvdata->cti_reset, 0, 0);
+ coresight_cti_map_trigin(drvdata->cti_reset, 2, 0);
} else if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB) {
drvdata->usbch = usb_qdss_open("qdss", drvdata,
usb_notifier);
@@ -676,7 +676,7 @@
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM &&
!drvdata->reset_flush_race) {
- coresight_cti_unmap_trigin(drvdata->cti_reset, 0, 0);
+ coresight_cti_unmap_trigin(drvdata->cti_reset, 2, 0);
coresight_cti_unmap_trigout(drvdata->cti_flush, 3, 0);
} else if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB) {
tmc_etr_bam_disable(drvdata);
@@ -996,7 +996,7 @@
if (!drvdata->reset_flush_race) {
coresight_cti_map_trigout(drvdata->cti_flush, 3, 0);
- coresight_cti_map_trigin(drvdata->cti_reset, 0, 0);
+ coresight_cti_map_trigin(drvdata->cti_reset, 2, 0);
}
tmc_etr_bam_disable(drvdata);
@@ -1020,7 +1020,7 @@
spin_unlock_irqrestore(&drvdata->spinlock, flags);
if (!drvdata->reset_flush_race) {
- coresight_cti_unmap_trigin(drvdata->cti_reset, 0, 0);
+ coresight_cti_unmap_trigin(drvdata->cti_reset, 2, 0);
coresight_cti_unmap_trigout(drvdata->cti_flush, 3, 0);
}
diff --git a/drivers/coresight/coresight-tpiu.c b/drivers/coresight/coresight-tpiu.c
index 7ea71d3..53df0f9 100644
--- a/drivers/coresight/coresight-tpiu.c
+++ b/drivers/coresight/coresight-tpiu.c
@@ -218,6 +218,24 @@
return 0;
}
+static int tpiu_reg_set_optimum_mode(struct regulator *reg,
+ unsigned int reg_hpm)
+{
+ if (regulator_count_voltages(reg) <= 0)
+ return 0;
+
+ return regulator_set_optimum_mode(reg, reg_hpm);
+}
+
+static int tpiu_reg_set_voltage(struct regulator *reg, unsigned int reg_low,
+ unsigned int reg_high)
+{
+ if (regulator_count_voltages(reg) <= 0)
+ return 0;
+
+ return regulator_set_voltage(reg, reg_low, reg_high);
+}
+
static int __tpiu_enable_to_sdc(struct tpiu_drvdata *drvdata)
{
int ret;
@@ -225,11 +243,11 @@
if (!drvdata->reg)
return -EINVAL;
- ret = regulator_set_optimum_mode(drvdata->reg, drvdata->reg_hpm);
+ ret = tpiu_reg_set_optimum_mode(drvdata->reg, drvdata->reg_hpm);
if (ret < 0)
return ret;
- ret = regulator_set_voltage(drvdata->reg, drvdata->reg_low,
- drvdata->reg_high);
+ ret = tpiu_reg_set_voltage(drvdata->reg, drvdata->reg_low,
+ drvdata->reg_high);
if (ret)
goto err0;
ret = regulator_enable(drvdata->reg);
@@ -248,9 +266,9 @@
return 0;
err1:
- regulator_set_voltage(drvdata->reg, 0, drvdata->reg_high);
+ tpiu_reg_set_voltage(drvdata->reg, 0, drvdata->reg_high);
err0:
- regulator_set_optimum_mode(drvdata->reg, 0);
+ tpiu_reg_set_optimum_mode(drvdata->reg, 0);
return ret;
}
@@ -325,8 +343,8 @@
msm_tlmm_misc_reg_write(TLMM_ETM_MODE_REG, 0);
regulator_disable(drvdata->reg);
- regulator_set_optimum_mode(drvdata->reg, 0);
- regulator_set_voltage(drvdata->reg, 0, drvdata->reg_high);
+ tpiu_reg_set_optimum_mode(drvdata->reg, 0);
+ tpiu_reg_set_voltage(drvdata->reg, 0, drvdata->reg_high);
}
static void tpiu_disable(struct coresight_device *csdev)
@@ -530,8 +548,7 @@
prop = of_get_property(node, "qcom,vdd-voltage-level", &len);
if (!prop || (len != (2 * sizeof(__be32)))) {
- of_node_put(reg_node);
- return -EINVAL;
+ dev_err(dev, "sdc voltage levels not specified\n");
} else {
drvdata->reg_low = be32_to_cpup(&prop[0]);
drvdata->reg_high = be32_to_cpup(&prop[1]);
@@ -539,8 +556,7 @@
prop = of_get_property(node, "qcom,vdd-current-level", &len);
if (!prop || (len != (2 * sizeof(__be32)))) {
- of_node_put(reg_node);
- return -EINVAL;
+ dev_err(dev, "sdc current levels not specified\n");
} else {
drvdata->reg_lpm = be32_to_cpup(&prop[0]);
drvdata->reg_hpm = be32_to_cpup(&prop[1]);
@@ -615,6 +631,8 @@
for (i = 0; i < drvdata->seta_gpiocnt; i++)
drvdata->seta_cfgs[i].dir = seta_cfgs[i];
+
+ devm_kfree(dev, seta_cfgs);
} else {
dev_err(dev, "seta gpios not specified\n");
}
@@ -681,6 +699,8 @@
for (i = 0; i < drvdata->setb_gpiocnt; i++)
drvdata->setb_cfgs[i].dir = setb_cfgs[i];
+
+ devm_kfree(dev, setb_cfgs);
} else {
dev_err(dev, "setb gpios not specified\n");
}
diff --git a/drivers/coresight/coresight.c b/drivers/coresight/coresight.c
index aef3d26..e237fb7 100644
--- a/drivers/coresight/coresight.c
+++ b/drivers/coresight/coresight.c
@@ -368,7 +368,7 @@
pr_err("coresight: enable failed\n");
return ret;
}
-EXPORT_SYMBOL_GPL(coresight_enable);
+EXPORT_SYMBOL(coresight_enable);
void coresight_disable(struct coresight_device *csdev)
{
@@ -391,7 +391,7 @@
out:
up(&coresight_mutex);
}
-EXPORT_SYMBOL_GPL(coresight_disable);
+EXPORT_SYMBOL(coresight_disable);
void coresight_abort(void)
{
@@ -415,7 +415,7 @@
out:
up(&coresight_mutex);
}
-EXPORT_SYMBOL_GPL(coresight_abort);
+EXPORT_SYMBOL(coresight_abort);
static ssize_t coresight_show_type(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -681,7 +681,7 @@
err_kzalloc_csdev:
return ERR_PTR(ret);
}
-EXPORT_SYMBOL_GPL(coresight_register);
+EXPORT_SYMBOL(coresight_register);
void coresight_unregister(struct coresight_device *csdev)
{
@@ -693,7 +693,7 @@
put_device(&csdev->dev);
}
}
-EXPORT_SYMBOL_GPL(coresight_unregister);
+EXPORT_SYMBOL(coresight_unregister);
static int __init coresight_init(void)
{
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index 235a340..85c28d4 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -80,6 +80,8 @@
*/
static DEFINE_MUTEX(dbs_mutex);
+static struct workqueue_struct *dbs_wq;
+
static struct dbs_tuners {
unsigned int sampling_rate;
unsigned int sampling_down_factor;
@@ -455,7 +457,7 @@
dbs_check_cpu(dbs_info);
- schedule_delayed_work_on(cpu, &dbs_info->work, delay);
+ queue_delayed_work_on(cpu, dbs_wq, &dbs_info->work, delay);
mutex_unlock(&dbs_info->timer_mutex);
}
@@ -467,7 +469,7 @@
dbs_info->enable = 1;
INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer);
- schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, delay);
+ queue_delayed_work_on(dbs_info->cpu, dbs_wq, &dbs_info->work, delay);
}
static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info)
@@ -602,12 +604,19 @@
static int __init cpufreq_gov_dbs_init(void)
{
+ dbs_wq = alloc_workqueue("conservative_dbs_wq", WQ_HIGHPRI, 0);
+ if (!dbs_wq) {
+ printk(KERN_ERR "Failed to create conservative_dbs_wq workqueue\n");
+ return -EFAULT;
+ }
+
return cpufreq_register_governor(&cpufreq_gov_conservative);
}
static void __exit cpufreq_gov_dbs_exit(void)
{
cpufreq_unregister_governor(&cpufreq_gov_conservative);
+ destroy_workqueue(dbs_wq);
}
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index 63cdc68..7d1952c 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -20,97 +20,94 @@
#include <linux/cpumask.h>
#include <linux/cpufreq.h>
#include <linux/module.h>
-#include <linux/mutex.h>
+#include <linux/moduleparam.h>
+#include <linux/rwsem.h>
#include <linux/sched.h>
#include <linux/tick.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/kthread.h>
-#include <linux/mutex.h>
#include <linux/slab.h>
-#include <linux/input.h>
#include <asm/cputime.h>
#define CREATE_TRACE_POINTS
#include <trace/events/cpufreq_interactive.h>
-static atomic_t active_count = ATOMIC_INIT(0);
+static int active_count;
struct cpufreq_interactive_cpuinfo {
struct timer_list cpu_timer;
- int timer_idlecancel;
+ struct timer_list cpu_slack_timer;
+ spinlock_t load_lock; /* protects the next 4 fields */
u64 time_in_idle;
- u64 idle_exit_time;
- u64 timer_run_time;
- int idling;
- u64 target_set_time;
- u64 target_set_time_in_idle;
+ u64 time_in_idle_timestamp;
+ u64 cputime_speedadj;
+ u64 cputime_speedadj_timestamp;
struct cpufreq_policy *policy;
struct cpufreq_frequency_table *freq_table;
unsigned int target_freq;
unsigned int floor_freq;
u64 floor_validate_time;
u64 hispeed_validate_time;
+ struct rw_semaphore enable_sem;
int governor_enabled;
};
static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo);
-/* Workqueues handle frequency scaling */
-static struct task_struct *up_task;
-static struct workqueue_struct *down_wq;
-static struct work_struct freq_scale_down_work;
-static cpumask_t up_cpumask;
-static spinlock_t up_cpumask_lock;
-static cpumask_t down_cpumask;
-static spinlock_t down_cpumask_lock;
-static struct mutex set_speed_lock;
+/* realtime thread handles frequency scaling */
+static struct task_struct *speedchange_task;
+static cpumask_t speedchange_cpumask;
+static spinlock_t speedchange_cpumask_lock;
+static struct mutex gov_lock;
/* Hi speed to bump to from lo speed when load burst (default max) */
-static u64 hispeed_freq;
+static unsigned int hispeed_freq;
/* Go to hi speed when CPU load at or above this value. */
-#define DEFAULT_GO_HISPEED_LOAD 85
-static unsigned long go_hispeed_load;
+#define DEFAULT_GO_HISPEED_LOAD 99
+static unsigned long go_hispeed_load = DEFAULT_GO_HISPEED_LOAD;
+
+/* Target load. Lower values result in higher CPU speeds. */
+#define DEFAULT_TARGET_LOAD 90
+static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD};
+static spinlock_t target_loads_lock;
+static unsigned int *target_loads = default_target_loads;
+static int ntarget_loads = ARRAY_SIZE(default_target_loads);
/*
* The minimum amount of time to spend at a frequency before we can ramp down.
*/
#define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC)
-static unsigned long min_sample_time;
+static unsigned long min_sample_time = DEFAULT_MIN_SAMPLE_TIME;
/*
* The sample rate of the timer used to increase frequency
*/
#define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC)
-static unsigned long timer_rate;
+static unsigned long timer_rate = DEFAULT_TIMER_RATE;
/*
* Wait this long before raising speed above hispeed, by default a single
* timer interval.
*/
#define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE
-static unsigned long above_hispeed_delay_val;
+static unsigned long above_hispeed_delay_val = DEFAULT_ABOVE_HISPEED_DELAY;
-/*
- * Boost pulse to hispeed on touchscreen input.
- */
-
-static int input_boost_val;
-
-struct cpufreq_interactive_inputopen {
- struct input_handle *handle;
- struct work_struct inputopen_work;
-};
-
-static struct cpufreq_interactive_inputopen inputopen;
-
-/*
- * Non-zero means longer-term speed boost active.
- */
-
+/* Non-zero means indefinite speed boost active */
static int boost_val;
+/* Duration of a boot pulse in usecs */
+static int boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME;
+/* End time of boost pulse in ktime converted to usecs */
+static u64 boostpulse_endtime;
+
+/*
+ * Max additional time to wait in idle, beyond timer_rate, at speeds above
+ * minimum before wakeup to reduce speed, or -1 if unnecessary.
+ */
+#define DEFAULT_TIMER_SLACK (4 * DEFAULT_TIMER_RATE)
+static int timer_slack_val = DEFAULT_TIMER_SLACK;
static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
unsigned int event);
@@ -125,104 +122,210 @@
.owner = THIS_MODULE,
};
-static void cpufreq_interactive_timer(unsigned long data)
+static void cpufreq_interactive_timer_resched(
+ struct cpufreq_interactive_cpuinfo *pcpu)
{
- unsigned int delta_idle;
- unsigned int delta_time;
- int cpu_load;
- int load_since_change;
- u64 time_in_idle;
- u64 idle_exit_time;
- struct cpufreq_interactive_cpuinfo *pcpu =
- &per_cpu(cpuinfo, data);
- u64 now_idle;
- unsigned int new_freq;
- unsigned int index;
+ unsigned long expires = jiffies + usecs_to_jiffies(timer_rate);
unsigned long flags;
- smp_rmb();
+ mod_timer_pinned(&pcpu->cpu_timer, expires);
+ if (timer_slack_val >= 0 && pcpu->target_freq > pcpu->policy->min) {
+ expires += usecs_to_jiffies(timer_slack_val);
+ mod_timer_pinned(&pcpu->cpu_slack_timer, expires);
+ }
+ spin_lock_irqsave(&pcpu->load_lock, flags);
+ pcpu->time_in_idle =
+ get_cpu_idle_time_us(smp_processor_id(),
+ &pcpu->time_in_idle_timestamp);
+ pcpu->cputime_speedadj = 0;
+ pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp;
+ spin_unlock_irqrestore(&pcpu->load_lock, flags);
+}
+
+static unsigned int freq_to_targetload(unsigned int freq)
+{
+ int i;
+ unsigned int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&target_loads_lock, flags);
+
+ for (i = 0; i < ntarget_loads - 1 && freq >= target_loads[i+1]; i += 2)
+ ;
+
+ ret = target_loads[i];
+ spin_unlock_irqrestore(&target_loads_lock, flags);
+ return ret;
+}
+
+/*
+ * If increasing frequencies never map to a lower target load then
+ * choose_freq() will find the minimum frequency that does not exceed its
+ * target load given the current load.
+ */
+
+static unsigned int choose_freq(
+ struct cpufreq_interactive_cpuinfo *pcpu, unsigned int loadadjfreq)
+{
+ unsigned int freq = pcpu->policy->cur;
+ unsigned int prevfreq, freqmin, freqmax;
+ unsigned int tl;
+ int index;
+
+ freqmin = 0;
+ freqmax = UINT_MAX;
+
+ do {
+ prevfreq = freq;
+ tl = freq_to_targetload(freq);
+
+ /*
+ * Find the lowest frequency where the computed load is less
+ * than or equal to the target load.
+ */
+
+ cpufreq_frequency_table_target(
+ pcpu->policy, pcpu->freq_table, loadadjfreq / tl,
+ CPUFREQ_RELATION_L, &index);
+ freq = pcpu->freq_table[index].frequency;
+
+ if (freq > prevfreq) {
+ /* The previous frequency is too low. */
+ freqmin = prevfreq;
+
+ if (freq >= freqmax) {
+ /*
+ * Find the highest frequency that is less
+ * than freqmax.
+ */
+ cpufreq_frequency_table_target(
+ pcpu->policy, pcpu->freq_table,
+ freqmax - 1, CPUFREQ_RELATION_H,
+ &index);
+ freq = pcpu->freq_table[index].frequency;
+
+ if (freq == freqmin) {
+ /*
+ * The first frequency below freqmax
+ * has already been found to be too
+ * low. freqmax is the lowest speed
+ * we found that is fast enough.
+ */
+ freq = freqmax;
+ break;
+ }
+ }
+ } else if (freq < prevfreq) {
+ /* The previous frequency is high enough. */
+ freqmax = prevfreq;
+
+ if (freq <= freqmin) {
+ /*
+ * Find the lowest frequency that is higher
+ * than freqmin.
+ */
+ cpufreq_frequency_table_target(
+ pcpu->policy, pcpu->freq_table,
+ freqmin + 1, CPUFREQ_RELATION_L,
+ &index);
+ freq = pcpu->freq_table[index].frequency;
+
+ /*
+ * If freqmax is the first frequency above
+ * freqmin then we have already found that
+ * this speed is fast enough.
+ */
+ if (freq == freqmax)
+ break;
+ }
+ }
+
+ /* If same frequency chosen as previous then done. */
+ } while (freq != prevfreq);
+
+ return freq;
+}
+
+static u64 update_load(int cpu)
+{
+ struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu);
+ u64 now;
+ u64 now_idle;
+ unsigned int delta_idle;
+ unsigned int delta_time;
+ u64 active_time;
+
+ now_idle = get_cpu_idle_time_us(cpu, &now);
+ delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle);
+ delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp);
+ active_time = delta_time - delta_idle;
+ pcpu->cputime_speedadj += active_time * pcpu->policy->cur;
+
+ pcpu->time_in_idle = now_idle;
+ pcpu->time_in_idle_timestamp = now;
+ return now;
+}
+
+static void cpufreq_interactive_timer(unsigned long data)
+{
+ u64 now;
+ unsigned int delta_time;
+ u64 cputime_speedadj;
+ int cpu_load;
+ struct cpufreq_interactive_cpuinfo *pcpu =
+ &per_cpu(cpuinfo, data);
+ unsigned int new_freq;
+ unsigned int loadadjfreq;
+ unsigned int index;
+ unsigned long flags;
+ bool boosted;
+
+ if (!down_read_trylock(&pcpu->enable_sem))
+ return;
if (!pcpu->governor_enabled)
goto exit;
- /*
- * Once pcpu->timer_run_time is updated to >= pcpu->idle_exit_time,
- * this lets idle exit know the current idle time sample has
- * been processed, and idle exit can generate a new sample and
- * re-arm the timer. This prevents a concurrent idle
- * exit on that CPU from writing a new set of info at the same time
- * the timer function runs (the timer function can't use that info
- * until more time passes).
- */
- time_in_idle = pcpu->time_in_idle;
- idle_exit_time = pcpu->idle_exit_time;
- now_idle = get_cpu_idle_time_us(data, &pcpu->timer_run_time);
- smp_wmb();
+ spin_lock_irqsave(&pcpu->load_lock, flags);
+ now = update_load(data);
+ delta_time = (unsigned int)(now - pcpu->cputime_speedadj_timestamp);
+ cputime_speedadj = pcpu->cputime_speedadj;
+ spin_unlock_irqrestore(&pcpu->load_lock, flags);
- /* If we raced with cancelling a timer, skip. */
- if (!idle_exit_time)
- goto exit;
-
- delta_idle = (unsigned int)(now_idle - time_in_idle);
- delta_time = (unsigned int)(pcpu->timer_run_time - idle_exit_time);
-
- /*
- * If timer ran less than 1ms after short-term sample started, retry.
- */
- if (delta_time < 1000)
+ if (WARN_ON_ONCE(!delta_time))
goto rearm;
- if (delta_idle > delta_time)
- cpu_load = 0;
- else
- cpu_load = 100 * (delta_time - delta_idle) / delta_time;
+ do_div(cputime_speedadj, delta_time);
+ loadadjfreq = (unsigned int)cputime_speedadj * 100;
+ cpu_load = loadadjfreq / pcpu->target_freq;
+ boosted = boost_val || now < boostpulse_endtime;
- delta_idle = (unsigned int)(now_idle - pcpu->target_set_time_in_idle);
- delta_time = (unsigned int)(pcpu->timer_run_time -
- pcpu->target_set_time);
-
- if ((delta_time == 0) || (delta_idle > delta_time))
- load_since_change = 0;
- else
- load_since_change =
- 100 * (delta_time - delta_idle) / delta_time;
-
- /*
- * Choose greater of short-term load (since last idle timer
- * started or timer function re-armed itself) or long-term load
- * (since last frequency change).
- */
- if (load_since_change > cpu_load)
- cpu_load = load_since_change;
-
- if (cpu_load >= go_hispeed_load || boost_val) {
- if (pcpu->target_freq <= pcpu->policy->min) {
+ if (cpu_load >= go_hispeed_load || boosted) {
+ if (pcpu->target_freq < hispeed_freq) {
new_freq = hispeed_freq;
} else {
- new_freq = pcpu->policy->max * cpu_load / 100;
+ new_freq = choose_freq(pcpu, loadadjfreq);
if (new_freq < hispeed_freq)
new_freq = hispeed_freq;
-
- if (pcpu->target_freq == hispeed_freq &&
- new_freq > hispeed_freq &&
- pcpu->timer_run_time - pcpu->hispeed_validate_time
- < above_hispeed_delay_val) {
- trace_cpufreq_interactive_notyet(data, cpu_load,
- pcpu->target_freq,
- new_freq);
- goto rearm;
- }
}
} else {
- new_freq = pcpu->policy->max * cpu_load / 100;
+ new_freq = choose_freq(pcpu, loadadjfreq);
}
- if (new_freq <= hispeed_freq)
- pcpu->hispeed_validate_time = pcpu->timer_run_time;
+ if (pcpu->target_freq >= hispeed_freq &&
+ new_freq > pcpu->target_freq &&
+ now - pcpu->hispeed_validate_time < above_hispeed_delay_val) {
+ trace_cpufreq_interactive_notyet(
+ data, cpu_load, pcpu->target_freq,
+ pcpu->policy->cur, new_freq);
+ goto rearm;
+ }
+
+ pcpu->hispeed_validate_time = now;
if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table,
- new_freq, CPUFREQ_RELATION_H,
+ new_freq, CPUFREQ_RELATION_L,
&index)) {
pr_warn_once("timer %d: cpufreq_frequency_table_target error\n",
(int) data);
@@ -236,41 +339,42 @@
* floor frequency for the minimum sample time since last validated.
*/
if (new_freq < pcpu->floor_freq) {
- if (pcpu->timer_run_time - pcpu->floor_validate_time
- < min_sample_time) {
- trace_cpufreq_interactive_notyet(data, cpu_load,
- pcpu->target_freq, new_freq);
+ if (now - pcpu->floor_validate_time < min_sample_time) {
+ trace_cpufreq_interactive_notyet(
+ data, cpu_load, pcpu->target_freq,
+ pcpu->policy->cur, new_freq);
goto rearm;
}
}
- pcpu->floor_freq = new_freq;
- pcpu->floor_validate_time = pcpu->timer_run_time;
+ /*
+ * Update the timestamp for checking whether speed has been held at
+ * or above the selected frequency for a minimum of min_sample_time,
+ * if not boosted to hispeed_freq. If boosted to hispeed_freq then we
+ * allow the speed to drop as soon as the boostpulse duration expires
+ * (or the indefinite boost is turned off).
+ */
+
+ if (!boosted || new_freq > hispeed_freq) {
+ pcpu->floor_freq = new_freq;
+ pcpu->floor_validate_time = now;
+ }
if (pcpu->target_freq == new_freq) {
- trace_cpufreq_interactive_already(data, cpu_load,
- pcpu->target_freq, new_freq);
+ trace_cpufreq_interactive_already(
+ data, cpu_load, pcpu->target_freq,
+ pcpu->policy->cur, new_freq);
goto rearm_if_notmax;
}
trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq,
- new_freq);
- pcpu->target_set_time_in_idle = now_idle;
- pcpu->target_set_time = pcpu->timer_run_time;
+ pcpu->policy->cur, new_freq);
- if (new_freq < pcpu->target_freq) {
- pcpu->target_freq = new_freq;
- spin_lock_irqsave(&down_cpumask_lock, flags);
- cpumask_set_cpu(data, &down_cpumask);
- spin_unlock_irqrestore(&down_cpumask_lock, flags);
- queue_work(down_wq, &freq_scale_down_work);
- } else {
- pcpu->target_freq = new_freq;
- spin_lock_irqsave(&up_cpumask_lock, flags);
- cpumask_set_cpu(data, &up_cpumask);
- spin_unlock_irqrestore(&up_cpumask_lock, flags);
- wake_up_process(up_task);
- }
+ pcpu->target_freq = new_freq;
+ spin_lock_irqsave(&speedchange_cpumask_lock, flags);
+ cpumask_set_cpu(data, &speedchange_cpumask);
+ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
+ wake_up_process(speedchange_task);
rearm_if_notmax:
/*
@@ -281,28 +385,11 @@
goto exit;
rearm:
- if (!timer_pending(&pcpu->cpu_timer)) {
- /*
- * If already at min: if that CPU is idle, don't set timer.
- * Else cancel the timer if that CPU goes idle. We don't
- * need to re-evaluate speed until the next idle exit.
- */
- if (pcpu->target_freq == pcpu->policy->min) {
- smp_rmb();
-
- if (pcpu->idling)
- goto exit;
-
- pcpu->timer_idlecancel = 1;
- }
-
- pcpu->time_in_idle = get_cpu_idle_time_us(
- data, &pcpu->idle_exit_time);
- mod_timer(&pcpu->cpu_timer,
- jiffies + usecs_to_jiffies(timer_rate));
- }
+ if (!timer_pending(&pcpu->cpu_timer))
+ cpufreq_interactive_timer_resched(pcpu);
exit:
+ up_read(&pcpu->enable_sem);
return;
}
@@ -312,15 +399,16 @@
&per_cpu(cpuinfo, smp_processor_id());
int pending;
- if (!pcpu->governor_enabled)
+ if (!down_read_trylock(&pcpu->enable_sem))
return;
+ if (!pcpu->governor_enabled) {
+ up_read(&pcpu->enable_sem);
+ return;
+ }
- pcpu->idling = 1;
- smp_wmb();
pending = timer_pending(&pcpu->cpu_timer);
if (pcpu->target_freq != pcpu->policy->min) {
-#ifdef CONFIG_SMP
/*
* Entering idle while not at lowest speed. On some
* platforms this can hold the other CPU(s) at that speed
@@ -329,33 +417,11 @@
* min indefinitely. This should probably be a quirk of
* the CPUFreq driver.
*/
- if (!pending) {
- pcpu->time_in_idle = get_cpu_idle_time_us(
- smp_processor_id(), &pcpu->idle_exit_time);
- pcpu->timer_idlecancel = 0;
- mod_timer(&pcpu->cpu_timer,
- jiffies + usecs_to_jiffies(timer_rate));
- }
-#endif
- } else {
- /*
- * If at min speed and entering idle after load has
- * already been evaluated, and a timer has been set just in
- * case the CPU suddenly goes busy, cancel that timer. The
- * CPU didn't go busy; we'll recheck things upon idle exit.
- */
- if (pending && pcpu->timer_idlecancel) {
- del_timer(&pcpu->cpu_timer);
- /*
- * Ensure last timer run time is after current idle
- * sample start time, so next idle exit will always
- * start a new idle sampling period.
- */
- pcpu->idle_exit_time = 0;
- pcpu->timer_idlecancel = 0;
- }
+ if (!pending)
+ cpufreq_interactive_timer_resched(pcpu);
}
+ up_read(&pcpu->enable_sem);
}
static void cpufreq_interactive_idle_end(void)
@@ -363,34 +429,26 @@
struct cpufreq_interactive_cpuinfo *pcpu =
&per_cpu(cpuinfo, smp_processor_id());
- pcpu->idling = 0;
- smp_wmb();
-
- /*
- * Arm the timer for 1-2 ticks later if not already, and if the timer
- * function has already processed the previous load sampling
- * interval. (If the timer is not pending but has not processed
- * the previous interval, it is probably racing with us on another
- * CPU. Let it compute load based on the previous sample and then
- * re-arm the timer for another interval when it's done, rather
- * than updating the interval start time to be "now", which doesn't
- * give the timer function enough time to make a decision on this
- * run.)
- */
- if (timer_pending(&pcpu->cpu_timer) == 0 &&
- pcpu->timer_run_time >= pcpu->idle_exit_time &&
- pcpu->governor_enabled) {
- pcpu->time_in_idle =
- get_cpu_idle_time_us(smp_processor_id(),
- &pcpu->idle_exit_time);
- pcpu->timer_idlecancel = 0;
- mod_timer(&pcpu->cpu_timer,
- jiffies + usecs_to_jiffies(timer_rate));
+ if (!down_read_trylock(&pcpu->enable_sem))
+ return;
+ if (!pcpu->governor_enabled) {
+ up_read(&pcpu->enable_sem);
+ return;
}
+ /* Arm the timer for 1-2 ticks later if not already. */
+ if (!timer_pending(&pcpu->cpu_timer)) {
+ cpufreq_interactive_timer_resched(pcpu);
+ } else if (time_after_eq(jiffies, pcpu->cpu_timer.expires)) {
+ del_timer(&pcpu->cpu_timer);
+ del_timer(&pcpu->cpu_slack_timer);
+ cpufreq_interactive_timer(smp_processor_id());
+ }
+
+ up_read(&pcpu->enable_sem);
}
-static int cpufreq_interactive_up_task(void *data)
+static int cpufreq_interactive_speedchange_task(void *data)
{
unsigned int cpu;
cpumask_t tmp_mask;
@@ -399,34 +457,35 @@
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
- spin_lock_irqsave(&up_cpumask_lock, flags);
+ spin_lock_irqsave(&speedchange_cpumask_lock, flags);
- if (cpumask_empty(&up_cpumask)) {
- spin_unlock_irqrestore(&up_cpumask_lock, flags);
+ if (cpumask_empty(&speedchange_cpumask)) {
+ spin_unlock_irqrestore(&speedchange_cpumask_lock,
+ flags);
schedule();
if (kthread_should_stop())
break;
- spin_lock_irqsave(&up_cpumask_lock, flags);
+ spin_lock_irqsave(&speedchange_cpumask_lock, flags);
}
set_current_state(TASK_RUNNING);
- tmp_mask = up_cpumask;
- cpumask_clear(&up_cpumask);
- spin_unlock_irqrestore(&up_cpumask_lock, flags);
+ tmp_mask = speedchange_cpumask;
+ cpumask_clear(&speedchange_cpumask);
+ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
for_each_cpu(cpu, &tmp_mask) {
unsigned int j;
unsigned int max_freq = 0;
pcpu = &per_cpu(cpuinfo, cpu);
- smp_rmb();
-
- if (!pcpu->governor_enabled)
+ if (!down_read_trylock(&pcpu->enable_sem))
continue;
-
- mutex_lock(&set_speed_lock);
+ if (!pcpu->governor_enabled) {
+ up_read(&pcpu->enable_sem);
+ continue;
+ }
for_each_cpu(j, pcpu->policy->cpus) {
struct cpufreq_interactive_cpuinfo *pjcpu =
@@ -440,57 +499,17 @@
__cpufreq_driver_target(pcpu->policy,
max_freq,
CPUFREQ_RELATION_H);
- mutex_unlock(&set_speed_lock);
- trace_cpufreq_interactive_up(cpu, pcpu->target_freq,
+ trace_cpufreq_interactive_setspeed(cpu,
+ pcpu->target_freq,
pcpu->policy->cur);
+
+ up_read(&pcpu->enable_sem);
}
}
return 0;
}
-static void cpufreq_interactive_freq_down(struct work_struct *work)
-{
- unsigned int cpu;
- cpumask_t tmp_mask;
- unsigned long flags;
- struct cpufreq_interactive_cpuinfo *pcpu;
-
- spin_lock_irqsave(&down_cpumask_lock, flags);
- tmp_mask = down_cpumask;
- cpumask_clear(&down_cpumask);
- spin_unlock_irqrestore(&down_cpumask_lock, flags);
-
- for_each_cpu(cpu, &tmp_mask) {
- unsigned int j;
- unsigned int max_freq = 0;
-
- pcpu = &per_cpu(cpuinfo, cpu);
- smp_rmb();
-
- if (!pcpu->governor_enabled)
- continue;
-
- mutex_lock(&set_speed_lock);
-
- for_each_cpu(j, pcpu->policy->cpus) {
- struct cpufreq_interactive_cpuinfo *pjcpu =
- &per_cpu(cpuinfo, j);
-
- if (pjcpu->target_freq > max_freq)
- max_freq = pjcpu->target_freq;
- }
-
- if (max_freq != pcpu->policy->cur)
- __cpufreq_driver_target(pcpu->policy, max_freq,
- CPUFREQ_RELATION_H);
-
- mutex_unlock(&set_speed_lock);
- trace_cpufreq_interactive_down(cpu, pcpu->target_freq,
- pcpu->policy->cur);
- }
-}
-
static void cpufreq_interactive_boost(void)
{
int i;
@@ -498,17 +517,16 @@
unsigned long flags;
struct cpufreq_interactive_cpuinfo *pcpu;
- spin_lock_irqsave(&up_cpumask_lock, flags);
+ spin_lock_irqsave(&speedchange_cpumask_lock, flags);
for_each_online_cpu(i) {
pcpu = &per_cpu(cpuinfo, i);
if (pcpu->target_freq < hispeed_freq) {
pcpu->target_freq = hispeed_freq;
- cpumask_set_cpu(i, &up_cpumask);
- pcpu->target_set_time_in_idle =
- get_cpu_idle_time_us(i, &pcpu->target_set_time);
- pcpu->hispeed_validate_time = pcpu->target_set_time;
+ cpumask_set_cpu(i, &speedchange_cpumask);
+ pcpu->hispeed_validate_time =
+ ktime_to_us(ktime_get());
anyboost = 1;
}
@@ -521,106 +539,126 @@
pcpu->floor_validate_time = ktime_to_us(ktime_get());
}
- spin_unlock_irqrestore(&up_cpumask_lock, flags);
+ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
if (anyboost)
- wake_up_process(up_task);
+ wake_up_process(speedchange_task);
}
-/*
- * Pulsed boost on input event raises CPUs to hispeed_freq and lets
- * usual algorithm of min_sample_time decide when to allow speed
- * to drop.
- */
-
-static void cpufreq_interactive_input_event(struct input_handle *handle,
- unsigned int type,
- unsigned int code, int value)
+static int cpufreq_interactive_notifier(
+ struct notifier_block *nb, unsigned long val, void *data)
{
- if (input_boost_val && type == EV_SYN && code == SYN_REPORT) {
- trace_cpufreq_interactive_boost("input");
- cpufreq_interactive_boost();
+ struct cpufreq_freqs *freq = data;
+ struct cpufreq_interactive_cpuinfo *pcpu;
+ int cpu;
+ unsigned long flags;
+
+ if (val == CPUFREQ_POSTCHANGE) {
+ pcpu = &per_cpu(cpuinfo, freq->cpu);
+ if (!down_read_trylock(&pcpu->enable_sem))
+ return 0;
+ if (!pcpu->governor_enabled) {
+ up_read(&pcpu->enable_sem);
+ return 0;
+ }
+
+ for_each_cpu(cpu, pcpu->policy->cpus) {
+ struct cpufreq_interactive_cpuinfo *pjcpu =
+ &per_cpu(cpuinfo, cpu);
+ spin_lock_irqsave(&pjcpu->load_lock, flags);
+ update_load(cpu);
+ spin_unlock_irqrestore(&pjcpu->load_lock, flags);
+ }
+
+ up_read(&pcpu->enable_sem);
}
-}
-
-static void cpufreq_interactive_input_open(struct work_struct *w)
-{
- struct cpufreq_interactive_inputopen *io =
- container_of(w, struct cpufreq_interactive_inputopen,
- inputopen_work);
- int error;
-
- error = input_open_device(io->handle);
- if (error)
- input_unregister_handle(io->handle);
-}
-
-static int cpufreq_interactive_input_connect(struct input_handler *handler,
- struct input_dev *dev,
- const struct input_device_id *id)
-{
- struct input_handle *handle;
- int error;
-
- pr_info("%s: connect to %s\n", __func__, dev->name);
- handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
- if (!handle)
- return -ENOMEM;
-
- handle->dev = dev;
- handle->handler = handler;
- handle->name = "cpufreq_interactive";
-
- error = input_register_handle(handle);
- if (error)
- goto err;
-
- inputopen.handle = handle;
- queue_work(down_wq, &inputopen.inputopen_work);
return 0;
-err:
- kfree(handle);
- return error;
}
-static void cpufreq_interactive_input_disconnect(struct input_handle *handle)
+static struct notifier_block cpufreq_notifier_block = {
+ .notifier_call = cpufreq_interactive_notifier,
+};
+
+static ssize_t show_target_loads(
+ struct kobject *kobj, struct attribute *attr, char *buf)
{
- input_close_device(handle);
- input_unregister_handle(handle);
- kfree(handle);
+ int i;
+ ssize_t ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&target_loads_lock, flags);
+
+ for (i = 0; i < ntarget_loads; i++)
+ ret += sprintf(buf + ret, "%u%s", target_loads[i],
+ i & 0x1 ? ":" : " ");
+
+ ret += sprintf(buf + ret, "\n");
+ spin_unlock_irqrestore(&target_loads_lock, flags);
+ return ret;
}
-static const struct input_device_id cpufreq_interactive_ids[] = {
- {
- .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
- INPUT_DEVICE_ID_MATCH_ABSBIT,
- .evbit = { BIT_MASK(EV_ABS) },
- .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
- BIT_MASK(ABS_MT_POSITION_X) |
- BIT_MASK(ABS_MT_POSITION_Y) },
- }, /* multi-touch touchscreen */
- {
- .flags = INPUT_DEVICE_ID_MATCH_KEYBIT |
- INPUT_DEVICE_ID_MATCH_ABSBIT,
- .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
- .absbit = { [BIT_WORD(ABS_X)] =
- BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },
- }, /* touchpad */
- { },
-};
+static ssize_t store_target_loads(
+ struct kobject *kobj, struct attribute *attr, const char *buf,
+ size_t count)
+{
+ int ret;
+ const char *cp;
+ unsigned int *new_target_loads = NULL;
+ int ntokens = 1;
+ int i;
+ unsigned long flags;
-static struct input_handler cpufreq_interactive_input_handler = {
- .event = cpufreq_interactive_input_event,
- .connect = cpufreq_interactive_input_connect,
- .disconnect = cpufreq_interactive_input_disconnect,
- .name = "cpufreq_interactive",
- .id_table = cpufreq_interactive_ids,
-};
+ cp = buf;
+ while ((cp = strpbrk(cp + 1, " :")))
+ ntokens++;
+
+ if (!(ntokens & 0x1))
+ goto err_inval;
+
+ new_target_loads = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL);
+ if (!new_target_loads) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ cp = buf;
+ i = 0;
+ while (i < ntokens) {
+ if (sscanf(cp, "%u", &new_target_loads[i++]) != 1)
+ goto err_inval;
+
+ cp = strpbrk(cp, " :");
+ if (!cp)
+ break;
+ cp++;
+ }
+
+ if (i != ntokens)
+ goto err_inval;
+
+ spin_lock_irqsave(&target_loads_lock, flags);
+ if (target_loads != default_target_loads)
+ kfree(target_loads);
+ target_loads = new_target_loads;
+ ntarget_loads = ntokens;
+ spin_unlock_irqrestore(&target_loads_lock, flags);
+ return count;
+
+err_inval:
+ ret = -EINVAL;
+err:
+ kfree(new_target_loads);
+ return ret;
+}
+
+static struct global_attr target_loads_attr =
+ __ATTR(target_loads, S_IRUGO | S_IWUSR,
+ show_target_loads, store_target_loads);
static ssize_t show_hispeed_freq(struct kobject *kobj,
struct attribute *attr, char *buf)
{
- return sprintf(buf, "%llu\n", hispeed_freq);
+ return sprintf(buf, "%u\n", hispeed_freq);
}
static ssize_t store_hispeed_freq(struct kobject *kobj,
@@ -628,9 +666,9 @@
size_t count)
{
int ret;
- u64 val;
+ long unsigned int val;
- ret = strict_strtoull(buf, 0, &val);
+ ret = strict_strtoul(buf, 0, &val);
if (ret < 0)
return ret;
hispeed_freq = val;
@@ -729,26 +767,28 @@
static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644,
show_timer_rate, store_timer_rate);
-static ssize_t show_input_boost(struct kobject *kobj, struct attribute *attr,
- char *buf)
+static ssize_t show_timer_slack(
+ struct kobject *kobj, struct attribute *attr, char *buf)
{
- return sprintf(buf, "%u\n", input_boost_val);
+ return sprintf(buf, "%d\n", timer_slack_val);
}
-static ssize_t store_input_boost(struct kobject *kobj, struct attribute *attr,
- const char *buf, size_t count)
+static ssize_t store_timer_slack(
+ struct kobject *kobj, struct attribute *attr, const char *buf,
+ size_t count)
{
int ret;
unsigned long val;
- ret = strict_strtoul(buf, 0, &val);
+ ret = kstrtol(buf, 10, &val);
if (ret < 0)
return ret;
- input_boost_val = val;
+
+ timer_slack_val = val;
return count;
}
-define_one_global_rw(input_boost);
+define_one_global_rw(timer_slack);
static ssize_t show_boost(struct kobject *kobj, struct attribute *attr,
char *buf)
@@ -790,6 +830,7 @@
if (ret < 0)
return ret;
+ boostpulse_endtime = ktime_to_us(ktime_get()) + boostpulse_duration_val;
trace_cpufreq_interactive_boost("pulse");
cpufreq_interactive_boost();
return count;
@@ -798,15 +839,40 @@
static struct global_attr boostpulse =
__ATTR(boostpulse, 0200, NULL, store_boostpulse);
+static ssize_t show_boostpulse_duration(
+ struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", boostpulse_duration_val);
+}
+
+static ssize_t store_boostpulse_duration(
+ struct kobject *kobj, struct attribute *attr, const char *buf,
+ size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ boostpulse_duration_val = val;
+ return count;
+}
+
+define_one_global_rw(boostpulse_duration);
+
static struct attribute *interactive_attributes[] = {
+ &target_loads_attr.attr,
&hispeed_freq_attr.attr,
&go_hispeed_load_attr.attr,
&above_hispeed_delay.attr,
&min_sample_time_attr.attr,
&timer_rate_attr.attr,
- &input_boost.attr,
+ &timer_slack.attr,
&boost.attr,
&boostpulse.attr,
+ &boostpulse_duration.attr,
NULL,
};
@@ -815,102 +881,6 @@
.name = "interactive",
};
-static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
- unsigned int event)
-{
- int rc;
- unsigned int j;
- struct cpufreq_interactive_cpuinfo *pcpu;
- struct cpufreq_frequency_table *freq_table;
-
- switch (event) {
- case CPUFREQ_GOV_START:
- if (!cpu_online(policy->cpu))
- return -EINVAL;
-
- freq_table =
- cpufreq_frequency_get_table(policy->cpu);
-
- for_each_cpu(j, policy->cpus) {
- pcpu = &per_cpu(cpuinfo, j);
- pcpu->policy = policy;
- pcpu->target_freq = policy->cur;
- pcpu->freq_table = freq_table;
- pcpu->target_set_time_in_idle =
- get_cpu_idle_time_us(j,
- &pcpu->target_set_time);
- pcpu->floor_freq = pcpu->target_freq;
- pcpu->floor_validate_time =
- pcpu->target_set_time;
- pcpu->hispeed_validate_time =
- pcpu->target_set_time;
- pcpu->governor_enabled = 1;
- pcpu->idle_exit_time = pcpu->target_set_time;
- mod_timer(&pcpu->cpu_timer,
- jiffies + usecs_to_jiffies(timer_rate));
- smp_wmb();
- }
-
- if (!hispeed_freq)
- hispeed_freq = policy->max;
-
- /*
- * Do not register the idle hook and create sysfs
- * entries if we have already done so.
- */
- if (atomic_inc_return(&active_count) > 1)
- return 0;
-
- rc = sysfs_create_group(cpufreq_global_kobject,
- &interactive_attr_group);
- if (rc)
- return rc;
-
- rc = input_register_handler(&cpufreq_interactive_input_handler);
- if (rc)
- pr_warn("%s: failed to register input handler\n",
- __func__);
-
- break;
-
- case CPUFREQ_GOV_STOP:
- for_each_cpu(j, policy->cpus) {
- pcpu = &per_cpu(cpuinfo, j);
- pcpu->governor_enabled = 0;
- smp_wmb();
- del_timer_sync(&pcpu->cpu_timer);
-
- /*
- * Reset idle exit time since we may cancel the timer
- * before it can run after the last idle exit time,
- * to avoid tripping the check in idle exit for a timer
- * that is trying to run.
- */
- pcpu->idle_exit_time = 0;
- }
-
- flush_work(&freq_scale_down_work);
- if (atomic_dec_return(&active_count) > 0)
- return 0;
-
- input_unregister_handler(&cpufreq_interactive_input_handler);
- sysfs_remove_group(cpufreq_global_kobject,
- &interactive_attr_group);
-
- break;
-
- case CPUFREQ_GOV_LIMITS:
- if (policy->max < policy->cur)
- __cpufreq_driver_target(policy,
- policy->max, CPUFREQ_RELATION_H);
- else if (policy->min > policy->cur)
- __cpufreq_driver_target(policy,
- policy->min, CPUFREQ_RELATION_L);
- break;
- }
- return 0;
-}
-
static int cpufreq_interactive_idle_notifier(struct notifier_block *nb,
unsigned long val,
void *data)
@@ -931,57 +901,148 @@
.notifier_call = cpufreq_interactive_idle_notifier,
};
+static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
+ unsigned int event)
+{
+ int rc;
+ unsigned int j;
+ struct cpufreq_interactive_cpuinfo *pcpu;
+ struct cpufreq_frequency_table *freq_table;
+
+ switch (event) {
+ case CPUFREQ_GOV_START:
+ if (!cpu_online(policy->cpu))
+ return -EINVAL;
+
+ mutex_lock(&gov_lock);
+
+ freq_table =
+ cpufreq_frequency_get_table(policy->cpu);
+ if (!hispeed_freq)
+ hispeed_freq = policy->max;
+
+ for_each_cpu(j, policy->cpus) {
+ unsigned long expires;
+
+ pcpu = &per_cpu(cpuinfo, j);
+ pcpu->policy = policy;
+ pcpu->target_freq = policy->cur;
+ pcpu->freq_table = freq_table;
+ pcpu->floor_freq = pcpu->target_freq;
+ pcpu->floor_validate_time =
+ ktime_to_us(ktime_get());
+ pcpu->hispeed_validate_time =
+ pcpu->floor_validate_time;
+ down_write(&pcpu->enable_sem);
+ expires = jiffies + usecs_to_jiffies(timer_rate);
+ pcpu->cpu_timer.expires = expires;
+ add_timer_on(&pcpu->cpu_timer, j);
+ if (timer_slack_val >= 0) {
+ expires += usecs_to_jiffies(timer_slack_val);
+ pcpu->cpu_slack_timer.expires = expires;
+ add_timer_on(&pcpu->cpu_slack_timer, j);
+ }
+ pcpu->governor_enabled = 1;
+ up_write(&pcpu->enable_sem);
+ }
+
+ /*
+ * Do not register the idle hook and create sysfs
+ * entries if we have already done so.
+ */
+ if (++active_count > 1) {
+ mutex_unlock(&gov_lock);
+ return 0;
+ }
+
+ rc = sysfs_create_group(cpufreq_global_kobject,
+ &interactive_attr_group);
+ if (rc) {
+ mutex_unlock(&gov_lock);
+ return rc;
+ }
+
+ idle_notifier_register(&cpufreq_interactive_idle_nb);
+ cpufreq_register_notifier(
+ &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
+ mutex_unlock(&gov_lock);
+ break;
+
+ case CPUFREQ_GOV_STOP:
+ mutex_lock(&gov_lock);
+ for_each_cpu(j, policy->cpus) {
+ pcpu = &per_cpu(cpuinfo, j);
+ down_write(&pcpu->enable_sem);
+ pcpu->governor_enabled = 0;
+ del_timer_sync(&pcpu->cpu_timer);
+ del_timer_sync(&pcpu->cpu_slack_timer);
+ up_write(&pcpu->enable_sem);
+ }
+
+ if (--active_count > 0) {
+ mutex_unlock(&gov_lock);
+ return 0;
+ }
+
+ cpufreq_unregister_notifier(
+ &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
+ idle_notifier_unregister(&cpufreq_interactive_idle_nb);
+ sysfs_remove_group(cpufreq_global_kobject,
+ &interactive_attr_group);
+ mutex_unlock(&gov_lock);
+
+ break;
+
+ case CPUFREQ_GOV_LIMITS:
+ if (policy->max < policy->cur)
+ __cpufreq_driver_target(policy,
+ policy->max, CPUFREQ_RELATION_H);
+ else if (policy->min > policy->cur)
+ __cpufreq_driver_target(policy,
+ policy->min, CPUFREQ_RELATION_L);
+ break;
+ }
+ return 0;
+}
+
+static void cpufreq_interactive_nop_timer(unsigned long data)
+{
+}
+
static int __init cpufreq_interactive_init(void)
{
unsigned int i;
struct cpufreq_interactive_cpuinfo *pcpu;
struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
- go_hispeed_load = DEFAULT_GO_HISPEED_LOAD;
- min_sample_time = DEFAULT_MIN_SAMPLE_TIME;
- above_hispeed_delay_val = DEFAULT_ABOVE_HISPEED_DELAY;
- timer_rate = DEFAULT_TIMER_RATE;
-
/* Initalize per-cpu timers */
for_each_possible_cpu(i) {
pcpu = &per_cpu(cpuinfo, i);
- init_timer(&pcpu->cpu_timer);
+ init_timer_deferrable(&pcpu->cpu_timer);
pcpu->cpu_timer.function = cpufreq_interactive_timer;
pcpu->cpu_timer.data = i;
+ init_timer(&pcpu->cpu_slack_timer);
+ pcpu->cpu_slack_timer.function = cpufreq_interactive_nop_timer;
+ spin_lock_init(&pcpu->load_lock);
+ init_rwsem(&pcpu->enable_sem);
}
- up_task = kthread_create(cpufreq_interactive_up_task, NULL,
- "kinteractiveup");
- if (IS_ERR(up_task))
- return PTR_ERR(up_task);
+ spin_lock_init(&target_loads_lock);
+ spin_lock_init(&speedchange_cpumask_lock);
+ mutex_init(&gov_lock);
+ speedchange_task =
+ kthread_create(cpufreq_interactive_speedchange_task, NULL,
+ "cfinteractive");
+ if (IS_ERR(speedchange_task))
+ return PTR_ERR(speedchange_task);
- sched_setscheduler_nocheck(up_task, SCHED_FIFO, ¶m);
- get_task_struct(up_task);
+ sched_setscheduler_nocheck(speedchange_task, SCHED_FIFO, ¶m);
+ get_task_struct(speedchange_task);
- /* No rescuer thread, bind to CPU queuing the work for possibly
- warm cache (probably doesn't matter much). */
- down_wq = alloc_workqueue("knteractive_down", 0, 1);
+ /* NB: wake up so the thread does not look hung to the freezer */
+ wake_up_process(speedchange_task);
- if (!down_wq)
- goto err_freeuptask;
-
- INIT_WORK(&freq_scale_down_work,
- cpufreq_interactive_freq_down);
-
- spin_lock_init(&up_cpumask_lock);
- spin_lock_init(&down_cpumask_lock);
- mutex_init(&set_speed_lock);
-
- /* Kick the kthread to idle */
- wake_up_process(up_task);
-
- idle_notifier_register(&cpufreq_interactive_idle_nb);
- INIT_WORK(&inputopen.inputopen_work, cpufreq_interactive_input_open);
return cpufreq_register_governor(&cpufreq_gov_interactive);
-
-err_freeuptask:
- put_task_struct(up_task);
- return -ENOMEM;
}
#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE
@@ -993,9 +1054,8 @@
static void __exit cpufreq_interactive_exit(void)
{
cpufreq_unregister_governor(&cpufreq_gov_interactive);
- kthread_stop(up_task);
- put_task_struct(up_task);
- destroy_workqueue(down_wq);
+ kthread_stop(speedchange_task);
+ put_task_struct(speedchange_task);
}
module_exit(cpufreq_interactive_exit);
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index bf022ac..fda64e5 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -4,6 +4,7 @@
* Copyright (C) 2001 Russell King
* (C) 2003 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
* Jun Nakajima <jun.nakajima@intel.com>
+ * (c) 2013 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 as
@@ -111,11 +112,11 @@
static unsigned int dbs_enable; /* number of CPUs using this policy */
/*
- * dbs_mutex protects dbs_enable in governor start/stop.
+ * dbs_mutex protects dbs_enable and dbs_info during start/stop.
*/
static DEFINE_MUTEX(dbs_mutex);
-static struct workqueue_struct *input_wq;
+static struct workqueue_struct *dbs_wq;
struct dbs_work_struct {
struct work_struct work;
@@ -124,6 +125,14 @@
static DEFINE_PER_CPU(struct dbs_work_struct, dbs_refresh_work);
+struct dbs_sync_work_struct {
+ struct work_struct work;
+ unsigned int src_cpu;
+ unsigned int targ_cpu;
+};
+
+static DEFINE_PER_CPU(struct dbs_sync_work_struct, dbs_sync_work);
+
static struct dbs_tuners {
unsigned int sampling_rate;
unsigned int up_threshold;
@@ -367,8 +376,8 @@
cancel_delayed_work_sync(&dbs_info->work);
mutex_lock(&dbs_info->timer_mutex);
- schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work,
- usecs_to_jiffies(new_rate));
+ queue_delayed_work_on(dbs_info->cpu, dbs_wq,
+ &dbs_info->work, usecs_to_jiffies(new_rate));
}
mutex_unlock(&dbs_info->timer_mutex);
@@ -576,6 +585,10 @@
POWERSAVE_BIAS_MINLEVEL));
dbs_tuners_ins.powersave_bias = input;
+
+ mutex_lock(&dbs_mutex);
+ get_online_cpus();
+
if (!bypass) {
if (reenable_timer) {
/* reinstate dbs timer */
@@ -643,6 +656,9 @@
}
}
+ put_online_cpus();
+ mutex_unlock(&dbs_mutex);
+
return count;
}
@@ -878,9 +894,10 @@
freq_next = dbs_tuners_ins.sync_freq;
if (max_load_freq >
- (dbs_tuners_ins.up_threshold_multi_core -
+ ((dbs_tuners_ins.up_threshold_multi_core -
dbs_tuners_ins.down_differential_multi_core) *
- policy->cur)
+ policy->cur) &&
+ freq_next < dbs_tuners_ins.optimal_freq)
freq_next = dbs_tuners_ins.optimal_freq;
}
@@ -931,7 +948,7 @@
dbs_info->freq_lo, CPUFREQ_RELATION_H);
delay = dbs_info->freq_lo_jiffies;
}
- schedule_delayed_work_on(cpu, &dbs_info->work, delay);
+ queue_delayed_work_on(cpu, dbs_wq, &dbs_info->work, delay);
mutex_unlock(&dbs_info->timer_mutex);
}
@@ -945,7 +962,7 @@
dbs_info->sample_type = DBS_NORMAL_SAMPLE;
INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer);
- schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, delay);
+ queue_delayed_work_on(dbs_info->cpu, dbs_wq, &dbs_info->work, delay);
}
static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info)
@@ -1019,6 +1036,92 @@
return;
}
+static int dbs_migration_notify(struct notifier_block *nb,
+ unsigned long target_cpu, void *arg)
+{
+ struct dbs_sync_work_struct *sync_work =
+ &per_cpu(dbs_sync_work, target_cpu);
+ sync_work->src_cpu = (unsigned int)arg;
+
+ queue_work_on(target_cpu, dbs_wq,
+ &per_cpu(dbs_sync_work, target_cpu).work);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block dbs_migration_nb = {
+ .notifier_call = dbs_migration_notify,
+};
+
+void dbs_synchronize(struct work_struct *work)
+{
+ struct cpufreq_policy *policy;
+ struct cpu_dbs_info_s *this_dbs_info, *src_dbs_info;
+ struct dbs_sync_work_struct *dbs_work;
+ unsigned int cpu, src_cpu;
+ unsigned int src_freq, src_max_load;
+ int delay;
+
+ dbs_work = container_of(work, struct dbs_sync_work_struct, work);
+ cpu = dbs_work->targ_cpu;
+ src_cpu = dbs_work->src_cpu;
+
+ get_online_cpus();
+
+ /* Getting source cpu info */
+ src_dbs_info = &per_cpu(od_cpu_dbs_info, src_cpu);
+ if (src_dbs_info != NULL && src_dbs_info->cur_policy != NULL) {
+ src_freq = src_dbs_info->cur_policy->cur;
+ src_max_load = src_dbs_info->max_load;
+ } else {
+ src_freq = dbs_tuners_ins.sync_freq;
+ src_max_load = 0;
+ }
+
+ if (lock_policy_rwsem_write(cpu) < 0)
+ goto bail_acq_sema_failed;
+
+ this_dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
+ policy = this_dbs_info->cur_policy;
+ if (!policy) {
+ /* CPU not using ondemand governor */
+ goto bail_incorrect_governor;
+ }
+
+ delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+
+ if (policy->cur < src_freq) {
+
+ /* Cancelling the next ondemand sample */
+ cancel_delayed_work_sync(&this_dbs_info->work);
+
+ /*
+ * Arch specific cpufreq driver may fail.
+ * Don't update governor frequency upon failure.
+ */
+ if (__cpufreq_driver_target(policy, src_freq,
+ CPUFREQ_RELATION_L) >= 0) {
+ policy->cur = src_freq;
+ if (src_max_load > this_dbs_info->max_load) {
+ this_dbs_info->max_load = src_max_load;
+ this_dbs_info->prev_load = src_max_load;
+ }
+ }
+
+ /* Rescheduling the next ondemand sample */
+ mutex_lock(&this_dbs_info->timer_mutex);
+ schedule_delayed_work_on(cpu, &this_dbs_info->work,
+ delay);
+ mutex_unlock(&this_dbs_info->timer_mutex);
+ }
+bail_incorrect_governor:
+ unlock_policy_rwsem_write(cpu);
+
+bail_acq_sema_failed:
+ put_online_cpus();
+ return;
+}
+
static void dbs_input_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
@@ -1031,7 +1134,7 @@
}
for_each_online_cpu(i)
- queue_work_on(i, input_wq, &per_cpu(dbs_refresh_work, i).work);
+ queue_work_on(i, dbs_wq, &per_cpu(dbs_refresh_work, i).work);
}
static int dbs_input_connect(struct input_handler *handler,
@@ -1147,6 +1250,9 @@
if (dbs_tuners_ins.sync_freq == 0)
dbs_tuners_ins.sync_freq = policy->min;
+
+ atomic_notifier_chain_register(&migration_notifier_head,
+ &dbs_migration_nb);
}
if (!cpu)
rc = input_register_handler(&dbs_input_handler);
@@ -1170,9 +1276,14 @@
this_dbs_info->cur_policy = NULL;
if (!cpu)
input_unregister_handler(&dbs_input_handler);
- if (!dbs_enable)
+ if (!dbs_enable) {
sysfs_remove_group(cpufreq_global_kobject,
&dbs_attr_group);
+ atomic_notifier_chain_unregister(
+ &migration_notifier_head,
+ &dbs_migration_nb);
+ }
+
mutex_unlock(&dbs_mutex);
break;
@@ -1221,9 +1332,9 @@
MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10);
}
- input_wq = create_workqueue("iewq");
- if (!input_wq) {
- printk(KERN_ERR "Failed to create iewq workqueue\n");
+ dbs_wq = alloc_workqueue("ondemand_dbs_wq", WQ_HIGHPRI, 0);
+ if (!dbs_wq) {
+ printk(KERN_ERR "Failed to create ondemand_dbs_wq workqueue\n");
return -EFAULT;
}
for_each_possible_cpu(i) {
@@ -1231,10 +1342,17 @@
&per_cpu(od_cpu_dbs_info, i);
struct dbs_work_struct *dbs_work =
&per_cpu(dbs_refresh_work, i);
+ struct dbs_sync_work_struct *dbs_sync =
+ &per_cpu(dbs_sync_work, i);
mutex_init(&this_dbs_info->timer_mutex);
INIT_WORK(&dbs_work->work, dbs_refresh_callback);
dbs_work->cpu = i;
+
+ INIT_WORK(&dbs_sync->work, dbs_synchronize);
+ dbs_sync->src_cpu = 0;
+ dbs_sync->targ_cpu = i;
+
}
return cpufreq_register_governor(&cpufreq_gov_ondemand);
@@ -1250,7 +1368,7 @@
&per_cpu(od_cpu_dbs_info, i);
mutex_destroy(&this_dbs_info->timer_mutex);
}
- destroy_workqueue(input_wq);
+ destroy_workqueue(dbs_wq);
}
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index c758b3a..3422f05 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -306,7 +306,7 @@
config CRYPTO_DEV_QCE
tristate "Qualcomm Crypto Engine (QCE) module"
select CRYPTO_DEV_QCE40 if ARCH_MSM8960 || ARCH_MSM9615
- select CRYPTO_DEV_QCE50 if ARCH_MSM8974 || ARCH_MSM9625
+ select CRYPTO_DEV_QCE50 if ARCH_MSM8974 || ARCH_MSM9625 || ARCH_MSM8226 || ARCH_MSM8610
default n
help
This driver supports Qualcomm Crypto Engine in MSM7x30, MSM8660
diff --git a/drivers/crypto/msm/ota_crypto.c b/drivers/crypto/msm/ota_crypto.c
index af53543..5051a3a 100644
--- a/drivers/crypto/msm/ota_crypto.c
+++ b/drivers/crypto/msm/ota_crypto.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013, 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
@@ -42,7 +42,7 @@
struct ota_dev_control;
struct ota_async_req {
- struct list_head list;
+ struct list_head rlist;
struct completion complete;
int err;
enum qce_ota_oper_enum op;
@@ -52,7 +52,7 @@
struct qce_f8_multi_pkt_req f8_mp_req;
} req;
- struct ota_dev_control *podev;
+ struct ota_qce_dev *pqce;
};
/*
@@ -68,19 +68,29 @@
/* misc device */
struct miscdevice miscdevice;
+ struct list_head ready_commands;
+ unsigned magic;
+ struct list_head qce_dev;
+ spinlock_t lock;
+ struct mutex register_lock;
+ bool registered;
+ uint32_t total_units;
+};
+struct ota_qce_dev {
+ struct list_head qlist;
/* qce handle */
void *qce;
/* platform device */
struct platform_device *pdev;
- unsigned magic;
-
- struct list_head ready_commands;
struct ota_async_req *active_command;
- spinlock_t lock;
struct tasklet_struct done_tasklet;
+ struct ota_dev_control *podev;
+ uint32_t unit;
+ u32 totalReq;
+ u32 errReq;
};
#define OTA_MAGIC 0x4f544143
@@ -89,7 +99,7 @@
unsigned cmd, unsigned long arg);
static int qcota_open(struct inode *inode, struct file *file);
static int qcota_release(struct inode *inode, struct file *file);
-static int start_req(struct ota_dev_control *podev);
+static int start_req(struct ota_qce_dev *pqce, struct ota_async_req *areq);
static const struct file_operations qcota_fops = {
.owner = THIS_MODULE,
@@ -98,35 +108,15 @@
.release = qcota_release,
};
-static struct ota_dev_control qcota_dev[] = {
- {
- .miscdevice = {
+static struct ota_dev_control qcota_dev = {
+ .miscdevice = {
.minor = MISC_DYNAMIC_MINOR,
.name = "qcota0",
.fops = &qcota_fops,
- },
- .magic = OTA_MAGIC,
},
- {
- .miscdevice = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "qcota1",
- .fops = &qcota_fops,
- },
- .magic = OTA_MAGIC,
- },
- {
- .miscdevice = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "qcota2",
- .fops = &qcota_fops,
- },
- .magic = OTA_MAGIC,
- }
+ .magic = OTA_MAGIC,
};
-#define MAX_OTA_DEVICE ARRAY_SIZE(qcota_dev)
-
#define DEBUG_MAX_FNAME 16
#define DEBUG_MAX_RW_BUF 1024
@@ -141,27 +131,22 @@
u32 f9_op_success;
u32 f9_op_fail;
};
-static struct qcota_stat _qcota_stat[MAX_OTA_DEVICE];
+static struct qcota_stat _qcota_stat;
static struct dentry *_debug_dent;
static char _debug_read_buf[DEBUG_MAX_RW_BUF];
-static int _debug_qcota[MAX_OTA_DEVICE];
+static int _debug_qcota;
-static struct ota_dev_control *qcota_minor_to_control(unsigned n)
+static struct ota_dev_control *qcota_control(void)
{
- int i;
- for (i = 0; i < MAX_OTA_DEVICE; i++) {
- if (qcota_dev[i].miscdevice.minor == n)
- return &qcota_dev[i];
- }
- return NULL;
+ return &qcota_dev;
}
static int qcota_open(struct inode *inode, struct file *file)
{
struct ota_dev_control *podev;
- podev = qcota_minor_to_control(MINOR(inode->i_rdev));
+ podev = qcota_control();
if (podev == NULL) {
pr_err("%s: no such device %d\n", __func__,
MINOR(inode->i_rdev));
@@ -191,38 +176,52 @@
static void req_done(unsigned long data)
{
- struct ota_dev_control *podev = (struct ota_dev_control *)data;
+ struct ota_qce_dev *pqce = (struct ota_qce_dev *)data;
+ struct ota_dev_control *podev = pqce->podev;
struct ota_async_req *areq;
unsigned long flags;
struct ota_async_req *new_req = NULL;
int ret = 0;
+
spin_lock_irqsave(&podev->lock, flags);
- areq = podev->active_command;
- podev->active_command = NULL;
+
+ areq = pqce->active_command;
+ if (unlikely(areq == NULL))
+ pr_err("ota_crypto: req_done, no active request\n");
+ pqce->active_command = NULL;
again:
if (!list_empty(&podev->ready_commands)) {
new_req = container_of(podev->ready_commands.next,
- struct ota_async_req, list);
- list_del(&new_req->list);
- podev->active_command = new_req;
+ struct ota_async_req, rlist);
+ list_del(&new_req->rlist);
+ pqce->active_command = new_req;
+ spin_unlock_irqrestore(&podev->lock, flags);
+
new_req->err = 0;
- ret = start_req(podev);
- }
+ ret = start_req(pqce, new_req); /* start a new request */
- spin_unlock_irqrestore(&podev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&podev->lock, flags);
+ };
- if (areq)
+ if (areq) {
complete(&areq->complete);
-
- if (new_req && ret) {
- complete(&new_req->complete);
- spin_lock_irqsave(&podev->lock, flags);
- podev->active_command = NULL;
areq = NULL;
+ };
+
+ /* if error from issuing request */
+ if (unlikely(new_req && ret)) {
+ new_req->err = ret;
+ complete(&new_req->complete);
ret = 0;
new_req = NULL;
+
+ spin_lock_irqsave(&podev->lock, flags);
+ pqce->active_command = NULL;
+
+ /* try to get next new request */
goto again;
}
@@ -233,64 +232,61 @@
int ret)
{
struct ota_async_req *areq = (struct ota_async_req *) cookie;
- struct ota_dev_control *podev;
- struct qcota_stat *pstat;
+ struct ota_qce_dev *pqce;
- podev = areq->podev;
- pstat = &_qcota_stat[podev->pdev->id];
+ pqce = areq->pqce;
areq->req.f9_req.mac_i = (uint32_t) icv;
- if (ret)
+ if (ret) {
+ pqce->errReq++;
areq->err = -ENXIO;
- else
+ } else
areq->err = 0;
- tasklet_schedule(&podev->done_tasklet);
-};
+ tasklet_schedule(&pqce->done_tasklet);
+}
static void f8_cb(void *cookie, unsigned char *icv, unsigned char *iv,
int ret)
{
struct ota_async_req *areq = (struct ota_async_req *) cookie;
- struct ota_dev_control *podev;
- struct qcota_stat *pstat;
+ struct ota_qce_dev *pqce;
- podev = areq->podev;
- pstat = &_qcota_stat[podev->pdev->id];
+ pqce = areq->pqce;
- if (ret)
+ if (ret) {
+ pqce->errReq++;
areq->err = -ENXIO;
- else
+ } else {
areq->err = 0;
+ }
- tasklet_schedule(&podev->done_tasklet);
-};
+ tasklet_schedule(&pqce->done_tasklet);
+}
-static int start_req(struct ota_dev_control *podev)
+static int start_req(struct ota_qce_dev *pqce, struct ota_async_req *areq)
{
- struct ota_async_req *areq;
struct qce_f9_req *pf9;
struct qce_f8_multi_pkt_req *p_mp_f8;
struct qce_f8_req *pf8;
int ret = 0;
- /* start the command on the podev->active_command */
- areq = podev->active_command;
- areq->podev = podev;
+ /* command should be on the podev->active_command */
+ areq->pqce = pqce;
switch (areq->op) {
case QCE_OTA_F8_OPER:
pf8 = &areq->req.f8_req;
- ret = qce_f8_req(podev->qce, pf8, areq, f8_cb);
+ ret = qce_f8_req(pqce->qce, pf8, areq, f8_cb);
break;
case QCE_OTA_MPKT_F8_OPER:
p_mp_f8 = &areq->req.f8_mp_req;
- ret = qce_f8_multi_pkt_req(podev->qce, p_mp_f8, areq, f8_cb);
+ ret = qce_f8_multi_pkt_req(pqce->qce, p_mp_f8, areq, f8_cb);
break;
case QCE_OTA_F9_OPER:
pf9 = &areq->req.f9_req;
- ret = qce_f9_req(podev->qce, pf9, areq, f9_cb);
+ ret = qce_f9_req(pqce->qce, pf9, areq, f9_cb);
break;
default:
@@ -298,32 +294,60 @@
break;
};
areq->err = ret;
+ pqce->totalReq++;
+ if (ret)
+ pqce->errReq++;
return ret;
-};
+}
+
+static struct ota_qce_dev *schedule_qce(struct ota_dev_control *podev)
+{
+ /* do this function with spinlock set */
+ struct ota_qce_dev *p;
+
+ if (unlikely(list_empty(&podev->qce_dev))) {
+ pr_err("%s: no valid qce to schedule\n", __func__);
+ return NULL;
+ }
+
+ list_for_each_entry(p, &podev->qce_dev, qlist) {
+ if (p->active_command == NULL)
+ return p;
+ }
+ return NULL;
+}
static int submit_req(struct ota_async_req *areq, struct ota_dev_control *podev)
{
unsigned long flags;
int ret = 0;
struct qcota_stat *pstat;
+ struct ota_qce_dev *pqce;
areq->err = 0;
- spin_lock_irqsave(&podev->lock, flags);
- if (podev->active_command == NULL) {
- podev->active_command = areq;
- ret = start_req(podev);
- } else {
- list_add_tail(&areq->list, &podev->ready_commands);
- }
- if (ret != 0)
- podev->active_command = NULL;
- spin_unlock_irqrestore(&podev->lock, flags);
+ spin_lock_irqsave(&podev->lock, flags);
+ pqce = schedule_qce(podev);
+ if (pqce) {
+ pqce->active_command = areq;
+ spin_unlock_irqrestore(&podev->lock, flags);
+
+ ret = start_req(pqce, areq);
+ if (ret != 0) {
+ spin_lock_irqsave(&podev->lock, flags);
+ pqce->active_command = NULL;
+ spin_unlock_irqrestore(&podev->lock, flags);
+ }
+
+ } else {
+ list_add_tail(&areq->rlist, &podev->ready_commands);
+ spin_unlock_irqrestore(&podev->lock, flags);
+ }
if (ret == 0)
wait_for_completion(&areq->complete);
- pstat = &_qcota_stat[podev->pdev->id];
+ pstat = &_qcota_stat;
switch (areq->op) {
case QCE_OTA_F8_OPER:
if (areq->err)
@@ -350,7 +374,7 @@
};
return areq->err;
-};
+}
static long qcota_ioctl(struct file *file,
unsigned cmd, unsigned long arg)
@@ -377,7 +401,7 @@
init_completion(&areq.complete);
- pstat = &_qcota_stat[podev->pdev->id];
+ pstat = &_qcota_stat;
switch (cmd) {
case QCOTA_F9_REQ:
@@ -523,67 +547,112 @@
int rc = 0;
struct ota_dev_control *podev;
struct ce_hw_support ce_support;
+ struct ota_qce_dev *pqce;
+ unsigned long flags;
- if (pdev->id >= MAX_OTA_DEVICE) {
- pr_err("%s: device id %d exceeds allowed %d\n",
- __func__, pdev->id, MAX_OTA_DEVICE);
- return -ENOENT;
+ podev = &qcota_dev;
+ pqce = kzalloc(sizeof(*pqce), GFP_KERNEL);
+ if (!pqce) {
+ pr_err("qcota_probe: Memory allocation FAIL\n");
+ return -ENOMEM;
}
- podev = &qcota_dev[pdev->id];
-
- INIT_LIST_HEAD(&podev->ready_commands);
- podev->active_command = NULL;
- spin_lock_init(&podev->lock);
- tasklet_init(&podev->done_tasklet, req_done, (unsigned long)podev);
+ pqce->podev = podev;
+ pqce->active_command = NULL;
+ tasklet_init(&pqce->done_tasklet, req_done, (unsigned long)pqce);
/* open qce */
handle = qce_open(pdev, &rc);
if (handle == NULL) {
- pr_err("%s: device id %d, can not open qce\n",
- __func__, pdev->id);
- platform_set_drvdata(pdev, NULL);
- return rc;
+ pr_err("%s: device %s, can not open qce\n",
+ __func__, pdev->name);
+ goto err;
}
if (qce_hw_support(handle, &ce_support) < 0 ||
ce_support.ota == false) {
- pr_err("%s: device id %d, qce does not support ota capability\n",
- __func__, pdev->id);
+ pr_err("%s: device %s, qce does not support ota capability\n",
+ __func__, pdev->name);
rc = -ENODEV;
goto err;
}
- podev->qce = handle;
- podev->pdev = pdev;
- platform_set_drvdata(pdev, podev);
+ pqce->qce = handle;
+ pqce->pdev = pdev;
+ pqce->totalReq = 0;
+ pqce->errReq = 0;
+ platform_set_drvdata(pdev, pqce);
- rc = misc_register(&podev->miscdevice);
- if (rc < 0)
+ mutex_lock(&podev->register_lock);
+ rc = 0;
+ if (podev->registered == false) {
+ rc = misc_register(&podev->miscdevice);
+ if (rc == 0) {
+ pqce->unit = podev->total_units;
+ podev->total_units++;
+ podev->registered = true;
+ };
+ } else {
+ pqce->unit = podev->total_units;
+ podev->total_units++;
+ }
+ mutex_unlock(&podev->register_lock);
+ if (rc) {
+ pr_err("ion: failed to register misc device.\n");
goto err;
+ }
+
+ spin_lock_irqsave(&podev->lock, flags);
+ list_add_tail(&pqce->qlist, &podev->qce_dev);
+ spin_unlock_irqrestore(&podev->lock, flags);
return 0;
err:
if (handle)
qce_close(handle);
+
platform_set_drvdata(pdev, NULL);
- podev->qce = NULL;
- podev->pdev = NULL;
+ tasklet_kill(&pqce->done_tasklet);
+ kfree(pqce);
return rc;
-};
+}
static int qcota_remove(struct platform_device *pdev)
{
struct ota_dev_control *podev;
+ struct ota_qce_dev *pqce;
+ unsigned long flags;
- podev = platform_get_drvdata(pdev);
- if (!podev)
+ pqce = platform_get_drvdata(pdev);
+ if (!pqce)
return 0;
- if (podev->qce)
- qce_close(podev->qce);
+ if (pqce->qce)
+ qce_close(pqce->qce);
- if (podev->miscdevice.minor != MISC_DYNAMIC_MINOR)
- misc_deregister(&podev->miscdevice);
- tasklet_kill(&podev->done_tasklet);
+ podev = pqce->podev;
+ if (!podev)
+ goto ret;
+
+ spin_lock_irqsave(&podev->lock, flags);
+ list_del(&pqce->qlist);
+ spin_unlock_irqrestore(&podev->lock, flags);
+
+ mutex_lock(&podev->register_lock);
+ if (--podev->total_units == 0) {
+ if (podev->miscdevice.minor != MISC_DYNAMIC_MINOR)
+ misc_deregister(&podev->miscdevice);
+ podev->registered = false;
+ }
+ mutex_unlock(&podev->register_lock);
+ret:
+
+ tasklet_kill(&pqce->done_tasklet);
+ kfree(pqce);
return 0;
+}
+
+static struct of_device_id qcota_match[] = {
+ { .compatible = "qcom,qcota",
+ },
+ {}
};
static struct platform_driver qcota_plat_driver = {
@@ -592,18 +661,21 @@
.driver = {
.name = "qcota",
.owner = THIS_MODULE,
+ .of_match_table = qcota_match,
},
};
-static int _disp_stats(int id)
+static int _disp_stats(void)
{
struct qcota_stat *pstat;
int len = 0;
+ struct ota_dev_control *podev = &qcota_dev;
+ unsigned long flags;
+ struct ota_qce_dev *p;
- pstat = &_qcota_stat[id];
+ pstat = &_qcota_stat;
len = snprintf(_debug_read_buf, DEBUG_MAX_RW_BUF - 1,
- "\nQualcomm OTA crypto accelerator %d Statistics:\n",
- id + 1);
+ "\nQualcomm OTA crypto accelerator Statistics:\n");
len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
" F8 request : %d\n",
@@ -635,6 +707,27 @@
" F9 operation fail : %d\n",
pstat->f9_op_fail);
+ spin_lock_irqsave(&podev->lock, flags);
+
+ list_for_each_entry(p, &podev->qce_dev, qlist) {
+ len += snprintf(
+ _debug_read_buf + len,
+ DEBUG_MAX_RW_BUF - len - 1,
+ " Engine %d Req : %d\n",
+ p->unit,
+ p->totalReq
+ );
+ len += snprintf(
+ _debug_read_buf + len,
+ DEBUG_MAX_RW_BUF - len - 1,
+ " Engine %d Req Error : %d\n",
+ p->unit,
+ p->errReq
+ );
+ }
+
+ spin_unlock_irqrestore(&podev->lock, flags);
+
return len;
}
@@ -648,10 +741,9 @@
size_t count, loff_t *ppos)
{
int rc = -EINVAL;
- int qcota = *((int *) file->private_data);
int len;
- len = _disp_stats(qcota);
+ len = _disp_stats();
rc = simple_read_from_buffer((void __user *) buf, len,
ppos, (void *) _debug_read_buf, len);
@@ -662,12 +754,23 @@
static ssize_t _debug_stats_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
+ struct ota_dev_control *podev = &qcota_dev;
+ unsigned long flags;
+ struct ota_qce_dev *p;
- int qcota = *((int *) file->private_data);
+ memset((char *)&_qcota_stat, 0, sizeof(struct qcota_stat));
- memset((char *)&_qcota_stat[qcota], 0, sizeof(struct qcota_stat));
+ spin_lock_irqsave(&podev->lock, flags);
+
+ list_for_each_entry(p, &podev->qce_dev, qlist) {
+ p->totalReq = 0;
+ p->errReq = 0;
+ }
+
+ spin_unlock_irqrestore(&podev->lock, flags);
+
return count;
-};
+}
static const struct file_operations _debug_stats_ops = {
.open = _debug_stats_open,
@@ -679,7 +782,6 @@
{
int rc;
char name[DEBUG_MAX_FNAME];
- int i;
struct dentry *dent;
_debug_dent = debugfs_create_dir("qcota", NULL);
@@ -689,17 +791,15 @@
return PTR_ERR(_debug_dent);
}
- for (i = 0; i < MAX_OTA_DEVICE; i++) {
- snprintf(name, DEBUG_MAX_FNAME-1, "stats-%d", i+1);
- _debug_qcota[i] = i;
- dent = debugfs_create_file(name, 0644, _debug_dent,
- &_debug_qcota[i], &_debug_stats_ops);
- if (dent == NULL) {
- pr_err("qcota debugfs_create_file fail, error %ld\n",
+ snprintf(name, DEBUG_MAX_FNAME-1, "stats-0");
+ _debug_qcota = 0;
+ dent = debugfs_create_file(name, 0644, _debug_dent,
+ &_debug_qcota, &_debug_stats_ops);
+ if (dent == NULL) {
+ pr_err("qcota debugfs_create_file fail, error %ld\n",
PTR_ERR(dent));
- rc = PTR_ERR(dent);
- goto err;
- }
+ rc = PTR_ERR(dent);
+ goto err;
}
return 0;
err:
@@ -710,10 +810,20 @@
static int __init qcota_init(void)
{
int rc;
+ struct ota_dev_control *podev;
rc = _qcota_debug_init();
if (rc)
return rc;
+
+ podev = &qcota_dev;
+ INIT_LIST_HEAD(&podev->ready_commands);
+ INIT_LIST_HEAD(&podev->qce_dev);
+ spin_lock_init(&podev->lock);
+ mutex_init(&podev->register_lock);
+ podev->registered = false;
+ podev->total_units = 0;
+
return platform_driver_register(&qcota_plat_driver);
}
static void __exit qcota_exit(void)
@@ -725,7 +835,7 @@
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Rohit Vaswani <rvaswani@codeaurora.org>");
MODULE_DESCRIPTION("Qualcomm Ota Crypto driver");
-MODULE_VERSION("1.01");
+MODULE_VERSION("1.02");
module_init(qcota_init);
module_exit(qcota_exit);
diff --git a/drivers/crypto/msm/qce.h b/drivers/crypto/msm/qce.h
index 51a74b6..c14143f 100644
--- a/drivers/crypto/msm/qce.h
+++ b/drivers/crypto/msm/qce.h
@@ -1,6 +1,6 @@
/* Qualcomm Crypto Engine driver API
*
- * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2013, 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
@@ -32,6 +32,8 @@
#define SHA256_DIGESTSIZE 32
#define SHA1_DIGESTSIZE 20
+#define AES_CE_BLOCK_SIZE 16
+
/* key size in bytes */
#define HMAC_KEY_SIZE (SHA1_DIGESTSIZE) /* hmac-sha1 */
#define SHA_HMAC_KEY_SIZE 64
@@ -114,6 +116,7 @@
bool aligned_only;
bool bam;
bool is_shared;
+ bool hw_key;
};
/* Sha operation parameters */
@@ -129,6 +132,7 @@
bool last_blk; /* last block indicator */
unsigned int size; /* data length in bytes */
void *areq;
+ unsigned int flags;
};
struct qce_req {
@@ -152,6 +156,7 @@
unsigned int cryptlen; /* data length */
unsigned int use_pmem; /* is source of data PMEM allocated? */
struct qcedev_pmem_info *pmem; /* pointer to pmem_info structure*/
+ unsigned int flags;
};
void *qce_open(struct platform_device *pdev, int *rc);
diff --git a/drivers/crypto/msm/qce40.c b/drivers/crypto/msm/qce40.c
index 5249917..1dcd3bc 100644
--- a/drivers/crypto/msm/qce40.c
+++ b/drivers/crypto/msm/qce40.c
@@ -412,6 +412,9 @@
/* write seg size */
*((uint32_t *)(pce_dev->ce_dm.buffer.seg_size)) = sreq->size;
+ /* clear status */
+ *((uint32_t *)(pce_dev->ce_dm.buffer.status)) = 0;
+
_ce_setup_hash_cmdrptrlist(pce_dev, sreq);
return 0;
@@ -685,6 +688,9 @@
*((uint32_t *)(buffer->seg_size)) = totallen_in;
+ /* clear status */
+ *((uint32_t *)(pce_dev->ce_dm.buffer.status)) = 0;
+
_ce_setup_cipher_cmdrptrlist(pce_dev, creq);
return 0;
};
@@ -708,13 +714,19 @@
/* check MAC */
if (pce_dev->mode == QCE_MODE_CCM) {
- uint32_t result;
+ int32_t result = 0;
result =
(uint32_t)(*((uint32_t *)pce_dev->ce_dm.buffer.status));
result &= (1 << CRYPTO_MAC_FAILED);
result |= (pce_dev->ce_dm.chan_ce_in_status |
pce_dev->ce_dm.chan_ce_out_status);
+ if (pce_dev->ce_dm.chan_ce_in_status |
+ pce_dev->ce_dm.chan_ce_out_status)
+ result = -ENXIO;
+ else if (result & (1 << CRYPTO_MAC_FAILED))
+ result = -EBADMSG;
+
pce_dev->qce_cb(areq, pce_dev->ce_dm.buffer.auth_result, NULL,
result);
}
@@ -1617,16 +1629,16 @@
pscmd++;
- /* SET SEG SIZE REGISTER and OCB COMMAND LIST */
- pce_dev->ce_dm.cmdlist.set_seg_size_ocb = pscmd;
- pscmd->cmd = CMD_LC | CMD_MODE_SINGLE | CMD_OCB;
+ /* SET SEG SIZE REGISTER LIST */
+ pce_dev->ce_dm.cmdlist.set_seg_size = pscmd;
+ pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
pscmd->dst = (unsigned) (CRYPTO_SEG_SIZE_REG + pce_dev->phy_iobase);
pscmd->len = CRYPTO_REG_SIZE;
pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.seg_size);
pscmd++;
- /* OCU COMMAND LIST */
+ /* Get status and OCU COMMAND LIST */
pce_dev->ce_dm.cmdlist.get_status_ocu = pscmd;
pscmd->cmd = CMD_LC | CMD_MODE_SINGLE | CMD_OCU;
pscmd->src = (unsigned) (CRYPTO_STATUS_REG + pce_dev->phy_iobase);
@@ -1634,7 +1646,7 @@
pscmd->dst = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.status);
pscmd++;
- /* CLEAR STATUS COMMAND LIST */
+ /* CLEAR STATUS and OCU COMMAND LIST */
pce_dev->ce_dm.cmdlist.clear_status = pscmd;
pscmd->cmd = CMD_LC | CMD_MODE_SINGLE | CMD_OCU;
pscmd->dst = (unsigned) (CRYPTO_STATUS_REG + pce_dev->phy_iobase);
@@ -1642,6 +1654,14 @@
pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.status);
pscmd++;
+ /* CLEAR STATUS and OCB COMMAND LIST */
+ pce_dev->ce_dm.cmdlist.clear_status_ocb = pscmd;
+ pscmd->cmd = CMD_LC | CMD_MODE_SINGLE | CMD_OCB;
+ pscmd->dst = (unsigned) (CRYPTO_STATUS_REG + pce_dev->phy_iobase);
+ pscmd->len = CRYPTO_REG_SIZE;
+ pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.status);
+ pscmd++;
+
/* SET GO_PROC REGISTERS COMMAND LIST */
pce_dev->ce_dm.cmdlist.set_go_proc = pscmd;
pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
@@ -1715,7 +1735,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_aes_128_cbc_ctr = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_128_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_iv);
@@ -1727,7 +1748,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_aes_256_cbc_ctr = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_256_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_iv);
@@ -1739,7 +1761,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_aes_128_ecb = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_128_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
@@ -1750,7 +1773,8 @@
cmd_ptr_vaddr = (uint32_t *)ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_aes_256_ecb = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_256_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
@@ -1761,7 +1785,8 @@
cmd_ptr_vaddr = (uint32_t *)ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_aes_128_xts = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_128_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_128_xts_key);
@@ -1775,7 +1800,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_aes_256_xts = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_256_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_256_xts_key);
@@ -1789,7 +1815,8 @@
cmd_ptr_vaddr = (uint32_t *)ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_des_cbc = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_des_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_des_iv);
@@ -1800,7 +1827,8 @@
cmd_ptr_vaddr = (uint32_t *)ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_des_ecb = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_des_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
@@ -1810,7 +1838,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_3des_cbc = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_3des_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_des_iv);
@@ -1821,7 +1850,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_3des_ecb = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_3des_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
@@ -1861,7 +1891,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->auth_sha1 = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_iv_20);
@@ -1879,7 +1910,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->auth_sha256 = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_iv_32);
@@ -1897,7 +1929,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->auth_sha1_hmac = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_key_512);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
@@ -1916,7 +1949,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->auth_sha256_hmac = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_key_512);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
@@ -1935,7 +1969,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->auth_aes_128_cmac = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_iv);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_key);
@@ -1953,7 +1988,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->auth_aes_256_cmac = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_iv);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_key);
@@ -1988,7 +2024,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->aead_aes_128_ccm = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_iv);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_byte_count);
@@ -2005,7 +2042,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->aead_aes_256_ccm = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_iv);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_byte_count);
diff --git a/drivers/crypto/msm/qce40.h b/drivers/crypto/msm/qce40.h
index 0d19106..179250c 100644
--- a/drivers/crypto/msm/qce40.h
+++ b/drivers/crypto/msm/qce40.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013, 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
@@ -118,6 +118,7 @@
struct ce_cmdlists {
dmov_s *get_hw_version;
dmov_s *clear_status;
+ dmov_s *clear_status_ocb;
dmov_s *get_status_ocu;
dmov_s *set_cipher_cfg;
@@ -162,7 +163,7 @@
dmov_s *reset_auth_cfg;
dmov_s *reset_auth_byte_count;
- dmov_s *set_seg_size_ocb;
+ dmov_s *set_seg_size;
dmov_s *get_status_wait;
dmov_s *set_go_proc;
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 245272b..bff9db5 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -33,6 +33,7 @@
#include <mach/dma.h>
#include <mach/clk.h>
#include <mach/socinfo.h>
+#include <mach/qcrypto.h>
#include "qce.h"
#include "qce50.h"
@@ -62,6 +63,8 @@
dma_addr_t coh_pmem; /* Allocated coherent physical memory */
int memsize; /* Memory allocated */
int is_shared; /* CE HW is shared */
+ bool support_cmd_dscr;
+ bool support_hw_key;
void __iomem *iobase; /* Virtual io base of CE HW */
unsigned int phy_iobase; /* Physical io base of CE HW */
@@ -84,6 +87,7 @@
int dir;
void *areq;
enum qce_cipher_mode_enum mode;
+ struct qce_ce_cfg_reg_setting reg;
struct ce_sps_data ce_sps;
};
@@ -456,31 +460,25 @@
else
key_size = creq->encklen;
- _byte_stream_to_net_words(enckey32, creq->enckey, key_size);
pce = cmdlistinfo->go_proc;
-
- /* check for null key. If null, use hw key*/
- enck_size_in_word = key_size/sizeof(uint32_t);
- for (i = 0; i < enck_size_in_word; i++) {
- if (enckey32[i] != 0)
- break;
- }
- if (i == enck_size_in_word) {
+ if ((creq->flags & QCRYPTO_CTX_USE_HW_KEY) == QCRYPTO_CTX_USE_HW_KEY) {
use_hw_key = true;
- pce->addr = (uint32_t)(CRYPTO_GOPROC_QC_KEY_REG +
- pce_dev->phy_iobase);
- }
- if (use_hw_key == false) {
- for (i = 0; i < enck_size_in_word; i++) {
- if (enckey32[i] != 0xFFFFFFFF)
- break;
- }
- if (i == enck_size_in_word)
+ } else {
+ if ((creq->flags & QCRYPTO_CTX_USE_PIPE_KEY) ==
+ QCRYPTO_CTX_USE_PIPE_KEY)
use_pipe_key = true;
}
- if (use_hw_key == false)
+ pce = cmdlistinfo->go_proc;
+ if (use_hw_key == true)
+ pce->addr = (uint32_t)(CRYPTO_GOPROC_QC_KEY_REG +
+ pce_dev->phy_iobase);
+ else
pce->addr = (uint32_t)(CRYPTO_GOPROC_REG +
pce_dev->phy_iobase);
+ if ((use_pipe_key == false) && (use_hw_key == false)) {
+ _byte_stream_to_net_words(enckey32, creq->enckey, key_size);
+ enck_size_in_word = key_size/sizeof(uint32_t);
+ }
if ((creq->op == QCE_REQ_AEAD) && (creq->mode == QCE_MODE_CCM)) {
uint32_t authklen32 = creq->encklen/sizeof(uint32_t);
@@ -494,25 +492,17 @@
for (i = 0; i < noncelen32; i++, pce++)
pce->data = nonce32[i];
- /* TBD NEW FEATURE partial AES CCM pkt support set last bit */
- auth_cfg |= ((1 << CRYPTO_LAST) | (1 << CRYPTO_FIRST));
+ if (creq->authklen == AES128_KEY_SIZE)
+ auth_cfg = pce_dev->reg.auth_cfg_aes_ccm_128;
+ else {
+ if (creq->authklen == AES256_KEY_SIZE)
+ auth_cfg = pce_dev->reg.auth_cfg_aes_ccm_256;
+ }
if (creq->dir == QCE_ENCRYPT)
auth_cfg |= (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS);
else
auth_cfg |= (CRYPTO_AUTH_POS_AFTER << CRYPTO_AUTH_POS);
auth_cfg |= ((creq->authsize - 1) << CRYPTO_AUTH_SIZE);
- auth_cfg |= (CRYPTO_AUTH_MODE_CCM << CRYPTO_AUTH_MODE);
- if (creq->authklen == AES128_KEY_SIZE)
- auth_cfg |= (CRYPTO_AUTH_KEY_SZ_AES128 <<
- CRYPTO_AUTH_KEY_SIZE);
- else {
- if (creq->authklen == AES256_KEY_SIZE)
- auth_cfg |= (CRYPTO_AUTH_KEY_SZ_AES256 <<
- CRYPTO_AUTH_KEY_SIZE);
- }
- auth_cfg |= (CRYPTO_AUTH_ALG_AES << CRYPTO_AUTH_ALG);
- auth_cfg |= ((MAX_NONCE/sizeof(uint32_t)) <<
- CRYPTO_AUTH_NONCE_NUM_WORDS);
if (use_hw_key == true) {
auth_cfg |= (1 << CRYPTO_USE_HW_KEY_AUTH);
@@ -542,21 +532,37 @@
}
switch (creq->mode) {
case QCE_MODE_ECB:
- encr_cfg |= (CRYPTO_ENCR_MODE_ECB << CRYPTO_ENCR_MODE);
+ if (key_size == AES128_KEY_SIZE)
+ encr_cfg = pce_dev->reg.encr_cfg_aes_ecb_128;
+ else
+ encr_cfg = pce_dev->reg.encr_cfg_aes_ecb_256;
break;
case QCE_MODE_CBC:
- encr_cfg |= (CRYPTO_ENCR_MODE_CBC << CRYPTO_ENCR_MODE);
+ if (key_size == AES128_KEY_SIZE)
+ encr_cfg = pce_dev->reg.encr_cfg_aes_cbc_128;
+ else
+ encr_cfg = pce_dev->reg.encr_cfg_aes_cbc_256;
break;
case QCE_MODE_XTS:
- encr_cfg |= (CRYPTO_ENCR_MODE_XTS << CRYPTO_ENCR_MODE);
+ if (key_size == AES128_KEY_SIZE)
+ encr_cfg = pce_dev->reg.encr_cfg_aes_xts_128;
+ else
+ encr_cfg = pce_dev->reg.encr_cfg_aes_xts_256;
break;
case QCE_MODE_CCM:
+ if (key_size == AES128_KEY_SIZE)
+ encr_cfg = pce_dev->reg.encr_cfg_aes_ccm_128;
+ else
+ encr_cfg = pce_dev->reg.encr_cfg_aes_ccm_256;
encr_cfg |= (CRYPTO_ENCR_MODE_CCM << CRYPTO_ENCR_MODE) |
(CRYPTO_LAST_CCM_XFR << CRYPTO_LAST_CCM);
break;
case QCE_MODE_CTR:
default:
- encr_cfg |= (CRYPTO_ENCR_MODE_CTR << CRYPTO_ENCR_MODE);
+ if (key_size == AES128_KEY_SIZE)
+ encr_cfg = pce_dev->reg.encr_cfg_aes_ctr_128;
+ else
+ encr_cfg = pce_dev->reg.encr_cfg_aes_ctr_256;
break;
}
pce_dev->mode = creq->mode;
@@ -600,20 +606,23 @@
uint32_t xtsklen =
creq->encklen/(2 * sizeof(uint32_t));
- _byte_stream_to_net_words(xtskey32, (creq->enckey +
- creq->encklen/2), creq->encklen/2);
- /* write xts encr key */
- pce = cmdlistinfo->encr_xts_key;
- for (i = 0; i < xtsklen; i++, pce++)
- pce->data = xtskey32[i];
-
+ if ((use_hw_key == false) && (use_pipe_key == false)) {
+ _byte_stream_to_net_words(xtskey32,
+ (creq->enckey + creq->encklen/2),
+ creq->encklen/2);
+ /* write xts encr key */
+ pce = cmdlistinfo->encr_xts_key;
+ for (i = 0; i < xtsklen; i++, pce++)
+ pce->data = xtskey32[i];
+ }
/* write xts du size */
pce = cmdlistinfo->encr_xts_du_size;
- if (use_pipe_key == true)
+ if (!(creq->flags & QCRYPTO_CTX_XTS_MASK))
+ pce->data = creq->cryptlen;
+ else
pce->data = min((unsigned int)QCE_SECTOR_SIZE,
creq->cryptlen);
- else
- pce->data = creq->cryptlen;
+
}
if (creq->mode != QCE_MODE_ECB) {
if (creq->mode == QCE_MODE_XTS)
@@ -642,26 +651,13 @@
if (creq->op == QCE_REQ_ABLK_CIPHER_NO_KEY) {
encr_cfg |= (CRYPTO_ENCR_KEY_SZ_AES128 <<
CRYPTO_ENCR_KEY_SZ);
- encr_cfg |= CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG;
} else {
if (use_hw_key == false) {
/* write encr key */
pce = cmdlistinfo->encr_key;
for (i = 0; i < enck_size_in_word; i++, pce++)
pce->data = enckey32[i];
- switch (key_size) {
- case AES128_KEY_SIZE:
- encr_cfg |= (CRYPTO_ENCR_KEY_SZ_AES128
- << CRYPTO_ENCR_KEY_SZ);
- break;
- case AES256_KEY_SIZE:
- default:
- encr_cfg |= (CRYPTO_ENCR_KEY_SZ_AES256
- << CRYPTO_ENCR_KEY_SZ);
- break;
- } /* end of switch (creq->encklen) */
}
- encr_cfg |= CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG;
} /* else of if (creq->op == QCE_REQ_ABLK_CIPHER_NO_KEY) */
break;
} /* end of switch (creq->mode) */
@@ -706,10 +702,496 @@
return 0;
};
+static int _ce_setup_hash_direct(struct qce_device *pce_dev,
+ struct qce_sha_req *sreq)
+{
+ uint32_t auth32[SHA256_DIGEST_SIZE / sizeof(uint32_t)];
+ uint32_t diglen;
+ bool use_hw_key = false;
+ int i;
+ uint32_t mackey32[SHA_HMAC_KEY_SIZE/sizeof(uint32_t)] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ bool sha1 = false;
+ uint32_t auth_cfg = 0;
+
+ /* clear status */
+ writel_relaxed(0, pce_dev->iobase + CRYPTO_STATUS_REG);
+
+ writel_relaxed(pce_dev->reg.crypto_cfg_be, (pce_dev->iobase +
+ CRYPTO_CONFIG_REG));
+ /*
+ * Ensure previous instructions (setting the CONFIG register)
+ * was completed before issuing starting to set other config register
+ * This is to ensure the configurations are done in correct endian-ness
+ * as set in the CONFIG registers
+ */
+ mb();
+
+ if (sreq->alg == QCE_HASH_AES_CMAC) {
+ /* write seg_cfg */
+ writel_relaxed(0, pce_dev->iobase + CRYPTO_AUTH_SEG_CFG_REG);
+ /* write seg_cfg */
+ writel_relaxed(0, pce_dev->iobase + CRYPTO_ENCR_SEG_CFG_REG);
+ /* write seg_cfg */
+ writel_relaxed(0, pce_dev->iobase + CRYPTO_ENCR_SEG_SIZE_REG);
+
+ /* Clear auth_ivn, auth_keyn registers */
+ for (i = 0; i < 16; i++) {
+ writel_relaxed(0, (pce_dev->iobase +
+ (CRYPTO_AUTH_IV0_REG + i*sizeof(uint32_t))));
+ writel_relaxed(0, (pce_dev->iobase +
+ (CRYPTO_AUTH_KEY0_REG + i*sizeof(uint32_t))));
+ }
+ /* write auth_bytecnt 0/1/2/3, start with 0 */
+ for (i = 0; i < 4; i++)
+ writel_relaxed(0, pce_dev->iobase +
+ CRYPTO_AUTH_BYTECNT0_REG +
+ i * sizeof(uint32_t));
+
+ if (sreq->authklen == AES128_KEY_SIZE)
+ auth_cfg = pce_dev->reg.auth_cfg_cmac_128;
+ else
+ auth_cfg = pce_dev->reg.auth_cfg_cmac_256;
+ }
+
+ if ((sreq->alg == QCE_HASH_SHA1_HMAC) ||
+ (sreq->alg == QCE_HASH_SHA256_HMAC) ||
+ (sreq->alg == QCE_HASH_AES_CMAC)) {
+ uint32_t authk_size_in_word = sreq->authklen/sizeof(uint32_t);
+
+ _byte_stream_to_net_words(mackey32, sreq->authkey,
+ sreq->authklen);
+
+ /* check for null key. If null, use hw key*/
+ for (i = 0; i < authk_size_in_word; i++) {
+ if (mackey32[i] != 0)
+ break;
+ }
+
+ if (i == authk_size_in_word)
+ use_hw_key = true;
+ else
+ /* Clear auth_ivn, auth_keyn registers */
+ for (i = 0; i < authk_size_in_word; i++)
+ writel_relaxed(mackey32[i], (pce_dev->iobase +
+ (CRYPTO_AUTH_KEY0_REG +
+ i*sizeof(uint32_t))));
+ }
+
+ if (sreq->alg == QCE_HASH_AES_CMAC)
+ goto go_proc;
+
+ /* if not the last, the size has to be on the block boundary */
+ if (sreq->last_blk == 0 && (sreq->size % SHA256_BLOCK_SIZE))
+ return -EIO;
+
+ switch (sreq->alg) {
+ case QCE_HASH_SHA1:
+ auth_cfg = pce_dev->reg.auth_cfg_sha1;
+ diglen = SHA1_DIGEST_SIZE;
+ sha1 = true;
+ break;
+ case QCE_HASH_SHA1_HMAC:
+ auth_cfg = pce_dev->reg.auth_cfg_hmac_sha1;
+ diglen = SHA1_DIGEST_SIZE;
+ sha1 = true;
+ break;
+ case QCE_HASH_SHA256:
+ auth_cfg = pce_dev->reg.auth_cfg_sha256;
+ diglen = SHA256_DIGEST_SIZE;
+ break;
+ case QCE_HASH_SHA256_HMAC:
+ auth_cfg = pce_dev->reg.auth_cfg_hmac_sha256;
+ diglen = SHA256_DIGEST_SIZE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* write 20/32 bytes, 5/8 words into auth_iv for SHA1/SHA256 */
+ if (sreq->first_blk) {
+ if (sha1) {
+ for (i = 0; i < 5; i++)
+ auth32[i] = _std_init_vector_sha1[i];
+ } else {
+ for (i = 0; i < 8; i++)
+ auth32[i] = _std_init_vector_sha256[i];
+ }
+ } else {
+ _byte_stream_to_net_words(auth32, sreq->digest, diglen);
+ }
+
+ /* Set auth_ivn, auth_keyn registers */
+ for (i = 0; i < 5; i++)
+ writel_relaxed(auth32[i], (pce_dev->iobase +
+ (CRYPTO_AUTH_IV0_REG + i*sizeof(uint32_t))));
+
+ if ((sreq->alg == QCE_HASH_SHA256) ||
+ (sreq->alg == QCE_HASH_SHA256_HMAC)) {
+ for (i = 5; i < 8; i++)
+ writel_relaxed(auth32[i], (pce_dev->iobase +
+ (CRYPTO_AUTH_IV0_REG + i*sizeof(uint32_t))));
+ }
+
+
+ /* write auth_bytecnt 0/1/2/3, start with 0 */
+ for (i = 0; i < 2; i++)
+ writel_relaxed(sreq->auth_data[i], pce_dev->iobase +
+ CRYPTO_AUTH_BYTECNT0_REG +
+ i * sizeof(uint32_t));
+
+ /* Set/reset last bit in CFG register */
+ if (sreq->last_blk)
+ auth_cfg |= 1 << CRYPTO_LAST;
+ else
+ auth_cfg &= ~(1 << CRYPTO_LAST);
+ if (sreq->first_blk)
+ auth_cfg |= 1 << CRYPTO_FIRST;
+ else
+ auth_cfg &= ~(1 << CRYPTO_FIRST);
+go_proc:
+ /* write seg_cfg */
+ writel_relaxed(auth_cfg, pce_dev->iobase + CRYPTO_AUTH_SEG_CFG_REG);
+ /* write auth seg_size */
+ writel_relaxed(sreq->size, pce_dev->iobase + CRYPTO_AUTH_SEG_SIZE_REG);
+
+ /* write auth_seg_start */
+ writel_relaxed(0, pce_dev->iobase + CRYPTO_AUTH_SEG_START_REG);
+
+ /* reset encr seg_cfg */
+ writel_relaxed(0, pce_dev->iobase + CRYPTO_ENCR_SEG_CFG_REG);
+
+ /* write seg_size */
+ writel_relaxed(sreq->size, pce_dev->iobase + CRYPTO_SEG_SIZE_REG);
+
+ writel_relaxed(pce_dev->reg.crypto_cfg_le, (pce_dev->iobase +
+ CRYPTO_CONFIG_REG));
+ /* issue go to crypto */
+ if (use_hw_key == false)
+ writel_relaxed(((1 << CRYPTO_GO) | (1 << CRYPTO_RESULTS_DUMP)),
+ pce_dev->iobase + CRYPTO_GOPROC_REG);
+ else
+ writel_relaxed(((1 << CRYPTO_GO) | (1 << CRYPTO_RESULTS_DUMP)),
+ pce_dev->iobase + CRYPTO_GOPROC_QC_KEY_REG);
+ /*
+ * Ensure previous instructions (setting the GO register)
+ * was completed before issuing a DMA transfer request
+ */
+ mb();
+ return 0;
+}
+
+static int _ce_setup_cipher_direct(struct qce_device *pce_dev,
+ struct qce_req *creq, uint32_t totallen_in, uint32_t coffset)
+{
+ uint32_t enckey32[(MAX_CIPHER_KEY_SIZE * 2)/sizeof(uint32_t)] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ uint32_t enciv32[MAX_IV_LENGTH / sizeof(uint32_t)] = {
+ 0, 0, 0, 0};
+ uint32_t enck_size_in_word = 0;
+ uint32_t key_size;
+ bool use_hw_key = false;
+ bool use_pipe_key = false;
+ uint32_t encr_cfg = 0;
+ uint32_t ivsize = creq->ivsize;
+ int i;
+
+ /* clear status */
+ writel_relaxed(0, pce_dev->iobase + CRYPTO_STATUS_REG);
+
+ writel_relaxed(pce_dev->reg.crypto_cfg_be, (pce_dev->iobase +
+ CRYPTO_CONFIG_REG));
+ /*
+ * Ensure previous instructions (setting the CONFIG register)
+ * was completed before issuing starting to set other config register
+ * This is to ensure the configurations are done in correct endian-ness
+ * as set in the CONFIG registers
+ */
+ mb();
+
+ if (creq->mode == QCE_MODE_XTS)
+ key_size = creq->encklen/2;
+ else
+ key_size = creq->encklen;
+
+ if ((creq->flags & QCRYPTO_CTX_USE_HW_KEY) == QCRYPTO_CTX_USE_HW_KEY) {
+ use_hw_key = true;
+ } else {
+ if ((creq->flags & QCRYPTO_CTX_USE_PIPE_KEY) ==
+ QCRYPTO_CTX_USE_PIPE_KEY)
+ use_pipe_key = true;
+ }
+ if ((use_pipe_key == false) && (use_hw_key == false)) {
+ _byte_stream_to_net_words(enckey32, creq->enckey, key_size);
+ enck_size_in_word = key_size/sizeof(uint32_t);
+ }
+ if ((creq->op == QCE_REQ_AEAD) && (creq->mode == QCE_MODE_CCM)) {
+ uint32_t authklen32 = creq->encklen/sizeof(uint32_t);
+ uint32_t noncelen32 = MAX_NONCE/sizeof(uint32_t);
+ uint32_t nonce32[MAX_NONCE/sizeof(uint32_t)] = {0, 0, 0, 0};
+ uint32_t auth_cfg = 0;
+
+ /* Clear auth_ivn, auth_keyn registers */
+ for (i = 0; i < 16; i++) {
+ writel_relaxed(0, (pce_dev->iobase +
+ (CRYPTO_AUTH_IV0_REG + i*sizeof(uint32_t))));
+ writel_relaxed(0, (pce_dev->iobase +
+ (CRYPTO_AUTH_KEY0_REG + i*sizeof(uint32_t))));
+ }
+ /* write auth_bytecnt 0/1/2/3, start with 0 */
+ for (i = 0; i < 4; i++)
+ writel_relaxed(0, pce_dev->iobase +
+ CRYPTO_AUTH_BYTECNT0_REG +
+ i * sizeof(uint32_t));
+ /* write nonce */
+ _byte_stream_to_net_words(nonce32, creq->nonce, MAX_NONCE);
+ for (i = 0; i < noncelen32; i++)
+ writel_relaxed(nonce32[i], pce_dev->iobase +
+ CRYPTO_AUTH_INFO_NONCE0_REG +
+ (i*sizeof(uint32_t)));
+
+ if (creq->authklen == AES128_KEY_SIZE)
+ auth_cfg = pce_dev->reg.auth_cfg_aes_ccm_128;
+ else {
+ if (creq->authklen == AES256_KEY_SIZE)
+ auth_cfg = pce_dev->reg.auth_cfg_aes_ccm_256;
+ }
+ if (creq->dir == QCE_ENCRYPT)
+ auth_cfg |= (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS);
+ else
+ auth_cfg |= (CRYPTO_AUTH_POS_AFTER << CRYPTO_AUTH_POS);
+ auth_cfg |= ((creq->authsize - 1) << CRYPTO_AUTH_SIZE);
+
+ if (use_hw_key == true) {
+ auth_cfg |= (1 << CRYPTO_USE_HW_KEY_AUTH);
+ } else {
+ auth_cfg &= ~(1 << CRYPTO_USE_HW_KEY_AUTH);
+ /* write auth key */
+ for (i = 0; i < authklen32; i++)
+ writel_relaxed(enckey32[i], pce_dev->iobase +
+ CRYPTO_AUTH_KEY0_REG + (i*sizeof(uint32_t)));
+ }
+ writel_relaxed(auth_cfg, pce_dev->iobase +
+ CRYPTO_AUTH_SEG_CFG_REG);
+ if (creq->dir == QCE_ENCRYPT)
+ writel_relaxed(totallen_in, pce_dev->iobase +
+ CRYPTO_AUTH_SEG_SIZE_REG);
+ else
+ writel_relaxed((totallen_in - creq->authsize),
+ pce_dev->iobase + CRYPTO_AUTH_SEG_SIZE_REG);
+ writel_relaxed(0, pce_dev->iobase + CRYPTO_AUTH_SEG_START_REG);
+ } else {
+ if (creq->op != QCE_REQ_AEAD)
+ writel_relaxed(0, pce_dev->iobase +
+ CRYPTO_AUTH_SEG_CFG_REG);
+ }
+ /*
+ * Ensure previous instructions (write to all AUTH registers)
+ * was completed before accessing a register that is not in
+ * in the same 1K range.
+ */
+ mb();
+ switch (creq->mode) {
+ case QCE_MODE_ECB:
+ if (key_size == AES128_KEY_SIZE)
+ encr_cfg = pce_dev->reg.encr_cfg_aes_ecb_128;
+ else
+ encr_cfg = pce_dev->reg.encr_cfg_aes_ecb_256;
+ break;
+ case QCE_MODE_CBC:
+ if (key_size == AES128_KEY_SIZE)
+ encr_cfg = pce_dev->reg.encr_cfg_aes_cbc_128;
+ else
+ encr_cfg = pce_dev->reg.encr_cfg_aes_cbc_256;
+ break;
+ case QCE_MODE_XTS:
+ if (key_size == AES128_KEY_SIZE)
+ encr_cfg = pce_dev->reg.encr_cfg_aes_xts_128;
+ else
+ encr_cfg = pce_dev->reg.encr_cfg_aes_xts_256;
+ break;
+ case QCE_MODE_CCM:
+ if (key_size == AES128_KEY_SIZE)
+ encr_cfg = pce_dev->reg.encr_cfg_aes_ccm_128;
+ else
+ encr_cfg = pce_dev->reg.encr_cfg_aes_ccm_256;
+ break;
+ case QCE_MODE_CTR:
+ default:
+ if (key_size == AES128_KEY_SIZE)
+ encr_cfg = pce_dev->reg.encr_cfg_aes_ctr_128;
+ else
+ encr_cfg = pce_dev->reg.encr_cfg_aes_ctr_256;
+ break;
+ }
+ pce_dev->mode = creq->mode;
+
+ switch (creq->alg) {
+ case CIPHER_ALG_DES:
+ if (creq->mode != QCE_MODE_ECB) {
+ encr_cfg = pce_dev->reg.encr_cfg_des_cbc;
+ _byte_stream_to_net_words(enciv32, creq->iv, ivsize);
+ writel_relaxed(enciv32[0], pce_dev->iobase +
+ CRYPTO_CNTR0_IV0_REG);
+ writel_relaxed(enciv32[1], pce_dev->iobase +
+ CRYPTO_CNTR1_IV1_REG);
+ } else {
+ encr_cfg = pce_dev->reg.encr_cfg_des_ecb;
+ }
+ if (use_hw_key == false) {
+ writel_relaxed(enckey32[0], pce_dev->iobase +
+ CRYPTO_ENCR_KEY0_REG);
+ writel_relaxed(enckey32[1], pce_dev->iobase +
+ CRYPTO_ENCR_KEY1_REG);
+ }
+ break;
+ case CIPHER_ALG_3DES:
+ if (creq->mode != QCE_MODE_ECB) {
+ _byte_stream_to_net_words(enciv32, creq->iv, ivsize);
+ writel_relaxed(enciv32[0], pce_dev->iobase +
+ CRYPTO_CNTR0_IV0_REG);
+ writel_relaxed(enciv32[1], pce_dev->iobase +
+ CRYPTO_CNTR1_IV1_REG);
+ encr_cfg = pce_dev->reg.encr_cfg_3des_cbc;
+ } else {
+ encr_cfg = pce_dev->reg.encr_cfg_3des_ecb;
+ }
+ if (use_hw_key == false) {
+ /* write encr key */
+ for (i = 0; i < 6; i++)
+ writel_relaxed(enckey32[0], (pce_dev->iobase +
+ (CRYPTO_ENCR_KEY0_REG + i * sizeof(uint32_t))));
+ }
+ break;
+ case CIPHER_ALG_AES:
+ default:
+ if (creq->mode == QCE_MODE_XTS) {
+ uint32_t xtskey32[MAX_CIPHER_KEY_SIZE/sizeof(uint32_t)]
+ = {0, 0, 0, 0, 0, 0, 0, 0};
+ uint32_t xtsklen =
+ creq->encklen/(2 * sizeof(uint32_t));
+
+ if ((use_hw_key == false) && (use_pipe_key == false)) {
+ _byte_stream_to_net_words(xtskey32,
+ (creq->enckey + creq->encklen/2),
+ creq->encklen/2);
+ /* write xts encr key */
+ for (i = 0; i < xtsklen; i++)
+ writel_relaxed(xtskey32[i],
+ pce_dev->iobase +
+ CRYPTO_ENCR_XTS_KEY0_REG +
+ (i * sizeof(uint32_t)));
+ }
+ /* write xts du size */
+ if (use_pipe_key == true)
+ writel_relaxed(min((uint32_t)QCE_SECTOR_SIZE,
+ creq->cryptlen),
+ pce_dev->iobase +
+ CRYPTO_ENCR_XTS_DU_SIZE_REG);
+ else
+ writel_relaxed(creq->cryptlen ,
+ pce_dev->iobase +
+ CRYPTO_ENCR_XTS_DU_SIZE_REG);
+ }
+ if (creq->mode != QCE_MODE_ECB) {
+ if (creq->mode == QCE_MODE_XTS)
+ _byte_stream_swap_to_net_words(enciv32,
+ creq->iv, ivsize);
+ else
+ _byte_stream_to_net_words(enciv32, creq->iv,
+ ivsize);
+
+ /* write encr cntr iv */
+ for (i = 0; i <= 3; i++)
+ writel_relaxed(enciv32[i], pce_dev->iobase +
+ CRYPTO_CNTR0_IV0_REG +
+ (i * sizeof(uint32_t)));
+
+ if (creq->mode == QCE_MODE_CCM) {
+ /* write cntr iv for ccm */
+ for (i = 0; i <= 3; i++)
+ writel_relaxed(enciv32[i],
+ pce_dev->iobase +
+ CRYPTO_ENCR_CCM_INT_CNTR0_REG +
+ (i * sizeof(uint32_t)));
+ /* update cntr_iv[3] by one */
+ writel_relaxed((enciv32[3] + 1),
+ pce_dev->iobase +
+ CRYPTO_CNTR0_IV0_REG +
+ (3 * sizeof(uint32_t)));
+ }
+ }
+
+ if (creq->op == QCE_REQ_ABLK_CIPHER_NO_KEY) {
+ encr_cfg |= (CRYPTO_ENCR_KEY_SZ_AES128 <<
+ CRYPTO_ENCR_KEY_SZ);
+ } else {
+ if ((use_hw_key == false) && (use_pipe_key == false)) {
+ for (i = 0; i < enck_size_in_word; i++)
+ writel_relaxed(enckey32[i],
+ pce_dev->iobase +
+ CRYPTO_ENCR_KEY0_REG +
+ (i * sizeof(uint32_t)));
+ }
+ } /* else of if (creq->op == QCE_REQ_ABLK_CIPHER_NO_KEY) */
+ break;
+ } /* end of switch (creq->mode) */
+
+ if (use_pipe_key)
+ encr_cfg |= (CRYPTO_USE_PIPE_KEY_ENCR_ENABLED
+ << CRYPTO_USE_PIPE_KEY_ENCR);
+
+ /* write encr seg cfg */
+ encr_cfg |= ((creq->dir == QCE_ENCRYPT) ? 1 : 0) << CRYPTO_ENCODE;
+ if (use_hw_key == true)
+ encr_cfg |= (CRYPTO_USE_HW_KEY << CRYPTO_USE_HW_KEY_ENCR);
+ else
+ encr_cfg &= ~(CRYPTO_USE_HW_KEY << CRYPTO_USE_HW_KEY_ENCR);
+ /* write encr seg cfg */
+ writel_relaxed(encr_cfg, pce_dev->iobase + CRYPTO_ENCR_SEG_CFG_REG);
+
+ /* write encr seg size */
+ if ((creq->mode == QCE_MODE_CCM) && (creq->dir == QCE_DECRYPT))
+ writel_relaxed((creq->cryptlen + creq->authsize),
+ pce_dev->iobase + CRYPTO_ENCR_SEG_SIZE_REG);
+ else
+ writel_relaxed(creq->cryptlen,
+ pce_dev->iobase + CRYPTO_ENCR_SEG_SIZE_REG);
+
+ /* write encr seg start */
+ writel_relaxed((coffset & 0xffff),
+ pce_dev->iobase + CRYPTO_ENCR_SEG_START_REG);
+ /* write encr seg start */
+ writel_relaxed(0xffffffff,
+ pce_dev->iobase + CRYPTO_CNTR_MASK_REG);
+
+ /* write seg size */
+ writel_relaxed(totallen_in, pce_dev->iobase + CRYPTO_SEG_SIZE_REG);
+
+ writel_relaxed(pce_dev->reg.crypto_cfg_le, (pce_dev->iobase +
+ CRYPTO_CONFIG_REG));
+ /* issue go to crypto */
+ if (use_hw_key == false)
+ writel_relaxed(((1 << CRYPTO_GO) | (1 << CRYPTO_RESULTS_DUMP)),
+ pce_dev->iobase + CRYPTO_GOPROC_REG);
+ else
+ writel_relaxed(((1 << CRYPTO_GO) | (1 << CRYPTO_RESULTS_DUMP)),
+ pce_dev->iobase + CRYPTO_GOPROC_QC_KEY_REG);
+ /*
+ * Ensure previous instructions (setting the GO register)
+ * was completed before issuing a DMA transfer request
+ */
+ mb();
+ return 0;
+};
+
static int _qce_unlock_other_pipes(struct qce_device *pce_dev)
{
int rc = 0;
+ if (pce_dev->support_cmd_dscr == false)
+ return rc;
+
pce_dev->ce_sps.consumer.event.callback = NULL;
rc = sps_transfer_one(pce_dev->ce_sps.consumer.pipe,
GET_PHYS_ADDR(pce_dev->ce_sps.cmdlistptr.unlock_all_pipes.cmdlist),
@@ -725,6 +1207,7 @@
{
struct aead_request *areq;
unsigned char mac[SHA256_DIGEST_SIZE];
+ uint32_t status;
areq = (struct aead_request *) pce_dev->areq;
if (areq->src != areq->dst) {
@@ -739,16 +1222,55 @@
/* check MAC */
memcpy(mac, (char *)(&pce_dev->ce_sps.result->auth_iv[0]),
SHA256_DIGEST_SIZE);
+
+ /* read status before unlock */
+ status = readl_relaxed(pce_dev->iobase + CRYPTO_STATUS_REG);
+
if (_qce_unlock_other_pipes(pce_dev))
return -EINVAL;
if (pce_dev->mode == QCE_MODE_CCM) {
- uint32_t result_status;
+ int32_t result_status;
+
+ /*
+ * Don't use result dump status. The operation may not
+ * be complete.
+ * Instead, use the status we just read of device.
+ * In case, we need to use result_status from result
+ * dump the result_status needs to be byte swapped,
+ * since we set the device to little endian.
+ */
+
result_status = pce_dev->ce_sps.result->status;
- result_status &= (1 << CRYPTO_MAC_FAILED);
- result_status |= (pce_dev->ce_sps.consumer_status |
- pce_dev->ce_sps.producer_status);
+ pce_dev->ce_sps.result->status = 0;
+
+ if (status & ((1 << CRYPTO_SW_ERR) | (1 << CRYPTO_AXI_ERR)
+ | (1 << CRYPTO_HSD_ERR))) {
+
+ pr_err("aead operation error. Status %x\n",
+ status);
+ result_status = -ENXIO;
+ } else if (pce_dev->ce_sps.consumer_status |
+ pce_dev->ce_sps.producer_status) {
+ pr_err("aead sps operation error. sps status %x %x\n",
+ pce_dev->ce_sps.consumer_status,
+ pce_dev->ce_sps.producer_status);
+ result_status = -ENXIO;
+ } else if ((status & (1 << CRYPTO_OPERATION_DONE)) == 0) {
+ pr_err("aead operation not done? Status %x, sps status %x %x\n",
+ status,
+ pce_dev->ce_sps.consumer_status,
+ pce_dev->ce_sps.producer_status);
+ result_status = -ENXIO;
+
+ } else if (status & (1 << CRYPTO_MAC_FAILED)) {
+ result_status = -EBADMSG;
+ } else {
+ result_status = 0;
+ }
+
pce_dev->qce_cb(areq, mac, NULL, result_status);
+
} else {
uint32_t ivsize = 0;
struct crypto_aead *aead;
@@ -772,6 +1294,8 @@
struct ahash_request *areq;
unsigned char digest[SHA256_DIGEST_SIZE];
uint32_t bytecount32[2];
+ int32_t result_status = pce_dev->ce_sps.result->status;
+ uint32_t status;
areq = (struct ahash_request *) pce_dev->areq;
qce_dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
@@ -781,10 +1305,39 @@
_byte_stream_to_net_words(bytecount32,
(unsigned char *)pce_dev->ce_sps.result->auth_byte_count,
2 * CRYPTO_REG_SIZE);
+
+ /* read status before unlock */
+ status = readl_relaxed(pce_dev->iobase + CRYPTO_STATUS_REG);
+
if (_qce_unlock_other_pipes(pce_dev))
return -EINVAL;
+
+ /*
+ * Don't use result dump status. The operation may not be complete.
+ * Instead, use the status we just read of device.
+ * In case, we need to use result_status from result
+ * dump the result_status needs to be byte swapped,
+ * since we set the device to little endian.
+ */
+
+ if (status & ((1 << CRYPTO_SW_ERR) | (1 << CRYPTO_AXI_ERR)
+ | (1 << CRYPTO_HSD_ERR))) {
+
+ pr_err("sha operation error. Status %x\n", status);
+ result_status = -ENXIO;
+ } else if (pce_dev->ce_sps.consumer_status) {
+ pr_err("sha sps operation error. sps status %x\n",
+ pce_dev->ce_sps.consumer_status);
+ result_status = -ENXIO;
+ } else if ((status & (1 << CRYPTO_OPERATION_DONE)) == 0) {
+ pr_err("sha operation not done? Status %x, sps status %x\n",
+ status, pce_dev->ce_sps.consumer_status);
+ result_status = -ENXIO;
+ } else {
+ result_status = 0;
+ }
pce_dev->qce_cb(areq, digest, (char *)bytecount32,
- pce_dev->ce_sps.consumer_status);
+ result_status);
return 0;
};
@@ -792,6 +1345,8 @@
{
struct ablkcipher_request *areq;
unsigned char iv[NUM_OF_CRYPTO_CNTR_IV_REG * CRYPTO_REG_SIZE];
+ uint32_t status;
+ int32_t result_status;
areq = (struct ablkcipher_request *) pce_dev->areq;
@@ -802,13 +1357,46 @@
qce_dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
DMA_TO_DEVICE);
+
+ /* read status before unlock */
+ status = readl_relaxed(pce_dev->iobase + CRYPTO_STATUS_REG);
+
if (_qce_unlock_other_pipes(pce_dev))
return -EINVAL;
+ /*
+ * Don't use result dump status. The operation may not be complete.
+ * Instead, use the status we just read of device.
+ * In case, we need to use result_status from result
+ * dump the result_status needs to be byte swapped,
+ * since we set the device to little endian.
+ */
+ if (status & ((1 << CRYPTO_SW_ERR) | (1 << CRYPTO_AXI_ERR)
+ | (1 << CRYPTO_HSD_ERR))) {
+ pr_err("ablk_cipher operation error. Status %x\n",
+ status);
+ result_status = -ENXIO;
+ } else if (pce_dev->ce_sps.consumer_status |
+ pce_dev->ce_sps.producer_status) {
+ pr_err("ablk_cipher sps operation error. sps status %x %x\n",
+ pce_dev->ce_sps.consumer_status,
+ pce_dev->ce_sps.producer_status);
+ result_status = -ENXIO;
+ } else if ((status & (1 << CRYPTO_OPERATION_DONE)) == 0) {
+ pr_err("ablk_cipher operation not done? Status %x, sps status %x %x\n",
+ status,
+ pce_dev->ce_sps.consumer_status,
+ pce_dev->ce_sps.producer_status);
+ result_status = -ENXIO;
+
+ } else {
+ result_status = 0;
+ }
+
if (pce_dev->mode == QCE_MODE_ECB) {
pce_dev->qce_cb(areq, NULL, NULL,
pce_dev->ce_sps.consumer_status |
- pce_dev->ce_sps.producer_status);
+ result_status);
} else {
if (pce_dev->ce_sps.minor_version == 0) {
if (pce_dev->mode == QCE_MODE_CBC) {
@@ -854,9 +1442,7 @@
(char *)(pce_dev->ce_sps.result->encr_cntr_iv),
sizeof(iv));
}
- pce_dev->qce_cb(areq, NULL, iv,
- pce_dev->ce_sps.consumer_status |
- pce_dev->ce_sps.producer_status);
+ pce_dev->qce_cb(areq, NULL, iv, result_status);
}
return 0;
};
@@ -906,6 +1492,45 @@
}
#endif
+
+static void _qce_dump_descr_fifos_fail(struct qce_device *pce_dev)
+{
+ int i, j, ents;
+ struct sps_iovec *iovec = pce_dev->ce_sps.in_transfer.iovec;
+ uint32_t cmd_flags = SPS_IOVEC_FLAG_CMD;
+
+ printk(KERN_INFO "==============================================\n");
+ printk(KERN_INFO "CONSUMER (TX/IN/DEST) PIPE DESCRIPTOR\n");
+ printk(KERN_INFO "==============================================\n");
+ for (i = 0; i < pce_dev->ce_sps.in_transfer.iovec_count; i++) {
+ printk(KERN_INFO " [%d] addr=0x%x size=0x%x flags=0x%x\n", i,
+ iovec->addr, iovec->size, iovec->flags);
+ if (iovec->flags & cmd_flags) {
+ struct sps_command_element *pced;
+
+ pced = (struct sps_command_element *)
+ (GET_VIRT_ADDR(iovec->addr));
+ ents = iovec->size/(sizeof(struct sps_command_element));
+ for (j = 0; j < ents; j++) {
+ printk(KERN_INFO " [%d] [0x%x] 0x%x\n", j,
+ pced->addr, pced->data);
+ pced++;
+ }
+ }
+ iovec++;
+ }
+
+ printk(KERN_INFO "==============================================\n");
+ printk(KERN_INFO "PRODUCER (RX/OUT/SRC) PIPE DESCRIPTOR\n");
+ printk(KERN_INFO "==============================================\n");
+ iovec = pce_dev->ce_sps.out_transfer.iovec;
+ for (i = 0; i < pce_dev->ce_sps.out_transfer.iovec_count; i++) {
+ printk(KERN_INFO " [%d] addr=0x%x size=0x%x flags=0x%x\n", i,
+ iovec->addr, iovec->size, iovec->flags);
+ iovec++;
+ }
+}
+
static void _qce_sps_iovec_count_init(struct qce_device *pce_dev)
{
pce_dev->ce_sps.in_transfer.iovec_count = 0;
@@ -1004,6 +1629,7 @@
if (rc) {
pr_err("sps_xfr() fail (consumer pipe=0x%x) rc = %d,",
(u32)pce_dev->ce_sps.consumer.pipe, rc);
+ _qce_dump_descr_fifos_fail(pce_dev);
return rc;
}
rc = sps_transfer(pce_dev->ce_sps.producer.pipe,
@@ -1216,7 +1842,14 @@
bam.summing_threshold = 64;
/* SPS driver wll handle the crypto BAM IRQ */
bam.irq = (u32)pce_dev->ce_sps.bam_irq;
- bam.manage = SPS_BAM_MGR_LOCAL;
+ /*
+ * Set flag to indicate BAM global device control is managed
+ * remotely.
+ */
+ if ((pce_dev->support_cmd_dscr == false) || (pce_dev->is_shared))
+ bam.manage = SPS_BAM_MGR_DEVICE_REMOTE;
+ else
+ bam.manage = SPS_BAM_MGR_LOCAL;
bam.ee = 1;
pr_debug("bam physical base=0x%x\n", (u32)bam.phys_addr);
@@ -1411,20 +2044,11 @@
uint32_t key_reg = 0;
uint32_t xts_key_reg = 0;
uint32_t iv_reg = 0;
- uint32_t crypto_cfg = 0;
- uint32_t beats = (pdev->ce_sps.ce_burst_size >> 3) - 1;
- uint32_t pipe_pair = pdev->ce_sps.pipe_pair_index;
*pvaddr = (unsigned char *) ALIGN(((unsigned int)(*pvaddr)),
pdev->ce_sps.ce_burst_size);
ce_vaddr = (struct sps_command_element *)(*pvaddr);
ce_vaddr_start = (uint32_t)(*pvaddr);
- crypto_cfg = (beats << CRYPTO_REQ_SIZE) |
- BIT(CRYPTO_MASK_DOUT_INTR) |
- BIT(CRYPTO_MASK_DIN_INTR) |
- BIT(CRYPTO_MASK_OP_DONE_INTR) |
- (0 << CRYPTO_HIGH_SPD_EN_N) |
- (pipe_pair << CRYPTO_PIPE_SET_SELECT);
/*
* Designate chunks of the allocated memory to various
* command list pointers related to AES cipher operations defined
@@ -1437,11 +2061,10 @@
cmdlistptr->cipher_aes_128_cbc_ctr.cmdlist =
(uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->cipher_aes_128_cbc_ctr);
-
- encr_cfg = (CRYPTO_ENCR_KEY_SZ_AES128 <<
- CRYPTO_ENCR_KEY_SZ) |
- (CRYPTO_ENCR_ALG_AES <<
- CRYPTO_ENCR_ALG);
+ if (mode == QCE_MODE_CBC)
+ encr_cfg = pdev->reg.encr_cfg_aes_cbc_128;
+ else
+ encr_cfg = pdev->reg.encr_cfg_aes_ctr_128;
iv_reg = 4;
key_reg = 4;
xts_key_reg = 0;
@@ -1450,10 +2073,10 @@
(uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->cipher_aes_256_cbc_ctr);
- encr_cfg = (CRYPTO_ENCR_KEY_SZ_AES256 <<
- CRYPTO_ENCR_KEY_SZ) |
- (CRYPTO_ENCR_ALG_AES <<
- CRYPTO_ENCR_ALG);
+ if (mode == QCE_MODE_CBC)
+ encr_cfg = pdev->reg.encr_cfg_aes_cbc_256;
+ else
+ encr_cfg = pdev->reg.encr_cfg_aes_ctr_256;
iv_reg = 4;
key_reg = 8;
xts_key_reg = 0;
@@ -1465,12 +2088,7 @@
(uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->cipher_aes_128_ecb);
- encr_cfg = (CRYPTO_ENCR_KEY_SZ_AES128 <<
- CRYPTO_ENCR_KEY_SZ) |
- (CRYPTO_ENCR_ALG_AES <<
- CRYPTO_ENCR_ALG) |
- (CRYPTO_ENCR_MODE_ECB <<
- CRYPTO_ENCR_MODE);
+ encr_cfg = pdev->reg.encr_cfg_aes_ecb_128;
iv_reg = 0;
key_reg = 4;
xts_key_reg = 0;
@@ -1479,12 +2097,7 @@
(uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->cipher_aes_256_ecb);
- encr_cfg = (CRYPTO_ENCR_KEY_SZ_AES256 <<
- CRYPTO_ENCR_KEY_SZ) |
- (CRYPTO_ENCR_ALG_AES <<
- CRYPTO_ENCR_ALG) |
- (CRYPTO_ENCR_MODE_ECB <<
- CRYPTO_ENCR_MODE);
+ encr_cfg = pdev->reg.encr_cfg_aes_ecb_256;
iv_reg = 0;
key_reg = 8;
xts_key_reg = 0;
@@ -1496,12 +2109,7 @@
(uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->cipher_aes_128_xts);
- encr_cfg = (CRYPTO_ENCR_KEY_SZ_AES128 <<
- CRYPTO_ENCR_KEY_SZ) |
- (CRYPTO_ENCR_ALG_AES <<
- CRYPTO_ENCR_ALG) |
- (CRYPTO_ENCR_MODE_XTS <<
- CRYPTO_ENCR_MODE);
+ encr_cfg = pdev->reg.encr_cfg_aes_xts_128;
iv_reg = 4;
key_reg = 4;
xts_key_reg = 4;
@@ -1510,12 +2118,7 @@
(uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->cipher_aes_256_xts);
- encr_cfg = (CRYPTO_ENCR_KEY_SZ_AES256 <<
- CRYPTO_ENCR_KEY_SZ) |
- (CRYPTO_ENCR_ALG_AES <<
- CRYPTO_ENCR_ALG) |
- (CRYPTO_ENCR_MODE_XTS <<
- CRYPTO_ENCR_MODE);
+ encr_cfg = pdev->reg.encr_cfg_aes_xts_256;
iv_reg = 4;
key_reg = 8;
xts_key_reg = 8;
@@ -1528,8 +2131,11 @@
break;
}
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG, crypto_cfg,
- &pcl_info->crypto_cfg);
+ /* clear status register */
+ qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_STATUS_REG, 0, NULL);
+
+ qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
+ pdev->reg.crypto_cfg_be, &pcl_info->crypto_cfg);
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_SEG_SIZE_REG, 0,
&pcl_info->seg_size);
@@ -1572,18 +2178,14 @@
if (mode == QCE_MODE_XTS) {
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_SIZE_REG,
0, &pcl_info->auth_seg_size);
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_CFG_REG,
- 0, &pcl_info->auth_seg_size);
} else {
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_SIZE_REG,
0, &pcl_info->auth_seg_size);
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_CFG_REG,
- 0, &pcl_info->auth_seg_size);
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_START_REG,
0, &pcl_info->auth_seg_size);
}
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
- (crypto_cfg | CRYPTO_LITTLE_ENDIAN_MASK), NULL);
+ pdev->reg.crypto_cfg_le, NULL);
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_GOPROC_REG,
((1 << CRYPTO_GO) | (1 << CRYPTO_RESULTS_DUMP)),
@@ -1608,20 +2210,11 @@
uint32_t encr_cfg = 0;
uint32_t key_reg = 0;
uint32_t iv_reg = 0;
- uint32_t crypto_cfg = 0;
- uint32_t beats = (pdev->ce_sps.ce_burst_size >> 3) - 1;
- uint32_t pipe_pair = pdev->ce_sps.pipe_pair_index;
*pvaddr = (unsigned char *) ALIGN(((unsigned int)(*pvaddr)),
pdev->ce_sps.ce_burst_size);
ce_vaddr = (struct sps_command_element *)(*pvaddr);
ce_vaddr_start = (uint32_t)(*pvaddr);
- crypto_cfg = (beats << CRYPTO_REQ_SIZE) |
- BIT(CRYPTO_MASK_DOUT_INTR) |
- BIT(CRYPTO_MASK_DIN_INTR) |
- BIT(CRYPTO_MASK_OP_DONE_INTR) |
- (0 << CRYPTO_HIGH_SPD_EN_N) |
- (pipe_pair << CRYPTO_PIPE_SET_SELECT);
/*
* Designate chunks of the allocated memory to various
@@ -1635,12 +2228,8 @@
(uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->cipher_des_cbc);
- encr_cfg = (CRYPTO_ENCR_KEY_SZ_DES <<
- CRYPTO_ENCR_KEY_SZ) |
- (CRYPTO_ENCR_ALG_DES <<
- CRYPTO_ENCR_ALG) |
- (CRYPTO_ENCR_MODE_CBC <<
- CRYPTO_ENCR_MODE);
+
+ encr_cfg = pdev->reg.encr_cfg_des_cbc;
iv_reg = 2;
key_reg = 2;
} else {
@@ -1648,12 +2237,7 @@
(uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->cipher_des_ecb);
- encr_cfg = (CRYPTO_ENCR_KEY_SZ_DES <<
- CRYPTO_ENCR_KEY_SZ) |
- (CRYPTO_ENCR_ALG_DES <<
- CRYPTO_ENCR_ALG) |
- (CRYPTO_ENCR_MODE_ECB <<
- CRYPTO_ENCR_MODE);
+ encr_cfg = pdev->reg.encr_cfg_des_ecb;
iv_reg = 0;
key_reg = 2;
}
@@ -1664,12 +2248,7 @@
(uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->cipher_3des_cbc);
- encr_cfg = (CRYPTO_ENCR_KEY_SZ_3DES <<
- CRYPTO_ENCR_KEY_SZ) |
- (CRYPTO_ENCR_ALG_DES <<
- CRYPTO_ENCR_ALG) |
- (CRYPTO_ENCR_MODE_CBC <<
- CRYPTO_ENCR_MODE);
+ encr_cfg = pdev->reg.encr_cfg_3des_cbc;
iv_reg = 2;
key_reg = 6;
} else {
@@ -1677,12 +2256,7 @@
(uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->cipher_3des_ecb);
- encr_cfg = (CRYPTO_ENCR_KEY_SZ_3DES <<
- CRYPTO_ENCR_KEY_SZ) |
- (CRYPTO_ENCR_ALG_DES <<
- CRYPTO_ENCR_ALG) |
- (CRYPTO_ENCR_MODE_ECB <<
- CRYPTO_ENCR_MODE);
+ encr_cfg = pdev->reg.encr_cfg_3des_ecb;
iv_reg = 0;
key_reg = 6;
}
@@ -1693,8 +2267,11 @@
break;
}
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG, crypto_cfg,
- &pcl_info->crypto_cfg);
+ /* clear status register */
+ qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_STATUS_REG, 0, NULL);
+
+ qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
+ pdev->reg.crypto_cfg_be, &pcl_info->crypto_cfg);
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_SEG_SIZE_REG, 0,
&pcl_info->seg_size);
@@ -1719,16 +2296,9 @@
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CNTR1_IV1_REG, 0,
NULL);
}
- /* Add dummy to align size to burst-size multiple */
- if (!mode_cbc) {
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_SIZE_REG,
- 0, &pcl_info->auth_seg_size);
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_CFG_REG,
- 0, &pcl_info->auth_seg_size);
- }
+
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
- (crypto_cfg | CRYPTO_LITTLE_ENDIAN_MASK),
- NULL);
+ pdev->reg.crypto_cfg_le, NULL);
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_GOPROC_REG,
((1 << CRYPTO_GO) | (1 << CRYPTO_RESULTS_DUMP)),
@@ -1752,20 +2322,12 @@
uint32_t key_reg = 0;
uint32_t auth_cfg = 0;
uint32_t iv_reg = 0;
- uint32_t crypto_cfg = 0;
- uint32_t beats = (pdev->ce_sps.ce_burst_size >> 3) - 1;
- uint32_t pipe_pair = pdev->ce_sps.pipe_pair_index;
*pvaddr = (unsigned char *) ALIGN(((unsigned int)(*pvaddr)),
pdev->ce_sps.ce_burst_size);
ce_vaddr_start = (uint32_t)(*pvaddr);
ce_vaddr = (struct sps_command_element *)(*pvaddr);
- crypto_cfg = (beats << CRYPTO_REQ_SIZE) |
- BIT(CRYPTO_MASK_DOUT_INTR) |
- BIT(CRYPTO_MASK_DIN_INTR) |
- BIT(CRYPTO_MASK_OP_DONE_INTR) |
- (0 << CRYPTO_HIGH_SPD_EN_N) |
- (pipe_pair << CRYPTO_PIPE_SET_SELECT);
+
/*
* Designate chunks of the allocated memory to various
* command list pointers related to authentication operations
@@ -1776,87 +2338,84 @@
cmdlistptr->auth_sha1.cmdlist = (uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->auth_sha1);
- auth_cfg = (CRYPTO_AUTH_MODE_HASH << CRYPTO_AUTH_MODE)|
- (CRYPTO_AUTH_SIZE_SHA1 << CRYPTO_AUTH_SIZE) |
- (CRYPTO_AUTH_ALG_SHA << CRYPTO_AUTH_ALG) |
- (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS);
+ auth_cfg = pdev->reg.auth_cfg_sha1;
iv_reg = 5;
+
+ /* clear status register */
+ qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_STATUS_REG,
+ 0, NULL);
+
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
- crypto_cfg, &pcl_info->crypto_cfg);
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_CFG_REG,
- 0, NULL);
+ pdev->reg.crypto_cfg_be, &pcl_info->crypto_cfg);
break;
case QCE_HASH_SHA256:
cmdlistptr->auth_sha256.cmdlist = (uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->auth_sha256);
- auth_cfg = (CRYPTO_AUTH_MODE_HASH << CRYPTO_AUTH_MODE)|
- (CRYPTO_AUTH_SIZE_SHA256 << CRYPTO_AUTH_SIZE) |
- (CRYPTO_AUTH_ALG_SHA << CRYPTO_AUTH_ALG) |
- (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS);
+ auth_cfg = pdev->reg.auth_cfg_sha256;
iv_reg = 8;
+
+ /* clear status register */
+ qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_STATUS_REG,
+ 0, NULL);
+
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
- crypto_cfg, &pcl_info->crypto_cfg);
+ pdev->reg.crypto_cfg_be, &pcl_info->crypto_cfg);
/* 1 dummy write */
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_ENCR_SEG_SIZE_REG,
0, NULL);
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_CFG_REG,
- 0, NULL);
break;
case QCE_HASH_SHA1_HMAC:
cmdlistptr->auth_sha1_hmac.cmdlist = (uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->auth_sha1_hmac);
- auth_cfg = (CRYPTO_AUTH_MODE_HMAC << CRYPTO_AUTH_MODE)|
- (CRYPTO_AUTH_SIZE_SHA1 << CRYPTO_AUTH_SIZE) |
- (CRYPTO_AUTH_ALG_SHA << CRYPTO_AUTH_ALG) |
- (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS);
+ auth_cfg = pdev->reg.auth_cfg_hmac_sha1;
key_reg = 16;
iv_reg = 5;
+
+ /* clear status register */
+ qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_STATUS_REG,
+ 0, NULL);
+
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
- crypto_cfg, &pcl_info->crypto_cfg);
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_CFG_REG,
- 0, NULL);
+ pdev->reg.crypto_cfg_be, &pcl_info->crypto_cfg);
break;
case QCE_AEAD_SHA1_HMAC:
cmdlistptr->aead_sha1_hmac.cmdlist = (uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->aead_sha1_hmac);
- auth_cfg = (CRYPTO_AUTH_MODE_HMAC << CRYPTO_AUTH_MODE)|
- (CRYPTO_AUTH_SIZE_SHA1 << CRYPTO_AUTH_SIZE) |
- (CRYPTO_AUTH_ALG_SHA << CRYPTO_AUTH_ALG) |
- (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS) |
- (1 << CRYPTO_LAST) | (1 << CRYPTO_FIRST);
-
+ auth_cfg = pdev->reg.auth_cfg_aead_sha1_hmac;
key_reg = 16;
iv_reg = 5;
+
+ /* clear status register */
+ qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_STATUS_REG,
+ 0, NULL);
+
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
- crypto_cfg, &pcl_info->crypto_cfg);
+ pdev->reg.crypto_cfg_be, &pcl_info->crypto_cfg);
/* 1 dummy write */
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_ENCR_SEG_SIZE_REG,
0, NULL);
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_CFG_REG,
- 0, NULL);
break;
case QCE_HASH_SHA256_HMAC:
cmdlistptr->auth_sha256_hmac.cmdlist = (uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->auth_sha256_hmac);
- auth_cfg = (CRYPTO_AUTH_MODE_HMAC << CRYPTO_AUTH_MODE)|
- (CRYPTO_AUTH_SIZE_SHA256 << CRYPTO_AUTH_SIZE) |
- (CRYPTO_AUTH_ALG_SHA << CRYPTO_AUTH_ALG) |
- (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS);
+ auth_cfg = pdev->reg.auth_cfg_hmac_sha256;
key_reg = 16;
iv_reg = 8;
+ /* clear status register */
+ qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_STATUS_REG, 0,
+ NULL);
+
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
- crypto_cfg, &pcl_info->crypto_cfg);
+ pdev->reg.crypto_cfg_be, &pcl_info->crypto_cfg);
/* 1 dummy write */
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_ENCR_SEG_SIZE_REG,
0, NULL);
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_CFG_REG,
- 0, NULL);
break;
case QCE_HASH_AES_CMAC:
if (key_128 == true) {
@@ -1864,37 +2423,26 @@
(uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->auth_aes_128_cmac);
- auth_cfg = (1 << CRYPTO_LAST) | (1 << CRYPTO_FIRST) |
- (CRYPTO_AUTH_MODE_CMAC << CRYPTO_AUTH_MODE)|
- (CRYPTO_AUTH_SIZE_ENUM_16_BYTES <<
- CRYPTO_AUTH_SIZE) |
- (CRYPTO_AUTH_ALG_AES << CRYPTO_AUTH_ALG) |
- (CRYPTO_AUTH_KEY_SZ_AES128 <<
- CRYPTO_AUTH_KEY_SIZE) |
- (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS);
+ auth_cfg = pdev->reg.auth_cfg_cmac_128;
key_reg = 4;
} else {
cmdlistptr->auth_aes_256_cmac.cmdlist =
(uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->auth_aes_256_cmac);
- auth_cfg = (1 << CRYPTO_LAST) | (1 << CRYPTO_FIRST)|
- (CRYPTO_AUTH_MODE_CMAC << CRYPTO_AUTH_MODE)|
- (CRYPTO_AUTH_SIZE_ENUM_16_BYTES <<
- CRYPTO_AUTH_SIZE) |
- (CRYPTO_AUTH_ALG_AES << CRYPTO_AUTH_ALG) |
- (CRYPTO_AUTH_KEY_SZ_AES256 <<
- CRYPTO_AUTH_KEY_SIZE) |
- (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS);
+ auth_cfg = pdev->reg.auth_cfg_cmac_256;
key_reg = 8;
}
+
+ /* clear status register */
+ qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_STATUS_REG, 0,
+ NULL);
+
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
- crypto_cfg, &pcl_info->crypto_cfg);
+ pdev->reg.crypto_cfg_be, &pcl_info->crypto_cfg);
/* 1 dummy write */
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_ENCR_SEG_SIZE_REG,
0, NULL);
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_CFG_REG,
- 0, NULL);
break;
default:
pr_err("Unknown algorithms %d received, exiting now\n", alg);
@@ -1946,8 +2494,7 @@
0, NULL);
}
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
- (crypto_cfg | CRYPTO_LITTLE_ENDIAN_MASK),
- NULL);
+ pdev->reg.crypto_cfg_le, NULL);
if (alg != QCE_AEAD_SHA1_HMAC)
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_GOPROC_REG,
@@ -1971,20 +2518,12 @@
uint32_t encr_cfg = 0;
uint32_t auth_cfg = 0;
uint32_t key_reg = 0;
- uint32_t crypto_cfg = 0;
- uint32_t beats = (pdev->ce_sps.ce_burst_size >> 3) - 1;
- uint32_t pipe_pair = pdev->ce_sps.pipe_pair_index;
*pvaddr = (unsigned char *) ALIGN(((unsigned int)(*pvaddr)),
pdev->ce_sps.ce_burst_size);
ce_vaddr_start = (uint32_t)(*pvaddr);
ce_vaddr = (struct sps_command_element *)(*pvaddr);
- crypto_cfg = (beats << CRYPTO_REQ_SIZE) |
- BIT(CRYPTO_MASK_DOUT_INTR) |
- BIT(CRYPTO_MASK_DIN_INTR) |
- BIT(CRYPTO_MASK_OP_DONE_INTR) |
- (0 << CRYPTO_HIGH_SPD_EN_N) |
- (pipe_pair << CRYPTO_PIPE_SET_SELECT);
+
/*
* Designate chunks of the allocated memory to various
* command list pointers related to aead operations
@@ -1994,38 +2533,26 @@
cmdlistptr->aead_aes_128_ccm.cmdlist = (uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->aead_aes_128_ccm);
- auth_cfg = (1 << CRYPTO_LAST) | (1 << CRYPTO_FIRST) |
- (CRYPTO_AUTH_MODE_CCM << CRYPTO_AUTH_MODE)|
- (CRYPTO_AUTH_ALG_AES << CRYPTO_AUTH_ALG) |
- (CRYPTO_AUTH_KEY_SZ_AES128 << CRYPTO_AUTH_KEY_SIZE);
- auth_cfg &= ~(1 << CRYPTO_USE_HW_KEY_AUTH);
- encr_cfg = (CRYPTO_ENCR_KEY_SZ_AES128 << CRYPTO_ENCR_KEY_SZ) |
- (CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG) |
- ((CRYPTO_ENCR_MODE_CCM << CRYPTO_ENCR_MODE));
+ auth_cfg = pdev->reg.auth_cfg_aes_ccm_128;
+ encr_cfg = pdev->reg.encr_cfg_aes_ccm_128;
key_reg = 4;
} else {
cmdlistptr->aead_aes_256_ccm.cmdlist = (uint32_t)ce_vaddr;
pcl_info = &(cmdlistptr->aead_aes_256_ccm);
- auth_cfg = (1 << CRYPTO_LAST) | (1 << CRYPTO_FIRST) |
- (CRYPTO_AUTH_MODE_CCM << CRYPTO_AUTH_MODE)|
- (CRYPTO_AUTH_ALG_AES << CRYPTO_AUTH_ALG) |
- (CRYPTO_AUTH_KEY_SZ_AES256 << CRYPTO_AUTH_KEY_SIZE) |
- ((MAX_NONCE/sizeof(uint32_t)) <<
- CRYPTO_AUTH_NONCE_NUM_WORDS);
- auth_cfg &= ~(1 << CRYPTO_USE_HW_KEY_AUTH);
- encr_cfg = (CRYPTO_ENCR_KEY_SZ_AES256 << CRYPTO_ENCR_KEY_SZ) |
- (CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG) |
- (CRYPTO_ENCR_MODE_CCM << CRYPTO_ENCR_MODE) |
- (CRYPTO_LAST_CCM_XFR << CRYPTO_LAST_CCM);
+ auth_cfg = pdev->reg.auth_cfg_aes_ccm_256;
+ encr_cfg = pdev->reg.encr_cfg_aes_ccm_256;
key_reg = 8;
}
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
- crypto_cfg, &pcl_info->crypto_cfg);
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_ENCR_SEG_SIZE_REG, 0, NULL);
+ /* clear status register */
+ qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_STATUS_REG, 0, NULL);
+
+ qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
+ pdev->reg.crypto_cfg_be, &pcl_info->crypto_cfg);
+
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_ENCR_SEG_CFG_REG, 0, NULL);
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_ENCR_SEG_START_REG, 0,
NULL);
@@ -2093,8 +2620,7 @@
0, NULL);
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
- (crypto_cfg | CRYPTO_LITTLE_ENDIAN_MASK),
- NULL);
+ pdev->reg.crypto_cfg_le, NULL);
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_GOPROC_REG,
((1 << CRYPTO_GO) | (1 << CRYPTO_RESULTS_DUMP)),
@@ -2197,12 +2723,173 @@
(uint32_t)GET_PHYS_ADDR(vaddr);
vaddr += QCE_MAX_NUM_DSCR * sizeof(struct sps_iovec);
- qce_setup_cmdlistptrs(pce_dev, &vaddr);
+ if (pce_dev->support_cmd_dscr)
+ qce_setup_cmdlistptrs(pce_dev, &vaddr);
vaddr = (unsigned char *) ALIGN(((unsigned int)vaddr),
pce_dev->ce_sps.ce_burst_size);
pce_dev->ce_sps.result_dump = (uint32_t)vaddr;
pce_dev->ce_sps.result = (struct ce_result_dump_format *)vaddr;
- vaddr += 128;
+ vaddr += CRYPTO_RESULT_DUMP_SIZE;
+
+ pce_dev->ce_sps.ignore_buffer = (uint32_t)vaddr;
+ vaddr += pce_dev->ce_sps.ce_burst_size * 2;
+
+ if ((vaddr - pce_dev->coh_vmem) > pce_dev->memsize)
+ panic("qce50: Not enough coherent memory. Allocate %x , need %x",
+ pce_dev->memsize, vaddr - pce_dev->coh_vmem);
+ return 0;
+}
+
+static int qce_init_ce_cfg_val(struct qce_device *pce_dev)
+{
+ uint32_t beats = (pce_dev->ce_sps.ce_burst_size >> 3) - 1;
+ uint32_t pipe_pair = pce_dev->ce_sps.pipe_pair_index;
+
+ pce_dev->reg.crypto_cfg_be = (beats << CRYPTO_REQ_SIZE) |
+ BIT(CRYPTO_MASK_DOUT_INTR) | BIT(CRYPTO_MASK_DIN_INTR) |
+ BIT(CRYPTO_MASK_OP_DONE_INTR) | (0 << CRYPTO_HIGH_SPD_EN_N) |
+ (pipe_pair << CRYPTO_PIPE_SET_SELECT);
+
+ pce_dev->reg.crypto_cfg_le =
+ (pce_dev->reg.crypto_cfg_be | CRYPTO_LITTLE_ENDIAN_MASK);
+
+ /* Initialize encr_cfg register for AES alg */
+ pce_dev->reg.encr_cfg_aes_cbc_128 =
+ (CRYPTO_ENCR_KEY_SZ_AES128 << CRYPTO_ENCR_KEY_SZ) |
+ (CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG) |
+ (CRYPTO_ENCR_MODE_CBC << CRYPTO_ENCR_MODE);
+
+ pce_dev->reg.encr_cfg_aes_cbc_256 =
+ (CRYPTO_ENCR_KEY_SZ_AES256 << CRYPTO_ENCR_KEY_SZ) |
+ (CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG) |
+ (CRYPTO_ENCR_MODE_CBC << CRYPTO_ENCR_MODE);
+
+ pce_dev->reg.encr_cfg_aes_ctr_128 =
+ (CRYPTO_ENCR_KEY_SZ_AES128 << CRYPTO_ENCR_KEY_SZ) |
+ (CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG) |
+ (CRYPTO_ENCR_MODE_CTR << CRYPTO_ENCR_MODE);
+
+ pce_dev->reg.encr_cfg_aes_ctr_256 =
+ (CRYPTO_ENCR_KEY_SZ_AES256 << CRYPTO_ENCR_KEY_SZ) |
+ (CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG) |
+ (CRYPTO_ENCR_MODE_CTR << CRYPTO_ENCR_MODE);
+
+ pce_dev->reg.encr_cfg_aes_xts_128 =
+ (CRYPTO_ENCR_KEY_SZ_AES128 << CRYPTO_ENCR_KEY_SZ) |
+ (CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG) |
+ (CRYPTO_ENCR_MODE_XTS << CRYPTO_ENCR_MODE);
+
+ pce_dev->reg.encr_cfg_aes_xts_256 =
+ (CRYPTO_ENCR_KEY_SZ_AES256 << CRYPTO_ENCR_KEY_SZ) |
+ (CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG) |
+ (CRYPTO_ENCR_MODE_XTS << CRYPTO_ENCR_MODE);
+
+ pce_dev->reg.encr_cfg_aes_ecb_128 =
+ (CRYPTO_ENCR_KEY_SZ_AES128 << CRYPTO_ENCR_KEY_SZ) |
+ (CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG) |
+ (CRYPTO_ENCR_MODE_ECB << CRYPTO_ENCR_MODE);
+
+ pce_dev->reg.encr_cfg_aes_ecb_256 =
+ (CRYPTO_ENCR_KEY_SZ_AES256 << CRYPTO_ENCR_KEY_SZ) |
+ (CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG) |
+ (CRYPTO_ENCR_MODE_ECB << CRYPTO_ENCR_MODE);
+
+ pce_dev->reg.encr_cfg_aes_ccm_128 =
+ (CRYPTO_ENCR_KEY_SZ_AES128 << CRYPTO_ENCR_KEY_SZ) |
+ (CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG) |
+ ((CRYPTO_ENCR_MODE_CCM << CRYPTO_ENCR_MODE));
+
+ pce_dev->reg.encr_cfg_aes_ccm_256 =
+ (CRYPTO_ENCR_KEY_SZ_AES256 << CRYPTO_ENCR_KEY_SZ) |
+ (CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG) |
+ (CRYPTO_ENCR_MODE_CCM << CRYPTO_ENCR_MODE) |
+ (CRYPTO_LAST_CCM_XFR << CRYPTO_LAST_CCM);
+
+ /* Initialize encr_cfg register for DES alg */
+ pce_dev->reg.encr_cfg_des_ecb =
+ (CRYPTO_ENCR_KEY_SZ_DES << CRYPTO_ENCR_KEY_SZ) |
+ (CRYPTO_ENCR_ALG_DES << CRYPTO_ENCR_ALG) |
+ (CRYPTO_ENCR_MODE_ECB << CRYPTO_ENCR_MODE);
+
+ pce_dev->reg.encr_cfg_des_cbc =
+ (CRYPTO_ENCR_KEY_SZ_DES << CRYPTO_ENCR_KEY_SZ) |
+ (CRYPTO_ENCR_ALG_DES << CRYPTO_ENCR_ALG) |
+ (CRYPTO_ENCR_MODE_CBC << CRYPTO_ENCR_MODE);
+
+ pce_dev->reg.encr_cfg_3des_ecb =
+ (CRYPTO_ENCR_KEY_SZ_3DES << CRYPTO_ENCR_KEY_SZ) |
+ (CRYPTO_ENCR_ALG_DES << CRYPTO_ENCR_ALG) |
+ (CRYPTO_ENCR_MODE_ECB << CRYPTO_ENCR_MODE);
+
+ pce_dev->reg.encr_cfg_3des_cbc =
+ (CRYPTO_ENCR_KEY_SZ_3DES << CRYPTO_ENCR_KEY_SZ) |
+ (CRYPTO_ENCR_ALG_DES << CRYPTO_ENCR_ALG) |
+ (CRYPTO_ENCR_MODE_CBC << CRYPTO_ENCR_MODE);
+
+ /* Initialize auth_cfg register for CMAC alg */
+ pce_dev->reg.auth_cfg_cmac_128 =
+ (1 << CRYPTO_LAST) | (1 << CRYPTO_FIRST) |
+ (CRYPTO_AUTH_MODE_CMAC << CRYPTO_AUTH_MODE)|
+ (CRYPTO_AUTH_SIZE_ENUM_16_BYTES << CRYPTO_AUTH_SIZE) |
+ (CRYPTO_AUTH_ALG_AES << CRYPTO_AUTH_ALG) |
+ (CRYPTO_AUTH_KEY_SZ_AES128 << CRYPTO_AUTH_KEY_SIZE);
+
+ pce_dev->reg.auth_cfg_cmac_256 =
+ (1 << CRYPTO_LAST) | (1 << CRYPTO_FIRST) |
+ (CRYPTO_AUTH_MODE_CMAC << CRYPTO_AUTH_MODE)|
+ (CRYPTO_AUTH_SIZE_ENUM_16_BYTES << CRYPTO_AUTH_SIZE) |
+ (CRYPTO_AUTH_ALG_AES << CRYPTO_AUTH_ALG) |
+ (CRYPTO_AUTH_KEY_SZ_AES256 << CRYPTO_AUTH_KEY_SIZE);
+
+ /* Initialize auth_cfg register for HMAC alg */
+ pce_dev->reg.auth_cfg_hmac_sha1 =
+ (CRYPTO_AUTH_MODE_HMAC << CRYPTO_AUTH_MODE)|
+ (CRYPTO_AUTH_SIZE_SHA1 << CRYPTO_AUTH_SIZE) |
+ (CRYPTO_AUTH_ALG_SHA << CRYPTO_AUTH_ALG) |
+ (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS);
+
+ pce_dev->reg.auth_cfg_hmac_sha256 =
+ (CRYPTO_AUTH_MODE_HMAC << CRYPTO_AUTH_MODE)|
+ (CRYPTO_AUTH_SIZE_SHA256 << CRYPTO_AUTH_SIZE) |
+ (CRYPTO_AUTH_ALG_SHA << CRYPTO_AUTH_ALG) |
+ (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS);
+
+ /* Initialize auth_cfg register for SHA1/256 alg */
+ pce_dev->reg.auth_cfg_sha1 =
+ (CRYPTO_AUTH_MODE_HASH << CRYPTO_AUTH_MODE)|
+ (CRYPTO_AUTH_SIZE_SHA1 << CRYPTO_AUTH_SIZE) |
+ (CRYPTO_AUTH_ALG_SHA << CRYPTO_AUTH_ALG) |
+ (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS);
+
+ pce_dev->reg.auth_cfg_sha256 =
+ (CRYPTO_AUTH_MODE_HASH << CRYPTO_AUTH_MODE)|
+ (CRYPTO_AUTH_SIZE_SHA256 << CRYPTO_AUTH_SIZE) |
+ (CRYPTO_AUTH_ALG_SHA << CRYPTO_AUTH_ALG) |
+ (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS);
+
+ /* Initialize auth_cfg register for AEAD alg */
+ pce_dev->reg.auth_cfg_aead_sha1_hmac =
+ (CRYPTO_AUTH_MODE_HMAC << CRYPTO_AUTH_MODE)|
+ (CRYPTO_AUTH_SIZE_SHA1 << CRYPTO_AUTH_SIZE) |
+ (CRYPTO_AUTH_ALG_SHA << CRYPTO_AUTH_ALG) |
+ (CRYPTO_AUTH_POS_BEFORE << CRYPTO_AUTH_POS) |
+ (1 << CRYPTO_LAST) | (1 << CRYPTO_FIRST);
+
+ pce_dev->reg.auth_cfg_aes_ccm_128 =
+ (1 << CRYPTO_LAST) | (1 << CRYPTO_FIRST) |
+ (CRYPTO_AUTH_MODE_CCM << CRYPTO_AUTH_MODE)|
+ (CRYPTO_AUTH_ALG_AES << CRYPTO_AUTH_ALG) |
+ (CRYPTO_AUTH_KEY_SZ_AES128 << CRYPTO_AUTH_KEY_SIZE) |
+ ((MAX_NONCE/sizeof(uint32_t)) << CRYPTO_AUTH_NONCE_NUM_WORDS);
+ pce_dev->reg.auth_cfg_aes_ccm_128 &= ~(1 << CRYPTO_USE_HW_KEY_AUTH);
+
+ pce_dev->reg.auth_cfg_aes_ccm_256 =
+ (1 << CRYPTO_LAST) | (1 << CRYPTO_FIRST) |
+ (CRYPTO_AUTH_MODE_CCM << CRYPTO_AUTH_MODE)|
+ (CRYPTO_AUTH_ALG_AES << CRYPTO_AUTH_ALG) |
+ (CRYPTO_AUTH_KEY_SZ_AES256 << CRYPTO_AUTH_KEY_SIZE) |
+ ((MAX_NONCE/sizeof(uint32_t)) << CRYPTO_AUTH_NONCE_NUM_WORDS);
+ pce_dev->reg.auth_cfg_aes_ccm_256 &= ~(1 << CRYPTO_USE_HW_KEY_AUTH);
return 0;
}
@@ -2301,10 +2988,16 @@
pce_dev->dst_nents = pce_dev->src_nents;
}
- _ce_get_cipher_cmdlistinfo(pce_dev, q_req, &cmdlistinfo);
- /* set up crypto device */
- rc = _ce_setup_cipher(pce_dev, q_req, totallen_in,
- areq->assoclen + ivsize, cmdlistinfo);
+ if (pce_dev->support_cmd_dscr) {
+ _ce_get_cipher_cmdlistinfo(pce_dev, q_req, &cmdlistinfo);
+ /* set up crypto device */
+ rc = _ce_setup_cipher(pce_dev, q_req, totallen_in,
+ areq->assoclen + ivsize, cmdlistinfo);
+ } else {
+ /* set up crypto device */
+ rc = _ce_setup_cipher_direct(pce_dev, q_req, totallen_in,
+ areq->assoclen + ivsize);
+ }
if (rc < 0)
goto bad;
@@ -2334,7 +3027,8 @@
}
_qce_sps_iovec_count_init(pce_dev);
- _qce_sps_add_cmd(pce_dev, SPS_IOVEC_FLAG_LOCK, cmdlistinfo,
+ if (pce_dev->support_cmd_dscr)
+ _qce_sps_add_cmd(pce_dev, SPS_IOVEC_FLAG_LOCK, cmdlistinfo,
&pce_dev->ce_sps.in_transfer);
if (pce_dev->ce_sps.minor_version == 0) {
@@ -2445,7 +3139,6 @@
pce_dev->src_nents = 0;
pce_dev->dst_nents = 0;
- _ce_get_cipher_cmdlistinfo(pce_dev, c_req, &cmdlistinfo);
/* cipher input */
pce_dev->src_nents = count_sg(areq->src, areq->nbytes);
@@ -2464,15 +3157,19 @@
pce_dev->dir = c_req->dir;
if ((pce_dev->ce_sps.minor_version == 0) && (c_req->dir == QCE_DECRYPT)
&& (c_req->mode == QCE_MODE_CBC)) {
- struct ablkcipher_request *areq =
- (struct ablkcipher_request *)pce_dev->areq;
memcpy(pce_dev->dec_iv, (unsigned char *)sg_virt(areq->src) +
areq->src->length - 16,
NUM_OF_CRYPTO_CNTR_IV_REG * CRYPTO_REG_SIZE);
}
/* set up crypto device */
- rc = _ce_setup_cipher(pce_dev, c_req, areq->nbytes, 0, cmdlistinfo);
+ if (pce_dev->support_cmd_dscr) {
+ _ce_get_cipher_cmdlistinfo(pce_dev, c_req, &cmdlistinfo);
+ rc = _ce_setup_cipher(pce_dev, c_req, areq->nbytes, 0,
+ cmdlistinfo);
+ } else {
+ rc = _ce_setup_cipher_direct(pce_dev, c_req, areq->nbytes, 0);
+ }
if (rc < 0)
goto bad;
@@ -2491,8 +3188,8 @@
goto bad;
}
_qce_sps_iovec_count_init(pce_dev);
-
- _qce_sps_add_cmd(pce_dev, SPS_IOVEC_FLAG_LOCK, cmdlistinfo,
+ if (pce_dev->support_cmd_dscr)
+ _qce_sps_add_cmd(pce_dev, SPS_IOVEC_FLAG_LOCK, cmdlistinfo,
&pce_dev->ce_sps.in_transfer);
if (_qce_sps_add_sg_data(pce_dev, areq->src, areq->nbytes,
&pce_dev->ce_sps.in_transfer))
@@ -2547,10 +3244,15 @@
struct qce_cmdlist_info *cmdlistinfo = NULL;
pce_dev->src_nents = count_sg(sreq->src, sreq->size);
- _ce_get_hash_cmdlistinfo(pce_dev, sreq, &cmdlistinfo);
qce_dma_map_sg(pce_dev->pdev, sreq->src, pce_dev->src_nents,
DMA_TO_DEVICE);
- rc = _ce_setup_hash(pce_dev, sreq, cmdlistinfo);
+
+ if (pce_dev->support_cmd_dscr) {
+ _ce_get_hash_cmdlistinfo(pce_dev, sreq, &cmdlistinfo);
+ rc = _ce_setup_hash(pce_dev, sreq, cmdlistinfo);
+ } else {
+ rc = _ce_setup_hash_direct(pce_dev, sreq);
+ }
if (rc < 0)
goto bad;
@@ -2568,7 +3270,8 @@
}
_qce_sps_iovec_count_init(pce_dev);
- _qce_sps_add_cmd(pce_dev, SPS_IOVEC_FLAG_LOCK, cmdlistinfo,
+ if (pce_dev->support_cmd_dscr)
+ _qce_sps_add_cmd(pce_dev, SPS_IOVEC_FLAG_LOCK, cmdlistinfo,
&pce_dev->ce_sps.in_transfer);
if (_qce_sps_add_sg_data(pce_dev, areq->src, areq->nbytes,
&pce_dev->ce_sps.in_transfer))
@@ -2602,7 +3305,8 @@
pce_dev->is_shared = of_property_read_bool((&pdev->dev)->of_node,
"qcom,ce-hw-shared");
-
+ pce_dev->support_hw_key = of_property_read_bool((&pdev->dev)->of_node,
+ "qcom,ce-hw-key");
if (of_property_read_u32((&pdev->dev)->of_node,
"qcom,bam-pipe-pair",
&pce_dev->ce_sps.pipe_pair_index)) {
@@ -2637,7 +3341,7 @@
pce_dev->ce_sps.bam_mem = resource->start;
pce_dev->ce_sps.bam_iobase = ioremap_nocache(resource->start,
resource_size(resource));
- if (!pce_dev->iobase) {
+ if (!pce_dev->ce_sps.bam_iobase) {
rc = -ENOMEM;
pr_err("Can not map BAM io memory\n");
goto err_getting_bam_info;
@@ -2650,7 +3354,6 @@
pr_warn("ce_bam_phy_reg_base=0x%x ", pce_dev->ce_sps.bam_mem);
pr_warn("ce_bam_virt_reg_base=0x%x\n",
(uint32_t)pce_dev->ce_sps.bam_iobase);
-
resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (resource) {
pce_dev->ce_sps.bam_irq = resource->start;
@@ -2816,6 +3519,7 @@
void *qce_open(struct platform_device *pdev, int *rc)
{
struct qce_device *pce_dev;
+ uint32_t bam_cfg = 0 ;
pce_dev = kzalloc(sizeof(struct qce_device), GFP_KERNEL);
if (!pce_dev) {
@@ -2857,9 +3561,16 @@
goto err;
}
*rc = 0;
+
+ bam_cfg = readl_relaxed(pce_dev->ce_sps.bam_iobase +
+ CRYPTO_BAM_CNFG_BITS_REG);
+ pce_dev->support_cmd_dscr = (bam_cfg & CRYPTO_BAM_CD_ENABLE_MASK) ?
+ true : false;
+ qce_init_ce_cfg_val(pce_dev);
qce_setup_ce_sps_data(pce_dev);
qce_sps_init(pce_dev);
+
qce_disable_clk(pce_dev);
return pce_dev;
@@ -2889,6 +3600,9 @@
if (handle == NULL)
return -ENODEV;
+ qce_enable_clk(pce_dev);
+ qce_sps_exit(pce_dev);
+
if (pce_dev->iobase)
iounmap(pce_dev->iobase);
if (pce_dev->coh_vmem)
@@ -2898,7 +3612,6 @@
qce_disable_clk(pce_dev);
__qce_deinit_clk(pce_dev);
- qce_sps_exit(pce_dev);
kfree(handle);
return 0;
@@ -2922,6 +3635,7 @@
ce_support->ota = false;
ce_support->bam = true;
ce_support->is_shared = (pce_dev->is_shared == 1) ? true : false;
+ ce_support->hw_key = pce_dev->support_hw_key;
ce_support->aes_ccm = true;
if (pce_dev->ce_sps.minor_version)
ce_support->aligned_only = false;
diff --git a/drivers/crypto/msm/qce50.h b/drivers/crypto/msm/qce50.h
index f5123df..38515fb 100644
--- a/drivers/crypto/msm/qce50.h
+++ b/drivers/crypto/msm/qce50.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013, 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
@@ -109,6 +109,45 @@
struct qce_cmdlist_info unlock_all_pipes;
};
+struct qce_ce_cfg_reg_setting {
+ uint32_t crypto_cfg_be;
+ uint32_t crypto_cfg_le;
+
+ uint32_t encr_cfg_aes_cbc_128;
+ uint32_t encr_cfg_aes_cbc_256;
+
+ uint32_t encr_cfg_aes_ecb_128;
+ uint32_t encr_cfg_aes_ecb_256;
+
+ uint32_t encr_cfg_aes_xts_128;
+ uint32_t encr_cfg_aes_xts_256;
+
+ uint32_t encr_cfg_aes_ctr_128;
+ uint32_t encr_cfg_aes_ctr_256;
+
+ uint32_t encr_cfg_aes_ccm_128;
+ uint32_t encr_cfg_aes_ccm_256;
+
+ uint32_t encr_cfg_des_cbc;
+ uint32_t encr_cfg_des_ecb;
+
+ uint32_t encr_cfg_3des_cbc;
+ uint32_t encr_cfg_3des_ecb;
+
+ uint32_t auth_cfg_cmac_128;
+ uint32_t auth_cfg_cmac_256;
+
+ uint32_t auth_cfg_sha1;
+ uint32_t auth_cfg_sha256;
+
+ uint32_t auth_cfg_hmac_sha1;
+ uint32_t auth_cfg_hmac_sha256;
+
+ uint32_t auth_cfg_aes_ccm_128;
+ uint32_t auth_cfg_aes_ccm_256;
+ uint32_t auth_cfg_aead_sha1_hmac;
+
+};
/* DM data structure with buffers, commandlists & commmand pointer lists */
struct ce_sps_data {
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index e91dcaa..d069f2e 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -323,10 +323,10 @@
u32 qcedev_sha_fail;
};
-static struct qcedev_stat _qcedev_stat[MAX_QCE_DEVICE];
+static struct qcedev_stat _qcedev_stat;
static struct dentry *_debug_dent;
static char _debug_read_buf[DEBUG_MAX_RW_BUF];
-static int _debug_qcedev[MAX_QCE_DEVICE];
+static int _debug_qcedev;
static struct qcedev_control *qcedev_minor_to_control(unsigned n)
{
@@ -693,7 +693,7 @@
if (ret)
qcedev_areq->err = -EIO;
- pstat = &_qcedev_stat[podev->pdev->id];
+ pstat = &_qcedev_stat;
if (qcedev_areq->op_type == QCEDEV_CRYPTO_OPER_CIPHER) {
switch (qcedev_areq->cipher_op_req.op) {
case QCEDEV_OPER_DEC:
@@ -1559,34 +1559,54 @@
static int qcedev_check_cipher_key(struct qcedev_cipher_op_req *req,
struct qcedev_control *podev)
{
+
+ if (req->encklen < 0) {
+ pr_err("%s: Invalid key size: %d\n", __func__, req->encklen);
+ return -EINVAL;
+ }
/* if intending to use HW key make sure key fields are set
* correctly and HW key is indeed supported in target
*/
if (req->encklen == 0) {
int i;
- for (i = 0; i < QCEDEV_MAX_KEY_SIZE; i++)
- if (req->enckey[i])
+ for (i = 0; i < QCEDEV_MAX_KEY_SIZE; i++) {
+ if (req->enckey[i]) {
+ pr_err("%s: Invalid key: non-zero key input\n",
+ __func__);
goto error;
+ }
+ }
if ((req->op != QCEDEV_OPER_ENC_NO_KEY) &&
(req->op != QCEDEV_OPER_DEC_NO_KEY))
- if (!podev->platform_support.hw_key_support)
+ if (!podev->platform_support.hw_key_support) {
+ pr_err("%s: Invalid op %d\n", __func__,
+ (uint32_t)req->op);
goto error;
+ }
} else {
if (req->encklen == QCEDEV_AES_KEY_192) {
- if (!podev->ce_support.aes_key_192)
+ if (!podev->ce_support.aes_key_192) {
+ pr_err("%s: AES-192 not supported\n", __func__);
goto error;
+ }
} else {
/* if not using HW key make sure key
* length is valid
*/
if ((req->mode == QCEDEV_AES_MODE_XTS)) {
- if (!((req->encklen == QCEDEV_AES_KEY_128*2) ||
- (req->encklen == QCEDEV_AES_KEY_256*2)))
+ if ((req->encklen != QCEDEV_AES_KEY_128*2) &&
+ (req->encklen != QCEDEV_AES_KEY_256*2)) {
+ pr_err("%s: unsupported key size: %d\n",
+ __func__, req->encklen);
goto error;
+ }
} else {
- if (!((req->encklen == QCEDEV_AES_KEY_128) ||
- (req->encklen == QCEDEV_AES_KEY_256)))
+ if ((req->encklen != QCEDEV_AES_KEY_128) &&
+ (req->encklen != QCEDEV_AES_KEY_256)) {
+ pr_err("%s: unsupported key size %d\n",
+ __func__, req->encklen);
goto error;
+ }
}
}
}
@@ -1602,32 +1622,52 @@
pr_err("%s: Use of PMEM is not supported\n", __func__);
goto error;
}
- if ((req->entries == 0) || (req->data_len == 0))
+ if ((req->entries == 0) || (req->data_len == 0) ||
+ (req->entries > QCEDEV_MAX_BUFFERS)) {
+ pr_err("%s: Invalid cipher length/entries\n", __func__);
goto error;
+ }
if ((req->alg >= QCEDEV_ALG_LAST) ||
- (req->mode >= QCEDEV_AES_DES_MODE_LAST))
+ (req->mode >= QCEDEV_AES_DES_MODE_LAST)) {
+ pr_err("%s: Invalid algorithm %d\n", __func__,
+ (uint32_t)req->alg);
goto error;
-
- if ((req->mode == QCEDEV_AES_MODE_XTS) && (!podev->ce_support.aes_xts))
- goto error;
-
- if (req->alg == QCEDEV_ALG_AES)
+ }
+ if ((req->mode == QCEDEV_AES_MODE_XTS) &&
+ (!podev->ce_support.aes_xts)) {
+ pr_err("%s: XTS algorithm is not supported\n", __func__);
+ goto error;
+ }
+ if (req->alg == QCEDEV_ALG_AES) {
if (qcedev_check_cipher_key(req, podev))
- goto error;
+ goto error;
+
+ }
/* if using a byteoffset, make sure it is CTR mode using vbuf */
if (req->byteoffset) {
- if (req->mode != QCEDEV_AES_MODE_CTR)
+ if (req->mode != QCEDEV_AES_MODE_CTR) {
+ pr_err("%s: Operation on byte offset not supported\n",
+ __func__);
goto error;
+ }
+ if (req->byteoffset >= AES_CE_BLOCK_SIZE) {
+ pr_err("%s: Invalid byte offset\n", __func__);
+ goto error;
+ }
}
/* Ensure zer ivlen for ECB mode */
- if (req->ivlen != 0) {
+ if (req->ivlen > 0) {
if ((req->mode == QCEDEV_AES_MODE_ECB) ||
- (req->mode == QCEDEV_DES_MODE_ECB))
+ (req->mode == QCEDEV_DES_MODE_ECB)) {
+ pr_err("%s: Expecting a zero length IV\n", __func__);
goto error;
+ }
} else {
if ((req->mode != QCEDEV_AES_MODE_ECB) &&
- (req->mode != QCEDEV_DES_MODE_ECB))
+ (req->mode != QCEDEV_DES_MODE_ECB)) {
+ pr_err("%s: Expecting a non-zero ength IV\n", __func__);
goto error;
+ }
}
return 0;
@@ -1640,20 +1680,42 @@
struct qcedev_control *podev)
{
if ((req->alg == QCEDEV_ALG_AES_CMAC) &&
- (!podev->ce_support.cmac))
+ (!podev->ce_support.cmac)) {
+ pr_err("%s: CMAC not supported\n", __func__);
goto sha_error;
-
- if ((req->entries == 0) || (req->data_len == 0))
+ }
+ if ((req->entries == 0) || (req->data_len == 0) ||
+ (req->entries > QCEDEV_MAX_BUFFERS)) {
+ pr_err("%s: Invalid data length (%d)/ num entries (%d)\n",
+ __func__, req->data_len, req->entries);
goto sha_error;
+ }
- if (req->alg >= QCEDEV_ALG_SHA_ALG_LAST)
+ if (req->alg >= QCEDEV_ALG_SHA_ALG_LAST) {
+ pr_err("%s: Invalid algorithm (%d)\n", __func__, req->alg);
goto sha_error;
-
+ }
if ((req->alg == QCEDEV_ALG_SHA1_HMAC) ||
(req->alg == QCEDEV_ALG_SHA1_HMAC)) {
- if (req->authklen == 0)
+ if (req->authkey == NULL) {
+ pr_err("%s: Invalid authkey pointer\n", __func__);
goto sha_error;
+ }
+ if (req->authklen <= 0) {
+ pr_err("%s: Invalid authkey length (%d)\n",
+ __func__, req->authklen);
+ goto sha_error;
+ }
}
+
+ if (req->alg == QCEDEV_ALG_AES_CMAC) {
+ if ((req->authklen != QCEDEV_AES_KEY_128) &&
+ (req->authklen != QCEDEV_AES_KEY_256)) {
+ pr_err("%s: unsupported key length\n", __func__);
+ goto sha_error;
+ }
+ }
+
return 0;
sha_error:
return -EINVAL;
@@ -1681,7 +1743,7 @@
return -ENOTTY;
init_completion(&qcedev_areq.complete);
- pstat = &_qcedev_stat[podev->pdev->id];
+ pstat = &_qcedev_stat;
switch (cmd) {
case QCEDEV_IOCTL_LOCK_CE:
@@ -1875,19 +1937,18 @@
rc = misc_register(&podev->miscdevice);
qce_hw_support(podev->qce, &podev->ce_support);
if (podev->ce_support.bam) {
- podev->platform_support.ce_shared = 0;
+ podev->platform_support.ce_shared = podev->ce_support.is_shared;
podev->platform_support.shared_ce_resource = 0;
- podev->platform_support.hw_key_support = 0;
+ podev->platform_support.hw_key_support =
+ podev->ce_support.hw_key;
podev->platform_support.bus_scale_table = NULL;
podev->platform_support.sha_hmac = 1;
- if (podev->ce_support.is_shared == false) {
- podev->platform_support.bus_scale_table =
- (struct msm_bus_scale_pdata *)
- msm_bus_cl_get_pdata(pdev);
- if (!podev->platform_support.bus_scale_table)
- pr_err("bus_scale_table is NULL\n");
- }
+ podev->platform_support.bus_scale_table =
+ (struct msm_bus_scale_pdata *)
+ msm_bus_cl_get_pdata(pdev);
+ if (!podev->platform_support.bus_scale_table)
+ pr_err("bus_scale_table is NULL\n");
} else {
platform_support =
(struct msm_ce_hw_support *)pdev->dev.platform_data;
@@ -1968,11 +2029,7 @@
struct qcedev_stat *pstat;
int len = 0;
- if (id < 0) {
- pr_err("Crypto id is %d, cannot be negative\n", id);
- return len;
- }
- pstat = &_qcedev_stat[id];
+ pstat = &_qcedev_stat;
len = snprintf(_debug_read_buf, DEBUG_MAX_RW_BUF - 1,
"\nQualcomm QCE dev driver %d Statistics:\n",
id + 1);
@@ -2018,10 +2075,7 @@
static ssize_t _debug_stats_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
-
- int qcedev = *((int *) file->private_data);
-
- memset((char *)&_qcedev_stat[qcedev], 0, sizeof(struct qcedev_stat));
+ memset((char *)&_qcedev_stat, 0, sizeof(struct qcedev_stat));
return count;
};
@@ -2035,7 +2089,6 @@
{
int rc;
char name[DEBUG_MAX_FNAME];
- int i;
struct dentry *dent;
_debug_dent = debugfs_create_dir("qcedev", NULL);
@@ -2045,17 +2098,15 @@
return PTR_ERR(_debug_dent);
}
- for (i = 0; i < MAX_QCE_DEVICE; i++) {
- snprintf(name, DEBUG_MAX_FNAME-1, "stats-%d", i+1);
- _debug_qcedev[i] = i;
- dent = debugfs_create_file(name, 0644, _debug_dent,
- &_debug_qcedev[i], &_debug_stats_ops);
- if (dent == NULL) {
- pr_err("qcedev debugfs_create_file fail, error %ld\n",
- PTR_ERR(dent));
- rc = PTR_ERR(dent);
- goto err;
- }
+ snprintf(name, DEBUG_MAX_FNAME-1, "stats-%d", 1);
+ _debug_qcedev = 0;
+ dent = debugfs_create_file(name, 0644, _debug_dent,
+ &_debug_qcedev, &_debug_stats_ops);
+ if (dent == NULL) {
+ pr_err("qcedev debugfs_create_file fail, error %ld\n",
+ PTR_ERR(dent));
+ rc = PTR_ERR(dent);
+ goto err;
}
return 0;
err:
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index 40fb29ac..39b9a46 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -39,10 +39,10 @@
#include <mach/scm.h>
#include <linux/platform_data/qcom_crypto_device.h>
#include <mach/msm_bus.h>
+#include <mach/qcrypto.h>
#include "qce.h"
-#define MAX_CRYPTO_DEVICE 3
#define DEBUG_MAX_FNAME 16
#define DEBUG_MAX_RW_BUF 1024
@@ -53,8 +53,11 @@
u32 aead_sha1_des_dec;
u32 aead_sha1_3des_enc;
u32 aead_sha1_3des_dec;
+ u32 aead_ccm_aes_enc;
+ u32 aead_ccm_aes_dec;
u32 aead_op_success;
u32 aead_op_fail;
+ u32 aead_bad_msg;
u32 ablk_cipher_aes_enc;
u32 ablk_cipher_aes_dec;
u32 ablk_cipher_des_enc;
@@ -72,7 +75,7 @@
u32 sha_hmac_op_success;
u32 sha_hmac_op_fail;
};
-static struct crypto_stat _qcrypto_stat[MAX_CRYPTO_DEVICE];
+static struct crypto_stat _qcrypto_stat;
static struct dentry *_debug_dent;
static char _debug_read_buf[DEBUG_MAX_RW_BUF];
@@ -215,6 +218,7 @@
unsigned int auth_key_len;
struct crypto_priv *cp;
+ unsigned int flags;
};
struct qcrypto_cipher_req_ctx {
@@ -273,6 +277,7 @@
struct scatterlist *sg;
struct scatterlist tmp_sg;
struct crypto_priv *cp;
+ unsigned int flags;
};
struct qcrypto_sha_req_ctx {
@@ -405,6 +410,39 @@
return i;
}
+size_t qcrypto_sg_copy_from_buffer(struct scatterlist *sgl, unsigned int nents,
+ void *buf, size_t buflen)
+{
+ int i;
+ size_t offset, len;
+
+ for (i = 0, offset = 0; i < nents; ++i) {
+ len = sg_copy_from_buffer(sgl, 1, buf, buflen);
+ buf += len;
+ buflen -= len;
+ offset += len;
+ sgl = scatterwalk_sg_next(sgl);
+ }
+
+ return offset;
+}
+
+size_t qcrypto_sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
+ void *buf, size_t buflen)
+{
+ int i;
+ size_t offset, len;
+
+ for (i = 0, offset = 0; i < nents; ++i) {
+ len = sg_copy_to_buffer(sgl, 1, buf, buflen);
+ buf += len;
+ buflen -= len;
+ offset += len;
+ sgl = scatterwalk_sg_next(sgl);
+ }
+
+ return offset;
+}
static struct qcrypto_alg *_qcrypto_sha_alg_alloc(struct crypto_priv *cp,
struct ahash_alg *template)
{
@@ -581,11 +619,7 @@
struct crypto_stat *pstat;
int len = 0;
- if (id < 0) {
- pr_err("Crypto id is %d, cannot be negative\n", id);
- return len;
- }
- pstat = &_qcrypto_stat[id];
+ pstat = &_qcrypto_stat;
len = snprintf(_debug_read_buf, DEBUG_MAX_RW_BUF - 1,
"\nQualcomm crypto accelerator %d Statistics:\n",
id + 1);
@@ -641,12 +675,22 @@
pstat->aead_sha1_3des_dec);
len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ " AEAD CCM-AES encryption : %d\n",
+ pstat->aead_ccm_aes_enc);
+ len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ " AEAD CCM-AES decryption : %d\n",
+ pstat->aead_ccm_aes_dec);
+
+ len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
" AEAD operation success : %d\n",
pstat->aead_op_success);
len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
" AEAD operation fail : %d\n",
pstat->aead_op_fail);
len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ " AEAD bad message : %d\n",
+ pstat->aead_bad_msg);
+ len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
" SHA1 digest : %d\n",
pstat->sha1_digest);
len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
@@ -730,11 +774,21 @@
struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
struct crypto_priv *cp = ctx->cp;
+ if ((ctx->flags & QCRYPTO_CTX_USE_HW_KEY) == QCRYPTO_CTX_USE_HW_KEY)
+ return 0;
+
if (_qcrypto_check_aes_keylen(cipher, cp, len)) {
return -EINVAL;
} else {
ctx->enc_key_len = len;
- memcpy(ctx->enc_key, key, len);
+ if (!(ctx->flags & QCRYPTO_CTX_USE_PIPE_KEY)) {
+ if (key != NULL) {
+ memcpy(ctx->enc_key, key, len);
+ } else {
+ pr_err("%s Inavlid key pointer\n", __func__);
+ return -EINVAL;
+ }
+ }
}
return 0;
};
@@ -746,12 +800,20 @@
struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
struct crypto_priv *cp = ctx->cp;
-
+ if ((ctx->flags & QCRYPTO_CTX_USE_HW_KEY) == QCRYPTO_CTX_USE_HW_KEY)
+ return 0;
if (_qcrypto_check_aes_keylen(cipher, cp, len/2)) {
return -EINVAL;
} else {
ctx->enc_key_len = len;
- memcpy(ctx->enc_key, key, len);
+ if (!(ctx->flags & QCRYPTO_CTX_USE_PIPE_KEY)) {
+ if (key != NULL) {
+ memcpy(ctx->enc_key, key, len);
+ } else {
+ pr_err("%s Inavlid key pointer\n", __func__);
+ return -EINVAL;
+ }
+ }
}
return 0;
};
@@ -764,6 +826,12 @@
u32 tmp[DES_EXPKEY_WORDS];
int ret = des_ekey(tmp, key);
+ if ((ctx->flags & QCRYPTO_CTX_USE_HW_KEY) == QCRYPTO_CTX_USE_HW_KEY) {
+ pr_err("%s HW KEY usage not supported for DES algorithm\n",
+ __func__);
+ return 0;
+ };
+
if (len != DES_KEY_SIZE) {
crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
return -EINVAL;
@@ -775,7 +843,14 @@
}
ctx->enc_key_len = len;
- memcpy(ctx->enc_key, key, len);
+ if (!(ctx->flags & QCRYPTO_CTX_USE_PIPE_KEY)) {
+ if (key != NULL) {
+ memcpy(ctx->enc_key, key, len);
+ } else {
+ pr_err("%s Inavlid key pointer\n", __func__);
+ return -EINVAL;
+ }
+ }
return 0;
};
@@ -785,12 +860,24 @@
struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
+ if ((ctx->flags & QCRYPTO_CTX_USE_HW_KEY) == QCRYPTO_CTX_USE_HW_KEY) {
+ pr_err("%s HW KEY usage not supported for 3DES algorithm\n",
+ __func__);
+ return 0;
+ };
if (len != DES3_EDE_KEY_SIZE) {
crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
return -EINVAL;
};
ctx->enc_key_len = len;
- memcpy(ctx->enc_key, key, len);
+ if (!(ctx->flags & QCRYPTO_CTX_USE_PIPE_KEY)) {
+ if (key != NULL) {
+ memcpy(ctx->enc_key, key, len);
+ } else {
+ pr_err("%s Inavlid key pointer\n", __func__);
+ return -EINVAL;
+ }
+ }
return 0;
};
@@ -858,7 +945,7 @@
uint32_t diglen = crypto_ahash_digestsize(ahash);
uint32_t *auth32 = (uint32_t *)authdata;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
#ifdef QCRYPTO_DEBUG
dev_info(&cp->pdev->dev, "_qce_ahash_complete: %p ret %d\n",
@@ -916,7 +1003,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
#ifdef QCRYPTO_DEBUG
dev_info(&cp->pdev->dev, "_qce_ablk_cipher_complete: %p ret %d\n",
@@ -943,7 +1030,7 @@
areq->dst = rctx->orig_dst;
num_sg = qcrypto_count_sg(areq->dst, areq->nbytes);
- bytes = sg_copy_from_buffer(areq->dst, num_sg,
+ bytes = qcrypto_sg_copy_from_buffer(areq->dst, num_sg,
rctx->data, areq->nbytes);
if (bytes != areq->nbytes)
pr_warn("bytes copied=0x%x bytes to copy= 0x%x", bytes,
@@ -967,7 +1054,7 @@
struct qcrypto_cipher_req_ctx *rctx;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
rctx = aead_request_ctx(areq);
@@ -988,7 +1075,7 @@
nbytes = areq->cryptlen -
crypto_aead_authsize(aead);
num_sg = qcrypto_count_sg(areq->dst, nbytes);
- bytes = sg_copy_from_buffer(areq->dst, num_sg,
+ bytes = qcrypto_sg_copy_from_buffer(areq->dst, num_sg,
((char *)rctx->data + areq->assoclen),
nbytes);
if (bytes != nbytes)
@@ -999,12 +1086,6 @@
kzfree(rctx->assoc);
areq->assoc = rctx->assoc_sg;
areq->assoclen = rctx->assoclen;
- if (ret) {
- if (ret == 0x2000000)
- ret = -EBADMSG;
- else
- ret = -ENXIO;
- }
} else {
if (ret == 0) {
if (rctx->dir == QCE_ENCRYPT) {
@@ -1033,11 +1114,15 @@
memcpy(ctx->iv, iv, crypto_aead_ivsize(aead));
}
- if (ret)
+ if (ret == (-EBADMSG))
+ pstat->aead_bad_msg++;
+ else if (ret)
pstat->aead_op_fail++;
else
pstat->aead_op_success++;
+ cp->res = ret;
+
if (cp->platform_support.ce_shared)
schedule_work(&cp->unlock_ce_ws);
tasklet_schedule(&cp->done_tasklet);
@@ -1118,7 +1203,7 @@
qreq->assoclen = ALIGN((alen + len), 16);
num_sg = qcrypto_count_sg(sg, alen);
- bytes = sg_copy_to_buffer(sg, num_sg, adata, alen);
+ bytes = qcrypto_sg_copy_to_buffer(sg, num_sg, adata, alen);
if (bytes != alen)
pr_warn("bytes copied=0x%x bytes to copy= 0x%x", bytes, alen);
@@ -1145,7 +1230,7 @@
rctx->orig_src = req->src;
rctx->orig_dst = req->dst;
- rctx->data = kzalloc((req->nbytes + 64), GFP_KERNEL);
+ rctx->data = kzalloc((req->nbytes + 64), GFP_ATOMIC);
if (rctx->data == NULL) {
pr_err("Mem Alloc fail rctx->data, err %ld for 0x%x\n",
@@ -1153,7 +1238,7 @@
return -ENOMEM;
}
num_sg = qcrypto_count_sg(req->src, req->nbytes);
- bytes = sg_copy_to_buffer(req->src, num_sg, rctx->data,
+ bytes = qcrypto_sg_copy_to_buffer(req->src, num_sg, rctx->data,
req->nbytes);
if (bytes != req->nbytes)
pr_warn("bytes copied=0x%x bytes to copy= 0x%x", bytes,
@@ -1177,6 +1262,7 @@
qreq.ivsize = crypto_ablkcipher_ivsize(tfm);
qreq.cryptlen = req->nbytes;
qreq.use_pmem = 0;
+ qreq.flags = cipher_ctx->flags;
if ((cipher_ctx->enc_key_len == 0) &&
(cp->platform_support.hw_key_support == 0))
@@ -1210,6 +1296,7 @@
sreq.last_blk = sha_ctx->last_blk;
sreq.size = req->nbytes;
sreq.areq = req;
+ sreq.flags = sha_ctx->flags;
switch (sha_ctx->alg) {
case QCE_HASH_SHA1:
@@ -1269,6 +1356,8 @@
qreq.authklen = cipher_ctx->auth_key_len;
qreq.authsize = crypto_aead_authsize(aead);
qreq.ivsize = crypto_aead_ivsize(aead);
+ qreq.flags = cipher_ctx->flags;
+
if (qreq.mode == QCE_MODE_CCM) {
if (qreq.dir == QCE_ENCRYPT)
qreq.cryptlen = req->cryptlen;
@@ -1294,7 +1383,7 @@
rctx->orig_src = req->src;
rctx->orig_dst = req->dst;
rctx->data = kzalloc((req->cryptlen + qreq.assoclen +
- qreq.authsize + 64*2), GFP_KERNEL);
+ qreq.authsize + 64*2), GFP_ATOMIC);
if (rctx->data == NULL) {
pr_err("Mem Alloc fail rctx->data, err %ld\n",
PTR_ERR(rctx->data));
@@ -1305,7 +1394,7 @@
memcpy((char *)rctx->data, qreq.assoc, qreq.assoclen);
num_sg = qcrypto_count_sg(req->src, req->cryptlen);
- bytes = sg_copy_to_buffer(req->src, num_sg,
+ bytes = qcrypto_sg_copy_to_buffer(req->src, num_sg,
rctx->data + qreq.assoclen , req->cryptlen);
if (bytes != req->cryptlen)
pr_warn("bytes copied=0x%x bytes to copy= 0x%x",
@@ -1358,7 +1447,7 @@
int ret = 0;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
again:
spin_lock_irqsave(&cp->lock, flags);
@@ -1434,7 +1523,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1458,7 +1547,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1482,7 +1571,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1506,7 +1595,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1533,7 +1622,7 @@
(ctx->auth_key_len != AES_KEYSIZE_256))
return -EINVAL;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
rctx = aead_request_ctx(req);
rctx->aead = 1;
@@ -1542,7 +1631,7 @@
rctx->mode = QCE_MODE_CCM;
rctx->iv = req->iv;
- pstat->aead_sha1_aes_enc++;
+ pstat->aead_ccm_aes_enc++;
return _qcrypto_queue_req(cp, &req->base);
}
@@ -1553,7 +1642,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1574,7 +1663,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1595,7 +1684,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1616,7 +1705,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1637,7 +1726,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1661,7 +1750,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1686,7 +1775,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1712,7 +1801,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1733,7 +1822,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1754,7 +1843,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1775,7 +1864,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1796,7 +1885,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
BUG_ON(crypto_tfm_alg_type(req->base.tfm) !=
CRYPTO_ALG_TYPE_ABLKCIPHER);
@@ -1824,7 +1913,7 @@
(ctx->auth_key_len != AES_KEYSIZE_256))
return -EINVAL;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
rctx = aead_request_ctx(req);
rctx->aead = 1;
@@ -1833,7 +1922,7 @@
rctx->mode = QCE_MODE_CCM;
rctx->iv = req->iv;
- pstat->aead_sha1_aes_dec++;
+ pstat->aead_ccm_aes_dec++;
return _qcrypto_queue_req(cp, &req->base);
}
@@ -1939,7 +2028,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
#ifdef QCRYPTO_DEBUG
dev_info(&cp->pdev->dev, "_qcrypto_aead_encrypt_aes_cbc: %p\n", req);
@@ -1963,7 +2052,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
#ifdef QCRYPTO_DEBUG
dev_info(&cp->pdev->dev, "_qcrypto_aead_decrypt_aes_cbc: %p\n", req);
@@ -1988,7 +2077,7 @@
struct qcrypto_cipher_req_ctx *rctx;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
rctx = aead_request_ctx(areq);
rctx->aead = 1;
@@ -2012,7 +2101,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
rctx = aead_request_ctx(req);
rctx->aead = 1;
@@ -2032,7 +2121,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
rctx = aead_request_ctx(req);
rctx->aead = 1;
@@ -2057,7 +2146,7 @@
struct qcrypto_cipher_req_ctx *rctx;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
rctx = aead_request_ctx(areq);
rctx->aead = 1;
@@ -2081,7 +2170,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
rctx = aead_request_ctx(req);
rctx->aead = 1;
@@ -2101,7 +2190,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
rctx = aead_request_ctx(req);
rctx->aead = 1;
@@ -2123,7 +2212,7 @@
struct qcrypto_cipher_req_ctx *rctx;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
rctx = aead_request_ctx(areq);
rctx->aead = 1;
@@ -2146,7 +2235,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
rctx = aead_request_ctx(req);
rctx->aead = 1;
@@ -2166,7 +2255,7 @@
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
rctx = aead_request_ctx(req);
rctx->aead = 1;
@@ -2188,7 +2277,7 @@
struct qcrypto_cipher_req_ctx *rctx;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
rctx = aead_request_ctx(areq);
rctx->aead = 1;
@@ -2220,10 +2309,9 @@
static int _sha1_init(struct ahash_request *req)
{
struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
- struct crypto_priv *cp = sha_ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
_sha_init(sha_ctx);
sha_ctx->alg = QCE_HASH_SHA1;
@@ -2241,10 +2329,9 @@
static int _sha256_init(struct ahash_request *req)
{
struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
- struct crypto_priv *cp = sha_ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
_sha_init(sha_ctx);
sha_ctx->alg = QCE_HASH_SHA256;
@@ -2347,7 +2434,7 @@
srctx = ahash_request_ctx(req);
srctx->orig_src = req->src;
- srctx->data = kzalloc((req->nbytes + 64), GFP_KERNEL);
+ srctx->data = kzalloc((req->nbytes + 64), GFP_ATOMIC);
if (srctx->data == NULL) {
pr_err("Mem Alloc fail rctx->data, err %ld for 0x%x\n",
PTR_ERR(srctx->data), (req->nbytes + 64));
@@ -2355,7 +2442,8 @@
}
num_sg = qcrypto_count_sg(req->src, req->nbytes);
- bytes = sg_copy_to_buffer(req->src, num_sg, srctx->data, req->nbytes);
+ bytes = qcrypto_sg_copy_to_buffer(req->src, num_sg, srctx->data,
+ req->nbytes);
if (bytes != req->nbytes)
pr_warn("bytes copied=0x%x bytes to copy= 0x%x", bytes,
req->nbytes);
@@ -2390,7 +2478,7 @@
if (total <= sha_block_size) {
k_src = &sha_ctx->trailing_buf[sha_ctx->trailing_buf_len];
num_sg = qcrypto_count_sg(req->src, len);
- bytes = sg_copy_to_buffer(req->src, num_sg, k_src, len);
+ bytes = qcrypto_sg_copy_to_buffer(req->src, num_sg, k_src, len);
sha_ctx->trailing_buf_len = total;
if (sha_ctx->alg == QCE_HASH_SHA1)
@@ -2431,13 +2519,13 @@
if (sha_ctx->trailing_buf_len) {
if (cp->ce_support.aligned_only) {
sha_ctx->sg = kzalloc(sizeof(struct scatterlist),
- GFP_KERNEL);
+ GFP_ATOMIC);
if (sha_ctx->sg == NULL) {
pr_err("MemAlloc fail sha_ctx->sg, error %ld\n",
PTR_ERR(sha_ctx->sg));
return -ENOMEM;
}
- rctx->data2 = kzalloc((req->nbytes + 64), GFP_KERNEL);
+ rctx->data2 = kzalloc((req->nbytes + 64), GFP_ATOMIC);
if (rctx->data2 == NULL) {
pr_err("Mem Alloc fail srctx->data2, err %ld\n",
PTR_ERR(rctx->data2));
@@ -2458,7 +2546,7 @@
} else {
sg_mark_end(sg_last);
sha_ctx->sg = kzalloc(2 * (sizeof(struct scatterlist)),
- GFP_KERNEL);
+ GFP_ATOMIC);
if (sha_ctx->sg == NULL) {
pr_err("MEMalloc fail sha_ctx->sg, error %ld\n",
PTR_ERR(sha_ctx->sg));
@@ -2688,7 +2776,7 @@
struct crypto_stat *pstat;
int ret = 0;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
pstat->sha1_hmac_digest++;
_sha_init(sha_ctx);
@@ -2715,7 +2803,7 @@
struct crypto_stat *pstat;
int ret = 0;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
pstat->sha256_hmac_digest++;
_sha_init(sha_ctx);
@@ -2853,10 +2941,9 @@
static int _sha1_hmac_digest(struct ahash_request *req)
{
struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
- struct crypto_priv *cp = sha_ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
pstat->sha1_hmac_digest++;
_sha_init(sha_ctx);
@@ -2871,10 +2958,9 @@
static int _sha256_hmac_digest(struct ahash_request *req)
{
struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(req->base.tfm);
- struct crypto_priv *cp = sha_ctx->cp;
struct crypto_stat *pstat;
- pstat = &_qcrypto_stat[cp->pdev->id];
+ pstat = &_qcrypto_stat;
pstat->sha256_hmac_digest++;
_sha_init(sha_ctx);
@@ -2886,6 +2972,99 @@
return _sha_digest(req);
}
+int qcrypto_cipher_set_flag(struct ablkcipher_request *req, unsigned int flags)
+{
+ struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct crypto_priv *cp = ctx->cp;
+
+ if ((flags & QCRYPTO_CTX_USE_HW_KEY) &&
+ (cp->platform_support.hw_key_support == false)) {
+ pr_err("%s HW key usage not supported\n", __func__);
+ return -EINVAL;
+ }
+ if (((flags | ctx->flags) & QCRYPTO_CTX_KEY_MASK) ==
+ QCRYPTO_CTX_KEY_MASK) {
+ pr_err("%s Cannot set all key flags\n", __func__);
+ return -EINVAL;
+ }
+
+ ctx->flags |= flags;
+ return 0;
+};
+EXPORT_SYMBOL(qcrypto_cipher_set_flag);
+
+int qcrypto_aead_set_flag(struct aead_request *req, unsigned int flags)
+{
+ struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct crypto_priv *cp = ctx->cp;
+
+ if ((flags & QCRYPTO_CTX_USE_HW_KEY) &&
+ (cp->platform_support.hw_key_support == false)) {
+ pr_err("%s HW key usage not supported\n", __func__);
+ return -EINVAL;
+ }
+ if (((flags | ctx->flags) & QCRYPTO_CTX_KEY_MASK) ==
+ QCRYPTO_CTX_KEY_MASK) {
+ pr_err("%s Cannot set all key flags\n", __func__);
+ return -EINVAL;
+ }
+
+ ctx->flags |= flags;
+ return 0;
+};
+EXPORT_SYMBOL(qcrypto_aead_set_flag);
+
+int qcrypto_ahash_set_flag(struct ahash_request *req, unsigned int flags)
+{
+ struct qcrypto_sha_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct crypto_priv *cp = ctx->cp;
+
+ if ((flags & QCRYPTO_CTX_USE_HW_KEY) &&
+ (cp->platform_support.hw_key_support == false)) {
+ pr_err("%s HW key usage not supported\n", __func__);
+ return -EINVAL;
+ }
+ if (((flags | ctx->flags) & QCRYPTO_CTX_KEY_MASK) ==
+ QCRYPTO_CTX_KEY_MASK) {
+ pr_err("%s Cannot set all key flags\n", __func__);
+ return -EINVAL;
+ }
+
+ ctx->flags |= flags;
+ return 0;
+};
+EXPORT_SYMBOL(qcrypto_ahash_set_flag);
+
+int qcrypto_cipher_clear_flag(struct ablkcipher_request *req,
+ unsigned int flags)
+{
+ struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+
+ ctx->flags &= ~flags;
+ return 0;
+
+};
+EXPORT_SYMBOL(qcrypto_cipher_clear_flag);
+
+int qcrypto_aead_clear_flag(struct aead_request *req, unsigned int flags)
+{
+ struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+
+ ctx->flags &= ~flags;
+ return 0;
+
+};
+EXPORT_SYMBOL(qcrypto_aead_clear_flag);
+
+int qcrypto_ahash_clear_flag(struct ahash_request *req, unsigned int flags)
+{
+ struct qcrypto_sha_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+
+ ctx->flags &= ~flags;
+ return 0;
+};
+EXPORT_SYMBOL(qcrypto_ahash_clear_flag);
+
static struct ahash_alg _qcrypto_ahash_algos[] = {
{
.init = _sha1_init,
@@ -3327,12 +3506,6 @@
int i;
struct msm_ce_hw_support *platform_support;
- if (pdev->id >= MAX_CRYPTO_DEVICE) {
- pr_err("%s: device id %d exceeds allowed %d\n",
- __func__, pdev->id, MAX_CRYPTO_DEVICE);
- return -ENOENT;
- }
-
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
if (!cp) {
pr_err("qcrypto Memory allocation of q_alg FAIL, error %ld\n",
@@ -3357,19 +3530,17 @@
cp->pdev = pdev;
qce_hw_support(cp->qce, &cp->ce_support);
if (cp->ce_support.bam) {
- cp->platform_support.ce_shared = 0;
+ cp->platform_support.ce_shared = cp->ce_support.is_shared;
cp->platform_support.shared_ce_resource = 0;
- cp->platform_support.hw_key_support = 0;
+ cp->platform_support.hw_key_support = cp->ce_support.hw_key;
cp->platform_support.bus_scale_table = NULL;
cp->platform_support.sha_hmac = 1;
- if (cp->ce_support.is_shared == false) {
- cp->platform_support.bus_scale_table =
- (struct msm_bus_scale_pdata *)
- msm_bus_cl_get_pdata(pdev);
- if (!cp->platform_support.bus_scale_table)
- pr_warn("bus_scale_table is NULL\n");
- }
+ cp->platform_support.bus_scale_table =
+ (struct msm_bus_scale_pdata *)
+ msm_bus_cl_get_pdata(pdev);
+ if (!cp->platform_support.bus_scale_table)
+ pr_warn("bus_scale_table is NULL\n");
} else {
platform_support =
(struct msm_ce_hw_support *)pdev->dev.platform_data;
@@ -3572,7 +3743,7 @@
},
};
-static int _debug_qcrypto[MAX_CRYPTO_DEVICE];
+static int _debug_qcrypto;
static int _debug_stats_open(struct inode *inode, struct file *file)
{
@@ -3599,9 +3770,7 @@
size_t count, loff_t *ppos)
{
- int qcrypto = *((int *) file->private_data);
-
- memset((char *)&_qcrypto_stat[qcrypto], 0, sizeof(struct crypto_stat));
+ memset((char *)&_qcrypto_stat, 0, sizeof(struct crypto_stat));
return count;
};
@@ -3615,7 +3784,6 @@
{
int rc;
char name[DEBUG_MAX_FNAME];
- int i;
struct dentry *dent;
_debug_dent = debugfs_create_dir("qcrypto", NULL);
@@ -3625,17 +3793,15 @@
return PTR_ERR(_debug_dent);
}
- for (i = 0; i < MAX_CRYPTO_DEVICE; i++) {
- snprintf(name, DEBUG_MAX_FNAME-1, "stats-%d", i+1);
- _debug_qcrypto[i] = i;
- dent = debugfs_create_file(name, 0644, _debug_dent,
- &_debug_qcrypto[i], &_debug_stats_ops);
- if (dent == NULL) {
- pr_err("qcrypto debugfs_create_file fail, error %ld\n",
- PTR_ERR(dent));
- rc = PTR_ERR(dent);
- goto err;
- }
+ snprintf(name, DEBUG_MAX_FNAME-1, "stats-%d", 1);
+ _debug_qcrypto = 0;
+ dent = debugfs_create_file(name, 0644, _debug_dent,
+ &_debug_qcrypto, &_debug_stats_ops);
+ if (dent == NULL) {
+ pr_err("qcrypto debugfs_create_file fail, error %ld\n",
+ PTR_ERR(dent));
+ rc = PTR_ERR(dent);
+ goto err;
}
return 0;
err:
diff --git a/drivers/crypto/msm/qcryptohw_50.h b/drivers/crypto/msm/qcryptohw_50.h
index 245d737..e93e25c 100644
--- a/drivers/crypto/msm/qcryptohw_50.h
+++ b/drivers/crypto/msm/qcryptohw_50.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -14,6 +14,10 @@
#define _DRIVERS_CRYPTO_MSM_QCRYPTOHW_50_H_
+#define CRYPTO_BAM_CNFG_BITS_REG 0x0007C
+#define CRYPTO_BAM_CD_ENABLE 27
+#define CRYPTO_BAM_CD_ENABLE_MASK (1 << CRYPTO_BAM_CD_ENABLE)
+
#define QCE_AUTH_REG_BYTE_COUNT 4
#define CRYPTO_VERSION_REG 0x1A000
@@ -28,7 +32,7 @@
#define CRYPTO_DATA_OUT3_REG 0x1A02C
#define CRYPTO_STATUS_REG 0x1A100
-#define CRYPTO_STATUS2_REG 0x1A100
+#define CRYPTO_STATUS2_REG 0x1A104
#define CRYPTO_ENGINES_AVAIL 0x1A108
#define CRYPTO_FIFO_SIZES_REG 0x1A10C
@@ -64,9 +68,9 @@
#define CRYPTO_ENCR_PIPE0_KEY2_REG 0x1E008
#define CRYPTO_ENCR_PIPE0_KEY3_REG 0x1E00C
#define CRYPTO_ENCR_PIPE0_KEY4_REG 0x1E010
-#define CRYPTO_ENCR_PIPE0_KEY5_REG 0x1E004
-#define CRYPTO_ENCR_PIPE0_KEY6_REG 0x1E008
-#define CRYPTO_ENCR_PIPE0_KEY7_REG 0x1E00C
+#define CRYPTO_ENCR_PIPE0_KEY5_REG 0x1E014
+#define CRYPTO_ENCR_PIPE0_KEY6_REG 0x1E018
+#define CRYPTO_ENCR_PIPE0_KEY7_REG 0x1E01C
#define CRYPTO_ENCR_PIPE1_KEY0_REG 0x1E020
#define CRYPTO_ENCR_PIPE1_KEY1_REG 0x1E024
@@ -185,56 +189,56 @@
#define CRYPTO_AUTH_PIPE0_KEY14_REG 0x1E838
#define CRYPTO_AUTH_PIPE0_KEY15_REG 0x1E83C
-#define CRYPTO_AUTH_PIPE1_KEY0_REG 0x1E800
-#define CRYPTO_AUTH_PIPE1_KEY1_REG 0x1E804
-#define CRYPTO_AUTH_PIPE1_KEY2_REG 0x1E808
-#define CRYPTO_AUTH_PIPE1_KEY3_REG 0x1E80C
-#define CRYPTO_AUTH_PIPE1_KEY4_REG 0x1E810
-#define CRYPTO_AUTH_PIPE1_KEY5_REG 0x1E814
-#define CRYPTO_AUTH_PIPE1_KEY6_REG 0x1E818
-#define CRYPTO_AUTH_PIPE1_KEY7_REG 0x1E81C
-#define CRYPTO_AUTH_PIPE1_KEY8_REG 0x1E820
-#define CRYPTO_AUTH_PIPE1_KEY9_REG 0x1E824
-#define CRYPTO_AUTH_PIPE1_KEY10_REG 0x1E828
-#define CRYPTO_AUTH_PIPE1_KEY11_REG 0x1E82C
-#define CRYPTO_AUTH_PIPE1_KEY12_REG 0x1E830
-#define CRYPTO_AUTH_PIPE1_KEY13_REG 0x1E834
-#define CRYPTO_AUTH_PIPE1_KEY14_REG 0x1E838
-#define CRYPTO_AUTH_PIPE1_KEY15_REG 0x1E83C
+#define CRYPTO_AUTH_PIPE1_KEY0_REG 0x1E880
+#define CRYPTO_AUTH_PIPE1_KEY1_REG 0x1E884
+#define CRYPTO_AUTH_PIPE1_KEY2_REG 0x1E888
+#define CRYPTO_AUTH_PIPE1_KEY3_REG 0x1E88C
+#define CRYPTO_AUTH_PIPE1_KEY4_REG 0x1E890
+#define CRYPTO_AUTH_PIPE1_KEY5_REG 0x1E894
+#define CRYPTO_AUTH_PIPE1_KEY6_REG 0x1E898
+#define CRYPTO_AUTH_PIPE1_KEY7_REG 0x1E89C
+#define CRYPTO_AUTH_PIPE1_KEY8_REG 0x1E8A0
+#define CRYPTO_AUTH_PIPE1_KEY9_REG 0x1E8A4
+#define CRYPTO_AUTH_PIPE1_KEY10_REG 0x1E8A8
+#define CRYPTO_AUTH_PIPE1_KEY11_REG 0x1E8AC
+#define CRYPTO_AUTH_PIPE1_KEY12_REG 0x1E8B0
+#define CRYPTO_AUTH_PIPE1_KEY13_REG 0x1E8B4
+#define CRYPTO_AUTH_PIPE1_KEY14_REG 0x1E8B8
+#define CRYPTO_AUTH_PIPE1_KEY15_REG 0x1E8BC
-#define CRYPTO_AUTH_PIPE2_KEY0_REG 0x1E840
-#define CRYPTO_AUTH_PIPE2_KEY1_REG 0x1E844
-#define CRYPTO_AUTH_PIPE2_KEY2_REG 0x1E848
-#define CRYPTO_AUTH_PIPE2_KEY3_REG 0x1E84C
-#define CRYPTO_AUTH_PIPE2_KEY4_REG 0x1E850
-#define CRYPTO_AUTH_PIPE2_KEY5_REG 0x1E854
-#define CRYPTO_AUTH_PIPE2_KEY6_REG 0x1E858
-#define CRYPTO_AUTH_PIPE2_KEY7_REG 0x1E85C
-#define CRYPTO_AUTH_PIPE2_KEY8_REG 0x1E860
-#define CRYPTO_AUTH_PIPE2_KEY9_REG 0x1E864
-#define CRYPTO_AUTH_PIPE2_KEY10_REG 0x1E868
-#define CRYPTO_AUTH_PIPE2_KEY11_REG 0x1E86C
-#define CRYPTO_AUTH_PIPE2_KEY12_REG 0x1E870
-#define CRYPTO_AUTH_PIPE2_KEY13_REG 0x1E874
-#define CRYPTO_AUTH_PIPE2_KEY14_REG 0x1E878
-#define CRYPTO_AUTH_PIPE2_KEY15_REG 0x1E87C
+#define CRYPTO_AUTH_PIPE2_KEY0_REG 0x1E900
+#define CRYPTO_AUTH_PIPE2_KEY1_REG 0x1E904
+#define CRYPTO_AUTH_PIPE2_KEY2_REG 0x1E908
+#define CRYPTO_AUTH_PIPE2_KEY3_REG 0x1E90C
+#define CRYPTO_AUTH_PIPE2_KEY4_REG 0x1E910
+#define CRYPTO_AUTH_PIPE2_KEY5_REG 0x1E914
+#define CRYPTO_AUTH_PIPE2_KEY6_REG 0x1E918
+#define CRYPTO_AUTH_PIPE2_KEY7_REG 0x1E91C
+#define CRYPTO_AUTH_PIPE2_KEY8_REG 0x1E920
+#define CRYPTO_AUTH_PIPE2_KEY9_REG 0x1E924
+#define CRYPTO_AUTH_PIPE2_KEY10_REG 0x1E928
+#define CRYPTO_AUTH_PIPE2_KEY11_REG 0x1E92C
+#define CRYPTO_AUTH_PIPE2_KEY12_REG 0x1E930
+#define CRYPTO_AUTH_PIPE2_KEY13_REG 0x1E934
+#define CRYPTO_AUTH_PIPE2_KEY14_REG 0x1E938
+#define CRYPTO_AUTH_PIPE2_KEY15_REG 0x1E93C
-#define CRYPTO_AUTH_PIPE3_KEY0_REG 0x1E880
-#define CRYPTO_AUTH_PIPE3_KEY1_REG 0x1E884
-#define CRYPTO_AUTH_PIPE3_KEY2_REG 0x1E888
-#define CRYPTO_AUTH_PIPE3_KEY3_REG 0x1E88C
-#define CRYPTO_AUTH_PIPE3_KEY4_REG 0x1E890
-#define CRYPTO_AUTH_PIPE3_KEY5_REG 0x1E894
-#define CRYPTO_AUTH_PIPE3_KEY6_REG 0x1E898
-#define CRYPTO_AUTH_PIPE3_KEY7_REG 0x1E89C
-#define CRYPTO_AUTH_PIPE3_KEY8_REG 0x1E8A0
-#define CRYPTO_AUTH_PIPE3_KEY9_REG 0x1E8A4
-#define CRYPTO_AUTH_PIPE3_KEY10_REG 0x1E8A8
-#define CRYPTO_AUTH_PIPE3_KEY11_REG 0x1E8AC
-#define CRYPTO_AUTH_PIPE3_KEY12_REG 0x1E8B0
-#define CRYPTO_AUTH_PIPE3_KEY13_REG 0x1E8B4
-#define CRYPTO_AUTH_PIPE3_KEY14_REG 0x1E8B8
-#define CRYPTO_AUTH_PIPE3_KEY15_REG 0x1E8BC
+#define CRYPTO_AUTH_PIPE3_KEY0_REG 0x1E980
+#define CRYPTO_AUTH_PIPE3_KEY1_REG 0x1E984
+#define CRYPTO_AUTH_PIPE3_KEY2_REG 0x1E988
+#define CRYPTO_AUTH_PIPE3_KEY3_REG 0x1E98C
+#define CRYPTO_AUTH_PIPE3_KEY4_REG 0x1E990
+#define CRYPTO_AUTH_PIPE3_KEY5_REG 0x1E994
+#define CRYPTO_AUTH_PIPE3_KEY6_REG 0x1E998
+#define CRYPTO_AUTH_PIPE3_KEY7_REG 0x1E99C
+#define CRYPTO_AUTH_PIPE3_KEY8_REG 0x1E9A0
+#define CRYPTO_AUTH_PIPE3_KEY9_REG 0x1E9A4
+#define CRYPTO_AUTH_PIPE3_KEY10_REG 0x1E9A8
+#define CRYPTO_AUTH_PIPE3_KEY11_REG 0x1E9AC
+#define CRYPTO_AUTH_PIPE3_KEY12_REG 0x1E9B0
+#define CRYPTO_AUTH_PIPE3_KEY13_REG 0x1E9B4
+#define CRYPTO_AUTH_PIPE3_KEY14_REG 0x1E9B8
+#define CRYPTO_AUTH_PIPE3_KEY15_REG 0x1E9BC
#define CRYPTO_AUTH_IV0_REG 0x1A310
@@ -293,6 +297,7 @@
#define CRYPTO_DOUT_SIZE_AVAIL_MASK (0x1F << CRYPTO_DOUT_SIZE_AVAIL)
#define CRYPTO_DIN_SIZE_AVAIL 21 /* bit 21-25 */
#define CRYPTO_DIN_SIZE_AVAIL_MASK (0x1F << CRYPTO_DIN_SIZE_AVAIL)
+#define CRYPTO_HSD_ERR 20
#define CRYPTO_ACCESS_VIOL 19
#define CRYPTO_PIPE_ACTIVE_ERR 18
#define CRYPTO_CFG_CHNG_ERR 17
@@ -375,7 +380,7 @@
#define CRYPTO_FIRST 17
#define CRYPTO_LAST 16
-#define CRYPTO_AUTH_POS 15 /* bit 15 .. 14*/
+#define CRYPTO_AUTH_POS 14 /* bit 15 .. 14*/
#define CRYPTO_AUTH_POS_MASK (0x3 << CRYPTO_AUTH_POS)
#define CRYPTO_AUTH_POS_BEFORE 0
#define CRYPTO_AUTH_POS_AFTER 1
@@ -421,6 +426,7 @@
#define CRYPTO_AUTH_ALG_AES 2
#define CRYPTO_AUTH_ALG_KASUMI 3
#define CRYPTO_AUTH_ALG_SNOW3G 4
+#define CRYPTO_AUTH_ALG_ZUC 5
/* encr_xts_du_size reg */
#define CRYPTO_ENCR_XTS_DU_SIZE 0 /* bit 19-0 */
@@ -471,33 +477,49 @@
#define CRYPTO_ENCR_KEY_SZ_3DES 1
#define CRYPTO_ENCR_KEY_SZ_AES128 0
#define CRYPTO_ENCR_KEY_SZ_AES256 2
-#define CRYPTO_ENCR_KEY_SZ_UEA1 0
-#define CRYPTO_ENCR_KEY_SZ_UEA2 1
#define CRYPTO_ENCR_ALG 0 /* bit 2-0 */
#define CRYPTO_ENCR_ALG_MASK (7 << CRYPTO_ENCR_ALG)
#define CRYPTO_ENCR_ALG_NONE 0
#define CRYPTO_ENCR_ALG_DES 1
#define CRYPTO_ENCR_ALG_AES 2
-#define CRYPTO_ENCR_ALG_KASUMI 3
+#define CRYPTO_ENCR_ALG_KASUMI 4
#define CRYPTO_ENCR_ALG_SNOW_3G 5
+#define CRYPTO_ENCR_ALG_ZUC 6
/* goproc reg */
#define CRYPTO_GO 0
#define CRYPTO_CLR_CNTXT 1
#define CRYPTO_RESULTS_DUMP 2
+/* F8 definition of CRYPTO_ENCR_CNTR1_IV1 REG */
+#define CRYPTO_CNTR1_IV1_REG_F8_PKT_CNT 16 /* bit 31 - 16 */
+#define CRYPTO_CNTR1_IV1_REG_F8_PKT_CNT_MASK \
+ (0xffff << CRYPTO_CNTR1_IV1_REG_F8_PKT_CNT)
+
+#define CRYPTO_CNTR1_IV1_REG_F8_BEARER 0 /* bit 4 - 0 */
+#define CRYPTO_CNTR1_IV1_REG_F8_BEARER_MASK \
+ (0x1f << CRYPTO_CNTR1_IV1_REG_F8_BEARER)
+
+/* F9 definition of CRYPTO_AUTH_IV4 REG */
+#define CRYPTO_AUTH_IV4_REG_F9_VALID_BIS 0 /* bit 2 - 0 */
+#define CRYPTO_AUTH_IV4_REG_F9_VALID_BIS_MASK \
+ (0x7 << CRYPTO_AUTH_IV4_REG_F9_VALID_BIS)
/* engines_avail */
#define CRYPTO_ENCR_AES_SEL 0
-#define CRYPTO_DES_SEL 3
-#define CRYPTO_ENCR_SNOW3G_SEL 4
-#define CRYPTO_ENCR_KASUMI_SEL 5
-#define CRYPTO_SHA_SEL 6
-#define CRYPTO_SHA512_SEL 7
-#define CRYPTO_AUTH_AES_SEL 8
-#define CRYPTO_AUTH_SNOW3G_SEL 9
-#define CRYPTO_AUTH_KASUMI_SEL 10
-#define CRYPTO_BAM_SEL 11
-
+#define CRYPTO_DES_SEL 1
+#define CRYPTO_ENCR_SNOW3G_SEL 2
+#define CRYPTO_ENCR_KASUMI_SEL 3
+#define CRYPTO_SHA_SEL 4
+#define CRYPTO_SHA512_SEL 5
+#define CRYPTO_AUTH_AES_SEL 6
+#define CRYPTO_AUTH_SNOW3G_SEL 7
+#define CRYPTO_AUTH_KASUMI_SEL 8
+#define CRYPTO_BAM_PIPE_SETS 9 /* bit 12 - 9 */
+#define CRYPTO_AXI_WR_BEATS 13 /* bit 18 - 13 */
+#define CRYPTO_AXI_RD_BEATS 19 /* bit 24 - 19 */
+#define CRYPTO_ENCR_ZUC_SEL 26
+#define CRYPTO_AUTH_ZUC_SEL 27
+#define CRYPTO_ZUC_ENABLE 28
#endif /* _DRIVERS_CRYPTO_MSM_QCRYPTOHW_50_H_ */
diff --git a/drivers/gpio/gpio-msm-v1.c b/drivers/gpio/gpio-msm-v1.c
index f28ebb4..058a3f9 100644
--- a/drivers/gpio/gpio-msm-v1.c
+++ b/drivers/gpio/gpio-msm-v1.c
@@ -1,7 +1,7 @@
/* linux/arch/arm/mach-msm/gpio.c
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -24,7 +24,7 @@
#include <asm/mach/irq.h>
#include <mach/gpiomux.h>
#include <mach/msm_iomap.h>
-#include <mach/msm_smsm.h>
+#include <mach/msm_smem.h>
#include <mach/proc_comm.h>
diff --git a/drivers/gpu/ion/Kconfig b/drivers/gpu/ion/Kconfig
index 39133b5..5bb254b 100644
--- a/drivers/gpu/ion/Kconfig
+++ b/drivers/gpu/ion/Kconfig
@@ -16,12 +16,3 @@
depends on ARCH_MSM && ION
help
Choose this option if you wish to use ion on an MSM target.
-
-config ION_LEAK_CHECK
- bool "Check for leaked Ion buffers (debugging)"
- depends on ION
- help
- Choose this option if you wish to enable checking for leaked
- ion buffers at runtime. Choosing this option will also add a
- debugfs node under the ion directory that can be used to
- enable/disable the leak checking.
diff --git a/drivers/gpu/ion/Makefile b/drivers/gpu/ion/Makefile
index d7ff73a..0e460c8 100644
--- a/drivers/gpu/ion/Makefile
+++ b/drivers/gpu/ion/Makefile
@@ -1,6 +1,5 @@
-obj-$(CONFIG_ION) += ion.o ion_heap.o ion_system_heap.o ion_carveout_heap.o \
- ion_iommu_heap.o ion_cp_heap.o ion_removed_heap.o \
- ion_page_pool.o ion_chunk_heap.o
+obj-$(CONFIG_ION) += ion.o ion_heap.o ion_page_pool.o ion_system_heap.o \
+ ion_carveout_heap.o ion_chunk_heap.o
obj-$(CONFIG_CMA) += ion_cma_heap.o ion_cma_secure_heap.o
obj-$(CONFIG_ION_TEGRA) += tegra/
-obj-$(CONFIG_ION_MSM) += msm/
+obj-$(CONFIG_ION_MSM) += ion_iommu_heap.o ion_cp_heap.o ion_removed_heap.o msm/
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index 4282f02..250b387 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -16,7 +16,6 @@
*
*/
-#include <linux/module.h>
#include <linux/device.h>
#include <linux/file.h>
#include <linux/freezer.h>
@@ -27,6 +26,7 @@
#include <linux/list.h>
#include <linux/memblock.h>
#include <linux/miscdevice.h>
+#include <linux/export.h>
#include <linux/mm.h>
#include <linux/mm_types.h>
#include <linux/rbtree.h>
@@ -41,7 +41,6 @@
#include <trace/events/kmem.h>
-#include <mach/iommu_domains.h>
#include "ion_priv.h"
/**
@@ -83,7 +82,6 @@
struct ion_device *dev;
struct rb_root handles;
struct mutex lock;
- unsigned int heap_type_mask;
char *name;
struct task_struct *task;
pid_t pid;
@@ -108,7 +106,6 @@
struct ion_buffer *buffer;
struct rb_node node;
unsigned int kmap_cnt;
- unsigned int iommu_map_cnt;
};
bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer)
@@ -545,8 +542,8 @@
mutex_lock(&client->lock);
valid_handle = ion_handle_validate(client, handle);
if (!valid_handle) {
- mutex_unlock(&client->lock);
WARN(1, "%s: invalid handle passed to free.\n", __func__);
+ mutex_unlock(&client->lock);
return;
}
ion_handle_put(handle);
@@ -678,30 +675,21 @@
struct ion_client *client = s->private;
struct rb_node *n;
- seq_printf(s, "%16.16s: %16.16s : %16.16s : %12.12s : %12.12s : %s\n",
+ seq_printf(s, "%16.16s: %16.16s : %16.16s : %12.12s\n",
"heap_name", "size_in_bytes", "handle refcount",
- "buffer", "physical", "[domain,partition] - virt");
+ "buffer");
mutex_lock(&client->lock);
for (n = rb_first(&client->handles); n; n = rb_next(n)) {
struct ion_handle *handle = rb_entry(n, struct ion_handle,
node);
- enum ion_heap_type type = handle->buffer->heap->type;
-
seq_printf(s, "%16.16s: %16x : %16d : %12p",
handle->buffer->heap->name,
handle->buffer->size,
atomic_read(&handle->ref.refcount),
handle->buffer);
- if (type == ION_HEAP_TYPE_SYSTEM_CONTIG ||
- type == ION_HEAP_TYPE_CARVEOUT ||
- type == (enum ion_heap_type) ION_HEAP_TYPE_CP)
- seq_printf(s, " : %12pa", &handle->buffer->priv_phys);
- else
- seq_printf(s, " : %12s", "N/A");
-
seq_printf(s, "\n");
}
mutex_unlock(&client->lock);
@@ -1287,8 +1275,8 @@
ion_free(client, data.handle);
break;
}
- case ION_IOC_MAP:
case ION_IOC_SHARE:
+ case ION_IOC_MAP:
{
struct ion_fd_data data;
if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
@@ -1391,7 +1379,7 @@
};
static size_t ion_debug_heap_total(struct ion_client *client,
- enum ion_heap_ids id)
+ unsigned int id)
{
size_t size = 0;
struct rb_node *n;
@@ -1560,7 +1548,6 @@
size_t total_size = 0;
size_t total_orphaned_size = 0;
- mutex_lock(&dev->buffer_lock);
seq_printf(s, "%16.s %16.s %16.s\n", "client", "pid", "size");
seq_printf(s, "----------------------------------------------------\n");
@@ -1584,25 +1571,31 @@
seq_printf(s, "----------------------------------------------------\n");
seq_printf(s, "orphaned allocations (info is from last known client):"
"\n");
+ mutex_lock(&dev->buffer_lock);
for (n = rb_first(&dev->buffers); n; n = rb_next(n)) {
struct ion_buffer *buffer = rb_entry(n, struct ion_buffer,
node);
- if (buffer->heap->type == heap->type)
- total_size += buffer->size;
+ if (buffer->heap->id != heap->id)
+ continue;
+ total_size += buffer->size;
if (!buffer->handle_count) {
- seq_printf(s, "%16.s %16u %16u\n", buffer->task_comm,
- buffer->pid, buffer->size);
+ seq_printf(s, "%16.s %16u %16u %d %d\n", buffer->task_comm,
+ buffer->pid, buffer->size, buffer->kmap_cnt,
+ atomic_read(&buffer->ref.refcount));
total_orphaned_size += buffer->size;
}
}
+ mutex_unlock(&dev->buffer_lock);
seq_printf(s, "----------------------------------------------------\n");
seq_printf(s, "%16.s %16u\n", "total orphaned",
total_orphaned_size);
seq_printf(s, "%16.s %16u\n", "total ", total_size);
seq_printf(s, "----------------------------------------------------\n");
+ if (heap->debug_show)
+ heap->debug_show(heap, s, unused);
+
ion_heap_print_debug(s, heap);
- mutex_unlock(&dev->buffer_lock);
return 0;
}
@@ -1662,8 +1655,8 @@
return false;
rt_mutex_lock(&heap->lock);
list_for_each_entry_safe(buffer, tmp, &heap->free_list, list) {
- _ion_buffer_destroy(buffer);
list_del(&buffer->list);
+ _ion_buffer_destroy(buffer);
}
BUG_ON(!list_empty(&heap->free_list));
rt_mutex_unlock(&heap->lock);
diff --git a/drivers/gpu/ion/ion_carveout_heap.c b/drivers/gpu/ion/ion_carveout_heap.c
index 0dd3054..08921299 100644
--- a/drivers/gpu/ion/ion_carveout_heap.c
+++ b/drivers/gpu/ion/ion_carveout_heap.c
@@ -37,7 +37,6 @@
ion_phys_addr_t base;
unsigned long allocated_bytes;
unsigned long total_size;
- unsigned int has_outer_cache;
};
ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap,
@@ -254,7 +253,6 @@
carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT;
carveout_heap->allocated_bytes = 0;
carveout_heap->total_size = heap_data->size;
- carveout_heap->has_outer_cache = heap_data->has_outer_cache;
return &carveout_heap->heap;
}
diff --git a/drivers/gpu/ion/ion_cma_heap.c b/drivers/gpu/ion/ion_cma_heap.c
index 193f4d4..e7f7836 100644
--- a/drivers/gpu/ion/ion_cma_heap.c
+++ b/drivers/gpu/ion/ion_cma_heap.c
@@ -115,6 +115,7 @@
dev_dbg(dev, "Release buffer %p\n", buffer);
/* release memory */
dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle);
+ sg_free_table(info->table);
/* release sg table */
kfree(info->table);
kfree(info);
diff --git a/drivers/gpu/ion/ion_cma_secure_heap.c b/drivers/gpu/ion/ion_cma_secure_heap.c
index e1b3eea..b3960b2 100644
--- a/drivers/gpu/ion/ion_cma_secure_heap.c
+++ b/drivers/gpu/ion/ion_cma_secure_heap.c
@@ -154,6 +154,7 @@
dev_dbg(dev, "Release buffer %p\n", buffer);
/* release memory */
dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle);
+ sg_free_table(info->table);
/* release sg table */
kfree(info->table);
kfree(info);
diff --git a/drivers/gpu/ion/ion_iommu_heap.c b/drivers/gpu/ion/ion_iommu_heap.c
index 53d853d..b1c1c5d 100644
--- a/drivers/gpu/ion/ion_iommu_heap.c
+++ b/drivers/gpu/ion/ion_iommu_heap.c
@@ -27,6 +27,7 @@
#include <asm/page.h>
#include <asm/cacheflush.h>
#include <mach/iommu_domains.h>
+#include <trace/events/kmem.h>
struct ion_iommu_heap {
struct ion_heap heap;
@@ -41,6 +42,7 @@
*/
struct ion_iommu_priv_data {
struct page **pages;
+ unsigned int pages_uses_vmalloc;
int nrpages;
unsigned long size;
};
@@ -83,9 +85,13 @@
} else {
gfp |= GFP_KERNEL;
}
+ trace_alloc_pages_iommu_start(gfp, orders[i]);
page = alloc_pages(gfp, orders[i]);
- if (!page)
+ trace_alloc_pages_iommu_end(gfp, orders[i]);
+ if (!page) {
+ trace_alloc_pages_iommu_fail(gfp, orders[i]);
continue;
+ }
info = kmalloc(sizeof(struct page_info), GFP_KERNEL);
info->page = page;
@@ -113,6 +119,7 @@
unsigned int npages_to_vmap, total_pages, num_large_pages = 0;
unsigned long size_remaining = PAGE_ALIGN(size);
unsigned int max_order = ION_IS_CACHED(flags) ? 0 : orders[0];
+ unsigned int page_tbl_size;
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
@@ -134,8 +141,24 @@
data->size = PFN_ALIGN(size);
data->nrpages = data->size >> PAGE_SHIFT;
- data->pages = kzalloc(sizeof(struct page *)*data->nrpages,
- GFP_KERNEL);
+ data->pages_uses_vmalloc = 0;
+ page_tbl_size = sizeof(struct page *) * data->nrpages;
+
+ if (page_tbl_size > SZ_8K) {
+ /*
+ * Do fallback to ensure we have a balance between
+ * performance and availability.
+ */
+ data->pages = kmalloc(page_tbl_size,
+ __GFP_COMP | __GFP_NORETRY |
+ __GFP_NO_KSWAPD | __GFP_NOWARN);
+ if (!data->pages) {
+ data->pages = vmalloc(page_tbl_size);
+ data->pages_uses_vmalloc = 1;
+ }
+ } else {
+ data->pages = kmalloc(page_tbl_size, GFP_KERNEL);
+ }
if (!data->pages) {
ret = -ENOMEM;
goto err_free_data;
@@ -217,7 +240,10 @@
kfree(buffer->sg_table);
buffer->sg_table = 0;
err1:
- kfree(data->pages);
+ if (data->pages_uses_vmalloc)
+ vfree(data->pages);
+ else
+ kfree(data->pages);
err_free_data:
kfree(data);
@@ -248,7 +274,10 @@
sg_free_table(table);
kfree(table);
table = 0;
- kfree(data->pages);
+ if (data->pages_uses_vmalloc)
+ vfree(data->pages);
+ else
+ kfree(data->pages);
kfree(data);
}
diff --git a/drivers/gpu/ion/ion_page_pool.c b/drivers/gpu/ion/ion_page_pool.c
index e8b5489..495dd24 100644
--- a/drivers/gpu/ion/ion_page_pool.c
+++ b/drivers/gpu/ion/ion_page_pool.c
@@ -44,6 +44,7 @@
sg_init_table(&sg, 1);
sg_set_page(&sg, page, PAGE_SIZE << pool->order, 0);
+ sg_dma_address(&sg) = sg_phys(&sg);
dma_sync_sg_for_device(NULL, &sg, 1, DMA_BIDIRECTIONAL);
return page;
diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h
index 4b724df..e3fbbda 100644
--- a/drivers/gpu/ion/ion_priv.h
+++ b/drivers/gpu/ion/ion_priv.h
@@ -247,14 +247,12 @@
unsigned long align);
void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
unsigned long size);
-
/**
* The carveout heap returns physical addresses, since 0 may be a valid
* physical address, this is used to indicate allocation failed
*/
#define ION_CARVEOUT_ALLOCATE_FAIL -1
-
/**
* functions for creating and destroying a heap pool -- allows you
* to keep a pool of pre allocated memory to use from your heap. Keeping
diff --git a/drivers/gpu/ion/ion_system_heap.c b/drivers/gpu/ion/ion_system_heap.c
index 02f6d93..2bab7c4 100644
--- a/drivers/gpu/ion/ion_system_heap.c
+++ b/drivers/gpu/ion/ion_system_heap.c
@@ -25,15 +25,9 @@
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
-#include <linux/seq_file.h>
#include "ion_priv.h"
-#include <mach/memory.h>
-#include <asm/cacheflush.h>
-#include <linux/msm_ion.h>
#include <linux/dma-mapping.h>
-
-static atomic_t system_heap_allocated;
-static atomic_t system_contig_heap_allocated;
+#include <trace/events/kmem.h>
static unsigned int high_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO |
__GFP_NOWARN | __GFP_NORETRY |
@@ -85,11 +79,16 @@
if (order > 4)
gfp_flags = high_order_gfp_flags;
+ trace_alloc_pages_sys_start(gfp_flags, order);
page = alloc_pages(gfp_flags, order);
- if (!page)
+ trace_alloc_pages_sys_end(gfp_flags, order);
+ if (!page) {
+ trace_alloc_pages_sys_fail(gfp_flags, order);
return 0;
+ }
sg_init_table(&sg, 1);
sg_set_page(&sg, page, PAGE_SIZE << order, 0);
+ sg_dma_address(&sg) = sg_phys(&sg);
dma_sync_sg_for_device(NULL, &sg, 1, DMA_BIDIRECTIONAL);
}
if (!page)
@@ -207,7 +206,6 @@
}
buffer->priv_virt = table;
- atomic_add(size, &system_heap_allocated);
return 0;
err1:
kfree(table);
@@ -241,7 +239,6 @@
get_order(sg_dma_len(sg)));
sg_free_table(table);
kfree(table);
- atomic_sub(buffer->size, &system_heap_allocated);
}
struct sg_table *ion_system_heap_map_dma(struct ion_heap *heap,
@@ -256,15 +253,6 @@
return;
}
-static int ion_system_print_debug(struct ion_heap *heap, struct seq_file *s,
- const struct rb_root *unused)
-{
- seq_printf(s, "total bytes currently allocated: %lx\n",
- (unsigned long) atomic_read(&system_heap_allocated));
-
- return 0;
-}
-
static struct ion_heap_ops system_heap_ops = {
.allocate = ion_system_heap_allocate,
.free = ion_system_heap_free,
@@ -273,10 +261,29 @@
.map_kernel = ion_heap_map_kernel,
.unmap_kernel = ion_heap_unmap_kernel,
.map_user = ion_heap_map_user,
- .print_debug = ion_system_print_debug,
};
-struct ion_heap *ion_system_heap_create(struct ion_platform_heap *pheap)
+static int ion_system_heap_debug_show(struct ion_heap *heap, struct seq_file *s,
+ void *unused)
+{
+
+ struct ion_system_heap *sys_heap = container_of(heap,
+ struct ion_system_heap,
+ heap);
+ int i;
+ for (i = 0; i < num_orders; i++) {
+ struct ion_page_pool *pool = sys_heap->pools[i];
+ seq_printf(s, "%d order %u highmem pages in pool = %lu total\n",
+ pool->high_count, pool->order,
+ (1 << pool->order) * PAGE_SIZE * pool->high_count);
+ seq_printf(s, "%d order %u lowmem pages in pool = %lu total\n",
+ pool->low_count, pool->order,
+ (1 << pool->order) * PAGE_SIZE * pool->low_count);
+ }
+ return 0;
+}
+
+struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused)
{
struct ion_system_heap *heap;
int i;
@@ -302,6 +309,7 @@
goto err_create_pool;
heap->pools[i] = pool;
}
+ heap->heap.debug_show = ion_system_heap_debug_show;
return &heap->heap;
err_create_pool:
for (i = 0; i < num_orders; i++)
@@ -335,14 +343,12 @@
buffer->priv_virt = kzalloc(len, GFP_KERNEL);
if (!buffer->priv_virt)
return -ENOMEM;
- atomic_add(len, &system_contig_heap_allocated);
return 0;
}
void ion_system_contig_heap_free(struct ion_buffer *buffer)
{
kfree(buffer->priv_virt);
- atomic_sub(buffer->size, &system_contig_heap_allocated);
}
static int ion_system_contig_heap_phys(struct ion_heap *heap,
@@ -380,44 +386,6 @@
kfree(buffer->sg_table);
}
-int ion_system_contig_heap_map_user(struct ion_heap *heap,
- struct ion_buffer *buffer,
- struct vm_area_struct *vma)
-{
- unsigned long pfn = __phys_to_pfn(virt_to_phys(buffer->priv_virt));
-
- if (ION_IS_CACHED(buffer->flags))
- return remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff,
- vma->vm_end - vma->vm_start,
- vma->vm_page_prot);
- else {
- pr_err("%s: cannot map system heap uncached\n", __func__);
- return -EINVAL;
- }
-}
-
-static int ion_system_contig_print_debug(struct ion_heap *heap,
- struct seq_file *s,
- const struct rb_root *unused)
-{
- seq_printf(s, "total bytes currently allocated: %lx\n",
- (unsigned long) atomic_read(&system_contig_heap_allocated));
-
- return 0;
-}
-
-void *ion_system_contig_heap_map_kernel(struct ion_heap *heap,
- struct ion_buffer *buffer)
-{
- return buffer->priv_virt;
-}
-
-void ion_system_contig_heap_unmap_kernel(struct ion_heap *heap,
- struct ion_buffer *buffer)
-{
- return;
-}
-
static struct ion_heap_ops kmalloc_ops = {
.allocate = ion_system_contig_heap_allocate,
.free = ion_system_contig_heap_free,
@@ -426,11 +394,10 @@
.unmap_dma = ion_system_contig_heap_unmap_dma,
.map_kernel = ion_heap_map_kernel,
.unmap_kernel = ion_heap_unmap_kernel,
- .map_user = ion_system_contig_heap_map_user,
- .print_debug = ion_system_contig_print_debug,
+ .map_user = ion_heap_map_user,
};
-struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *pheap)
+struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *unused)
{
struct ion_heap *heap;
diff --git a/drivers/gpu/ion/msm/ion_cp_common.c b/drivers/gpu/ion/msm/ion_cp_common.c
index 58eca24..623a174 100644
--- a/drivers/gpu/ion/msm/ion_cp_common.c
+++ b/drivers/gpu/ion/msm/ion_cp_common.c
@@ -13,6 +13,7 @@
*/
#include <linux/memory_alloc.h>
+#include <linux/slab.h>
#include <linux/types.h>
#include <mach/scm.h>
#include <linux/highmem.h>
@@ -97,18 +98,17 @@
nchunks = size / V2_CHUNK_SIZE;
- chunk_list = allocate_contiguous_ebi(sizeof(unsigned long)*nchunks,
- SZ_4K, 0);
+ chunk_list = kmalloc(sizeof(unsigned long)*nchunks, GFP_KERNEL);
if (!chunk_list)
return -ENOMEM;
for (i = 0; i < nchunks; i++)
chunk_list[i] = phy_base + i * V2_CHUNK_SIZE;
- ret = ion_cp_change_chunks_state(memory_pool_node_paddr(chunk_list),
+ ret = ion_cp_change_chunks_state(__pa(chunk_list),
nchunks, V2_CHUNK_SIZE, usage, lock);
- free_contiguous_memory(chunk_list);
+ kfree(chunk_list);
return ret;
}
diff --git a/drivers/gpu/ion/msm/ion_iommu_map.c b/drivers/gpu/ion/msm/ion_iommu_map.c
index 5ce03db..0a4fe1f 100644
--- a/drivers/gpu/ion/msm/ion_iommu_map.c
+++ b/drivers/gpu/ion/msm/ion_iommu_map.c
@@ -11,6 +11,7 @@
*
*/
+#include <linux/dma-buf.h>
#include <linux/export.h>
#include <linux/iommu.h>
#include <linux/ion.h>
@@ -69,6 +70,7 @@
struct sg_table *table;
unsigned long size;
struct mutex lock;
+ struct dma_buf *dbuf;
};
static struct rb_root iommu_root;
@@ -85,9 +87,9 @@
parent = *p;
entry = rb_entry(parent, struct ion_iommu_meta, node);
- if (meta->handle < entry->handle) {
+ if (meta->table < entry->table) {
p = &(*p)->rb_left;
- } else if (meta->handle > entry->handle) {
+ } else if (meta->table > entry->table) {
p = &(*p)->rb_right;
} else {
pr_err("%s: handle %p already exists\n", __func__,
@@ -101,7 +103,7 @@
}
-static struct ion_iommu_meta *ion_iommu_meta_lookup(struct ion_handle *handle)
+static struct ion_iommu_meta *ion_iommu_meta_lookup(struct sg_table *table)
{
struct rb_root *root = &iommu_root;
struct rb_node **p = &root->rb_node;
@@ -112,9 +114,9 @@
parent = *p;
entry = rb_entry(parent, struct ion_iommu_meta, node);
- if (handle < entry->handle)
+ if (table < entry->table)
p = &(*p)->rb_left;
- else if (handle > entry->handle)
+ else if (table > entry->table)
p = &(*p)->rb_right;
else
return entry;
@@ -319,7 +321,8 @@
return ERR_PTR(ret);
}
-static struct ion_iommu_meta *ion_iommu_meta_create(struct ion_handle *handle,
+static struct ion_iommu_meta *ion_iommu_meta_create(struct ion_client *client,
+ struct ion_handle *handle,
struct sg_table *table,
unsigned long size)
{
@@ -333,6 +336,7 @@
meta->handle = handle;
meta->table = table;
meta->size = size;
+ meta->dbuf = ion_share_dma_buf(client, handle);
kref_init(&meta->ref);
mutex_init(&meta->lock);
ion_iommu_meta_add(meta);
@@ -347,6 +351,7 @@
rb_erase(&meta->node, &iommu_root);
+ dma_buf_put(meta->dbuf);
kfree(meta);
}
@@ -427,13 +432,13 @@
}
mutex_lock(&msm_iommu_map_mutex);
- iommu_meta = ion_iommu_meta_lookup(handle);
+ iommu_meta = ion_iommu_meta_lookup(table);
if (!iommu_meta)
- iommu_meta = ion_iommu_meta_create(handle, table, size);
+ iommu_meta = ion_iommu_meta_create(client, handle, table, size);
else
kref_get(&iommu_meta->ref);
-
+ BUG_ON(iommu_meta->size != size);
mutex_unlock(&msm_iommu_map_mutex);
iommu_map = ion_iommu_lookup(iommu_meta, domain_num, partition_num);
@@ -493,6 +498,7 @@
{
struct ion_iommu_map *iommu_map;
struct ion_iommu_meta *meta;
+ struct sg_table *table;
if (IS_ERR_OR_NULL(client)) {
pr_err("%s: client pointer is invalid\n", __func__);
@@ -503,13 +509,14 @@
return;
}
+ table = ion_sg_table(client, handle);
mutex_lock(&msm_iommu_map_mutex);
- meta = ion_iommu_meta_lookup(handle);
+ meta = ion_iommu_meta_lookup(table);
if (!meta) {
WARN(1, "%s: (%d,%d) was never mapped for %p\n", __func__,
domain_num, partition_num, handle);
- mutex_lock(&msm_iommu_map_mutex);
+ mutex_unlock(&msm_iommu_map_mutex);
goto out;
}
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index f43d276..4a45313 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -89,7 +89,7 @@
},
{
.id = ION_QSECOM_HEAP_ID,
- .type = ION_HEAP_TYPE_CARVEOUT,
+ .type = ION_HEAP_TYPE_DMA,
.name = ION_QSECOM_HEAP_NAME,
},
{
diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile
index 3441afa..fc66328 100644
--- a/drivers/gpu/msm/Makefile
+++ b/drivers/gpu/msm/Makefile
@@ -25,6 +25,7 @@
adreno_drawctxt.o \
adreno_postmortem.o \
adreno_snapshot.o \
+ adreno_coresight.o \
adreno_a2xx.o \
adreno_a2xx_trace.o \
adreno_a2xx_snapshot.o \
diff --git a/drivers/gpu/msm/a3xx_reg.h b/drivers/gpu/msm/a3xx_reg.h
index c768bb7..0c398c4 100644
--- a/drivers/gpu/msm/a3xx_reg.h
+++ b/drivers/gpu/msm/a3xx_reg.h
@@ -165,8 +165,16 @@
#define A3XX_RBBM_PERFCTR_PWR_0_HI 0x0EB
#define A3XX_RBBM_PERFCTR_PWR_1_LO 0x0EC
#define A3XX_RBBM_PERFCTR_PWR_1_HI 0x0ED
-#define A3XX_RBBM_DEBUG_BUS_CTL 0x111
-#define A3XX_RBBM_DEBUG_BUS_DATA_STATUS 0x112
+#define A3XX_RBBM_DEBUG_BUS_CTL 0x111
+#define A3XX_RBBM_DEBUG_BUS_DATA_STATUS 0x112
+#define A3XX_RBBM_DEBUG_BUS_STB_CTL0 0x11B
+#define A3XX_RBBM_DEBUG_BUS_STB_CTL1 0x11C
+#define A3XX_RBBM_INT_TRACE_BUS_CTL 0x11D
+#define A3XX_RBBM_EXT_TRACE_BUS_CTL 0x11E
+#define A3XX_RBBM_EXT_TRACE_STOP_CNT 0x11F
+#define A3XX_RBBM_EXT_TRACE_START_CNT 0x120
+#define A3XX_RBBM_EXT_TRACE_PERIOD_CNT 0x121
+#define A3XX_RBBM_EXT_TRACE_CMD 0x122
/* Following two are same as on A2XX, just in a different place */
#define A3XX_CP_PFP_UCODE_ADDR 0x1C9
@@ -179,6 +187,7 @@
#define A3XX_CP_MEQ_ADDR 0x1DA
#define A3XX_CP_MEQ_DATA 0x1DB
#define A3XX_CP_PERFCOUNTER_SELECT 0x445
+#define A3XX_CP_WFI_PEND_CTR 0x01F5
#define A3XX_CP_HW_FAULT 0x45C
#define A3XX_CP_AHB_FAULT 0x54D
#define A3XX_CP_PROTECT_CTRL 0x45E
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index d5904b9..b917248 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -18,6 +18,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/delay.h>
+#include <linux/of_coresight.h>
#include <mach/socinfo.h>
#include <mach/msm_bus_board.h>
@@ -101,13 +102,6 @@
.iomemname = KGSL_3D0_REG_MEMORY,
.shadermemname = KGSL_3D0_SHADER_MEMORY,
.ftbl = &adreno_functable,
-#ifdef CONFIG_HAS_EARLYSUSPEND
- .display_off = {
- .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING,
- .suspend = kgsl_early_suspend_driver,
- .resume = kgsl_late_resume_driver,
- },
-#endif
},
.gmem_base = 0,
.gmem_size = SZ_256K,
@@ -129,7 +123,7 @@
#define LONG_IB_DETECT_REG_INDEX_START 1
#define LONG_IB_DETECT_REG_INDEX_END 5
-unsigned int ft_detect_regs[] = {
+unsigned int ft_detect_regs[FT_DETECT_REGS_COUNT] = {
A3XX_RBBM_STATUS,
REG_CP_RB_RPTR, /* LONG_IB_DETECT_REG_INDEX_START */
REG_CP_IB1_BASE,
@@ -144,8 +138,6 @@
0
};
-const unsigned int ft_detect_regs_count = ARRAY_SIZE(ft_detect_regs);
-
/*
* This is the master list of all GPU cores that are supported by this
* driver.
@@ -172,6 +164,15 @@
/* version of pfp microcode that supports sync_lock
between CPU and GPU for IOMMU-v0 programming */
unsigned int sync_lock_pfp_ver;
+ /* PM4 jump table index */
+ unsigned int pm4_jt_idx;
+ /* PM4 jump table load addr */
+ unsigned int pm4_jt_addr;
+ /* PFP jump table index */
+ unsigned int pfp_jt_idx;
+ /* PFP jump table load addr */
+ unsigned int pfp_jt_addr;
+
} adreno_gpulist[] = {
{ ADRENO_REV_A200, 0, 2, ANY_ID, ANY_ID,
"yamato_pm4.fw", "yamato_pfp.fw", &adreno_a2xx_gpudev,
@@ -208,10 +209,11 @@
512, 0, 2, SZ_512K, 0x3FF037, 0x3FF016 },
{ ADRENO_REV_A330, 3, 3, 0, ANY_ID,
"a330_pm4.fw", "a330_pfp.fw", &adreno_a3xx_gpudev,
- 512, 0, 2, SZ_1M, NO_VER, NO_VER },
+ 512, 0, 2, SZ_1M, NO_VER, NO_VER, 0x8AD, 0x2E4, 0x201, 0x200 },
{ ADRENO_REV_A305B, 3, 0, 5, 0x10,
"a330_pm4.fw", "a330_pfp.fw", &adreno_a3xx_gpudev,
- 512, 0, 2, SZ_128K, NO_VER, NO_VER },
+ 512, 0, 2, SZ_128K, NO_VER, NO_VER, 0x8AD, 0x2E4,
+ 0x201, 0x200 },
{ ADRENO_REV_A305C, 3, 0, 5, 0x20,
"a300_pm4.fw", "a300_pfp.fw", &adreno_a3xx_gpudev,
512, 0, 2, SZ_128K, 0x3FF037, 0x3FF016 },
@@ -531,18 +533,17 @@
result = adreno_dev->gpudev->irq_handler(adreno_dev);
+ device->pwrctrl.irq_last = 1;
if (device->requested_state == KGSL_STATE_NONE) {
- if (device->pwrctrl.nap_allowed == true) {
- kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
- queue_work(device->work_queue, &device->idle_check_ws);
- } else if (device->pwrscale.policy != NULL) {
- queue_work(device->work_queue, &device->idle_check_ws);
- }
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
+ queue_work(device->work_queue, &device->idle_check_ws);
}
/* Reset the time-out in our idle timer */
mod_timer_pending(&device->idle_timer,
jiffies + device->pwrctrl.interval_timeout);
+ mod_timer_pending(&device->hang_timer,
+ (jiffies + msecs_to_jiffies(KGSL_TIMEOUT_PART)));
return result;
}
@@ -606,41 +607,15 @@
return result;
}
-static void adreno_iommu_setstate(struct kgsl_device *device,
- unsigned int context_id,
- uint32_t flags)
+static unsigned int _adreno_iommu_setstate_v0(struct kgsl_device *device,
+ unsigned int *cmds_orig,
+ phys_addr_t pt_val,
+ int num_iommu_units, uint32_t flags)
{
- unsigned int pt_val, reg_pt_val;
- unsigned int link[230];
- unsigned int *cmds = &link[0];
- int sizedwords = 0;
+ phys_addr_t reg_pt_val;
+ unsigned int *cmds = cmds_orig;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- int num_iommu_units, i;
- struct kgsl_context *context;
- struct adreno_context *adreno_ctx = NULL;
-
- /*
- * If we're idle and we don't need to use the GPU to save context
- * state, use the CPU instead of the GPU to reprogram the
- * iommu for simplicity's sake.
- */
- if (!adreno_dev->drawctxt_active || device->ftbl->isidle(device))
- return kgsl_mmu_device_setstate(&device->mmu, flags);
-
- num_iommu_units = kgsl_mmu_get_num_iommu_units(&device->mmu);
-
- context = idr_find(&device->context_idr, context_id);
- if (context == NULL)
- return;
- adreno_ctx = context->devctxt;
-
- if (kgsl_mmu_enable_clk(&device->mmu,
- KGSL_IOMMU_CONTEXT_USER))
- return;
-
- cmds += __adreno_add_idle_indirect_cmds(cmds,
- device->mmu.setstate_memory.gpuaddr +
- KGSL_IOMMU_SETSTATE_NOP_OFFSET);
+ int i;
if (cpu_is_msm8960())
cmds += adreno_add_change_mh_phys_limit_cmds(cmds, 0xFFFFF000,
@@ -657,16 +632,16 @@
/* Acquire GPU-CPU sync Lock here */
cmds += kgsl_mmu_sync_lock(&device->mmu, cmds);
- pt_val = kgsl_mmu_get_pt_base_addr(&device->mmu,
- device->mmu.hwpagetable);
if (flags & KGSL_MMUFLAGS_PTUPDATE) {
/*
* We need to perfrom the following operations for all
* IOMMU units
*/
for (i = 0; i < num_iommu_units; i++) {
- reg_pt_val = (pt_val + kgsl_mmu_get_pt_lsb(&device->mmu,
- i, KGSL_IOMMU_CONTEXT_USER));
+ reg_pt_val = kgsl_mmu_get_default_ttbr0(&device->mmu,
+ i, KGSL_IOMMU_CONTEXT_USER);
+ reg_pt_val &= ~KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
+ reg_pt_val |= (pt_val & KGSL_IOMMU_CTX_TTBR0_ADDR_MASK);
/*
* Set address of the new pagetable by writng to IOMMU
* TTBR0 register
@@ -695,8 +670,11 @@
* tlb flush
*/
for (i = 0; i < num_iommu_units; i++) {
- reg_pt_val = (pt_val + kgsl_mmu_get_pt_lsb(&device->mmu,
+ reg_pt_val = (pt_val + kgsl_mmu_get_default_ttbr0(
+ &device->mmu,
i, KGSL_IOMMU_CONTEXT_USER));
+ reg_pt_val &= ~KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
+ reg_pt_val |= (pt_val & KGSL_IOMMU_CTX_TTBR0_ADDR_MASK);
*cmds++ = cp_type3_packet(CP_MEM_WRITE, 2);
*cmds++ = kgsl_mmu_get_reg_gpuaddr(&device->mmu, i,
@@ -735,25 +713,184 @@
cmds += adreno_add_idle_cmds(adreno_dev, cmds);
- sizedwords += (cmds - &link[0]);
- if (sizedwords) {
- /* invalidate all base pointers */
- *cmds++ = cp_type3_packet(CP_INVALIDATE_STATE, 1);
- *cmds++ = 0x7fff;
- sizedwords += 2;
- /* This returns the per context timestamp but we need to
- * use the global timestamp for iommu clock disablement */
- adreno_ringbuffer_issuecmds(device, adreno_ctx,
- KGSL_CMD_FLAGS_PMODE,
- &link[0], sizedwords);
- kgsl_mmu_disable_clk_on_ts(&device->mmu,
- adreno_dev->ringbuffer.timestamp[KGSL_MEMSTORE_GLOBAL], true);
+ return cmds - cmds_orig;
+}
+
+static unsigned int _adreno_iommu_setstate_v1(struct kgsl_device *device,
+ unsigned int *cmds_orig,
+ phys_addr_t pt_val,
+ int num_iommu_units, uint32_t flags)
+{
+ phys_addr_t ttbr0_val;
+ unsigned int reg_pt_val;
+ unsigned int *cmds = cmds_orig;
+ int i;
+ unsigned int ttbr0, tlbiall, tlbstatus, tlbsync, mmu_ctrl;
+
+ for (i = 0; i < num_iommu_units; i++) {
+ ttbr0_val = kgsl_mmu_get_default_ttbr0(&device->mmu,
+ i, KGSL_IOMMU_CONTEXT_USER);
+ ttbr0_val &= ~KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
+ ttbr0_val |= (pt_val & KGSL_IOMMU_CTX_TTBR0_ADDR_MASK);
+ if (flags & KGSL_MMUFLAGS_PTUPDATE) {
+ mmu_ctrl = kgsl_mmu_get_reg_ahbaddr(
+ &device->mmu, i,
+ KGSL_IOMMU_CONTEXT_USER,
+ KGSL_IOMMU_IMPLDEF_MICRO_MMU_CTRL) >> 2;
+
+ ttbr0 = kgsl_mmu_get_reg_ahbaddr(&device->mmu, i,
+ KGSL_IOMMU_CONTEXT_USER,
+ KGSL_IOMMU_CTX_TTBR0) >> 2;
+
+ if (kgsl_mmu_hw_halt_supported(&device->mmu, i)) {
+ *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+ *cmds++ = 0;
+ /*
+ * glue commands together until next
+ * WAIT_FOR_ME
+ */
+ cmds += adreno_wait_reg_eq(cmds,
+ A3XX_CP_WFI_PEND_CTR, 1, 0xFFFFFFFF, 0xF);
+
+ /* set the iommu lock bit */
+ *cmds++ = cp_type3_packet(CP_REG_RMW, 3);
+ *cmds++ = mmu_ctrl;
+ /* AND to unmask the lock bit */
+ *cmds++ =
+ ~(KGSL_IOMMU_IMPLDEF_MICRO_MMU_CTRL_HALT);
+ /* OR to set the IOMMU lock bit */
+ *cmds++ =
+ KGSL_IOMMU_IMPLDEF_MICRO_MMU_CTRL_HALT;
+ /* wait for smmu to lock */
+ cmds += adreno_wait_reg_eq(cmds, mmu_ctrl,
+ KGSL_IOMMU_IMPLDEF_MICRO_MMU_CTRL_IDLE,
+ KGSL_IOMMU_IMPLDEF_MICRO_MMU_CTRL_IDLE, 0xF);
+ }
+ /* set ttbr0 */
+ if (sizeof(phys_addr_t) > sizeof(unsigned long)) {
+ reg_pt_val = ttbr0_val & 0xFFFFFFFF;
+ *cmds++ = cp_type0_packet(ttbr0, 1);
+ *cmds++ = reg_pt_val;
+ reg_pt_val = (unsigned int)
+ ((ttbr0_val & 0xFFFFFFFF00000000ULL) >> 32);
+ *cmds++ = cp_type0_packet(ttbr0 + 1, 1);
+ *cmds++ = reg_pt_val;
+ } else {
+ reg_pt_val = ttbr0_val;
+ *cmds++ = cp_type0_packet(ttbr0, 1);
+ *cmds++ = reg_pt_val;
+ }
+ if (kgsl_mmu_hw_halt_supported(&device->mmu, i)) {
+ /* unlock the IOMMU lock */
+ *cmds++ = cp_type3_packet(CP_REG_RMW, 3);
+ *cmds++ = mmu_ctrl;
+ /* AND to unmask the lock bit */
+ *cmds++ =
+ ~(KGSL_IOMMU_IMPLDEF_MICRO_MMU_CTRL_HALT);
+ /* OR with 0 so lock bit is unset */
+ *cmds++ = 0;
+ /* release all commands with wait_for_me */
+ *cmds++ = cp_type3_packet(CP_WAIT_FOR_ME, 1);
+ *cmds++ = 0;
+ }
+ }
+ if (flags & KGSL_MMUFLAGS_TLBFLUSH) {
+ tlbiall = kgsl_mmu_get_reg_ahbaddr(&device->mmu, i,
+ KGSL_IOMMU_CONTEXT_USER,
+ KGSL_IOMMU_CTX_TLBIALL) >> 2;
+ *cmds++ = cp_type0_packet(tlbiall, 1);
+ *cmds++ = 1;
+
+ tlbsync = kgsl_mmu_get_reg_ahbaddr(&device->mmu, i,
+ KGSL_IOMMU_CONTEXT_USER,
+ KGSL_IOMMU_CTX_TLBSYNC) >> 2;
+ *cmds++ = cp_type0_packet(tlbsync, 1);
+ *cmds++ = 0;
+
+ tlbstatus = kgsl_mmu_get_reg_ahbaddr(&device->mmu, i,
+ KGSL_IOMMU_CONTEXT_USER,
+ KGSL_IOMMU_CTX_TLBSTATUS) >> 2;
+ cmds += adreno_wait_reg_eq(cmds, tlbstatus, 0,
+ KGSL_IOMMU_CTX_TLBSTATUS_SACTIVE, 0xF);
+ }
}
+ return cmds - cmds_orig;
+}
+
+static void adreno_iommu_setstate(struct kgsl_device *device,
+ unsigned int context_id,
+ uint32_t flags)
+{
+ phys_addr_t pt_val;
+ unsigned int link[230];
+ unsigned int *cmds = &link[0];
+ int sizedwords = 0;
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ int num_iommu_units;
+ struct kgsl_context *context;
+ struct adreno_context *adreno_ctx = NULL;
+ struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+
+ if (!adreno_dev->drawctxt_active ||
+ KGSL_STATE_ACTIVE != device->state ||
+ !device->active_cnt ||
+ device->cff_dump_enable) {
+ kgsl_mmu_device_setstate(&device->mmu, flags);
+ return;
+ }
+ num_iommu_units = kgsl_mmu_get_num_iommu_units(&device->mmu);
+
+ context = idr_find(&device->context_idr, context_id);
+ if (context == NULL)
+ return;
+
+ kgsl_context_get(context);
+
+ adreno_ctx = context->devctxt;
+
+ if (kgsl_mmu_enable_clk(&device->mmu,
+ KGSL_IOMMU_CONTEXT_USER))
+ return;
+
+ pt_val = kgsl_mmu_get_pt_base_addr(&device->mmu,
+ device->mmu.hwpagetable);
+
+ cmds += __adreno_add_idle_indirect_cmds(cmds,
+ device->mmu.setstate_memory.gpuaddr +
+ KGSL_IOMMU_SETSTATE_NOP_OFFSET);
+
+ if (msm_soc_version_supports_iommu_v0())
+ cmds += _adreno_iommu_setstate_v0(device, cmds, pt_val,
+ num_iommu_units, flags);
+ else
+ cmds += _adreno_iommu_setstate_v1(device, cmds, pt_val,
+ num_iommu_units, flags);
+
+ sizedwords += (cmds - &link[0]);
+ if (sizedwords == 0) {
+ KGSL_DRV_ERR(device, "no commands generated\n");
+ BUG();
+ }
+ /* invalidate all base pointers */
+ *cmds++ = cp_type3_packet(CP_INVALIDATE_STATE, 1);
+ *cmds++ = 0x7fff;
+ sizedwords += 2;
if (sizedwords > (ARRAY_SIZE(link))) {
KGSL_DRV_ERR(device, "Temp command buffer overflow\n");
BUG();
}
+ /*
+ * This returns the per context timestamp but we need to
+ * use the global timestamp for iommu clock disablement
+ */
+ adreno_ringbuffer_issuecmds(device, adreno_ctx, KGSL_CMD_FLAGS_PMODE,
+ &link[0], sizedwords);
+
+ kgsl_mmu_disable_clk_on_ts(&device->mmu,
+ rb->timestamp[KGSL_MEMSTORE_GLOBAL], true);
+
+ kgsl_context_put(context);
}
static void adreno_gpummu_setstate(struct kgsl_device *device,
@@ -780,7 +917,7 @@
* writes For CFF dump we must idle and use the registers so that it is
* easier to filter out the mmu accesses from the dump
*/
- if (!kgsl_cff_dump_enable && adreno_dev->drawctxt_active) {
+ if (!device->cff_dump_enable && adreno_dev->drawctxt_active) {
context = idr_find(&device->context_idr, context_id);
if (context == NULL)
return;
@@ -991,6 +1128,10 @@
adreno_dev->pix_shader_start = adreno_gpulist[i].pix_shader_start;
adreno_dev->instruction_size = adreno_gpulist[i].instruction_size;
adreno_dev->gmem_size = adreno_gpulist[i].gmem_size;
+ adreno_dev->pm4_jt_idx = adreno_gpulist[i].pm4_jt_idx;
+ adreno_dev->pm4_jt_addr = adreno_gpulist[i].pm4_jt_addr;
+ adreno_dev->pfp_jt_idx = adreno_gpulist[i].pfp_jt_idx;
+ adreno_dev->pfp_jt_addr = adreno_gpulist[i].pfp_jt_addr;
adreno_dev->gpulist_index = i;
}
@@ -1283,6 +1424,8 @@
data->physstart = reg_val[0];
data->physend = data->physstart + reg_val[1] - 1;
+ data->iommu_halt_enable = of_property_read_bool(node,
+ "qcom,iommu-enable-halt");
data->iommu_ctx_count = 0;
@@ -1376,10 +1519,6 @@
&pdata->idle_timeout))
pdata->idle_timeout = HZ/12;
- if (adreno_of_read_property(pdev->dev.of_node, "qcom,nap-allowed",
- &pdata->nap_allowed))
- pdata->nap_allowed = 1;
-
pdata->strtstp_sleepwake = of_property_read_bool(pdev->dev.of_node,
"qcom,strtstp-sleepwake");
@@ -1410,6 +1549,9 @@
if (ret)
goto err;
+ pdata->coresight_pdata = of_get_coresight_platform_data(&pdev->dev,
+ pdev->dev.of_node);
+
pdev->dev.platform_data = pdata;
return 0;
@@ -1483,6 +1625,7 @@
adreno_probe(struct platform_device *pdev)
{
struct kgsl_device *device;
+ struct kgsl_device_platform_data *pdata = NULL;
struct adreno_device *adreno_dev;
int status = -EINVAL;
bool is_dt;
@@ -1513,6 +1656,10 @@
kgsl_pwrscale_attach_policy(device, ADRENO_DEFAULT_PWRSCALE_POLICY);
device->flags &= ~KGSL_FLAGS_SOFT_RESET;
+ pdata = kgsl_device_get_drvdata(device);
+
+ adreno_coresight_init(pdev);
+
return 0;
error_close_rb:
@@ -1531,6 +1678,8 @@
device = (struct kgsl_device *)pdev->id_entry->driver_data;
adreno_dev = ADRENO_DEVICE(device);
+ adreno_coresight_remove(pdev);
+
kgsl_pwrscale_detach_policy(device);
kgsl_pwrscale_close(device);
@@ -1601,6 +1750,7 @@
{
int status = -EINVAL;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ unsigned int state = device->state;
kgsl_cffdump_open(device);
@@ -1649,6 +1799,9 @@
if (KGSL_STATE_DUMP_AND_FT != device->state)
mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT);
+ mod_timer(&device->hang_timer,
+ (jiffies + msecs_to_jiffies(KGSL_TIMEOUT_PART)));
+
adreno_perfcounter_start(adreno_dev);
device->reset_counter++;
@@ -1662,8 +1815,11 @@
kgsl_mmu_stop(&device->mmu);
error_clk_off:
- if (KGSL_STATE_DUMP_AND_FT != device->state)
+ if (KGSL_STATE_DUMP_AND_FT != device->state) {
kgsl_pwrctrl_disable(device);
+ /* set the state back to original state */
+ kgsl_pwrctrl_set_state(device, state);
+ }
return status;
}
@@ -1681,13 +1837,14 @@
device->ftbl->irqctrl(device, 0);
kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
del_timer_sync(&device->idle_timer);
+ del_timer_sync(&device->hang_timer);
adreno_ocmem_gmem_free(adreno_dev);
/* Power down the device */
kgsl_pwrctrl_disable(device);
- kgsl_cffdump_close(device->id);
+ kgsl_cffdump_close(device);
return 0;
}
@@ -1734,11 +1891,11 @@
while ((context = idr_get_next(&device->context_idr, &next))) {
temp_adreno_context = context->devctxt;
if (temp_adreno_context->flags & CTXT_FLAGS_GPU_HANG) {
- kgsl_sharedmem_writel(&device->memstore,
+ kgsl_sharedmem_writel(device, &device->memstore,
KGSL_MEMSTORE_OFFSET(context->id,
soptimestamp),
rb->timestamp[context->id]);
- kgsl_sharedmem_writel(&device->memstore,
+ kgsl_sharedmem_writel(device, &device->memstore,
KGSL_MEMSTORE_OFFSET(context->id,
eoptimestamp),
rb->timestamp[context->id]);
@@ -2036,10 +2193,70 @@
}
}
+/**
+ * adreno_soft_reset() - Do a soft reset of the GPU hardware
+ * @device: KGSL device to soft reset
+ *
+ * "soft reset" the GPU hardware - this is a fast path GPU reset
+ * The GPU hardware is reset but we never pull power so we can skip
+ * a lot of the standard adreno_stop/adreno_start sequence
+ */
+int adreno_soft_reset(struct kgsl_device *device)
+{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ int ret;
+
+ /* If the jump table index is 0 soft reset is not supported */
+ if ((!adreno_dev->pm4_jt_idx) || (!adreno_dev->gpudev->soft_reset)) {
+ dev_WARN_ONCE(device->dev, 1, "Soft reset not supported");
+ return -EINVAL;
+ }
+
+ adreno_dev->drawctxt_active = NULL;
+
+ /* Stop the ringbuffer */
+ adreno_ringbuffer_stop(&adreno_dev->ringbuffer);
+
+ /* Delete the idle timer */
+ del_timer_sync(&device->idle_timer);
+
+ /* Make sure we are totally awake */
+ kgsl_pwrctrl_enable(device);
+
+ /* Reset the GPU */
+ adreno_dev->gpudev->soft_reset(adreno_dev);
+
+ /* Reinitialize the GPU */
+ adreno_dev->gpudev->start(adreno_dev);
+
+ /* Enable IRQ */
+ kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON);
+ device->ftbl->irqctrl(device, 1);
+
+ /*
+ * Restart the ringbuffer - we can go down the warm start path because
+ * power was never yanked
+ */
+ ret = adreno_ringbuffer_warm_start(&adreno_dev->ringbuffer);
+ if (ret)
+ return ret;
+
+ device->reset_counter++;
+
+ return 0;
+}
+
static int
_adreno_ft_restart_device(struct kgsl_device *device,
struct kgsl_context *context)
{
+ /* If device soft reset fails try hard reset */
+ if (adreno_soft_reset(device))
+ KGSL_DEV_ERR_ONCE(device, "Device soft reset failed\n");
+ else
+ /* Soft reset is successful */
+ goto reset_done;
+
/* restart device */
if (adreno_stop(device)) {
KGSL_FT_ERR(device, "Device stop failed\n");
@@ -2056,6 +2273,7 @@
return 1;
}
+reset_done:
if (context) {
struct adreno_context *adreno_context = context->devctxt;
kgsl_mmu_setstate(&device->mmu, adreno_context->pagetable,
@@ -2162,6 +2380,7 @@
struct adreno_context *last_active_ctx = adreno_dev->drawctxt_active;
unsigned int long_ib = 0;
static int no_context_ft;
+ struct kgsl_mmu *mmu = &device->mmu;
context = idr_find(&device->context_idr, ft_data->context_id);
if (context == NULL) {
@@ -2186,6 +2405,7 @@
context->wait_on_invalid_ts = false;
if (!(adreno_context->flags & CTXT_FLAGS_PER_CONTEXT_TS)) {
+ ft_data->status = 1;
KGSL_FT_ERR(device, "Fault tolerance not supported\n");
goto play_good_cmds;
}
@@ -2204,7 +2424,7 @@
}
/* Check if we detected a long running IB, if false return */
- if (adreno_dev->long_ib) {
+ if ((adreno_context) && (adreno_dev->long_ib)) {
long_ib = _adreno_check_long_ib(device);
if (!long_ib) {
adreno_context->flags &= ~CTXT_FLAGS_GPU_HANG;
@@ -2220,6 +2440,7 @@
/* If long IB detected do not attempt replay of bad cmds */
if (long_ib) {
+ ft_data->status = 1;
_adreno_debug_ft_info(device, ft_data);
goto play_good_cmds;
}
@@ -2233,12 +2454,13 @@
/* Do not try the reply if hang is due to a pagefault */
if (adreno_context && adreno_context->pagefault) {
+ /* Resume MMU */
+ mmu->mmu_ops->mmu_pagefault_resume(mmu);
if ((ft_data->context_id == adreno_context->id) &&
(ft_data->global_eop == adreno_context->pagefault_ts)) {
ft_data->ft_policy &= ~KGSL_FT_REPLAY;
KGSL_FT_ERR(device, "MMU fault skipping replay\n");
}
-
adreno_context->pagefault = 0;
}
@@ -2413,7 +2635,7 @@
else
device->mmu.hwpagetable = device->mmu.defaultpagetable;
rb->timestamp[KGSL_MEMSTORE_GLOBAL] = timestamp;
- kgsl_sharedmem_writel(&device->memstore,
+ kgsl_sharedmem_writel(device, &device->memstore,
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
eoptimestamp),
rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
@@ -2452,6 +2674,7 @@
INIT_COMPLETION(device->ft_gate);
/* Detected a hang */
+ kgsl_cffdump_hang(device);
/* Run fault tolerance at max power level */
curr_pwrlevel = pwr->active_pwrlevel;
kgsl_pwrctrl_pwrlevel_change(device, pwr->max_pwrlevel);
@@ -2495,6 +2718,9 @@
} else {
kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE);
mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT);
+ mod_timer(&device->hang_timer,
+ (jiffies +
+ msecs_to_jiffies(KGSL_TIMEOUT_PART)));
}
complete_all(&device->ft_gate);
}
@@ -2612,8 +2838,6 @@
switch (type) {
case KGSL_PROP_PWRCTRL: {
unsigned int enable;
- struct kgsl_device_platform_data *pdata =
- kgsl_device_get_drvdata(device);
if (sizebytes != sizeof(enable))
break;
@@ -2625,12 +2849,11 @@
}
if (enable) {
- if (pdata->nap_allowed)
- device->pwrctrl.nap_allowed = true;
+ device->pwrctrl.ctrl_flags = 0;
adreno_dev->fast_hang_detect = 1;
kgsl_pwrscale_enable(device);
} else {
- device->pwrctrl.nap_allowed = false;
+ device->pwrctrl.ctrl_flags = KGSL_PWR_ON;
adreno_dev->fast_hang_detect = 0;
kgsl_pwrscale_disable(device);
}
@@ -2653,9 +2876,6 @@
unsigned long wait;
unsigned long timeout = jiffies + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT);
- if (!(rb->flags & KGSL_FLAGS_STARTED))
- return 0;
-
/*
* The first time into the loop, wait for 100 msecs and kick wptr again
* to ensure that the hardware has updated correctly. After that, kick
@@ -2692,11 +2912,11 @@
unsigned int rbbm_status;
unsigned long wait_time;
unsigned long wait_time_part;
- unsigned int prev_reg_val[ft_detect_regs_count];
+ unsigned int prev_reg_val[FT_DETECT_REGS_COUNT];
memset(prev_reg_val, 0, sizeof(prev_reg_val));
- kgsl_cffdump_regpoll(device->id,
+ kgsl_cffdump_regpoll(device,
adreno_dev->gpudev->reg_rbbm_status << 2,
0x00000000, 0x80000000);
@@ -2818,7 +3038,7 @@
/* Find a memory structure attached to an adreno context */
struct kgsl_memdesc *adreno_find_ctxtmem(struct kgsl_device *device,
- unsigned int pt_base, unsigned int gpuaddr, unsigned int size)
+ phys_addr_t pt_base, unsigned int gpuaddr, unsigned int size)
{
struct kgsl_context *context;
struct adreno_context *adreno_context = NULL;
@@ -2850,7 +3070,7 @@
}
struct kgsl_memdesc *adreno_find_region(struct kgsl_device *device,
- unsigned int pt_base,
+ phys_addr_t pt_base,
unsigned int gpuaddr,
unsigned int size)
{
@@ -2879,7 +3099,7 @@
return adreno_find_ctxtmem(device, pt_base, gpuaddr, size);
}
-uint8_t *adreno_convertaddr(struct kgsl_device *device, unsigned int pt_base,
+uint8_t *adreno_convertaddr(struct kgsl_device *device, phys_addr_t pt_base,
unsigned int gpuaddr, unsigned int size)
{
struct kgsl_memdesc *memdesc;
@@ -2953,7 +3173,7 @@
kgsl_trace_regwrite(device, offsetwords, value);
- kgsl_cffdump_regwrite(device->id, offsetwords << 2, value);
+ kgsl_cffdump_regwrite(device, offsetwords << 2, value);
reg = (unsigned int *)(device->reg_virt + (offsetwords << 2));
/*ensure previous writes post before this one,
@@ -3013,18 +3233,18 @@
/* Make sure the memstore read has posted */
mb();
if (timestamp_cmp(ref_ts, timestamp) >= 0) {
- kgsl_sharedmem_writel(&device->memstore,
+ kgsl_sharedmem_writel(device, &device->memstore,
KGSL_MEMSTORE_OFFSET(context_id,
ref_wait_ts), timestamp);
/* Make sure the memstore write is posted */
wmb();
}
} else {
- kgsl_sharedmem_writel(&device->memstore,
+ kgsl_sharedmem_writel(device, &device->memstore,
KGSL_MEMSTORE_OFFSET(context_id,
ref_wait_ts), timestamp);
enableflag = 1;
- kgsl_sharedmem_writel(&device->memstore,
+ kgsl_sharedmem_writel(device, &device->memstore,
KGSL_MEMSTORE_OFFSET(context_id,
ts_cmp_enable), enableflag);
@@ -3087,7 +3307,8 @@
unsigned int *prev_reg_val)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- unsigned int curr_reg_val[ft_detect_regs_count];
+ struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+ unsigned int curr_reg_val[FT_DETECT_REGS_COUNT];
unsigned int fast_hang_detected = 1;
unsigned int long_ib_detected = 1;
unsigned int i;
@@ -3107,7 +3328,9 @@
if (!(adreno_dev->ringbuffer.flags & KGSL_FLAGS_STARTED))
return 0;
- if (is_adreno_rbbm_status_idle(device)) {
+ if (is_adreno_rbbm_status_idle(device) &&
+ (kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED)
+ == rb->timestamp[KGSL_MEMSTORE_GLOBAL])) {
/*
* On A2XX if the RPTR != WPTR and the device is idle, then
@@ -3139,7 +3362,7 @@
msecs_to_jiffies(KGSL_TIMEOUT_PART-1));
/* Read the current Hang detect reg values here */
- for (i = 0; i < ft_detect_regs_count; i++) {
+ for (i = 0; i < FT_DETECT_REGS_COUNT; i++) {
if (ft_detect_regs[i] == 0)
continue;
adreno_regread(device, ft_detect_regs[i],
@@ -3174,7 +3397,7 @@
"Fault tolerance no context found\n");
}
}
- for (i = 0; i < ft_detect_regs_count; i++) {
+ for (i = 0; i < FT_DETECT_REGS_COUNT; i++) {
if (curr_reg_val[i] != prev_reg_val[i]) {
fast_hang_detected = 0;
@@ -3250,55 +3473,11 @@
/* If hangs are not detected copy the current reg values
* to previous values and return no hang */
- for (i = 0; i < ft_detect_regs_count; i++)
+ for (i = 0; i < FT_DETECT_REGS_COUNT; i++)
prev_reg_val[i] = curr_reg_val[i];
return 0;
}
-/**
- * adreno_handle_hang - Process a hang detected in adreno_waittimestamp
- * @device - pointer to a KGSL device structure
- * @context - pointer to the active KGSL context
- * @timestamp - the timestamp that the process was waiting for
- *
- * Process a possible GPU hang and try fault tolerance from it
- * cleanly
- */
-static int adreno_handle_hang(struct kgsl_device *device,
- struct kgsl_context *context, unsigned int timestamp)
-{
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- unsigned int context_id = _get_context_id(context);
- unsigned int ts_issued;
- unsigned int rptr;
-
- /* Do one last check to see if we somehow made it through */
- if (kgsl_check_timestamp(device, context, timestamp))
- return 0;
-
- ts_issued = adreno_dev->ringbuffer.timestamp[context_id];
-
- adreno_regread(device, REG_CP_RB_RPTR, &rptr);
-
- /* Make sure timestamp check finished before triggering a hang */
- mb();
-
- KGSL_DRV_WARN(device,
- "Device hang detected while waiting for timestamp: "
- "<%d:0x%x>, last submitted timestamp: <%d:0x%x>, "
- "retired timestamp: <%d:0x%x>, wptr: 0x%x, rptr: 0x%x\n",
- context_id, timestamp, context_id, ts_issued, context_id,
- kgsl_readtimestamp(device, context,
- KGSL_TIMESTAMP_RETIRED),
- adreno_dev->ringbuffer.wptr, rptr);
-
- /* Return 0 after a successful fault tolerance */
- if (!adreno_dump_and_exec_ft(device))
- return 0;
-
- return -ETIMEDOUT;
-}
-
static int _check_pending_timestamp(struct kgsl_device *device,
struct kgsl_context *context, unsigned int timestamp)
{
@@ -3347,7 +3526,6 @@
struct adreno_context *adreno_ctx = context ? context->devctxt : NULL;
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
unsigned int context_id = _get_context_id(context);
- unsigned int prev_reg_val[ft_detect_regs_count];
unsigned int time_elapsed = 0;
unsigned int wait;
int ts_compare = 1;
@@ -3375,10 +3553,6 @@
context->wait_on_invalid_ts = false;
}
-
- /* Clear the registers used for hang detection */
- memset(prev_reg_val, 0, sizeof(prev_reg_val));
-
/*
* On the first time through the loop only wait 100ms.
* this gives enough time for the engine to start moving and oddly
@@ -3405,12 +3579,6 @@
break;
}
- /* Check to see if the GPU is hung */
- if (adreno_ft_detect(device, prev_reg_val)) {
- ret = adreno_handle_hang(device, context, timestamp);
- break;
- }
-
/*
* For proper power accounting sometimes we need to call
* io_wait_interruptible_timeout and sometimes we need to call
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 3935cd8..531a77c 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -80,6 +80,15 @@
ADRENO_REV_A305B = 335,
};
+enum coresight_debug_reg {
+ DEBUG_BUS_CTL,
+ TRACE_STOP_CNT,
+ TRACE_START_CNT,
+ TRACE_PERIOD_CNT,
+ TRACE_CMD,
+ TRACE_BUS_CTL,
+};
+
struct adreno_gpudev;
struct adreno_device {
@@ -101,6 +110,10 @@
unsigned int mharb;
struct adreno_gpudev *gpudev;
unsigned int wait_timeout;
+ unsigned int pm4_jt_idx;
+ unsigned int pm4_jt_addr;
+ unsigned int pfp_jt_idx;
+ unsigned int pfp_jt_addr;
unsigned int istore_size;
unsigned int pix_shader_start;
unsigned int instruction_size;
@@ -187,6 +200,11 @@
uint64_t (*perfcounter_read)(struct adreno_device *adreno_dev,
unsigned int group, unsigned int counter,
unsigned int offset);
+ int (*coresight_enable) (struct kgsl_device *device);
+ void (*coresight_disable) (struct kgsl_device *device);
+ void (*coresight_config_debug_reg) (struct kgsl_device *device,
+ int debug_reg, unsigned int val);
+ void (*soft_reset)(struct adreno_device *device);
};
/*
@@ -229,6 +247,8 @@
unsigned int replay_for_snapshot;
};
+#define FT_DETECT_REGS_COUNT 12
+
/* Fault Tolerance policy flags */
#define KGSL_FT_DISABLE BIT(0)
#define KGSL_FT_REPLAY BIT(1)
@@ -267,8 +287,11 @@
extern const unsigned int a330_registers_count;
extern unsigned int ft_detect_regs[];
-extern const unsigned int ft_detect_regs_count;
+int adreno_coresight_enable(struct coresight_device *csdev);
+void adreno_coresight_disable(struct coresight_device *csdev);
+void adreno_coresight_remove(struct platform_device *pdev);
+int adreno_coresight_init(struct platform_device *pdev);
int adreno_idle(struct kgsl_device *device);
void adreno_regread(struct kgsl_device *device, unsigned int offsetwords,
@@ -285,15 +308,15 @@
*adreno_dev);
struct kgsl_memdesc *adreno_find_region(struct kgsl_device *device,
- unsigned int pt_base,
+ phys_addr_t pt_base,
unsigned int gpuaddr,
unsigned int size);
uint8_t *adreno_convertaddr(struct kgsl_device *device,
- unsigned int pt_base, unsigned int gpuaddr, unsigned int size);
+ phys_addr_t pt_base, unsigned int gpuaddr, unsigned int size);
struct kgsl_memdesc *adreno_find_ctxtmem(struct kgsl_device *device,
- unsigned int pt_base, unsigned int gpuaddr, unsigned int size);
+ phys_addr_t pt_base, unsigned int gpuaddr, unsigned int size);
void *adreno_snapshot(struct kgsl_device *device, void *snapshot, int *remain,
int hang);
@@ -313,6 +336,9 @@
int adreno_perfcounter_put(struct adreno_device *adreno_dev,
unsigned int groupid, unsigned int countable);
+int adreno_soft_reset(struct kgsl_device *device);
+
+
static inline int adreno_is_a200(struct adreno_device *adreno_dev)
{
return (adreno_dev->gpurev == ADRENO_REV_A200);
@@ -507,4 +533,25 @@
return cmds - start;
}
+/*
+ * adreno_wait_reg_eq() - Add a CP_WAIT_REG_EQ command
+ * @cmds: Pointer to memory where commands are to be added
+ * @addr: Regiater address to poll for
+ * @val: Value to poll for
+ * @mask: The value against which register value is masked
+ * @interval: wait interval
+ */
+static inline int adreno_wait_reg_eq(unsigned int *cmds, unsigned int addr,
+ unsigned int val, unsigned int mask,
+ unsigned int interval)
+{
+ unsigned int *start = cmds;
+ *cmds++ = cp_type3_packet(CP_WAIT_REG_EQ, 4);
+ *cmds++ = addr;
+ *cmds++ = val;
+ *cmds++ = mask;
+ *cmds++ = interval;
+ return cmds - start;
+}
+
#endif /*__ADRENO_H */
diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c
index dd9bdc3..b0a1fe8 100644
--- a/drivers/gpu/msm/adreno_a2xx.c
+++ b/drivers/gpu/msm/adreno_a2xx.c
@@ -1364,8 +1364,9 @@
drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW;
/* blank out gmem shadow. */
- kgsl_sharedmem_set(&drawctxt->context_gmem_shadow.gmemshadow, 0, 0,
- drawctxt->context_gmem_shadow.size);
+ kgsl_sharedmem_set(drawctxt->dev_priv->device,
+ &drawctxt->context_gmem_shadow.gmemshadow, 0, 0,
+ drawctxt->context_gmem_shadow.size);
/* build quad vertex buffer */
build_quad_vtxbuff(drawctxt, &drawctxt->context_gmem_shadow,
@@ -1388,7 +1389,7 @@
kgsl_cache_range_op(&drawctxt->context_gmem_shadow.gmemshadow,
KGSL_CACHE_OP_FLUSH);
- kgsl_cffdump_syncmem(NULL,
+ kgsl_cffdump_syncmem(drawctxt->dev_priv,
&drawctxt->context_gmem_shadow.gmemshadow,
drawctxt->context_gmem_shadow.gmemshadow.gpuaddr,
drawctxt->context_gmem_shadow.gmemshadow.size, false);
@@ -1414,8 +1415,8 @@
if (ret)
return ret;
- kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0,
- _context_size(adreno_dev));
+ kgsl_sharedmem_set(drawctxt->dev_priv->device, &drawctxt->gpustate, 0,
+ 0, _context_size(adreno_dev));
tmp_ctx.cmd = tmp_ctx.start
= (unsigned int *)((char *)drawctxt->gpustate.hostptr + CMD_OFFSET);
@@ -1439,7 +1440,7 @@
kgsl_cache_range_op(&drawctxt->gpustate,
KGSL_CACHE_OP_FLUSH);
- kgsl_cffdump_syncmem(NULL, &drawctxt->gpustate,
+ kgsl_cffdump_syncmem(drawctxt->dev_priv, &drawctxt->gpustate,
drawctxt->gpustate.gpuaddr,
drawctxt->gpustate.size, false);
@@ -1515,7 +1516,7 @@
"Current active context has caused gpu hang\n");
if (!(context->flags & CTXT_FLAGS_PREAMBLE)) {
- kgsl_cffdump_syncmem(NULL, &context->gpustate,
+ kgsl_cffdump_syncmem(context->dev_priv, &context->gpustate,
context->reg_save[1],
context->reg_save[2] << 2, true);
/* save registers and constants. */
@@ -1524,7 +1525,8 @@
context->reg_save, 3);
if (context->flags & CTXT_FLAGS_SHADER_SAVE) {
- kgsl_cffdump_syncmem(NULL, &context->gpustate,
+ kgsl_cffdump_syncmem(context->dev_priv,
+ &context->gpustate,
context->shader_save[1],
context->shader_save[2] << 2, true);
/* save shader partitioning and instructions. */
@@ -1532,7 +1534,8 @@
KGSL_CMD_FLAGS_PMODE,
context->shader_save, 3);
- kgsl_cffdump_syncmem(NULL, &context->gpustate,
+ kgsl_cffdump_syncmem(context->dev_priv,
+ &context->gpustate,
context->shader_fixup[1],
context->shader_fixup[2] << 2, true);
/*
@@ -1549,7 +1552,7 @@
if ((context->flags & CTXT_FLAGS_GMEM_SAVE) &&
(context->flags & CTXT_FLAGS_GMEM_SHADOW)) {
- kgsl_cffdump_syncmem(NULL, &context->gpustate,
+ kgsl_cffdump_syncmem(context->dev_priv, &context->gpustate,
context->context_gmem_shadow.gmem_save[1],
context->context_gmem_shadow.gmem_save[2] << 2, true);
/* save gmem.
@@ -1559,7 +1562,7 @@
KGSL_CMD_FLAGS_PMODE,
context->context_gmem_shadow.gmem_save, 3);
- kgsl_cffdump_syncmem(NULL, &context->gpustate,
+ kgsl_cffdump_syncmem(context->dev_priv, &context->gpustate,
context->chicken_restore[1],
context->chicken_restore[2] << 2, true);
@@ -1603,7 +1606,7 @@
* (note: changes shader. shader must not already be restored.)
*/
if (context->flags & CTXT_FLAGS_GMEM_RESTORE) {
- kgsl_cffdump_syncmem(NULL, &context->gpustate,
+ kgsl_cffdump_syncmem(context->dev_priv, &context->gpustate,
context->context_gmem_shadow.gmem_restore[1],
context->context_gmem_shadow.gmem_restore[2] << 2,
true);
@@ -1613,7 +1616,8 @@
context->context_gmem_shadow.gmem_restore, 3);
if (!(context->flags & CTXT_FLAGS_PREAMBLE)) {
- kgsl_cffdump_syncmem(NULL, &context->gpustate,
+ kgsl_cffdump_syncmem(context->dev_priv,
+ &context->gpustate,
context->chicken_restore[1],
context->chicken_restore[2] << 2, true);
@@ -1627,7 +1631,7 @@
}
if (!(context->flags & CTXT_FLAGS_PREAMBLE)) {
- kgsl_cffdump_syncmem(NULL, &context->gpustate,
+ kgsl_cffdump_syncmem(context->dev_priv, &context->gpustate,
context->reg_restore[1],
context->reg_restore[2] << 2, true);
@@ -1637,7 +1641,8 @@
/* restore shader instructions & partitioning. */
if (context->flags & CTXT_FLAGS_SHADER_RESTORE) {
- kgsl_cffdump_syncmem(NULL, &context->gpustate,
+ kgsl_cffdump_syncmem(context->dev_priv,
+ &context->gpustate,
context->shader_restore[1],
context->shader_restore[2] << 2, true);
@@ -1862,54 +1867,56 @@
cmds_gpu = rb->buffer_desc.gpuaddr + sizeof(uint)*(rb->wptr-19);
- GSL_RB_WRITE(cmds, cmds_gpu, cp_type3_packet(CP_ME_INIT, 18));
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, cp_type3_packet(CP_ME_INIT,
+ 18));
/* All fields present (bits 9:0) */
- GSL_RB_WRITE(cmds, cmds_gpu, 0x000003ff);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x000003ff);
/* Disable/Enable Real-Time Stream processing (present but ignored) */
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
/* Enable (2D <-> 3D) implicit synchronization (present but ignored) */
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
- GSL_RB_WRITE(cmds, cmds_gpu,
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu,
SUBBLOCK_OFFSET(REG_RB_SURFACE_INFO));
- GSL_RB_WRITE(cmds, cmds_gpu,
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu,
SUBBLOCK_OFFSET(REG_PA_SC_WINDOW_OFFSET));
- GSL_RB_WRITE(cmds, cmds_gpu,
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu,
SUBBLOCK_OFFSET(REG_VGT_MAX_VTX_INDX));
- GSL_RB_WRITE(cmds, cmds_gpu,
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu,
SUBBLOCK_OFFSET(REG_SQ_PROGRAM_CNTL));
- GSL_RB_WRITE(cmds, cmds_gpu,
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu,
SUBBLOCK_OFFSET(REG_RB_DEPTHCONTROL));
- GSL_RB_WRITE(cmds, cmds_gpu,
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu,
SUBBLOCK_OFFSET(REG_PA_SU_POINT_SIZE));
- GSL_RB_WRITE(cmds, cmds_gpu,
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu,
SUBBLOCK_OFFSET(REG_PA_SC_LINE_CNTL));
- GSL_RB_WRITE(cmds, cmds_gpu,
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu,
SUBBLOCK_OFFSET(REG_PA_SU_POLY_OFFSET_FRONT_SCALE));
/* Instruction memory size: */
- GSL_RB_WRITE(cmds, cmds_gpu,
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu,
(adreno_encode_istore_size(adreno_dev)
| adreno_dev->pix_shader_start));
/* Maximum Contexts */
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000001);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000001);
/* Write Confirm Interval and The CP will wait the
* wait_interval * 16 clocks between polling */
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
/* NQ and External Memory Swap */
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
/* Protected mode error checking
* If iommu is used then protection needs to be turned off
* to enable context bank switching */
if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype())
- GSL_RB_WRITE(cmds, cmds_gpu, 0);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0);
else
- GSL_RB_WRITE(cmds, cmds_gpu, GSL_RB_PROTECTED_MODE_CONTROL);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu,
+ GSL_RB_PROTECTED_MODE_CONTROL);
/* Disable header dumping and Header dump address */
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
/* Header dump size */
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
adreno_ringbuffer_submit(rb);
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index be5c786..29855f7 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -2360,7 +2360,8 @@
if (ret)
return ret;
- kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0, CONTEXT_SIZE);
+ kgsl_sharedmem_set(&adreno_dev->dev, &drawctxt->gpustate, 0, 0,
+ CONTEXT_SIZE);
tmp_ctx.cmd = drawctxt->gpustate.hostptr + CMD_OFFSET;
if (!(drawctxt->flags & CTXT_FLAGS_PREAMBLE)) {
@@ -2419,7 +2420,7 @@
* already be saved.)
*/
- kgsl_cffdump_syncmem(NULL,
+ kgsl_cffdump_syncmem(context->dev_priv,
&context->gpustate,
context->context_gmem_shadow.gmem_save[1],
context->context_gmem_shadow.gmem_save[2] << 2, true);
@@ -2461,7 +2462,7 @@
*/
if (context->flags & CTXT_FLAGS_GMEM_RESTORE) {
- kgsl_cffdump_syncmem(NULL,
+ kgsl_cffdump_syncmem(context->dev_priv,
&context->gpustate,
context->context_gmem_shadow.gmem_restore[1],
context->context_gmem_shadow.gmem_restore[2] << 2,
@@ -2509,25 +2510,26 @@
cmds_gpu = rb->buffer_desc.gpuaddr + sizeof(uint) * (rb->wptr - 18);
- GSL_RB_WRITE(cmds, cmds_gpu, cp_type3_packet(CP_ME_INIT, 17));
- GSL_RB_WRITE(cmds, cmds_gpu, 0x000003f7);
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000080);
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000100);
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000180);
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00006600);
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000150);
- GSL_RB_WRITE(cmds, cmds_gpu, 0x0000014e);
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000154);
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000001);
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu,
+ cp_type3_packet(CP_ME_INIT, 17));
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x000003f7);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000080);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000100);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000180);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00006600);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000150);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x0000014e);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000154);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000001);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
/* Protected mode control - turned off for A3XX */
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
- GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
adreno_ringbuffer_submit(rb);
@@ -2758,7 +2760,7 @@
{
unsigned int in, out, bit, sel;
- if (countable > 0x7f)
+ if (counter > 1 || countable > 0x7f)
return;
adreno_regread(device, A3XX_VBIF_PERF_CNT_EN, &in);
@@ -2767,7 +2769,7 @@
if (counter == 0) {
bit = VBIF_PERF_CNT_0;
sel = (sel & ~VBIF_PERF_CNT_0_SEL_MASK) | countable;
- } else if (counter == 1) {
+ } else {
bit = VBIF_PERF_CNT_1;
sel = (sel & ~VBIF_PERF_CNT_1_SEL_MASK)
| (countable << VBIF_PERF_CNT_1_SEL);
@@ -2821,10 +2823,10 @@
unsigned int val = 0;
struct a3xx_perfcounter_register *reg;
- if (group > ARRAY_SIZE(a3xx_perfcounter_reglist))
+ if (group >= ARRAY_SIZE(a3xx_perfcounter_reglist))
return;
- if (counter > a3xx_perfcounter_reglist[group].count)
+ if (counter >= a3xx_perfcounter_reglist[group].count)
return;
/* Special cases */
@@ -2858,7 +2860,10 @@
unsigned int lo = 0, hi = 0;
unsigned int val;
- if (group > ARRAY_SIZE(a3xx_perfcounter_reglist))
+ if (group >= ARRAY_SIZE(a3xx_perfcounter_reglist))
+ return 0;
+
+ if (counter >= a3xx_perfcounter_reglist[group].count)
return 0;
reg = &(a3xx_perfcounter_reglist[group].regs[counter]);
@@ -3232,6 +3237,108 @@
adreno_dev->gpu_cycles = 0;
}
+/**
+ * a3xx_coresight_enable() - Enables debugging through coresight
+ * debug bus for adreno a3xx devices.
+ * @device: Pointer to GPU device structure
+ */
+int a3xx_coresight_enable(struct kgsl_device *device)
+{
+ mutex_lock(&device->mutex);
+ if (!kgsl_active_count_get(device)) {
+ adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, 0x0001093F);
+ adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_STB_CTL0,
+ 0x00000000);
+ adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_STB_CTL1,
+ 0xFFFFFFFE);
+ adreno_regwrite(device, A3XX_RBBM_INT_TRACE_BUS_CTL,
+ 0x00201111);
+ adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_BUS_CTL,
+ 0x89100010);
+ adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_STOP_CNT,
+ 0x00017fff);
+ adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_START_CNT,
+ 0x0001000f);
+ adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_PERIOD_CNT ,
+ 0x0001ffff);
+ adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_CMD,
+ 0x00000001);
+ kgsl_active_count_put(device);
+ }
+ mutex_unlock(&device->mutex);
+ return 0;
+}
+
+/**
+ * a3xx_coresight_disable() - Disables debugging through coresight
+ * debug bus for adreno a3xx devices.
+ * @device: Pointer to GPU device structure
+ */
+void a3xx_coresight_disable(struct kgsl_device *device)
+{
+ mutex_lock(&device->mutex);
+ if (!kgsl_active_count_get(device)) {
+ adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, 0x0);
+ adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_STB_CTL0, 0x0);
+ adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_STB_CTL1, 0x0);
+ adreno_regwrite(device, A3XX_RBBM_INT_TRACE_BUS_CTL, 0x0);
+ adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_BUS_CTL, 0x0);
+ adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_STOP_CNT, 0x0);
+ adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_START_CNT, 0x0);
+ adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_PERIOD_CNT , 0x0);
+ adreno_regwrite(device, A3XX_RBBM_EXT_TRACE_CMD, 0x0);
+ kgsl_active_count_put(device);
+ }
+ mutex_unlock(&device->mutex);
+}
+
+static void a3xx_coresight_write_reg(struct kgsl_device *device,
+ unsigned int wordoffset, unsigned int val)
+{
+ mutex_lock(&device->mutex);
+ if (!kgsl_active_count_get(device)) {
+ adreno_regwrite(device, wordoffset, val);
+ kgsl_active_count_put(device);
+ }
+ mutex_unlock(&device->mutex);
+}
+
+void a3xx_coresight_config_debug_reg(struct kgsl_device *device,
+ int debug_reg, unsigned int val)
+{
+ switch (debug_reg) {
+
+ case DEBUG_BUS_CTL:
+ a3xx_coresight_write_reg(device, A3XX_RBBM_DEBUG_BUS_CTL, val);
+ break;
+
+ case TRACE_STOP_CNT:
+ a3xx_coresight_write_reg(device, A3XX_RBBM_EXT_TRACE_STOP_CNT,
+ val);
+ break;
+
+ case TRACE_START_CNT:
+ a3xx_coresight_write_reg(device, A3XX_RBBM_EXT_TRACE_START_CNT,
+ val);
+ break;
+
+ case TRACE_PERIOD_CNT:
+ a3xx_coresight_write_reg(device, A3XX_RBBM_EXT_TRACE_PERIOD_CNT,
+ val);
+ break;
+
+ case TRACE_CMD:
+ a3xx_coresight_write_reg(device, A3XX_RBBM_EXT_TRACE_CMD, val);
+ break;
+
+ case TRACE_BUS_CTL:
+ a3xx_coresight_write_reg(device, A3XX_RBBM_EXT_TRACE_BUS_CTL,
+ val);
+ break;
+ }
+
+}
+
/*
* Define the available perfcounter groups - these get used by
* adreno_perfcounter_get and adreno_perfcounter_put
@@ -3354,6 +3461,30 @@
ARRAY_SIZE(a3xx_perfcounter_groups),
};
+/*
+ * a3xx_soft_reset() - Soft reset GPU
+ * @adreno_dev: Pointer to adreno device
+ *
+ * Soft reset the GPU by doing a AHB write of value 1 to RBBM_SW_RESET
+ * register. This is used when we want to reset the GPU without
+ * turning off GFX power rail. The reset when asserted resets
+ * all the HW logic, restores GPU registers to default state and
+ * flushes out pending VBIF transactions.
+ */
+static void a3xx_soft_reset(struct adreno_device *adreno_dev)
+{
+ struct kgsl_device *device = &adreno_dev->dev;
+ unsigned int reg;
+
+ adreno_regwrite(device, A3XX_RBBM_SW_RESET_CMD, 1);
+ /*
+ * Do a dummy read to get a brief read cycle delay for the reset to take
+ * effect
+ */
+ adreno_regread(device, A3XX_RBBM_SW_RESET_CMD, ®);
+ adreno_regwrite(device, A3XX_RBBM_SW_RESET_CMD, 0);
+}
+
/* Defined in adreno_a3xx_snapshot.c */
void *a3xx_snapshot(struct adreno_device *adreno_dev, void *snapshot,
int *remain, int hang);
@@ -3378,4 +3509,8 @@
.snapshot = a3xx_snapshot,
.perfcounter_enable = a3xx_perfcounter_enable,
.perfcounter_read = a3xx_perfcounter_read,
+ .coresight_enable = a3xx_coresight_enable,
+ .coresight_disable = a3xx_coresight_disable,
+ .coresight_config_debug_reg = a3xx_coresight_config_debug_reg,
+ .soft_reset = a3xx_soft_reset,
};
diff --git a/drivers/gpu/msm/adreno_coresight.c b/drivers/gpu/msm/adreno_coresight.c
new file mode 100644
index 0000000..e18568d
--- /dev/null
+++ b/drivers/gpu/msm/adreno_coresight.c
@@ -0,0 +1,219 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/of_coresight.h>
+#include <linux/coresight.h>
+#include <linux/memory_alloc.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include "kgsl.h"
+#include "kgsl_device.h"
+#include "adreno.h"
+
+struct coresight_attr {
+ struct device_attribute attr;
+ int regname;
+};
+
+#define CORESIGHT_CREATE_REG_ATTR(_attrname, _regname) \
+ struct coresight_attr coresight_attr_##_attrname = \
+ { __ATTR(_attrname, S_IRUGO | S_IWUSR, gfx_show_reg, gfx_store_reg),\
+ _regname}
+
+/**
+ * adreno_coresight_enable() - Generic function to enable coresight debugging
+ * @csdev: Pointer to coresight's device struct
+ *
+ * This is a generic function to enable coresight debug bus on adreno
+ * devices. This should be used in all cases of enabling
+ * coresight debug bus for adreno devices. This function in turn calls
+ * the adreno device specific function through gpudev hook.
+ * This function is registered as the coresight enable function
+ * with coresight driver. It should only be called through coresight driver
+ * as that would ensure that the necessary setup required to be done
+ * on coresight driver's part is also done.
+ */
+int adreno_coresight_enable(struct coresight_device *csdev)
+{
+ struct kgsl_device *device = dev_get_drvdata(csdev->dev.parent);
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+ /* Check if coresight compatible device, return error otherwise */
+ if (adreno_dev->gpudev->coresight_enable)
+ return adreno_dev->gpudev->coresight_enable(device);
+ else
+ return -ENODEV;
+}
+
+/**
+ * adreno_coresight_disable() - Generic function to disable coresight debugging
+ * @csdev: Pointer to coresight's device struct
+ *
+ * This is a generic function to disable coresight debug bus on adreno
+ * devices. This should be used in all cases of disabling
+ * coresight debug bus for adreno devices. This function in turn calls
+ * the adreno device specific function through the gpudev hook.
+ * This function is registered as the coresight disable function
+ * with coresight driver. It should only be called through coresight driver
+ * as that would ensure that the necessary setup required to be done on
+ * coresight driver's part is also done.
+ */
+void adreno_coresight_disable(struct coresight_device *csdev)
+{
+ struct kgsl_device *device = dev_get_drvdata(csdev->dev.parent);
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+ /* Check if coresight compatible device, bail otherwise */
+ if (adreno_dev->gpudev->coresight_disable)
+ return adreno_dev->gpudev->coresight_disable(device);
+}
+
+static const struct coresight_ops_source adreno_coresight_ops_source = {
+ .enable = adreno_coresight_enable,
+ .disable = adreno_coresight_disable,
+};
+
+static const struct coresight_ops adreno_coresight_cs_ops = {
+ .source_ops = &adreno_coresight_ops_source,
+};
+
+void adreno_coresight_remove(struct platform_device *pdev)
+{
+ struct kgsl_device_platform_data *pdata = pdev->dev.platform_data;
+ coresight_unregister(pdata->csdev);
+}
+
+static ssize_t coresight_read_reg(struct kgsl_device *device,
+ unsigned int offset, char *buf)
+{
+ unsigned int regval = 0;
+
+ mutex_lock(&device->mutex);
+ if (!kgsl_active_count_get(device)) {
+ adreno_regread(device, offset, ®val);
+ kgsl_active_count_put(device);
+ }
+ mutex_unlock(&device->mutex);
+ return snprintf(buf, PAGE_SIZE, "0x%X", regval);
+}
+
+static inline unsigned int coresight_convert_reg(const char *buf)
+{
+ long regval = 0;
+ int rv = 0;
+
+ rv = kstrtoul(buf, 16, ®val);
+ if (!rv)
+ return (unsigned int)regval;
+ else
+ return rv;
+}
+
+static ssize_t gfx_show_reg(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct kgsl_device *device = dev_get_drvdata(dev->parent);
+ struct coresight_attr *csight_attr = container_of(attr,
+ struct coresight_attr, attr);
+ return coresight_read_reg(device, csight_attr->regname, buf);
+}
+
+static ssize_t gfx_store_reg(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct kgsl_device *device = dev_get_drvdata(dev->parent);
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ struct coresight_attr *csight_attr = container_of(attr,
+ struct coresight_attr, attr);
+ unsigned int regval = 0;
+
+ regval = coresight_convert_reg(buf);
+
+ if (adreno_dev->gpudev->coresight_config_debug_reg)
+ adreno_dev->gpudev->coresight_config_debug_reg(device,
+ csight_attr->regname, regval);
+ return size;
+}
+
+CORESIGHT_CREATE_REG_ATTR(config_debug_bus, DEBUG_BUS_CTL);
+CORESIGHT_CREATE_REG_ATTR(config_trace_stop_cnt, TRACE_STOP_CNT);
+CORESIGHT_CREATE_REG_ATTR(config_trace_start_cnt, TRACE_START_CNT);
+CORESIGHT_CREATE_REG_ATTR(config_trace_period_cnt, TRACE_PERIOD_CNT);
+CORESIGHT_CREATE_REG_ATTR(config_trace_cmd, TRACE_CMD);
+CORESIGHT_CREATE_REG_ATTR(config_trace_bus_ctl, TRACE_BUS_CTL);
+
+static struct attribute *gfx_attrs[] = {
+ &coresight_attr_config_debug_bus.attr.attr,
+ &coresight_attr_config_trace_start_cnt.attr.attr,
+ &coresight_attr_config_trace_stop_cnt.attr.attr,
+ &coresight_attr_config_trace_period_cnt.attr.attr,
+ &coresight_attr_config_trace_cmd.attr.attr,
+ &coresight_attr_config_trace_bus_ctl.attr.attr,
+ NULL,
+};
+
+static struct attribute_group gfx_attr_grp = {
+ .attrs = gfx_attrs,
+};
+
+static const struct attribute_group *gfx_attr_grps[] = {
+ &gfx_attr_grp,
+ NULL,
+};
+
+int adreno_coresight_init(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct kgsl_device_platform_data *pdata = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ struct coresight_desc *desc;
+
+ if (IS_ERR_OR_NULL(pdata->coresight_pdata))
+ return -ENODATA;
+
+
+ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+
+ desc->type = CORESIGHT_DEV_TYPE_SOURCE;
+ desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_BUS;
+ desc->ops = &adreno_coresight_cs_ops;
+ desc->pdata = pdata->coresight_pdata;
+ desc->dev = &pdev->dev;
+ desc->owner = THIS_MODULE;
+ desc->groups = gfx_attr_grps;
+ pdata->csdev = coresight_register(desc);
+ if (IS_ERR(pdata->csdev)) {
+ ret = PTR_ERR(pdata->csdev);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ devm_kfree(dev, desc);
+ return ret;
+}
+
diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c
index 980ff13..12b9e7c 100644
--- a/drivers/gpu/msm/adreno_debugfs.c
+++ b/drivers/gpu/msm/adreno_debugfs.c
@@ -19,27 +19,12 @@
#include "kgsl.h"
#include "adreno.h"
+#include "kgsl_cffdump.h"
#include "a2xx_reg.h"
unsigned int kgsl_cff_dump_enable;
-static int kgsl_cff_dump_enable_set(void *data, u64 val)
-{
-#ifdef CONFIG_MSM_KGSL_CFF_DUMP
- kgsl_cff_dump_enable = (val != 0);
- return 0;
-#else
- return -EINVAL;
-#endif
-}
-
-static int kgsl_cff_dump_enable_get(void *data, u64 *val)
-{
- *val = kgsl_cff_dump_enable;
- return 0;
-}
-
DEFINE_SIMPLE_ATTRIBUTE(kgsl_cff_dump_enable_fops, kgsl_cff_dump_enable_get,
kgsl_cff_dump_enable_set, "%llu\n");
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 176717d..b32cdae 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -192,19 +192,20 @@
drawctxt->type =
(*flags & KGSL_CONTEXT_TYPE_MASK) >> KGSL_CONTEXT_TYPE_SHIFT;
+ drawctxt->dev_priv = context->dev_priv;
ret = adreno_dev->gpudev->ctxt_create(adreno_dev, drawctxt);
if (ret)
goto err;
- kgsl_sharedmem_writel(&device->memstore,
+ kgsl_sharedmem_writel(device, &device->memstore,
KGSL_MEMSTORE_OFFSET(drawctxt->id, ref_wait_ts),
KGSL_INIT_REFTIMESTAMP);
- kgsl_sharedmem_writel(&device->memstore,
+ kgsl_sharedmem_writel(device, &device->memstore,
KGSL_MEMSTORE_OFFSET(drawctxt->id, ts_cmp_enable), 0);
- kgsl_sharedmem_writel(&device->memstore,
+ kgsl_sharedmem_writel(device, &device->memstore,
KGSL_MEMSTORE_OFFSET(drawctxt->id, soptimestamp), 0);
- kgsl_sharedmem_writel(&device->memstore,
+ kgsl_sharedmem_writel(device, &device->memstore,
KGSL_MEMSTORE_OFFSET(drawctxt->id, eoptimestamp), 0);
context->devctxt = drawctxt;
diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h
index f0f3b6b..2b8e600 100644
--- a/drivers/gpu/msm/adreno_drawctxt.h
+++ b/drivers/gpu/msm/adreno_drawctxt.h
@@ -131,6 +131,7 @@
struct kgsl_memdesc constant_load_commands[3];
struct kgsl_memdesc cond_execs[4];
struct kgsl_memdesc hlsqcontrol_restore_commands[1];
+ struct kgsl_device_private *dev_priv;
};
int adreno_drawctxt_create(struct kgsl_device *device,
diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c
index 5396196..c7b6b5b 100644
--- a/drivers/gpu/msm/adreno_postmortem.c
+++ b/drivers/gpu/msm/adreno_postmortem.c
@@ -69,6 +69,8 @@
{CP_SET_PROTECTED_MODE, "ST_PRT_M"},
{CP_SET_SHADER_BASES, "ST_SHD_B"},
{CP_WAIT_FOR_IDLE, "WAIT4IDL"},
+ {CP_WAIT_FOR_ME, "WAIT4ME"},
+ {CP_WAIT_REG_EQ, "WAITRGEQ"},
};
static const struct pm_id_name pm3_nop_values[] = {
@@ -188,8 +190,9 @@
}
}
-static void dump_ib(struct kgsl_device *device, char* buffId, uint32_t pt_base,
- uint32_t base_offset, uint32_t ib_base, uint32_t ib_size, bool dump)
+static void dump_ib(struct kgsl_device *device, char *buffId,
+ phys_addr_t pt_base, uint32_t base_offset, uint32_t ib_base,
+ uint32_t ib_size, bool dump)
{
uint8_t *base_addr = adreno_convertaddr(device, pt_base,
ib_base, ib_size*sizeof(uint32_t));
@@ -212,7 +215,7 @@
uint32_t offsets[IB_LIST_SIZE];
};
-static void dump_ib1(struct kgsl_device *device, uint32_t pt_base,
+static void dump_ib1(struct kgsl_device *device, phys_addr_t pt_base,
uint32_t base_offset,
uint32_t ib1_base, uint32_t ib1_size,
struct ib_list *ib_list, bool dump)
@@ -717,7 +720,7 @@
{
unsigned int cp_ib1_base, cp_ib1_bufsz;
unsigned int cp_ib2_base, cp_ib2_bufsz;
- unsigned int pt_base, cur_pt_base;
+ phys_addr_t pt_base, cur_pt_base;
unsigned int cp_rb_base, cp_rb_ctrl, rb_count;
unsigned int cp_rb_wptr, cp_rb_rptr;
unsigned int i;
@@ -854,21 +857,26 @@
(num_iommu_units && this_cmd ==
kgsl_mmu_get_reg_gpuaddr(&device->mmu, 0,
KGSL_IOMMU_CONTEXT_USER,
- KGSL_IOMMU_CTX_TTBR0))) {
- KGSL_LOG_DUMP(device, "Current pagetable: %x\t"
- "pagetable base: %x\n",
+ KGSL_IOMMU_CTX_TTBR0)) ||
+ (num_iommu_units && this_cmd == cp_type0_packet(
+ kgsl_mmu_get_reg_ahbaddr(
+ &device->mmu, 0,
+ KGSL_IOMMU_CONTEXT_USER,
+ KGSL_IOMMU_CTX_TTBR0), 1))) {
+ KGSL_LOG_DUMP(device,
+ "Current pagetable: %x\t pagetable base: %pa\n",
kgsl_mmu_get_ptname_from_ptbase(&device->mmu,
cur_pt_base),
- cur_pt_base);
+ &cur_pt_base);
/* Set cur_pt_base to the new pagetable base */
cur_pt_base = rb_copy[read_idx++];
- KGSL_LOG_DUMP(device, "New pagetable: %x\t"
- "pagetable base: %x\n",
+ KGSL_LOG_DUMP(device,
+ "New pagetable: %x\t pagetable base: %pa\n",
kgsl_mmu_get_ptname_from_ptbase(&device->mmu,
cur_pt_base),
- cur_pt_base);
+ &cur_pt_base);
}
}
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index 8c96884..70223db 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -14,6 +14,8 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/log2.h>
+#include <linux/time.h>
+#include <linux/delay.h>
#include "kgsl.h"
#include "kgsl_sharedmem.h"
@@ -64,7 +66,7 @@
unsigned long wait_time;
unsigned long wait_timeout = msecs_to_jiffies(ADRENO_IDLE_TIMEOUT);
unsigned long wait_time_part;
- unsigned int prev_reg_val[ft_detect_regs_count];
+ unsigned int prev_reg_val[FT_DETECT_REGS_COUNT];
memset(prev_reg_val, 0, sizeof(prev_reg_val));
@@ -76,7 +78,8 @@
cmds = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr;
cmds_gpu = rb->buffer_desc.gpuaddr + sizeof(uint)*rb->wptr;
- GSL_RB_WRITE(cmds, cmds_gpu, cp_nop_packet(nopcount));
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu,
+ cp_nop_packet(nopcount));
/* Make sure that rptr is not 0 before submitting
* commands at the end of ringbuffer. We do not
@@ -241,8 +244,16 @@
return ret;
}
-
-int adreno_ringbuffer_load_pm4_ucode(struct kgsl_device *device)
+/**
+ * adreno_ringbuffer_load_pm4_ucode() - Load pm4 ucode
+ * @device: Pointer to a KGSL device
+ * @start: Starting index in pm4 ucode to load
+ * @addr: Address to load the pm4 ucode
+ *
+ * Load the pm4 ucode from @start at @addr.
+ */
+int adreno_ringbuffer_load_pm4_ucode(struct kgsl_device *device,
+ unsigned int start, unsigned int addr)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
int i;
@@ -257,8 +268,8 @@
adreno_dev->pm4_fw_version);
adreno_regwrite(device, REG_CP_DEBUG, CP_DEBUG_DEFAULT);
- adreno_regwrite(device, REG_CP_ME_RAM_WADDR, 0);
- for (i = 1; i < adreno_dev->pm4_fw_size; i++)
+ adreno_regwrite(device, REG_CP_ME_RAM_WADDR, addr);
+ for (i = start; i < adreno_dev->pm4_fw_size; i++)
adreno_regwrite(device, REG_CP_ME_RAM_DATA,
adreno_dev->pm4_fw[i]);
@@ -296,7 +307,16 @@
return ret;
}
-int adreno_ringbuffer_load_pfp_ucode(struct kgsl_device *device)
+/**
+ * adreno_ringbuffer_load_pfp_ucode() - Load pfp ucode
+ * @device: Pointer to a KGSL device
+ * @start: Starting index in pfp ucode to load
+ * @addr: Address to load the pfp ucode
+ *
+ * Load the pfp ucode from @start at @addr.
+ */
+int adreno_ringbuffer_load_pfp_ucode(struct kgsl_device *device,
+ unsigned int start, unsigned int addr)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
int i;
@@ -310,8 +330,9 @@
KGSL_DRV_INFO(device, "loading pfp ucode version: %d\n",
adreno_dev->pfp_fw_version);
- adreno_regwrite(device, adreno_dev->gpudev->reg_cp_pfp_ucode_addr, 0);
- for (i = 1; i < adreno_dev->pfp_fw_size; i++)
+ adreno_regwrite(device, adreno_dev->gpudev->reg_cp_pfp_ucode_addr,
+ addr);
+ for (i = start; i < adreno_dev->pfp_fw_size; i++)
adreno_regwrite(device,
adreno_dev->gpudev->reg_cp_pfp_ucode_data,
adreno_dev->pfp_fw[i]);
@@ -319,7 +340,13 @@
return 0;
}
-int adreno_ringbuffer_start(struct adreno_ringbuffer *rb)
+/**
+ * _ringbuffer_start_common() - Ringbuffer start
+ * @rb: Pointer to adreno ringbuffer
+ *
+ * Setup ringbuffer for GPU.
+ */
+int _ringbuffer_start_common(struct adreno_ringbuffer *rb)
{
int status;
/*cp_rb_cntl_u cp_rb_cntl; */
@@ -331,10 +358,10 @@
if (rb->flags & KGSL_FLAGS_STARTED)
return 0;
- kgsl_sharedmem_set(&rb->memptrs_desc, 0, 0,
+ kgsl_sharedmem_set(rb->device, &rb->memptrs_desc, 0, 0,
sizeof(struct kgsl_rbmemptrs));
- kgsl_sharedmem_set(&rb->buffer_desc, 0, 0xAA,
+ kgsl_sharedmem_set(rb->device, &rb->buffer_desc, 0, 0xAA,
(rb->sizedwords << 2));
if (adreno_is_a2xx(adreno_dev)) {
@@ -419,16 +446,6 @@
adreno_regwrite(device, REG_SCRATCH_UMSK,
GSL_RB_MEMPTRS_SCRATCH_MASK);
- /* load the CP ucode */
- status = adreno_ringbuffer_load_pm4_ucode(device);
- if (status != 0)
- return status;
-
- /* load the prefetch parser ucode */
- status = adreno_ringbuffer_load_pfp_ucode(device);
- if (status != 0)
- return status;
-
/* CP ROQ queue sizes (bytes) - RB:16, ST:16, IB1:32, IB2:64 */
if (adreno_is_a305(adreno_dev) || adreno_is_a305c(adreno_dev) ||
adreno_is_a320(adreno_dev))
@@ -456,6 +473,54 @@
return status;
}
+/**
+ * adreno_ringbuffer_warm_start() - Ringbuffer warm start
+ * @rb: Pointer to adreno ringbuffer
+ *
+ * Start the ringbuffer but load only jump tables part of the
+ * microcode.
+ */
+int adreno_ringbuffer_warm_start(struct adreno_ringbuffer *rb)
+{
+ int status;
+ struct kgsl_device *device = rb->device;
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+ /* load the CP ucode */
+ status = adreno_ringbuffer_load_pm4_ucode(device,
+ adreno_dev->pm4_jt_idx, adreno_dev->pm4_jt_addr);
+ if (status != 0)
+ return status;
+
+ /* load the prefetch parser ucode */
+ status = adreno_ringbuffer_load_pfp_ucode(device,
+ adreno_dev->pfp_jt_idx, adreno_dev->pfp_jt_addr);
+ if (status != 0)
+ return status;
+
+ return _ringbuffer_start_common(rb);
+}
+
+int adreno_ringbuffer_start(struct adreno_ringbuffer *rb)
+{
+ int status;
+
+ if (rb->flags & KGSL_FLAGS_STARTED)
+ return 0;
+
+ /* load the CP ucode */
+ status = adreno_ringbuffer_load_pm4_ucode(rb->device, 1, 0);
+ if (status != 0)
+ return status;
+
+ /* load the prefetch parser ucode */
+ status = adreno_ringbuffer_load_pfp_ucode(rb->device, 1, 0);
+ if (status != 0)
+ return status;
+
+ return _ringbuffer_start_common(rb);
+}
+
void adreno_ringbuffer_stop(struct adreno_ringbuffer *rb)
{
struct kgsl_device *device = rb->device;
@@ -612,12 +677,13 @@
rcmd_gpu = rb->buffer_desc.gpuaddr
+ sizeof(uint)*(rb->wptr-total_sizedwords);
- GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_nop_packet(1));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, KGSL_CMD_IDENTIFIER);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, cp_nop_packet(1));
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, KGSL_CMD_IDENTIFIER);
if (flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE) {
- GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_nop_packet(1));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, KGSL_CMD_INTERNAL_IDENTIFIER);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, cp_nop_packet(1));
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
+ KGSL_CMD_INTERNAL_IDENTIFIER);
}
/* always increment the global timestamp. once. */
@@ -636,32 +702,35 @@
timestamp = rb->timestamp[context_id];
/* scratchpad ts for fault tolerance */
- GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_type0_packet(REG_CP_TIMESTAMP, 1));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
+ cp_type0_packet(REG_CP_TIMESTAMP, 1));
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
+ rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
/* start-of-pipeline timestamp */
- GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_type3_packet(CP_MEM_WRITE, 2));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
+ cp_type3_packet(CP_MEM_WRITE, 2));
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, (gpuaddr +
KGSL_MEMSTORE_OFFSET(context_id, soptimestamp)));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, timestamp);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, timestamp);
if (flags & KGSL_CMD_FLAGS_PMODE) {
/* disable protected mode error checking */
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
cp_type3_packet(CP_SET_PROTECTED_MODE, 1));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, 0);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, 0);
}
for (i = 0; i < sizedwords; i++) {
- GSL_RB_WRITE(ringcmds, rcmd_gpu, *cmds);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, *cmds);
cmds++;
}
if (flags & KGSL_CMD_FLAGS_PMODE) {
/* re-enable protected mode error checking */
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
cp_type3_packet(CP_SET_PROTECTED_MODE, 1));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, 1);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, 1);
}
/* HW Workaround for MMU Page fault
@@ -669,9 +738,9 @@
* GPU completes it.
*/
if (adreno_is_a2xx(adreno_dev)) {
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
cp_type3_packet(CP_WAIT_FOR_IDLE, 1));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x00);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, 0x00);
}
if (adreno_is_a3xx(adreno_dev)) {
@@ -680,12 +749,13 @@
* resources pending for indirect loads after the timestamp
*/
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
cp_type3_packet(CP_EVENT_WRITE, 1));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x07); /* HLSQ_FLUSH */
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds,
+ rcmd_gpu, 0x07); /* HLSQ_FLUSH */
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
cp_type3_packet(CP_WAIT_FOR_IDLE, 1));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x00);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, 0x00);
}
/*
@@ -693,62 +763,64 @@
* enabled, then context_id will be KGSL_MEMSTORE_GLOBAL so all
* eop timestamps will work out.
*/
- GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_type3_packet(CP_EVENT_WRITE, 3));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH_TS);
- GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
+ cp_type3_packet(CP_EVENT_WRITE, 3));
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, CACHE_FLUSH_TS);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, (gpuaddr +
KGSL_MEMSTORE_OFFSET(context_id, eoptimestamp)));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, timestamp);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, timestamp);
if (KGSL_MEMSTORE_GLOBAL != context_id) {
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
cp_type3_packet(CP_MEM_WRITE, 2));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, (gpuaddr +
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
eoptimestamp)));
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
}
if (adreno_is_a20x(adreno_dev)) {
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
cp_type3_packet(CP_EVENT_WRITE, 1));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, CACHE_FLUSH);
}
if (context) {
/* Conditional execution based on memory values */
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
cp_type3_packet(CP_COND_EXEC, 4));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, (gpuaddr +
KGSL_MEMSTORE_OFFSET(
context_id, ts_cmp_enable)) >> 2);
- GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, (gpuaddr +
KGSL_MEMSTORE_OFFSET(
context_id, ref_wait_ts)) >> 2);
- GSL_RB_WRITE(ringcmds, rcmd_gpu, timestamp);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, timestamp);
/* # of conditional command DWORDs */
- GSL_RB_WRITE(ringcmds, rcmd_gpu, 8);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, 8);
/* Clear the ts_cmp_enable for the context */
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
cp_type3_packet(CP_MEM_WRITE, 2));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, gpuaddr +
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, gpuaddr +
KGSL_MEMSTORE_OFFSET(
context_id, ts_cmp_enable));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x0);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, 0x0);
/* Clear the ts_cmp_enable for the global timestamp */
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
cp_type3_packet(CP_MEM_WRITE, 2));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, gpuaddr +
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, gpuaddr +
KGSL_MEMSTORE_OFFSET(
KGSL_MEMSTORE_GLOBAL, ts_cmp_enable));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x0);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, 0x0);
/* Trigger the interrupt */
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
cp_type3_packet(CP_INTERRUPT, 1));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, CP_INT_CNTL__RB_INT_MASK);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
+ CP_INT_CNTL__RB_INT_MASK);
}
/*
@@ -758,24 +830,25 @@
if ((context) && (context->flags & CTXT_FLAGS_PER_CONTEXT_TS) &&
(flags & (KGSL_CMD_FLAGS_INTERNAL_ISSUE |
KGSL_CMD_FLAGS_GET_INT))) {
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
cp_type3_packet(CP_INTERRUPT, 1));
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
CP_INT_CNTL__RB_INT_MASK);
}
if (adreno_is_a3xx(adreno_dev)) {
/* Dummy set-constant to trigger context rollover */
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
cp_type3_packet(CP_SET_CONSTANT, 2));
- GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
(0x4<<16)|(A3XX_HLSQ_CL_KERNEL_GROUP_X_REG - 0x2000));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, 0);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, 0);
}
if (flags & KGSL_CMD_FLAGS_EOF) {
- GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_nop_packet(1));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, KGSL_END_OF_FRAME_IDENTIFIER);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, cp_nop_packet(1));
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
+ KGSL_END_OF_FRAME_IDENTIFIER);
}
adreno_ringbuffer_submit(rb);
@@ -1122,8 +1195,10 @@
ret = 0;
done:
- kgsl_trace_issueibcmds(device, context->id, ibdesc, numibs,
- *timestamp, flags, ret, drawctxt->type);
+ device->pwrctrl.irq_last = 0;
+ kgsl_trace_issueibcmds(device, context ? context->id : 0, ibdesc,
+ numibs, *timestamp, flags, ret,
+ drawctxt ? drawctxt->type : 0);
kfree(link);
return ret;
@@ -1148,7 +1223,8 @@
if (val[i] == cp_nop_packet(4)) {
temp_rb_rptr = adreno_ringbuffer_dec_wrapped(
temp_rb_rptr, size);
- kgsl_sharedmem_writel(&rb->buffer_desc,
+ kgsl_sharedmem_writel(rb->device,
+ &rb->buffer_desc,
temp_rb_rptr, cp_nop_packet(1));
}
KGSL_FT_INFO(rb->device,
@@ -1310,7 +1386,7 @@
ringcmds = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr;
rcmd_gpu = rb->buffer_desc.gpuaddr + sizeof(unsigned int) * rb->wptr;
for (i = 0; i < num_rb_contents; i++)
- GSL_RB_WRITE(ringcmds, rcmd_gpu, rb_buff[i]);
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, rb_buff[i]);
rb->wptr += num_rb_contents;
adreno_ringbuffer_submit(rb);
}
diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h
index e563ec7..f59b834 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.h
+++ b/drivers/gpu/msm/adreno_ringbuffer.h
@@ -60,11 +60,11 @@
};
-#define GSL_RB_WRITE(ring, gpuaddr, data) \
+#define GSL_RB_WRITE(device, ring, gpuaddr, data) \
do { \
*ring = data; \
wmb(); \
- kgsl_cffdump_setmem(gpuaddr, data, 4); \
+ kgsl_cffdump_setmem(device, gpuaddr, data, 4); \
ring++; \
gpuaddr += sizeof(uint); \
} while (0)
@@ -97,6 +97,8 @@
int adreno_ringbuffer_init(struct kgsl_device *device);
+int adreno_ringbuffer_warm_start(struct adreno_ringbuffer *rb);
+
int adreno_ringbuffer_start(struct adreno_ringbuffer *rb);
void adreno_ringbuffer_stop(struct adreno_ringbuffer *rb);
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index a76ed87..d6ce298 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -34,7 +34,7 @@
static struct kgsl_snapshot_obj {
int type;
uint32_t gpuaddr;
- uint32_t ptbase;
+ phys_addr_t ptbase;
void *ptr;
int dwords;
} objbuf[SNAPSHOT_OBJ_BUFSIZE];
@@ -43,7 +43,8 @@
static int objbufptr;
/* Push a new buffer object onto the list */
-static void push_object(struct kgsl_device *device, int type, uint32_t ptbase,
+static void push_object(struct kgsl_device *device, int type,
+ phys_addr_t ptbase,
uint32_t gpuaddr, int dwords)
{
int index;
@@ -94,7 +95,7 @@
* to be dumped
*/
-static int find_object(int type, unsigned int gpuaddr, unsigned int ptbase)
+static int find_object(int type, unsigned int gpuaddr, phys_addr_t ptbase)
{
int index;
@@ -184,7 +185,7 @@
};
static int ib_parse_load_state(struct kgsl_device *device, unsigned int *pkt,
- unsigned int ptbase)
+ phys_addr_t ptbase)
{
unsigned int block, source, type;
int ret = 0;
@@ -243,7 +244,7 @@
*/
static int ib_parse_set_bin_data(struct kgsl_device *device, unsigned int *pkt,
- unsigned int ptbase)
+ phys_addr_t ptbase)
{
int ret;
@@ -276,7 +277,7 @@
*/
static int ib_parse_mem_write(struct kgsl_device *device, unsigned int *pkt,
- unsigned int ptbase)
+ phys_addr_t ptbase)
{
int ret;
@@ -307,7 +308,7 @@
*/
static int ib_parse_draw_indx(struct kgsl_device *device, unsigned int *pkt,
- unsigned int ptbase)
+ phys_addr_t ptbase)
{
int ret = 0, i;
@@ -439,7 +440,7 @@
*/
static int ib_parse_type3(struct kgsl_device *device, unsigned int *ptr,
- unsigned int ptbase)
+ phys_addr_t ptbase)
{
int opcode = cp_type3_opcode(*ptr);
@@ -464,7 +465,7 @@
*/
static void ib_parse_type0(struct kgsl_device *device, unsigned int *ptr,
- unsigned int ptbase)
+ phys_addr_t ptbase)
{
int size = type0_pkt_size(*ptr);
int offset = type0_pkt_offset(*ptr);
@@ -542,12 +543,12 @@
}
}
-static inline int parse_ib(struct kgsl_device *device, unsigned int ptbase,
+static inline int parse_ib(struct kgsl_device *device, phys_addr_t ptbase,
unsigned int gpuaddr, unsigned int dwords);
/* Add an IB as a GPU object, but first, parse it to find more goodies within */
-static int ib_add_gpu_object(struct kgsl_device *device, unsigned int ptbase,
+static int ib_add_gpu_object(struct kgsl_device *device, phys_addr_t ptbase,
unsigned int gpuaddr, unsigned int dwords)
{
int i, ret, rem = dwords;
@@ -625,7 +626,7 @@
* access the dynamic data from the sysfs file. Push all other IBs on the
* dynamic list
*/
-static inline int parse_ib(struct kgsl_device *device, unsigned int ptbase,
+static inline int parse_ib(struct kgsl_device *device, phys_addr_t ptbase,
unsigned int gpuaddr, unsigned int dwords)
{
unsigned int ib1base, ib2base;
@@ -657,7 +658,8 @@
unsigned int *data = snapshot + sizeof(*header);
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
- unsigned int ptbase, rptr, *rbptr, ibbase;
+ unsigned int rptr, *rbptr, ibbase;
+ phys_addr_t ptbase;
int index, size, i;
int parse_ibs = 0, ib_parse_start;
@@ -785,13 +787,13 @@
struct kgsl_memdesc *memdesc =
adreno_find_ctxtmem(device, ptbase, ibaddr,
- ibsize);
+ ibsize << 2);
/* IOMMU uses a NOP IB placed in setsate memory */
if (NULL == memdesc)
if (kgsl_gpuaddr_in_memdesc(
&device->mmu.setstate_memory,
- ibaddr, ibsize))
+ ibaddr, ibsize << 2))
memdesc = &device->mmu.setstate_memory;
/*
* The IB from CP_IB1_BASE and the IBs for legacy
@@ -822,8 +824,9 @@
int remain, void *priv)
{
struct kgsl_snapshot_replay_mem_list *header = snapshot;
- struct kgsl_process_private *private;
- unsigned int ptbase;
+ struct kgsl_process_private *private = NULL;
+ struct kgsl_process_private *tmp_private;
+ phys_addr_t ptbase;
struct rb_node *node;
struct kgsl_mem_entry *entry = NULL;
int num_mem;
@@ -831,10 +834,12 @@
ptbase = kgsl_mmu_get_current_ptbase(&device->mmu);
mutex_lock(&kgsl_driver.process_mutex);
- list_for_each_entry(private, &kgsl_driver.process_list, list) {
- if (kgsl_mmu_pt_equal(&device->mmu, private->pagetable,
- ptbase))
+ list_for_each_entry(tmp_private, &kgsl_driver.process_list, list) {
+ if (kgsl_mmu_pt_equal(&device->mmu, tmp_private->pagetable,
+ ptbase)) {
+ private = tmp_private;
break;
+ }
}
mutex_unlock(&kgsl_driver.process_mutex);
if (!private) {
@@ -858,7 +863,7 @@
return 0;
}
header->num_entries = num_mem;
- header->ptbase = ptbase;
+ header->ptbase = (__u32)ptbase;
/*
* Walk throught the memory list and store the
* tuples(gpuaddr, size, memtype) in snapshot
@@ -894,7 +899,7 @@
/* Write the sub-header for the section */
header->gpuaddr = obj->gpuaddr;
- header->ptbase = obj->ptbase;
+ header->ptbase = (__u32)obj->ptbase;
header->size = obj->dwords;
/* Write the contents of the ib */
@@ -954,8 +959,9 @@
int hang)
{
int i;
- uint32_t ptbase, ibbase, ibsize;
+ uint32_t ibbase, ibsize;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ phys_addr_t ptbase;
/* Reset the list of objects */
objbufptr = 0;
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 5275267..becb4ef 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -18,17 +18,18 @@
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
-
+#include <linux/dma-buf.h>
#include <linux/vmalloc.h>
#include <linux/pm_runtime.h>
#include <linux/genlock.h>
#include <linux/rbtree.h>
#include <linux/ashmem.h>
#include <linux/major.h>
-#include <linux/msm_ion.h>
#include <linux/io.h>
#include <mach/socinfo.h>
#include <linux/mman.h>
+#include <linux/sort.h>
+#include <asm/cacheflush.h>
#include "kgsl.h"
#include "kgsl_debugfs.h"
@@ -38,6 +39,7 @@
#include "kgsl_device.h"
#include "kgsl_trace.h"
#include "kgsl_sync.h"
+#include "adreno.h"
#undef MODULE_PARAM_PREFIX
#define MODULE_PARAM_PREFIX "kgsl."
@@ -51,7 +53,59 @@
MODULE_PARM_DESC(ksgl_mmu_type,
"Type of MMU to be used for graphics. Valid values are 'iommu' or 'gpummu' or 'nommu'");
-static struct ion_client *kgsl_ion_client;
+struct kgsl_dma_buf_meta {
+ struct dma_buf_attachment *attach;
+ struct dma_buf *dmabuf;
+ struct sg_table *table;
+};
+
+/**
+ * kgsl_hang_check() - Check for GPU hang
+ * data: KGSL device structure
+ *
+ * This function is called every KGSL_TIMEOUT_PART time when
+ * GPU is active to check for hang. If a hang is detected we
+ * trigger fault tolerance.
+ */
+void kgsl_hang_check(struct work_struct *work)
+{
+ struct kgsl_device *device = container_of(work, struct kgsl_device,
+ hang_check_ws);
+ static unsigned int prev_reg_val[FT_DETECT_REGS_COUNT];
+
+ mutex_lock(&device->mutex);
+
+ if (device->state == KGSL_STATE_ACTIVE) {
+
+ /* Check to see if the GPU is hung */
+ if (adreno_ft_detect(device, prev_reg_val))
+ adreno_dump_and_exec_ft(device);
+
+ mod_timer(&device->hang_timer,
+ (jiffies + msecs_to_jiffies(KGSL_TIMEOUT_PART)));
+ }
+
+ mutex_unlock(&device->mutex);
+}
+
+/**
+ * hang_timer() - Hang timer function
+ * data: KGSL device structure
+ *
+ * This function is called when hang timer expires, in this
+ * function we check if GPU is in active state and queue the
+ * work on device workqueue to check for the hang. We restart
+ * the timer after KGSL_TIMEOUT_PART time.
+ */
+void hang_timer(unsigned long data)
+{
+ struct kgsl_device *device = (struct kgsl_device *) data;
+
+ if (device->state == KGSL_STATE_ACTIVE) {
+ /* Have work run in a non-interrupt context. */
+ queue_work(device->work_queue, &device->hang_check_ws);
+ }
+}
/**
* kgsl_trace_issueibcmds() - Call trace_issueibcmds by proxy
@@ -144,10 +198,13 @@
* @ptbase - the pagetable base of the object
* @gpuaddr - the GPU address of the object
* @size - Size of the region to search
+ *
+ * Caller must kgsl_mem_entry_put() the returned entry when finished using it.
*/
-struct kgsl_mem_entry *kgsl_get_mem_entry(struct kgsl_device *device,
- unsigned int ptbase, unsigned int gpuaddr, unsigned int size)
+struct kgsl_mem_entry * __must_check
+kgsl_get_mem_entry(struct kgsl_device *device,
+ phys_addr_t ptbase, unsigned int gpuaddr, unsigned int size)
{
struct kgsl_process_private *priv;
struct kgsl_mem_entry *entry;
@@ -157,15 +214,12 @@
list_for_each_entry(priv, &kgsl_driver.process_list, list) {
if (!kgsl_mmu_pt_equal(&device->mmu, priv->pagetable, ptbase))
continue;
- spin_lock(&priv->mem_lock);
entry = kgsl_sharedmem_find_region(priv, gpuaddr, size);
if (entry) {
- spin_unlock(&priv->mem_lock);
mutex_unlock(&kgsl_driver.process_mutex);
return entry;
}
- spin_unlock(&priv->mem_lock);
}
mutex_unlock(&kgsl_driver.process_mutex);
@@ -186,6 +240,14 @@
return entry;
}
+static void kgsl_destroy_ion(struct kgsl_dma_buf_meta *meta)
+{
+ dma_buf_unmap_attachment(meta->attach, meta->table, DMA_FROM_DEVICE);
+ dma_buf_detach(meta->dmabuf, meta->attach);
+ dma_buf_put(meta->dmabuf);
+ kfree(meta);
+}
+
void
kgsl_mem_entry_destroy(struct kref *kref)
{
@@ -215,7 +277,7 @@
fput(entry->priv_data);
break;
case KGSL_MEM_ENTRY_ION:
- ion_free(kgsl_ion_client, entry->priv_data);
+ kgsl_destroy_ion(entry->priv_data);
break;
}
@@ -308,16 +370,28 @@
static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry)
{
+ bool had_gpuaddr = false;
+
if (entry == NULL)
return;
+ /*
+ * Unmap the entry first so that there isn't a period of
+ * time where kgsl doesn't know about the address range
+ * but it is still present in the pagetable. Unmapping will
+ * clear the gpuaddr field, so remember if we had a mapping,
+ * and an rbtree entry for later.
+ */
+ had_gpuaddr = entry->memdesc.gpuaddr != 0;
+ kgsl_mmu_unmap(entry->memdesc.pagetable, &entry->memdesc);
+
spin_lock(&entry->priv->mem_lock);
if (entry->id != 0)
idr_remove(&entry->priv->mem_idr, entry->id);
entry->id = 0;
- if (entry->memdesc.gpuaddr != 0)
+ if (had_gpuaddr)
rb_erase(&entry->node, &entry->priv->mem_rb);
spin_unlock(&entry->priv->mem_lock);
@@ -325,7 +399,6 @@
entry->priv->stats[entry->memtype].cur -= entry->memdesc.size;
entry->priv = NULL;
- kgsl_mmu_unmap(entry->memdesc.pagetable, &entry->memdesc);
kgsl_mem_entry_put(entry);
}
@@ -501,7 +574,6 @@
static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state)
{
int status = -EINVAL;
- unsigned int nap_allowed_saved;
struct kgsl_pwrscale_policy *policy_saved;
if (!device)
@@ -510,8 +582,6 @@
KGSL_PWR_WARN(device, "suspend start\n");
mutex_lock(&device->mutex);
- nap_allowed_saved = device->pwrctrl.nap_allowed;
- device->pwrctrl.nap_allowed = false;
policy_saved = device->pwrscale.policy;
device->pwrscale.policy = NULL;
kgsl_pwrctrl_request_state(device, KGSL_STATE_SUSPEND);
@@ -521,8 +591,15 @@
*/
kgsl_active_count_wait(device);
+ /*
+ * An interrupt could have snuck in and requested NAP in
+ * the meantime, make sure we're on the SUSPEND path.
+ */
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_SUSPEND);
+
/* Don't let the timer wake us during suspended sleep. */
del_timer_sync(&device->idle_timer);
+ del_timer_sync(&device->hang_timer);
switch (device->state) {
case KGSL_STATE_INIT:
break;
@@ -551,7 +628,6 @@
goto end;
}
kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
- device->pwrctrl.nap_allowed = nap_allowed_saved;
device->pwrscale.policy = policy_saved;
status = 0;
@@ -563,23 +639,32 @@
static int kgsl_resume_device(struct kgsl_device *device)
{
- int status = -EINVAL;
-
if (!device)
return -EINVAL;
KGSL_PWR_WARN(device, "resume start\n");
mutex_lock(&device->mutex);
if (device->state == KGSL_STATE_SUSPEND) {
- kgsl_pwrctrl_set_state(device, KGSL_STATE_SLUMBER);
- status = 0;
complete_all(&device->hwaccess_gate);
+ } else {
+ /*
+ * This is an error situation,so wait for the device
+ * to idle and then put the device to SLUMBER state.
+ * This will put the device to the right state when
+ * we resume.
+ */
+ device->ftbl->idle(device);
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_SLUMBER);
+ kgsl_pwrctrl_sleep(device);
+ KGSL_PWR_ERR(device,
+ "resume invoked without a suspend\n");
}
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_SLUMBER);
kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
mutex_unlock(&device->mutex);
KGSL_PWR_WARN(device, "resume end\n");
- return status;
+ return 0;
}
static int kgsl_suspend(struct device *dev)
@@ -614,20 +699,6 @@
};
EXPORT_SYMBOL(kgsl_pm_ops);
-void kgsl_early_suspend_driver(struct early_suspend *h)
-{
- struct kgsl_device *device = container_of(h,
- struct kgsl_device, display_off);
- KGSL_PWR_WARN(device, "early suspend start\n");
- mutex_lock(&device->mutex);
- device->pwrctrl.restore_slumber = true;
- kgsl_pwrctrl_request_state(device, KGSL_STATE_SLUMBER);
- kgsl_pwrctrl_sleep(device);
- mutex_unlock(&device->mutex);
- KGSL_PWR_WARN(device, "early suspend end\n");
-}
-EXPORT_SYMBOL(kgsl_early_suspend_driver);
-
int kgsl_suspend_driver(struct platform_device *pdev,
pm_message_t state)
{
@@ -643,101 +714,44 @@
}
EXPORT_SYMBOL(kgsl_resume_driver);
-void kgsl_late_resume_driver(struct early_suspend *h)
+/**
+ * kgsl_destroy_process_private() - Cleanup function to free process private
+ * @kref: - Pointer to object being destroyed's kref struct
+ * Free struct object and all other resources attached to it.
+ * Since the function can be used when not all resources inside process
+ * private have been allocated, there is a check to (before each resource
+ * cleanup) see if the struct member being cleaned is in fact allocated or not.
+ * If the value is not NULL, resource is freed.
+ */
+static void kgsl_destroy_process_private(struct kref *kref)
{
- struct kgsl_device *device = container_of(h,
- struct kgsl_device, display_off);
- KGSL_PWR_WARN(device, "late resume start\n");
- mutex_lock(&device->mutex);
- device->pwrctrl.restore_slumber = false;
- if (device->pwrscale.policy == NULL)
- kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO);
- if (kgsl_pwrctrl_wake(device) != 0)
- return;
- /*
- * We don't have a way to go directly from
- * a deeper sleep state to NAP, which is
- * the desired state here.
- */
- kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
- kgsl_pwrctrl_sleep(device);
- mutex_unlock(&device->mutex);
- KGSL_PWR_WARN(device, "late resume end\n");
-}
-EXPORT_SYMBOL(kgsl_late_resume_driver);
-/* file operations */
-static struct kgsl_process_private *
-kgsl_get_process_private(struct kgsl_device_private *cur_dev_priv)
-{
- struct kgsl_process_private *private;
-
- mutex_lock(&kgsl_driver.process_mutex);
- list_for_each_entry(private, &kgsl_driver.process_list, list) {
- if (private->pid == task_tgid_nr(current)) {
- private->refcnt++;
- goto out;
- }
- }
-
- /* no existing process private found for this dev_priv, create one */
- private = kzalloc(sizeof(struct kgsl_process_private), GFP_KERNEL);
- if (private == NULL) {
- KGSL_DRV_ERR(cur_dev_priv->device, "kzalloc(%d) failed\n",
- sizeof(struct kgsl_process_private));
- goto out;
- }
-
- spin_lock_init(&private->mem_lock);
- private->refcnt = 1;
- private->pid = task_tgid_nr(current);
- private->mem_rb = RB_ROOT;
-
- idr_init(&private->mem_idr);
-
- if (kgsl_mmu_enabled())
- {
- unsigned long pt_name;
- struct kgsl_mmu *mmu = &cur_dev_priv->device->mmu;
-
- pt_name = task_tgid_nr(current);
- private->pagetable = kgsl_mmu_getpagetable(mmu, pt_name);
- if (private->pagetable == NULL) {
- kfree(private);
- private = NULL;
- goto out;
- }
- }
-
- list_add(&private->list, &kgsl_driver.process_list);
-
- kgsl_process_init_sysfs(private);
- kgsl_process_init_debugfs(private);
-
-out:
- mutex_unlock(&kgsl_driver.process_mutex);
- return private;
-}
-
-static void
-kgsl_put_process_private(struct kgsl_device *device,
- struct kgsl_process_private *private)
-{
struct kgsl_mem_entry *entry = NULL;
int next = 0;
- if (!private)
+
+ struct kgsl_process_private *private = container_of(kref,
+ struct kgsl_process_private, refcount);
+
+ /*
+ * Remove this process from global process list
+ * We do not acquire a lock first as it is expected that
+ * kgsl_destroy_process_private() is only going to be called
+ * through kref_put() which is only called after acquiring
+ * the lock.
+ */
+ if (!private) {
+ KGSL_CORE_ERR("Cannot destroy null process private\n");
+ mutex_unlock(&kgsl_driver.process_mutex);
return;
-
- mutex_lock(&kgsl_driver.process_mutex);
-
- if (--private->refcnt)
- goto unlock;
-
- kgsl_process_uninit_sysfs(private);
- debugfs_remove_recursive(private->debug_root);
-
+ }
list_del(&private->list);
+ mutex_unlock(&kgsl_driver.process_mutex);
+
+ if (private->kobj.ktype)
+ kgsl_process_uninit_sysfs(private);
+ if (private->debug_root)
+ debugfs_remove_recursive(private->debug_root);
while (1) {
rcu_read_lock();
@@ -755,11 +769,133 @@
}
kgsl_mmu_putpagetable(private->pagetable);
idr_destroy(&private->mem_idr);
+
kfree(private);
-unlock:
- mutex_unlock(&kgsl_driver.process_mutex);
+ return;
}
+static void
+kgsl_put_process_private(struct kgsl_device *device,
+ struct kgsl_process_private *private)
+{
+ mutex_lock(&kgsl_driver.process_mutex);
+
+ /*
+ * kref_put() returns 1 when the refcnt has reached 0 and the destroy
+ * function is called. Mutex is released in the destroy function if
+ * its called, so only release mutex if kref_put() return 0
+ */
+ if (!kref_put(&private->refcount, kgsl_destroy_process_private))
+ mutex_unlock(&kgsl_driver.process_mutex);
+ return;
+}
+
+/**
+ * find_process_private() - Helper function to search for process private
+ * @cur_dev_priv: Pointer to device private structure which contains pointers
+ * to device and process_private structs.
+ * Returns: Pointer to the found/newly created private struct
+ */
+static struct kgsl_process_private *
+kgsl_find_process_private(struct kgsl_device_private *cur_dev_priv)
+{
+ struct kgsl_process_private *private;
+
+ /* Search in the process list */
+ mutex_lock(&kgsl_driver.process_mutex);
+ list_for_each_entry(private, &kgsl_driver.process_list, list) {
+ if (private->pid == task_tgid_nr(current)) {
+ kref_get(&private->refcount);
+ goto done;
+ }
+ }
+
+ /* no existing process private found for this dev_priv, create one */
+ private = kzalloc(sizeof(struct kgsl_process_private), GFP_KERNEL);
+ if (private == NULL) {
+ KGSL_DRV_ERR(cur_dev_priv->device, "kzalloc(%d) failed\n",
+ sizeof(struct kgsl_process_private));
+ goto done;
+ }
+
+ kref_init(&private->refcount);
+
+ private->pid = task_tgid_nr(current);
+ spin_lock_init(&private->mem_lock);
+ mutex_init(&private->process_private_mutex);
+ /* Add the newly created process struct obj to the process list */
+ list_add(&private->list, &kgsl_driver.process_list);
+done:
+ mutex_unlock(&kgsl_driver.process_mutex);
+ return private;
+}
+
+/**
+ * kgsl_get_process_private() - Used to find the process private structure
+ * @cur_dev_priv: Current device pointer
+ * Finds or creates a new porcess private structire and initializes its members
+ * Returns: Pointer to the private process struct obj found/created or
+ * NULL if pagetable creation for this process private obj failed.
+ */
+static struct kgsl_process_private *
+kgsl_get_process_private(struct kgsl_device_private *cur_dev_priv)
+{
+ struct kgsl_process_private *private;
+
+ private = kgsl_find_process_private(cur_dev_priv);
+
+ mutex_lock(&private->process_private_mutex);
+
+ if (!private->mem_rb.rb_node) {
+ private->mem_rb = RB_ROOT;
+ idr_init(&private->mem_idr);
+ }
+
+ if ((!private->pagetable) && kgsl_mmu_enabled()) {
+ unsigned long pt_name;
+ struct kgsl_mmu *mmu = &cur_dev_priv->device->mmu;
+
+ pt_name = task_tgid_nr(current);
+ private->pagetable = kgsl_mmu_getpagetable(mmu, pt_name);
+ if (private->pagetable == NULL) {
+ mutex_unlock(&private->process_private_mutex);
+ kgsl_put_process_private(cur_dev_priv->device,
+ private);
+ return NULL;
+ }
+ }
+
+ if (!private->kobj.ktype)
+ kgsl_process_init_sysfs(private);
+ if (!private->debug_root)
+ kgsl_process_init_debugfs(private);
+
+ mutex_unlock(&private->process_private_mutex);
+
+ return private;
+}
+
+int kgsl_close_device(struct kgsl_device *device)
+{
+ int result = 0;
+ device->open_count--;
+ if (device->open_count == 0) {
+ BUG_ON(device->active_cnt > 1);
+ result = device->ftbl->stop(device);
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
+ /*
+ * active_cnt special case: we just stopped the device,
+ * so no need to use kgsl_active_count_put()
+ */
+ device->active_cnt--;
+ } else {
+ kgsl_active_count_put(device);
+ }
+ return result;
+
+}
+EXPORT_SYMBOL(kgsl_close_device);
+
static int kgsl_release(struct inode *inodep, struct file *filep)
{
int result = 0;
@@ -779,8 +915,10 @@
if (context == NULL)
break;
- if (context->dev_priv == dev_priv)
+ if (context->dev_priv == dev_priv) {
kgsl_context_detach(context);
+ context->dev_priv = NULL;
+ }
next = next + 1;
}
@@ -792,19 +930,7 @@
*/
kgsl_cancel_events(device, dev_priv);
- device->open_count--;
- if (device->open_count == 0) {
- BUG_ON(device->active_cnt > 1);
- result = device->ftbl->stop(device);
- kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
- /*
- * active_cnt special case: we just stopped the device,
- * so no need to use kgsl_active_count_put()
- */
- device->active_cnt--;
- } else {
- kgsl_active_count_put(device);
- }
+ result = kgsl_close_device(device);
mutex_unlock(&device->mutex);
kfree(dev_priv);
@@ -814,6 +940,43 @@
return result;
}
+int kgsl_open_device(struct kgsl_device *device)
+{
+ int result = 0;
+ if (device->open_count == 0) {
+ /*
+ * active_cnt special case: we are starting up for the first
+ * time, so use this sequence instead of the kgsl_pwrctrl_wake()
+ * which will be called by kgsl_active_count_get().
+ */
+ device->active_cnt++;
+ kgsl_sharedmem_set(device, &device->memstore, 0, 0,
+ device->memstore.size);
+
+ result = device->ftbl->init(device);
+ if (result)
+ goto err;
+
+ result = device->ftbl->start(device);
+ if (result)
+ goto err;
+ /*
+ * Make sure the gates are open, so they don't block until
+ * we start suspend or FT.
+ */
+ complete_all(&device->ft_gate);
+ complete_all(&device->hwaccess_gate);
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE);
+ kgsl_active_count_put(device);
+ }
+ device->open_count++;
+err:
+ if (result)
+ device->active_cnt--;
+ return result;
+}
+EXPORT_SYMBOL(kgsl_open_device);
+
static int kgsl_open(struct inode *inodep, struct file *filep)
{
int result;
@@ -851,28 +1014,9 @@
mutex_lock(&device->mutex);
- if (device->open_count == 0) {
- /*
- * active_cnt special case: we are starting up for the first
- * time, so use this sequence instead of the kgsl_pwrctrl_wake()
- * which will be called by kgsl_active_count_get().
- */
- device->active_cnt++;
- kgsl_sharedmem_set(&device->memstore, 0, 0,
- device->memstore.size);
-
- result = device->ftbl->init(device);
- if (result)
- goto err_freedevpriv;
-
- result = device->ftbl->start(device);
- if (result)
- goto err_freedevpriv;
-
- kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE);
- kgsl_active_count_put(device);
- }
- device->open_count++;
+ result = kgsl_open_device(device);
+ if (result)
+ goto err_freedevpriv;
mutex_unlock(&device->mutex);
/*
@@ -900,11 +1044,9 @@
kgsl_pwrctrl_enable(device);
result = device->ftbl->stop(device);
kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
+ device->active_cnt--;
}
err_freedevpriv:
- /* only the first open takes an active count */
- if (device->open_count == 0)
- device->active_cnt--;
mutex_unlock(&device->mutex);
filep->private_data = NULL;
kfree(dev_priv);
@@ -913,8 +1055,17 @@
return result;
}
-/*call with private->mem_lock locked */
-struct kgsl_mem_entry *
+/**
+ * kgsl_sharedmem_find_region() - Find a gpu memory allocation
+ *
+ * @private: private data for the process to check.
+ * @gpuaddr: start address of the region
+ * @size: size of the region
+ *
+ * Find a gpu allocation. Caller must kgsl_mem_entry_put()
+ * the returned entry when finished using it.
+ */
+struct kgsl_mem_entry * __must_check
kgsl_sharedmem_find_region(struct kgsl_process_private *private,
unsigned int gpuaddr, size_t size)
{
@@ -923,46 +1074,57 @@
if (!kgsl_mmu_gpuaddr_in_range(private->pagetable, gpuaddr))
return NULL;
+ spin_lock(&private->mem_lock);
while (node != NULL) {
struct kgsl_mem_entry *entry;
entry = rb_entry(node, struct kgsl_mem_entry, node);
-
- if (kgsl_gpuaddr_in_memdesc(&entry->memdesc, gpuaddr, size))
+ if (kgsl_gpuaddr_in_memdesc(&entry->memdesc, gpuaddr, size)) {
+ kgsl_mem_entry_get(entry);
+ spin_unlock(&private->mem_lock);
return entry;
-
+ }
if (gpuaddr < entry->memdesc.gpuaddr)
node = node->rb_left;
else if (gpuaddr >=
(entry->memdesc.gpuaddr + entry->memdesc.size))
node = node->rb_right;
else {
+ spin_unlock(&private->mem_lock);
return NULL;
}
}
+ spin_unlock(&private->mem_lock);
return NULL;
}
EXPORT_SYMBOL(kgsl_sharedmem_find_region);
-/*call with private->mem_lock locked */
-static inline struct kgsl_mem_entry *
+/**
+ * kgsl_sharedmem_find() - Find a gpu memory allocation
+ *
+ * @private: private data for the process to check.
+ * @gpuaddr: start address of the region
+ *
+ * Find a gpu allocation. Caller must kgsl_mem_entry_put()
+ * the returned entry when finished using it.
+ */
+static inline struct kgsl_mem_entry * __must_check
kgsl_sharedmem_find(struct kgsl_process_private *private, unsigned int gpuaddr)
{
return kgsl_sharedmem_find_region(private, gpuaddr, 1);
}
/**
- * kgsl_sharedmem_region_empty - Check if an addression region is empty
+ * kgsl_sharedmem_region_empty() - Check if an addression region is empty
*
* @private: private data for the process to check.
* @gpuaddr: start address of the region
* @size: length of the region.
*
* Checks that there are no existing allocations within an address
- * region. Note that unlike other kgsl_sharedmem* search functions,
- * this one manages locking on its own.
+ * region.
*/
int
kgsl_sharedmem_region_empty(struct kgsl_process_private *private,
@@ -1006,19 +1168,24 @@
}
/**
- * kgsl_sharedmem_find_id - find a memory entry by id
+ * kgsl_sharedmem_find_id() - find a memory entry by id
* @process: the owning process
* @id: id to find
*
* @returns - the mem_entry or NULL
+ *
+ * Caller must kgsl_mem_entry_put() the returned entry, when finished using
+ * it.
*/
-static inline struct kgsl_mem_entry *
+static inline struct kgsl_mem_entry * __must_check
kgsl_sharedmem_find_id(struct kgsl_process_private *process, unsigned int id)
{
struct kgsl_mem_entry *entry;
rcu_read_lock();
entry = idr_find(&process->mem_idr, id);
+ if (entry)
+ kgsl_mem_entry_get(entry);
rcu_read_unlock();
return entry;
@@ -1314,23 +1481,26 @@
struct kgsl_device *device = dev_priv->device;
unsigned int context_id = context ? context->id : KGSL_MEMSTORE_GLOBAL;
- spin_lock(&dev_priv->process_priv->mem_lock);
entry = kgsl_sharedmem_find(dev_priv->process_priv, gpuaddr);
- spin_unlock(&dev_priv->process_priv->mem_lock);
if (!entry) {
KGSL_DRV_ERR(dev_priv->device,
"invalid gpuaddr %08x\n", gpuaddr);
- result = -EINVAL;
- goto done;
+ return -EINVAL;
}
+ if (entry->memdesc.priv & KGSL_MEMDESC_FREE_PENDING) {
+ kgsl_mem_entry_put(entry);
+ return -EBUSY;
+ }
+ entry->memdesc.priv |= KGSL_MEMDESC_FREE_PENDING;
+
trace_kgsl_mem_timestamp_queue(device, entry, context_id,
kgsl_readtimestamp(device, context,
KGSL_TIMESTAMP_RETIRED),
timestamp);
result = kgsl_add_event(dev_priv->device, context_id, timestamp,
kgsl_freemem_event_cb, entry, dev_priv);
-done:
+ kgsl_mem_entry_put(entry);
return result;
}
@@ -1416,15 +1586,18 @@
struct kgsl_process_private *private = dev_priv->process_priv;
struct kgsl_mem_entry *entry = NULL;
- spin_lock(&private->mem_lock);
entry = kgsl_sharedmem_find(private, param->gpuaddr);
- spin_unlock(&private->mem_lock);
-
if (!entry) {
KGSL_MEM_INFO(dev_priv->device, "invalid gpuaddr %08x\n",
param->gpuaddr);
return -EINVAL;
}
+
+ if (entry->memdesc.priv & KGSL_MEMDESC_FREE_PENDING) {
+ kgsl_mem_entry_put(entry);
+ return -EBUSY;
+ }
+
trace_kgsl_mem_free(entry);
kgsl_memfree_hist_set_event(entry->priv->pid,
@@ -1433,6 +1606,7 @@
entry->memdesc.flags);
kgsl_mem_entry_detach_process(entry);
+ kgsl_mem_entry_put(entry);
return 0;
}
@@ -1449,6 +1623,12 @@
KGSL_MEM_INFO(dev_priv->device, "invalid id %d\n", param->id);
return -EINVAL;
}
+
+ if (entry->memdesc.priv & KGSL_MEMDESC_FREE_PENDING) {
+ kgsl_mem_entry_put(entry);
+ return -EBUSY;
+ }
+
trace_kgsl_mem_free(entry);
kgsl_memfree_hist_set_event(entry->priv->pid,
@@ -1457,6 +1637,7 @@
entry->memdesc.flags);
kgsl_mem_entry_detach_process(entry);
+ kgsl_mem_entry_put(entry);
return 0;
}
@@ -1757,38 +1938,55 @@
#endif
static int kgsl_setup_ion(struct kgsl_mem_entry *entry,
- struct kgsl_pagetable *pagetable, void *data)
+ struct kgsl_pagetable *pagetable, void *data,
+ struct kgsl_device *device)
{
- struct ion_handle *handle;
struct scatterlist *s;
struct sg_table *sg_table;
struct kgsl_map_user_mem *param = data;
int fd = param->fd;
+ struct dma_buf *dmabuf;
+ struct dma_buf_attachment *attach;
+ struct kgsl_dma_buf_meta *meta;
+ int ret;
if (!param->len)
return -EINVAL;
- if (IS_ERR_OR_NULL(kgsl_ion_client))
- return -ENODEV;
+ meta = kzalloc(sizeof(*meta), GFP_KERNEL);
+ if (!meta)
+ return -ENOMEM;
- handle = ion_import_dma_buf(kgsl_ion_client, fd);
- if (IS_ERR(handle))
- return PTR_ERR(handle);
- else if (!handle)
- return -EINVAL;
+ dmabuf = dma_buf_get(fd);
+ if (IS_ERR_OR_NULL(dmabuf)) {
+ ret = PTR_ERR(dmabuf);
+ goto err1;
+ }
+
+ attach = dma_buf_attach(dmabuf, device->dev);
+ if (IS_ERR_OR_NULL(attach)) {
+ ret = PTR_ERR(attach);
+ goto err2;
+ }
+
+ meta->dmabuf = dmabuf;
+ meta->attach = attach;
entry->memtype = KGSL_MEM_ENTRY_ION;
- entry->priv_data = handle;
+ entry->priv_data = meta;
entry->memdesc.pagetable = pagetable;
entry->memdesc.size = 0;
/* USE_CPU_MAP is not impemented for ION. */
entry->memdesc.flags &= ~KGSL_MEMFLAGS_USE_CPU_MAP;
- sg_table = ion_sg_table(kgsl_ion_client, handle);
+ sg_table = dma_buf_map_attachment(attach, DMA_TO_DEVICE);
- if (IS_ERR_OR_NULL(sg_table))
- goto err;
+ if (IS_ERR_OR_NULL(sg_table)) {
+ ret = PTR_ERR(sg_table);
+ goto err3;
+ }
+ meta->table = sg_table;
entry->memdesc.sg = sg_table->sgl;
/* Calculate the size of the memdesc from the sglist */
@@ -1803,9 +2001,13 @@
entry->memdesc.size = PAGE_ALIGN(entry->memdesc.size);
return 0;
-err:
- ion_free(kgsl_ion_client, handle);
- return -ENOMEM;
+err3:
+ dma_buf_detach(dmabuf, attach);
+err2:
+ dma_buf_put(dmabuf);
+err1:
+ kfree(meta);
+ return ret;
}
static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv,
@@ -1842,6 +2044,9 @@
if (!kgsl_mmu_use_cpu_map(private->pagetable->mmu))
entry->memdesc.flags &= ~KGSL_MEMFLAGS_USE_CPU_MAP;
+ if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU)
+ entry->memdesc.priv |= KGSL_MEMDESC_GUARD_PAGE;
+
switch (memtype) {
case KGSL_USER_MEM_TYPE_PMEM:
if (param->fd == 0 || param->len == 0)
@@ -1890,7 +2095,8 @@
entry->memtype = KGSL_MEM_ENTRY_ASHMEM;
break;
case KGSL_USER_MEM_TYPE_ION:
- result = kgsl_setup_ion(entry, private->pagetable, data);
+ result = kgsl_setup_ion(entry, private->pagetable, data,
+ dev_priv->device);
break;
default:
KGSL_CORE_ERR("Invalid memory type: %x\n", memtype);
@@ -1937,7 +2143,7 @@
fput(entry->priv_data);
break;
case KGSL_MEM_ENTRY_ION:
- ion_free(kgsl_ion_client, entry->priv_data);
+ kgsl_destroy_ion(entry->priv_data);
break;
default:
break;
@@ -1990,6 +2196,7 @@
struct kgsl_gpumem_sync_cache *param = data;
struct kgsl_process_private *private = dev_priv->process_priv;
struct kgsl_mem_entry *entry = NULL;
+ long ret;
if (param->id != 0) {
entry = kgsl_sharedmem_find_id(private, param->id);
@@ -1999,9 +2206,7 @@
return -EINVAL;
}
} else if (param->gpuaddr != 0) {
- spin_lock(&private->mem_lock);
entry = kgsl_sharedmem_find(private, param->gpuaddr);
- spin_unlock(&private->mem_lock);
if (entry == NULL) {
KGSL_MEM_INFO(dev_priv->device,
"can't find gpuaddr %x\n",
@@ -2012,7 +2217,100 @@
return -EINVAL;
}
- return _kgsl_gpumem_sync_cache(entry, param->op);
+ ret = _kgsl_gpumem_sync_cache(entry, param->op);
+ kgsl_mem_entry_put(entry);
+ return ret;
+}
+
+static int mem_id_cmp(const void *_a, const void *_b)
+{
+ const unsigned int *a = _a, *b = _b;
+ int cmp = a - b;
+ return (cmp < 0) ? -1 : (cmp > 0);
+}
+
+static long
+kgsl_ioctl_gpumem_sync_cache_bulk(struct kgsl_device_private *dev_priv,
+ unsigned int cmd, void *data)
+{
+ int i;
+ struct kgsl_gpumem_sync_cache_bulk *param = data;
+ struct kgsl_process_private *private = dev_priv->process_priv;
+ unsigned int id, last_id = 0, *id_list = NULL, actual_count = 0;
+ struct kgsl_mem_entry **entries = NULL;
+ long ret = 0;
+ size_t op_size = 0;
+ bool full_flush = false;
+
+ if (param->id_list == NULL || param->count == 0
+ || param->count > (UINT_MAX/sizeof(unsigned int)))
+ return -EINVAL;
+
+ id_list = kzalloc(param->count * sizeof(unsigned int), GFP_KERNEL);
+ if (id_list == NULL)
+ return -ENOMEM;
+
+ entries = kzalloc(param->count * sizeof(*entries), GFP_KERNEL);
+ if (entries == NULL) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ if (copy_from_user(id_list, param->id_list,
+ param->count * sizeof(unsigned int))) {
+ ret = -EFAULT;
+ goto end;
+ }
+ /* sort the ids so we can weed out duplicates */
+ sort(id_list, param->count, sizeof(int), mem_id_cmp, NULL);
+
+ for (i = 0; i < param->count; i++) {
+ unsigned int cachemode;
+ struct kgsl_mem_entry *entry = NULL;
+
+ id = id_list[i];
+ /* skip 0 ids or duplicates */
+ if (id == last_id)
+ continue;
+
+ entry = kgsl_sharedmem_find_id(private, id);
+ if (entry == NULL)
+ continue;
+
+ /* skip uncached memory */
+ cachemode = kgsl_memdesc_get_cachemode(&entry->memdesc);
+ if (cachemode != KGSL_CACHEMODE_WRITETHROUGH &&
+ cachemode != KGSL_CACHEMODE_WRITEBACK) {
+ kgsl_mem_entry_put(entry);
+ continue;
+ }
+
+ op_size += entry->memdesc.size;
+ entries[actual_count++] = entry;
+
+ /* If we exceed the breakeven point, flush the entire cache */
+ if (op_size >= kgsl_driver.full_cache_threshold &&
+ param->op == KGSL_GPUMEM_CACHE_FLUSH) {
+ full_flush = true;
+ break;
+ }
+ last_id = id;
+ }
+ if (full_flush) {
+ trace_kgsl_mem_sync_full_cache(actual_count, op_size,
+ param->op);
+ __cpuc_flush_kern_all();
+ }
+
+ for (i = 0; i < actual_count; i++) {
+ if (!full_flush)
+ _kgsl_gpumem_sync_cache(entries[i], param->op);
+ kgsl_mem_entry_put(entries[i]);
+ }
+end:
+ kfree(entries);
+ kfree(id_list);
+ return ret;
}
/* Legacy cache function, does a flush (clean + invalidate) */
@@ -2024,10 +2322,9 @@
struct kgsl_sharedmem_free *param = data;
struct kgsl_process_private *private = dev_priv->process_priv;
struct kgsl_mem_entry *entry = NULL;
+ long ret;
- spin_lock(&private->mem_lock);
entry = kgsl_sharedmem_find(private, param->gpuaddr);
- spin_unlock(&private->mem_lock);
if (entry == NULL) {
KGSL_MEM_INFO(dev_priv->device,
"can't find gpuaddr %x\n",
@@ -2035,7 +2332,9 @@
return -EINVAL;
}
- return _kgsl_gpumem_sync_cache(entry, KGSL_GPUMEM_CACHE_FLUSH);
+ ret = _kgsl_gpumem_sync_cache(entry, KGSL_GPUMEM_CACHE_FLUSH);
+ kgsl_mem_entry_put(entry);
+ return ret;
}
/*
@@ -2064,6 +2363,9 @@
if (entry == NULL)
return -ENOMEM;
+ if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU)
+ entry->memdesc.priv |= KGSL_MEMDESC_GUARD_PAGE;
+
result = kgsl_allocate_user(&entry->memdesc, private->pagetable, size,
flags);
if (result != 0)
@@ -2173,9 +2475,7 @@
return -EINVAL;
}
} else if (param->gpuaddr != 0) {
- spin_lock(&private->mem_lock);
entry = kgsl_sharedmem_find(private, param->gpuaddr);
- spin_unlock(&private->mem_lock);
if (entry == NULL) {
KGSL_MEM_INFO(dev_priv->device,
"can't find gpuaddr %lx\n",
@@ -2191,6 +2491,8 @@
param->size = entry->memdesc.size;
param->mmapsize = kgsl_memdesc_mmapsize(&entry->memdesc);
param->useraddr = entry->memdesc.useraddr;
+
+ kgsl_mem_entry_put(entry);
return result;
}
@@ -2202,14 +2504,14 @@
struct kgsl_process_private *private = dev_priv->process_priv;
struct kgsl_mem_entry *entry = NULL;
- spin_lock(&private->mem_lock);
entry = kgsl_sharedmem_find_region(private, param->gpuaddr, param->len);
- if (entry)
- kgsl_cffdump_syncmem(dev_priv, &entry->memdesc, param->gpuaddr,
- param->len, true);
- else
- result = -EINVAL;
- spin_unlock(&private->mem_lock);
+ if (!entry)
+ return -EINVAL;
+
+ kgsl_cffdump_syncmem(dev_priv, &entry->memdesc, param->gpuaddr,
+ param->len, true);
+
+ kgsl_mem_entry_put(entry);
return result;
}
@@ -2219,7 +2521,8 @@
int result = 0;
struct kgsl_cff_user_event *param = data;
- kgsl_cffdump_user_event(param->cff_opcode, param->op1, param->op2,
+ kgsl_cffdump_user_event(dev_priv->device, param->cff_opcode,
+ param->op1, param->op2,
param->op3, param->op4, param->op5);
return result;
@@ -2417,6 +2720,8 @@
kgsl_ioctl_gpumem_get_info, 0),
KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_SYNC_CACHE,
kgsl_ioctl_gpumem_sync_cache, 0),
+ KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_SYNC_CACHE_BULK,
+ kgsl_ioctl_gpumem_sync_cache_bulk, 0),
};
static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
@@ -2601,21 +2906,17 @@
struct kgsl_mem_entry **out_entry, unsigned long pgoff,
unsigned long len)
{
- int ret = -EINVAL;
+ int ret = 0;
struct kgsl_mem_entry *entry;
entry = kgsl_sharedmem_find_id(private, pgoff);
if (entry == NULL) {
- spin_lock(&private->mem_lock);
entry = kgsl_sharedmem_find(private, pgoff << PAGE_SHIFT);
- spin_unlock(&private->mem_lock);
}
if (!entry)
return -EINVAL;
- kgsl_mem_entry_get(entry);
-
if (!entry->memdesc.ops ||
!entry->memdesc.ops->vmflags ||
!entry->memdesc.ops->vmfault) {
@@ -2720,16 +3021,17 @@
ret = ALIGN(ret, (1 << align));
/*make sure there isn't a GPU only mapping at this address */
- if (kgsl_sharedmem_region_empty(private, ret, len))
+ if (kgsl_sharedmem_region_empty(private, ret, orig_len))
break;
- trace_kgsl_mem_unmapped_area_collision(entry, addr, len, ret);
+ trace_kgsl_mem_unmapped_area_collision(entry, addr, orig_len,
+ ret);
/*
* If we collided, bump the hint address so that
* get_umapped_area knows to look somewhere else.
*/
- addr = (addr == 0) ? ret + len : addr + len;
+ addr = (addr == 0) ? ret + orig_len : addr + orig_len;
/*
* The addr hint can be set by userspace to be near
@@ -2768,6 +3070,10 @@
if (vma_offset == device->memstore.gpuaddr)
return kgsl_mmap_memstore(device, vma);
+ /*
+ * The reference count on the entry that we get from
+ * get_mmap_entry() will be held until kgsl_gpumem_vm_close().
+ */
ret = get_mmap_entry(private, &entry, vma->vm_pgoff,
vma->vm_end - vma->vm_start);
if (ret)
@@ -2817,10 +3123,6 @@
int sglen = entry->memdesc.sglen;
unsigned long addr = vma->vm_start;
- /* don't map in the guard page, it should always fault */
- if (kgsl_memdesc_has_guard_page(&entry->memdesc))
- sglen--;
-
for_each_sg(entry->memdesc.sg, s, sglen, i) {
int j;
for (j = 0; j < (sg_dma_len(s) >> PAGE_SHIFT); j++) {
@@ -2837,7 +3139,6 @@
entry->memdesc.useraddr = vma->vm_start;
trace_kgsl_mem_mmap(entry);
-
return 0;
}
@@ -2864,6 +3165,11 @@
.devlock = __MUTEX_INITIALIZER(kgsl_driver.devlock),
.memfree_hist_mutex =
__MUTEX_INITIALIZER(kgsl_driver.memfree_hist_mutex),
+ /*
+ * Full cache flushes are faster than line by line on at least
+ * 8064 and 8974 once the region to be flushed is > 16mb.
+ */
+ .full_cache_threshold = SZ_16M,
};
EXPORT_SYMBOL(kgsl_driver);
@@ -2944,8 +3250,6 @@
if (status)
goto error;
- kgsl_ion_client = msm_ion_client_create(UINT_MAX, KGSL_NAME);
-
/* Get starting physical address of device registers */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
device->iomemname);
@@ -3038,6 +3342,7 @@
setup_timer(&device->idle_timer, kgsl_timer, (unsigned long) device);
+ setup_timer(&device->hang_timer, hang_timer, (unsigned long) device);
status = kgsl_create_device_workqueue(device);
if (status)
goto error_pwrctrl_close;
@@ -3084,12 +3389,11 @@
int kgsl_postmortem_dump(struct kgsl_device *device, int manual)
{
- bool saved_nap;
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
BUG_ON(device == NULL);
- kgsl_cffdump_hang(device->id);
+ kgsl_cffdump_hang(device);
/* For a manual dump, make sure that the system is idle */
@@ -3104,8 +3408,8 @@
if (device->pm_dump_enable) {
KGSL_LOG_DUMP(device,
- "POWER: NAP ALLOWED = %d | START_STOP_SLEEP_WAKE = %d\n"
- , pwr->nap_allowed, pwr->strtstp_sleepwake);
+ "POWER: START_STOP_SLEEP_WAKE = %d\n",
+ pwr->strtstp_sleepwake);
KGSL_LOG_DUMP(device,
"POWER: FLAGS = %08lX | ACTIVE POWERLEVEL = %08X",
@@ -3118,14 +3422,7 @@
/* Disable the idle timer so we don't get interrupted */
del_timer_sync(&device->idle_timer);
- mutex_unlock(&device->mutex);
- flush_workqueue(device->work_queue);
- mutex_lock(&device->mutex);
-
- /* Turn off napping to make sure we have the clocks full
- attention through the following process */
- saved_nap = device->pwrctrl.nap_allowed;
- device->pwrctrl.nap_allowed = false;
+ del_timer_sync(&device->hang_timer);
/* Force on the clocks */
kgsl_pwrctrl_wake(device);
@@ -3136,9 +3433,6 @@
/*Call the device specific postmortem dump function*/
device->ftbl->postmortem_dump(device, manual);
- /* Restore nap mode */
- device->pwrctrl.nap_allowed = saved_nap;
-
/* On a manual trigger, turn on the interrupts and put
the clocks to sleep. They will recover themselves
on the next event. For a hang, leave things as they
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index abe9100..0983111 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -46,7 +46,11 @@
#define KGSL_PAGETABLE_ENTRY_SIZE 4
/* Pagetable Virtual Address base */
+#ifndef CONFIG_MSM_KGSL_CFF_DUMP
#define KGSL_PAGETABLE_BASE 0x10000000
+#else
+#define KGSL_PAGETABLE_BASE 0xE0000000
+#endif
/* Extra accounting entries needed in the pagetable */
#define KGSL_PT_EXTRA_ENTRIES 16
@@ -130,6 +134,7 @@
unsigned int mapped_max;
unsigned int histogram[16];
} stats;
+ unsigned int full_cache_threshold;
};
extern struct kgsl_driver kgsl_driver;
@@ -151,6 +156,8 @@
#define KGSL_MEMDESC_GLOBAL BIT(1)
/* The memdesc is frozen during a snapshot */
#define KGSL_MEMDESC_FROZEN BIT(2)
+/* The memdesc is scheduled to be freed on a timestamp */
+#define KGSL_MEMDESC_FREE_PENDING BIT(3)
/* shared memory allocation */
struct kgsl_memdesc {
@@ -196,11 +203,12 @@
#define MMU_CONFIG 1
#endif
+void kgsl_hang_check(struct work_struct *work);
void kgsl_mem_entry_destroy(struct kref *kref);
int kgsl_postmortem_dump(struct kgsl_device *device, int manual);
struct kgsl_mem_entry *kgsl_get_mem_entry(struct kgsl_device *device,
- unsigned int ptbase, unsigned int gpuaddr, unsigned int size);
+ phys_addr_t ptbase, unsigned int gpuaddr, unsigned int size);
struct kgsl_mem_entry *kgsl_sharedmem_find_region(
struct kgsl_process_private *private, unsigned int gpuaddr,
@@ -220,11 +228,8 @@
extern const struct dev_pm_ops kgsl_pm_ops;
-struct early_suspend;
int kgsl_suspend_driver(struct platform_device *pdev, pm_message_t state);
int kgsl_resume_driver(struct platform_device *pdev);
-void kgsl_early_suspend_driver(struct early_suspend *h);
-void kgsl_late_resume_driver(struct early_suspend *h);
void kgsl_trace_regwrite(struct kgsl_device *device, unsigned int offset,
unsigned int value);
@@ -234,6 +239,10 @@
unsigned int timestamp, unsigned int flags,
int result, unsigned int type);
+int kgsl_open_device(struct kgsl_device *device);
+
+int kgsl_close_device(struct kgsl_device *device);
+
#ifdef CONFIG_MSM_KGSL_DRM
extern int kgsl_drm_init(struct platform_device *dev);
extern void kgsl_drm_exit(void);
@@ -251,6 +260,10 @@
static inline int kgsl_gpuaddr_in_memdesc(const struct kgsl_memdesc *memdesc,
unsigned int gpuaddr, unsigned int size)
{
+ /* set a minimum size to search for */
+ if (!size)
+ size = 1;
+
/* don't overflow */
if ((gpuaddr + size) < gpuaddr)
return 0;
diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c
index 6dc2ccc..99f4235 100644
--- a/drivers/gpu/msm/kgsl_cffdump.c
+++ b/drivers/gpu/msm/kgsl_cffdump.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2012,2013, 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
@@ -335,8 +335,6 @@
return;
}
- kgsl_cff_dump_enable = 0;
-
spin_lock_init(&cffdump_lock);
dir = debugfs_create_dir("cff", debugfs_dir);
@@ -360,41 +358,55 @@
void kgsl_cffdump_open(struct kgsl_device *device)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ if (!device->cff_dump_enable)
+ return;
if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()) {
- kgsl_cffdump_memory_base(device->id,
- kgsl_mmu_get_base_addr(&device->mmu),
- kgsl_mmu_get_ptsize(&device->mmu) +
- KGSL_IOMMU_GLOBAL_MEM_SIZE, adreno_dev->gmem_size);
+ kgsl_cffdump_memory_base(device,
+ KGSL_PAGETABLE_BASE,
+ KGSL_IOMMU_GLOBAL_MEM_BASE +
+ KGSL_IOMMU_GLOBAL_MEM_SIZE -
+ KGSL_PAGETABLE_BASE,
+ adreno_dev->gmem_size);
} else {
- kgsl_cffdump_memory_base(device->id,
+ kgsl_cffdump_memory_base(device,
kgsl_mmu_get_base_addr(&device->mmu),
kgsl_mmu_get_ptsize(&device->mmu),
adreno_dev->gmem_size);
}
}
-void kgsl_cffdump_memory_base(enum kgsl_deviceid device_id, unsigned int base,
+void kgsl_cffdump_memory_base(struct kgsl_device *device, unsigned int base,
unsigned int range, unsigned gmemsize)
{
- cffdump_printline(device_id, CFF_OP_MEMORY_BASE, base,
+ if (!device->cff_dump_enable)
+ return;
+ cffdump_printline(device->id, CFF_OP_MEMORY_BASE, base,
range, gmemsize, 0, 0);
}
-void kgsl_cffdump_hang(enum kgsl_deviceid device_id)
+void kgsl_cffdump_hang(struct kgsl_device *device)
{
- cffdump_printline(device_id, CFF_OP_HANG, 0, 0, 0, 0, 0);
+ if (!device->cff_dump_enable)
+ return;
+ cffdump_printline(device->id, CFF_OP_HANG, 0, 0, 0, 0, 0);
}
-void kgsl_cffdump_close(enum kgsl_deviceid device_id)
+void kgsl_cffdump_close(struct kgsl_device *device)
{
- cffdump_printline(device_id, CFF_OP_EOF, 0, 0, 0, 0, 0);
+ if (!device->cff_dump_enable)
+ return;
+ cffdump_printline(device->id, CFF_OP_EOF, 0, 0, 0, 0, 0);
}
-void kgsl_cffdump_user_event(unsigned int cff_opcode, unsigned int op1,
+
+void kgsl_cffdump_user_event(struct kgsl_device *device,
+ unsigned int cff_opcode, unsigned int op1,
unsigned int op2, unsigned int op3,
unsigned int op4, unsigned int op5)
{
+ if (!device->cff_dump_enable)
+ return;
cffdump_printline(-1, cff_opcode, op1, op2, op3, op4, op5);
}
@@ -402,9 +414,10 @@
struct kgsl_memdesc *memdesc, uint gpuaddr, uint sizebytes,
bool clean_cache)
{
+ struct kgsl_device *device = dev_priv->device;
const void *src;
- if (!kgsl_cff_dump_enable)
+ if (!device->cff_dump_enable)
return;
total_syncmem += sizebytes;
@@ -424,9 +437,9 @@
}
src = (uint *)kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr);
if (memdesc->hostptr == NULL) {
- KGSL_CORE_ERR("no kernel mapping for "
- "gpuaddr: 0x%08x, m->host: 0x%p, phys: 0x%08x\n",
- gpuaddr, memdesc->hostptr, memdesc->physaddr);
+ KGSL_CORE_ERR(
+ "no kernel map for gpuaddr: 0x%08x, m->host: 0x%p, phys: %pa\n",
+ gpuaddr, memdesc->hostptr, &memdesc->physaddr);
return;
}
@@ -452,9 +465,10 @@
0, 0, 0);
}
-void kgsl_cffdump_setmem(uint addr, uint value, uint sizebytes)
+void kgsl_cffdump_setmem(struct kgsl_device *device,
+ uint addr, uint value, uint sizebytes)
{
- if (!kgsl_cff_dump_enable)
+ if (!device || !device->cff_dump_enable)
return;
while (sizebytes > 3) {
@@ -470,37 +484,37 @@
0, 0, 0);
}
-void kgsl_cffdump_regwrite(enum kgsl_deviceid device_id, uint addr,
+void kgsl_cffdump_regwrite(struct kgsl_device *device, uint addr,
uint value)
{
- if (!kgsl_cff_dump_enable)
+ if (!device->cff_dump_enable)
return;
- cffdump_printline(device_id, CFF_OP_WRITE_REG, addr, value,
+ cffdump_printline(device->id, CFF_OP_WRITE_REG, addr, value,
0, 0, 0);
}
-void kgsl_cffdump_regpoll(enum kgsl_deviceid device_id, uint addr,
+void kgsl_cffdump_regpoll(struct kgsl_device *device, uint addr,
uint value, uint mask)
{
- if (!kgsl_cff_dump_enable)
+ if (!device->cff_dump_enable)
return;
- cffdump_printline(device_id, CFF_OP_POLL_REG, addr, value,
+ cffdump_printline(device->id, CFF_OP_POLL_REG, addr, value,
mask, 0, 0);
}
-void kgsl_cffdump_slavewrite(uint addr, uint value)
+void kgsl_cffdump_slavewrite(struct kgsl_device *device, uint addr, uint value)
{
- if (!kgsl_cff_dump_enable)
+ if (!device->cff_dump_enable)
return;
cffdump_printline(-1, CFF_OP_WRITE_REG, addr, value, 0, 0, 0);
}
-int kgsl_cffdump_waitirq(void)
+int kgsl_cffdump_waitirq(struct kgsl_device *device)
{
- if (!kgsl_cff_dump_enable)
+ if (!device->cff_dump_enable)
return 0;
cffdump_printline(-1, CFF_OP_WAIT_IRQ, 0, 0, 0, 0, 0);
@@ -601,3 +615,59 @@
}
}
+int kgsl_cff_dump_enable_set(void *data, u64 val)
+{
+ int ret = 0;
+ struct kgsl_device *device = (struct kgsl_device *)data;
+ int i;
+
+ mutex_lock(&kgsl_driver.devlock);
+ /*
+ * If CFF dump enabled then set active count to prevent device
+ * from restarting because simulator cannot run device restarts
+ */
+ if (val) {
+ /* Check if CFF is on for some other device already */
+ for (i = 0; i < KGSL_DEVICE_MAX; i++) {
+ if (kgsl_driver.devp[i]) {
+ struct kgsl_device *device_temp =
+ kgsl_driver.devp[i];
+ if (device_temp->cff_dump_enable &&
+ device != device_temp) {
+ KGSL_CORE_ERR(
+ "CFF is on for another device %d\n",
+ device_temp->id);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+ }
+ if (!device->cff_dump_enable) {
+ mutex_lock(&device->mutex);
+ device->cff_dump_enable = 1;
+ ret = kgsl_open_device(device);
+ if (!ret)
+ ret = kgsl_active_count_get(device);
+ if (ret)
+ device->cff_dump_enable = 0;
+ mutex_unlock(&device->mutex);
+ }
+ } else if (device->cff_dump_enable && !val) {
+ mutex_lock(&device->mutex);
+ ret = kgsl_close_device(device);
+ device->cff_dump_enable = 0;
+ mutex_unlock(&device->mutex);
+ }
+done:
+ mutex_unlock(&kgsl_driver.devlock);
+ return ret;
+}
+EXPORT_SYMBOL(kgsl_cff_dump_enable_set);
+
+int kgsl_cff_dump_enable_get(void *data, u64 *val)
+{
+ struct kgsl_device *device = (struct kgsl_device *)data;
+ *val = device->cff_dump_enable;
+ return 0;
+}
+EXPORT_SYMBOL(kgsl_cff_dump_enable_get);
diff --git a/drivers/gpu/msm/kgsl_cffdump.h b/drivers/gpu/msm/kgsl_cffdump.h
index d5656f8..641348e 100644
--- a/drivers/gpu/msm/kgsl_cffdump.h
+++ b/drivers/gpu/msm/kgsl_cffdump.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2011,2013, 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
@@ -14,6 +14,8 @@
#ifndef __KGSL_CFFDUMP_H
#define __KGSL_CFFDUMP_H
+extern unsigned int kgsl_cff_dump_enable;
+
#ifdef CONFIG_MSM_KGSL_CFF_DUMP
#include <linux/types.h>
@@ -23,46 +25,64 @@
void kgsl_cffdump_init(void);
void kgsl_cffdump_destroy(void);
void kgsl_cffdump_open(struct kgsl_device *device);
-void kgsl_cffdump_close(enum kgsl_deviceid device_id);
+void kgsl_cffdump_close(struct kgsl_device *device);
void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv,
struct kgsl_memdesc *memdesc, uint physaddr, uint sizebytes,
bool clean_cache);
-void kgsl_cffdump_setmem(uint addr, uint value, uint sizebytes);
-void kgsl_cffdump_regwrite(enum kgsl_deviceid device_id, uint addr,
+void kgsl_cffdump_setmem(struct kgsl_device *device, uint addr,
+ uint value, uint sizebytes);
+void kgsl_cffdump_regwrite(struct kgsl_device *device, uint addr,
uint value);
-void kgsl_cffdump_regpoll(enum kgsl_deviceid device_id, uint addr,
+void kgsl_cffdump_regpoll(struct kgsl_device *device, uint addr,
uint value, uint mask);
bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv,
const struct kgsl_memdesc *memdesc, uint gpuaddr, int sizedwords,
bool check_only);
-void kgsl_cffdump_user_event(unsigned int cff_opcode, unsigned int op1,
+void kgsl_cffdump_user_event(struct kgsl_device *device,
+ unsigned int cff_opcode, unsigned int op1,
unsigned int op2, unsigned int op3,
unsigned int op4, unsigned int op5);
static inline bool kgsl_cffdump_flags_no_memzero(void) { return true; }
-void kgsl_cffdump_memory_base(enum kgsl_deviceid device_id, unsigned int base,
+void kgsl_cffdump_memory_base(struct kgsl_device *device, unsigned int base,
unsigned int range, unsigned int gmemsize);
-void kgsl_cffdump_hang(enum kgsl_deviceid device_id);
+void kgsl_cffdump_hang(struct kgsl_device *device);
+int kgsl_cff_dump_enable_set(void *data, u64 val);
+int kgsl_cff_dump_enable_get(void *data, u64 *val);
#else
#define kgsl_cffdump_init() (void)0
#define kgsl_cffdump_destroy() (void)0
#define kgsl_cffdump_open(device) (void)0
-#define kgsl_cffdump_close(device_id) (void)0
+#define kgsl_cffdump_close(device) (void)0
#define kgsl_cffdump_syncmem(dev_priv, memdesc, addr, sizebytes, clean_cache) \
(void) 0
-#define kgsl_cffdump_setmem(addr, value, sizebytes) (void)0
-#define kgsl_cffdump_regwrite(device_id, addr, value) (void)0
-#define kgsl_cffdump_regpoll(device_id, addr, value, mask) (void)0
+#define kgsl_cffdump_setmem(device, addr, value, sizebytes) (void)0
+#define kgsl_cffdump_regwrite(device, addr, value) (void)0
+#define kgsl_cffdump_regpoll(device, addr, value, mask) (void)0
#define kgsl_cffdump_parse_ibs(dev_priv, memdesc, gpuaddr, \
sizedwords, check_only) true
#define kgsl_cffdump_flags_no_memzero() true
-#define kgsl_cffdump_memory_base(base, range, gmemsize) (void)0
-#define kgsl_cffdump_hang(device_id) (void)0
-#define kgsl_cffdump_user_event(cff_opcode, op1, op2, op3, op4, op5) \
- (void)param
+#define kgsl_cffdump_memory_base(davice, base, range, gmemsize) (void)0
+#define kgsl_cffdump_hang(device) (void)0
+static inline void kgsl_cffdump_user_event(struct kgsl_device *device,
+ unsigned int cff_opcode, unsigned int op1,
+ unsigned int op2, unsigned int op3,
+ unsigned int op4, unsigned int op5)
+{
+ return;
+}
+static inline int kgsl_cff_dump_enable_set(void *data, u64 val)
+{
+ return -EINVAL;
+}
+
+static inline int kgsl_cff_dump_enable_get(void *data, u64 *val)
+{
+ return -EINVAL;
+}
#endif /* CONFIG_MSM_KGSL_CFF_DUMP */
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index ac82820..94792a0 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -15,7 +15,6 @@
#include <linux/idr.h>
#include <linux/pm_qos.h>
-#include <linux/earlysuspend.h>
#include "kgsl.h"
#include "kgsl_mmu.h"
@@ -173,7 +172,9 @@
struct completion hwaccess_gate;
const struct kgsl_functable *ftbl;
struct work_struct idle_check_ws;
+ struct work_struct hang_check_ws;
struct timer_list idle_timer;
+ struct timer_list hang_timer;
struct kgsl_pwrctrl pwrctrl;
int open_count;
@@ -190,12 +191,12 @@
struct completion ft_gate;
struct dentry *d_debugfs;
struct idr context_idr;
- struct early_suspend display_off;
void *snapshot; /* Pointer to the snapshot memory region */
int snapshot_maxsize; /* Max size of the snapshot region */
int snapshot_size; /* Current size of the snapshot region */
u32 snapshot_timestamp; /* Timestamp of the last valid snapshot */
+ u32 snapshot_faultcount; /* Total number of faults since boot */
int snapshot_frozen; /* 1 if the snapshot output is frozen until
it gets read by the user. This avoids
losing the output on multiple hangs */
@@ -227,6 +228,7 @@
int pm_ib_enabled;
int reset_counter; /* Track how many GPU core resets have occured */
+ int cff_dump_enable;
};
void kgsl_process_events(struct work_struct *work);
@@ -237,6 +239,8 @@
.ft_gate = COMPLETION_INITIALIZER((_dev).ft_gate),\
.idle_check_ws = __WORK_INITIALIZER((_dev).idle_check_ws,\
kgsl_idle_check),\
+ .hang_check_ws = __WORK_INITIALIZER((_dev).hang_check_ws,\
+ kgsl_hang_check),\
.ts_expired_ws = __WORK_INITIALIZER((_dev).ts_expired_ws,\
kgsl_process_events),\
.context_idr = IDR_INIT((_dev).context_idr),\
@@ -280,6 +284,12 @@
unsigned int refcnt;
pid_t pid;
spinlock_t mem_lock;
+
+ /* General refcount for process private struct obj */
+ struct kref refcount;
+ /* Mutex to synchronize access to each process_private struct obj */
+ struct mutex process_private_mutex;
+
struct rb_root mem_rb;
struct idr mem_idr;
struct kgsl_pagetable *pagetable;
diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c
index d872783..a1fc5a2 100644
--- a/drivers/gpu/msm/kgsl_events.c
+++ b/drivers/gpu/msm/kgsl_events.c
@@ -143,6 +143,15 @@
cur = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED);
id = context->id;
+ /*
+ * Increment the refcount to avoid freeing the context while
+ * cancelling its events
+ */
+ kgsl_context_get(context);
+
+ /* Remove ourselves from the master pending list */
+ list_del_init(&context->events_list);
+
list_for_each_entry_safe(event, event_tmp, &context->events, list) {
/*
* "cancel" the events by calling their callback.
@@ -165,9 +174,7 @@
kgsl_active_count_put(device);
}
-
- /* Remove ourselves from the master pending list */
- list_del_init(&context->events_list);
+ kgsl_context_put(context);
}
/**
@@ -261,7 +268,8 @@
* timestamp on the event has passed - return that up a layer
*/
- return device->ftbl->next_event(device, event);
+ if (device->ftbl->next_event)
+ return device->ftbl->next_event(device, event);
}
return 0;
@@ -313,12 +321,18 @@
events_list) {
/*
+ * Increment the refcount to make sure that the list_del_init
+ * is called with a valid context's list
+ */
+ kgsl_context_get(context);
+ /*
* If kgsl_timestamp_expired_context returns 0 then it no longer
* has any pending events and can be removed from the list
*/
if (kgsl_process_context_events(device, context) == 0)
list_del_init(&context->events_list);
+ kgsl_context_put(context);
}
mutex_unlock(&device->mutex);
diff --git a/drivers/gpu/msm/kgsl_gpummu.c b/drivers/gpu/msm/kgsl_gpummu.c
index 5cc0dff..68052b1 100644
--- a/drivers/gpu/msm/kgsl_gpummu.c
+++ b/drivers/gpu/msm/kgsl_gpummu.c
@@ -359,7 +359,7 @@
int kgsl_gpummu_pt_equal(struct kgsl_mmu *mmu,
struct kgsl_pagetable *pt,
- unsigned int pt_base)
+ phys_addr_t pt_base)
{
struct kgsl_gpummu_pt *gpummu_pt = pt ? pt->priv : NULL;
return gpummu_pt && pt_base && (gpummu_pt->base.gpuaddr == pt_base);
@@ -458,15 +458,20 @@
if (gpummu_pt->base.hostptr == NULL)
goto err_flushfilter;
+ /* Do a check before truncating phys_addr_t to unsigned 32 */
+ if (sizeof(phys_addr_t) > sizeof(unsigned int)) {
+ WARN_ONCE(1, "Cannot use LPAE with gpummu\n");
+ goto err_flushfilter;
+ }
+ gpummu_pt->base.gpuaddr = gpummu_pt->base.physaddr;
+ gpummu_pt->base.size = KGSL_PAGETABLE_SIZE;
+
/* ptpool allocations are from coherent memory, so update the
device statistics acordingly */
KGSL_STATS_ADD(KGSL_PAGETABLE_SIZE, kgsl_driver.stats.coherent,
kgsl_driver.stats.coherent_max);
- gpummu_pt->base.gpuaddr = gpummu_pt->base.physaddr;
- gpummu_pt->base.size = KGSL_PAGETABLE_SIZE;
-
return (void *)gpummu_pt;
err_flushfilter:
@@ -576,7 +581,7 @@
kgsl_regwrite(device, MH_INTERRUPT_MASK,
GSL_MMU_INT_MASK | MH_INTERRUPT_MASK__MMU_PAGE_FAULT);
- kgsl_sharedmem_set(&mmu->setstate_memory, 0, 0,
+ kgsl_sharedmem_set(device, &mmu->setstate_memory, 0, 0,
mmu->setstate_memory.size);
/* TRAN_ERROR needs a 32 byte (32 byte aligned) chunk of memory
@@ -615,7 +620,7 @@
{
unsigned int numpages;
unsigned int pte, ptefirst, ptelast, superpte;
- unsigned int range = kgsl_sg_size(memdesc->sg, memdesc->sglen);
+ unsigned int range = memdesc->size;
struct kgsl_gpummu_pt *gpummu_pt = pt->priv;
/* All GPU addresses as assigned are page aligned, but some
@@ -723,7 +728,7 @@
return 0;
}
-static unsigned int
+static phys_addr_t
kgsl_gpummu_get_current_ptbase(struct kgsl_mmu *mmu)
{
unsigned int ptbase;
@@ -731,7 +736,7 @@
return ptbase;
}
-static unsigned int
+static phys_addr_t
kgsl_gpummu_get_pt_base_addr(struct kgsl_mmu *mmu,
struct kgsl_pagetable *pt)
{
@@ -757,9 +762,11 @@
.mmu_get_pt_base_addr = kgsl_gpummu_get_pt_base_addr,
.mmu_enable_clk = NULL,
.mmu_disable_clk_on_ts = NULL,
- .mmu_get_pt_lsb = NULL,
+ .mmu_get_default_ttbr0 = NULL,
.mmu_get_reg_gpuaddr = NULL,
+ .mmu_get_reg_ahbaddr = NULL,
.mmu_get_num_iommu_units = kgsl_gpummu_get_num_iommu_units,
+ .mmu_hw_halt_supported = NULL,
};
struct kgsl_mmu_pt_ops gpummu_pt_ops = {
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 739fcff..0bacc5e 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -37,33 +37,60 @@
static struct kgsl_iommu_register_list kgsl_iommuv0_reg[KGSL_IOMMU_REG_MAX] = {
- { 0, 0, 0 }, /* GLOBAL_BASE */
- { 0x10, 0x0003FFFF, 14 }, /* TTBR0 */
- { 0x14, 0x0003FFFF, 14 }, /* TTBR1 */
- { 0x20, 0, 0 }, /* FSR */
- { 0x800, 0, 0 }, /* TLBIALL */
- { 0x820, 0, 0 }, /* RESUME */
- { 0x03C, 0, 0 }, /* TLBLKCR */
- { 0x818, 0, 0 }, /* V2PUR */
- { 0x2C, 0, 0 }, /* FSYNR0 */
- { 0x2C, 0, 0 }, /* FSYNR0 */
+ { 0, 0 }, /* GLOBAL_BASE */
+ { 0x10, 1 }, /* TTBR0 */
+ { 0x14, 1 }, /* TTBR1 */
+ { 0x20, 1 }, /* FSR */
+ { 0x800, 1 }, /* TLBIALL */
+ { 0x820, 1 }, /* RESUME */
+ { 0x03C, 1 }, /* TLBLKCR */
+ { 0x818, 1 }, /* V2PUR */
+ { 0x2C, 1 }, /* FSYNR0 */
+ { 0x30, 1 }, /* FSYNR1 */
+ { 0, 0 }, /* TLBSYNC, not in v0 */
+ { 0, 0 }, /* TLBSTATUS, not in v0 */
+ { 0, 0 } /* IMPLDEF_MICRO_MMU_CRTL, not in v0 */
};
static struct kgsl_iommu_register_list kgsl_iommuv1_reg[KGSL_IOMMU_REG_MAX] = {
- { 0, 0, 0 }, /* GLOBAL_BASE */
- { 0x20, 0x00FFFFFF, 14 }, /* TTBR0 */
- { 0x28, 0x00FFFFFF, 14 }, /* TTBR1 */
- { 0x58, 0, 0 }, /* FSR */
- { 0x618, 0, 0 }, /* TLBIALL */
- { 0x008, 0, 0 }, /* RESUME */
- { 0, 0, 0 }, /* TLBLKCR */
- { 0, 0, 0 }, /* V2PUR */
- { 0x68, 0, 0 }, /* FSYNR0 */
- { 0x6C, 0, 0 } /* FSYNR1 */
+ { 0, 0 }, /* GLOBAL_BASE */
+ { 0x20, 1 }, /* TTBR0 */
+ { 0x28, 1 }, /* TTBR1 */
+ { 0x58, 1 }, /* FSR */
+ { 0x618, 1 }, /* TLBIALL */
+ { 0x008, 1 }, /* RESUME */
+ { 0, 0 }, /* TLBLKCR not in V1 */
+ { 0, 0 }, /* V2PUR not in V1 */
+ { 0x68, 0 }, /* FSYNR0 */
+ { 0x6C, 0 }, /* FSYNR1 */
+ { 0x7F0, 1 }, /* TLBSYNC */
+ { 0x7F4, 1 }, /* TLBSTATUS */
+ { 0x2000, 0 } /* IMPLDEF_MICRO_MMU_CRTL */
};
+static struct iommu_access_ops *iommu_access_ops;
+
+static void _iommu_lock(void)
+{
+ if (iommu_access_ops && iommu_access_ops->iommu_lock_acquire)
+ iommu_access_ops->iommu_lock_acquire();
+}
+
+static void _iommu_unlock(void)
+{
+ if (iommu_access_ops && iommu_access_ops->iommu_lock_release)
+ iommu_access_ops->iommu_lock_release();
+}
+
struct remote_iommu_petersons_spinlock kgsl_iommu_sync_lock_vars;
+/*
+ * One page allocation for a guard region to protect against over-zealous
+ * GPU pre-fetch
+ */
+
+static struct page *kgsl_guard_page;
+
static int get_iommu_unit(struct device *dev, struct kgsl_mmu **mmu_out,
struct kgsl_iommu_unit **iommu_unit_out)
{
@@ -215,7 +242,7 @@
list_for_each_entry(private, &kgsl_driver.process_list, list) {
- if (private->pagetable->name != id)
+ if (private->pagetable && (private->pagetable->name != id))
continue;
spin_lock(&private->mem_lock);
@@ -248,6 +275,8 @@
void *base = kgsl_driver.memfree_hist.base_hist_rb;
struct kgsl_memfree_hist_elem *wptr;
struct kgsl_memfree_hist_elem *p;
+ char name[32];
+ memset(name, 0, sizeof(name));
mutex_lock(&kgsl_driver.memfree_hist_mutex);
wptr = kgsl_driver.memfree_hist.wptr;
@@ -257,12 +286,15 @@
if (addr >= p->gpuaddr &&
addr < (p->gpuaddr + p->size)) {
+ kgsl_get_memory_usage(name, sizeof(name) - 1,
+ p->flags),
KGSL_LOG_DUMP(iommu_dev->kgsldev,
"---- premature free ----\n");
KGSL_LOG_DUMP(iommu_dev->kgsldev,
- "[%8.8X-%8.8X] was already freed by pid %d\n",
+ "[%8.8X-%8.8X] (%s) was already freed by pid %d\n",
p->gpuaddr,
p->gpuaddr + p->size,
+ name,
p->pid);
}
p++;
@@ -327,22 +359,17 @@
KGSL_IOMMU_V1_FSYNR0_WNR_SHIFT)) ? 1 : 0);
pid = kgsl_mmu_get_ptname_from_ptbase(mmu, ptbase);
- KGSL_MEM_CRIT(iommu_dev->kgsldev,
- "GPU PAGE FAULT: addr = %lX pid = %d\n", addr, pid);
- KGSL_MEM_CRIT(iommu_dev->kgsldev,
- "context = %d FSR = %X FSYNR0 = %X FSYNR1 = %X(%s fault)\n",
- iommu_dev->ctx_id, fsr, fsynr0, fsynr1,
- write ? "write" : "read");
if (adreno_dev->ft_pf_policy & KGSL_FT_PAGEFAULT_LOG_ONE_PER_PAGE)
no_page_fault_log = kgsl_mmu_log_fault_addr(mmu, ptbase, addr);
if (!no_page_fault_log) {
KGSL_MEM_CRIT(iommu_dev->kgsldev,
- "GPU PAGE FAULT: addr = %lX pid = %d\n",
- addr, kgsl_mmu_get_ptname_from_ptbase(mmu, ptbase));
- KGSL_MEM_CRIT(iommu_dev->kgsldev, "context = %d FSR = %X\n",
- iommu_dev->ctx_id, fsr);
+ "GPU PAGE FAULT: addr = %lX pid = %d\n", addr, pid);
+ KGSL_MEM_CRIT(iommu_dev->kgsldev,
+ "context = %d FSR = %X FSYNR0 = %X FSYNR1 = %X(%s fault)\n",
+ iommu_dev->ctx_id, fsr, fsynr0, fsynr1,
+ write ? "write" : "read");
_check_if_freed(iommu_dev, addr, pid);
@@ -370,18 +397,20 @@
kgsl_sharedmem_readl(&device->memstore, &curr_context_id,
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context));
context = idr_find(&device->context_idr, curr_context_id);
- if (context != NULL)
+ if (context != NULL) {
curr_context = context->devctxt;
- kgsl_sharedmem_readl(&device->memstore, &curr_global_ts,
- KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, eoptimestamp));
+ kgsl_sharedmem_readl(&device->memstore, &curr_global_ts,
+ KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+ eoptimestamp));
- /*
- * Store pagefault's timestamp in adreno context,
- * this information will be used in GFT
- */
- curr_context->pagefault = 1;
- curr_context->pagefault_ts = curr_global_ts;
+ /*
+ * Store pagefault's timestamp in adreno context,
+ * this information will be used in GFT
+ */
+ curr_context->pagefault = 1;
+ curr_context->pagefault_ts = curr_global_ts;
+ }
trace_kgsl_mmu_pagefault(iommu_dev->kgsldev, addr,
kgsl_mmu_get_ptname_from_ptbase(mmu, ptbase),
@@ -580,20 +609,16 @@
*/
static int kgsl_iommu_pt_equal(struct kgsl_mmu *mmu,
struct kgsl_pagetable *pt,
- unsigned int pt_base)
+ phys_addr_t pt_base)
{
- struct kgsl_iommu *iommu = mmu->priv;
struct kgsl_iommu_pt *iommu_pt = pt ? pt->priv : NULL;
- unsigned int domain_ptbase = iommu_pt ?
+ phys_addr_t domain_ptbase = iommu_pt ?
iommu_get_pt_base_addr(iommu_pt->domain) : 0;
- /* Only compare the valid address bits of the pt_base */
- domain_ptbase &=
- (iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_mask <<
- iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_shift);
- pt_base &=
- (iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_mask <<
- iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_shift);
+ /* Only compare the valid address bits of the pt_base */
+ domain_ptbase &= KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
+
+ pt_base &= KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
return domain_ptbase && pt_base &&
(domain_ptbase == pt_base);
@@ -609,8 +634,10 @@
{
struct kgsl_iommu_pt *iommu_pt = pt->priv;
if (iommu_pt->domain)
- iommu_domain_free(iommu_pt->domain);
+ msm_unregister_domain(iommu_pt->domain);
+
kfree(iommu_pt);
+ iommu_pt = NULL;
}
/*
@@ -649,15 +676,18 @@
domain_num = msm_register_domain(&kgsl_layout);
if (domain_num >= 0) {
iommu_pt->domain = msm_get_iommu_domain(domain_num);
- iommu_set_fault_handler(iommu_pt->domain,
- kgsl_iommu_fault_handler, NULL);
- } else {
- KGSL_CORE_ERR("Failed to create iommu domain\n");
- kfree(iommu_pt);
- return NULL;
+
+ if (iommu_pt->domain) {
+ iommu_set_fault_handler(iommu_pt->domain,
+ kgsl_iommu_fault_handler, NULL);
+
+ return iommu_pt;
+ }
}
- return iommu_pt;
+ KGSL_CORE_ERR("Failed to create iommu domain\n");
+ kfree(iommu_pt);
+ return NULL;
}
/*
@@ -889,14 +919,17 @@
if (iommu->sync_lock_initialized)
return status;
- /* Get the physical address of the Lock variables */
- lock_phy_addr = (msm_iommu_lock_initialize()
- - MSM_SHARED_RAM_BASE + msm_shared_ram_phys);
+ iommu_access_ops = msm_get_iommu_access_ops();
- if (!lock_phy_addr) {
- KGSL_DRV_ERR(mmu->device,
- "GPU CPU sync lock is not supported by kernel\n");
- return -ENXIO;
+ if (iommu_access_ops && iommu_access_ops->iommu_lock_initialize) {
+ lock_phy_addr = (uint32_t)
+ iommu_access_ops->iommu_lock_initialize();
+ if (!lock_phy_addr) {
+ iommu_access_ops = NULL;
+ return status;
+ }
+ lock_phy_addr = lock_phy_addr - (uint32_t)MSM_SHARED_RAM_BASE +
+ (uint32_t)msm_shared_ram_phys;
}
/* Align the physical address to PAGE boundary and store the offset */
@@ -911,8 +944,10 @@
iommu->sync_lock_desc.physaddr,
iommu->sync_lock_desc.size);
- if (status)
+ if (status) {
+ iommu_access_ops = NULL;
return status;
+ }
/* Flag Sync Lock is Initialized */
iommu->sync_lock_initialized = 1;
@@ -1092,6 +1127,9 @@
iommu_unit->reg_map.size);
if (ret)
goto err;
+
+ iommu_unit->iommu_halt_enable = data.iommu_halt_enable;
+ iommu_unit->ahb_base = data.physstart - mmu->device->reg_phys;
}
iommu->unit_count = pdata_dev->iommu_count;
return ret;
@@ -1115,28 +1153,24 @@
* Return - actual pagetable address that the ttbr0 register is programmed
* with
*/
-static unsigned int kgsl_iommu_get_pt_base_addr(struct kgsl_mmu *mmu,
+static phys_addr_t kgsl_iommu_get_pt_base_addr(struct kgsl_mmu *mmu,
struct kgsl_pagetable *pt)
{
- struct kgsl_iommu *iommu = mmu->priv;
struct kgsl_iommu_pt *iommu_pt = pt->priv;
return iommu_get_pt_base_addr(iommu_pt->domain) &
- (iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_mask <<
- iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_shift);
+ KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
}
/*
- * kgsl_iommu_get_pt_lsb - Return the lsb of the ttbr0 IOMMU register
+ * kgsl_iommu_get_default_ttbr0 - Return the ttbr0 value programmed by
+ * iommu driver
* @mmu - Pointer to mmu structure
* @hostptr - Pointer to the IOMMU register map. This is used to match
* the iommu device whose lsb value is to be returned
* @ctx_id - The context bank whose lsb valus is to be returned
- * Return - returns the lsb which is the last 14 bits of the ttbr0 IOMMU
- * register. ttbr0 is the actual PTBR for of the IOMMU. The last 14 bits
- * are only programmed once in the beginning when a domain is attached
- * does not change.
+ * Return - returns the ttbr0 value programmed by iommu driver
*/
-static int kgsl_iommu_get_pt_lsb(struct kgsl_mmu *mmu,
+static phys_addr_t kgsl_iommu_get_default_ttbr0(struct kgsl_mmu *mmu,
unsigned int unit_id,
enum kgsl_iommu_context_id ctx_id)
{
@@ -1147,7 +1181,7 @@
for (j = 0; j < iommu_unit->dev_count; j++)
if (unit_id == i &&
ctx_id == iommu_unit->dev[j].ctx_id)
- return iommu_unit->dev[j].pt_lsb;
+ return iommu_unit->dev[j].default_ttbr0;
}
return 0;
}
@@ -1241,6 +1275,30 @@
}
+/*
+ * kgsl_iommu_get_reg_ahbaddr - Returns the ahb address of the register
+ * @mmu - Pointer to mmu structure
+ * @iommu_unit - The iommu unit for which base address is requested
+ * @ctx_id - The context ID of the IOMMU ctx
+ * @reg - The register for which address is required
+ *
+ * Return - The address of register which can be used in type0 packet
+ */
+static unsigned int kgsl_iommu_get_reg_ahbaddr(struct kgsl_mmu *mmu,
+ int iommu_unit, int ctx_id,
+ enum kgsl_iommu_reg_map reg)
+{
+ struct kgsl_iommu *iommu = mmu->priv;
+
+ if (iommu->iommu_reg_list[reg].ctx_reg)
+ return iommu->iommu_units[iommu_unit].ahb_base +
+ iommu->iommu_reg_list[reg].reg_offset +
+ (ctx_id << KGSL_IOMMU_CTX_SHIFT) + iommu->ctx_offset;
+ else
+ return iommu->iommu_units[iommu_unit].ahb_base +
+ iommu->iommu_reg_list[reg].reg_offset;
+}
+
static int kgsl_iommu_init(struct kgsl_mmu *mmu)
{
/*
@@ -1265,13 +1323,15 @@
status = kgsl_set_register_map(mmu);
if (status)
goto done;
- status = kgsl_iommu_init_sync_lock(mmu);
- if (status)
- goto done;
- /* We presently do not support per-process for IOMMU-v1 */
+ /*
+ * IOMMU-v1 requires hardware halt support to do in stream
+ * pagetable switching. This check assumes that if there are
+ * multiple units, they will be matching hardware.
+ */
mmu->pt_per_process = KGSL_MMU_USE_PER_PROCESS_PT &&
- msm_soc_version_supports_iommu_v0();
+ (msm_soc_version_supports_iommu_v0() ||
+ iommu->iommu_units[0].iommu_halt_enable);
/*
* For IOMMU per-process pagetables, the allocatable range
@@ -1284,16 +1344,33 @@
* we're better off with extra room.
*/
if (mmu->pt_per_process) {
+#ifndef CONFIG_MSM_KGSL_CFF_DUMP
mmu->pt_base = PAGE_OFFSET;
mmu->pt_size = KGSL_IOMMU_GLOBAL_MEM_BASE
- kgsl_mmu_get_base_addr(mmu) - SZ_1M;
mmu->use_cpu_map = true;
+#else
+ mmu->pt_base = KGSL_PAGETABLE_BASE;
+ mmu->pt_size = KGSL_IOMMU_GLOBAL_MEM_BASE +
+ KGSL_IOMMU_GLOBAL_MEM_SIZE -
+ KGSL_PAGETABLE_BASE;
+ mmu->use_cpu_map = false;
+#endif
} else {
mmu->pt_base = KGSL_PAGETABLE_BASE;
+#ifndef CONFIG_MSM_KGSL_CFF_DUMP
mmu->pt_size = SZ_2G;
+#else
+ mmu->pt_size = KGSL_IOMMU_GLOBAL_MEM_BASE +
+ KGSL_IOMMU_GLOBAL_MEM_SIZE -
+ KGSL_PAGETABLE_BASE;
+#endif
mmu->use_cpu_map = false;
}
+ status = kgsl_iommu_init_sync_lock(mmu);
+ if (status)
+ goto done;
iommu->iommu_reg_list = kgsl_iommuv0_reg;
iommu->ctx_offset = KGSL_IOMMU_CTX_OFFSET_V0;
@@ -1308,7 +1385,7 @@
/* A nop is required in an indirect buffer when switching
* pagetables in-stream */
- kgsl_sharedmem_writel(&mmu->setstate_memory,
+ kgsl_sharedmem_writel(mmu->device, &mmu->setstate_memory,
KGSL_IOMMU_SETSTATE_NOP_OFFSET,
cp_nop_packet(1));
@@ -1321,6 +1398,15 @@
iommu_ops.mmu_cleanup_pt = kgsl_iommu_cleanup_regs;
}
+ if (kgsl_guard_page == NULL) {
+ kgsl_guard_page = alloc_page(GFP_KERNEL | __GFP_ZERO |
+ __GFP_HIGHMEM);
+ if (kgsl_guard_page == NULL) {
+ status = -ENOMEM;
+ goto done;
+ }
+ }
+
dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n",
__func__);
done:
@@ -1535,22 +1621,30 @@
* changing pagetables we can use this lsb value of the pagetable w/o
* having to read it again
*/
- msm_iommu_lock();
+ _iommu_lock();
for (i = 0; i < iommu->unit_count; i++) {
struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
for (j = 0; j < iommu_unit->dev_count; j++) {
- iommu_unit->dev[j].pt_lsb = KGSL_IOMMMU_PT_LSB(iommu,
+ if (sizeof(phys_addr_t) > sizeof(unsigned long)) {
+ iommu_unit->dev[j].default_ttbr0 =
+ KGSL_IOMMU_GET_CTX_REG_LL(iommu,
+ iommu_unit,
+ iommu_unit->dev[j].ctx_id,
+ TTBR0);
+ } else {
+ iommu_unit->dev[j].default_ttbr0 =
KGSL_IOMMU_GET_CTX_REG(iommu,
iommu_unit,
iommu_unit->dev[j].ctx_id,
- TTBR0));
+ TTBR0);
+ }
}
}
kgsl_iommu_lock_rb_in_tlb(mmu);
- msm_iommu_unlock();
+ _iommu_unlock();
/* For complete CFF */
- kgsl_cffdump_setmem(mmu->setstate_memory.gpuaddr +
+ kgsl_cffdump_setmem(mmu->device, mmu->setstate_memory.gpuaddr +
KGSL_IOMMU_SETSTATE_NOP_OFFSET,
cp_nop_packet(1), sizeof(unsigned int));
@@ -1571,7 +1665,7 @@
unsigned int *tlb_flags)
{
int ret;
- unsigned int range = kgsl_sg_size(memdesc->sg, memdesc->sglen);
+ unsigned int range = memdesc->size;
struct kgsl_iommu_pt *iommu_pt = pt->priv;
/* All GPU addresses as assigned are page aligned, but some
@@ -1583,6 +1677,9 @@
if (range == 0 || gpuaddr == 0)
return 0;
+ if (kgsl_memdesc_has_guard_page(memdesc))
+ range += PAGE_SIZE;
+
ret = iommu_unmap_range(iommu_pt->domain, gpuaddr, range);
if (ret)
KGSL_CORE_ERR("iommu_unmap_range(%p, %x, %d) failed "
@@ -1607,14 +1704,10 @@
int ret;
unsigned int iommu_virt_addr;
struct kgsl_iommu_pt *iommu_pt = pt->priv;
- int size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
+ int size = memdesc->size;
BUG_ON(NULL == iommu_pt);
- /* if there's a guard page, we'll map it read only below */
- if ((protflags & IOMMU_WRITE) && kgsl_memdesc_has_guard_page(memdesc))
- size -= PAGE_SIZE;
-
iommu_virt_addr = memdesc->gpuaddr;
ret = iommu_map_range(iommu_pt->domain, iommu_virt_addr, memdesc->sg,
@@ -1625,16 +1718,14 @@
protflags, ret);
return ret;
}
- if ((protflags & IOMMU_WRITE) && kgsl_memdesc_has_guard_page(memdesc)) {
- struct scatterlist *sg = &memdesc->sg[memdesc->sglen - 1];
-
+ if (kgsl_memdesc_has_guard_page(memdesc)) {
ret = iommu_map(iommu_pt->domain, iommu_virt_addr + size,
- kgsl_get_sg_pa(sg), PAGE_SIZE,
+ page_to_phys(kgsl_guard_page), PAGE_SIZE,
protflags & ~IOMMU_WRITE);
if (ret) {
- KGSL_CORE_ERR("iommu_map(%p, %x, %x, %x) err: %d\n",
+ KGSL_CORE_ERR("iommu_map(%p, %x, guard, %x) err: %d\n",
iommu_pt->domain, iommu_virt_addr + size,
- kgsl_get_sg_pa(sg), protflags & ~IOMMU_WRITE,
+ protflags & ~IOMMU_WRITE,
ret);
/* cleanup the partial mapping */
iommu_unmap_range(iommu_pt->domain, iommu_virt_addr,
@@ -1644,10 +1735,36 @@
return ret;
}
-static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
+void kgsl_iommu_pagefault_resume(struct kgsl_mmu *mmu)
{
struct kgsl_iommu *iommu = mmu->priv;
int i, j;
+
+ if (mmu->fault) {
+ for (i = 0; i < iommu->unit_count; i++) {
+ struct kgsl_iommu_unit *iommu_unit =
+ &iommu->iommu_units[i];
+ for (j = 0; j < iommu_unit->dev_count; j++) {
+ if (iommu_unit->dev[j].fault) {
+ kgsl_iommu_enable_clk(mmu, j);
+ _iommu_lock();
+ KGSL_IOMMU_SET_CTX_REG(iommu,
+ iommu_unit,
+ iommu_unit->dev[j].ctx_id,
+ RESUME, 1);
+ _iommu_unlock();
+ iommu_unit->dev[j].fault = 0;
+ }
+ }
+ }
+ mmu->fault = 0;
+ }
+}
+
+
+static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
+{
+ struct kgsl_iommu *iommu = mmu->priv;
/*
* stop device mmu
*
@@ -1660,25 +1777,7 @@
mmu->flags &= ~KGSL_FLAGS_STARTED;
- if (mmu->fault) {
- for (i = 0; i < iommu->unit_count; i++) {
- struct kgsl_iommu_unit *iommu_unit =
- &iommu->iommu_units[i];
- for (j = 0; j < iommu_unit->dev_count; j++) {
- if (iommu_unit->dev[j].fault) {
- kgsl_iommu_enable_clk(mmu, j);
- msm_iommu_lock();
- KGSL_IOMMU_SET_CTX_REG(iommu,
- iommu_unit,
- iommu_unit->dev[j].ctx_id,
- RESUME, 1);
- msm_iommu_unlock();
- iommu_unit->dev[j].fault = 0;
- }
- }
- }
- mmu->fault = 0;
- }
+ kgsl_iommu_pagefault_resume(mmu);
}
/* switch off MMU clocks and cancel any events it has queued */
iommu->clk_event_queued = false;
@@ -1714,13 +1813,18 @@
kfree(iommu);
+ if (kgsl_guard_page != NULL) {
+ __free_page(kgsl_guard_page);
+ kgsl_guard_page = NULL;
+ }
+
return 0;
}
-static unsigned int
+static phys_addr_t
kgsl_iommu_get_current_ptbase(struct kgsl_mmu *mmu)
{
- unsigned int pt_base;
+ phys_addr_t pt_base;
struct kgsl_iommu *iommu = mmu->priv;
/* We cannot enable or disable the clocks in interrupt context, this
function is called from interrupt context if there is an axi error */
@@ -1732,9 +1836,7 @@
KGSL_IOMMU_CONTEXT_USER,
TTBR0);
kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
- return pt_base &
- (iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_mask <<
- iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_shift);
+ return pt_base & KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
}
/*
@@ -1755,24 +1857,21 @@
struct kgsl_iommu *iommu = mmu->priv;
int temp;
int i;
- unsigned int pt_base = kgsl_iommu_get_pt_base_addr(mmu,
+ phys_addr_t pt_base = kgsl_iommu_get_pt_base_addr(mmu,
mmu->hwpagetable);
- unsigned int pt_val;
+ phys_addr_t pt_val;
if (kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER)) {
KGSL_DRV_ERR(mmu->device, "Failed to enable iommu clocks\n");
return;
}
- /* Mask off the lsb of the pt base address since lsb will not change */
- pt_base &= (iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_mask <<
- iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_shift);
/* For v0 SMMU GPU needs to be idle for tlb invalidate as well */
if (msm_soc_version_supports_iommu_v0())
kgsl_idle(mmu->device);
/* Acquire GPU-CPU sync Lock here */
- msm_iommu_lock();
+ _iommu_lock();
if (flags & KGSL_MMUFLAGS_PTUPDATE) {
if (!msm_soc_version_supports_iommu_v0())
@@ -1780,12 +1879,21 @@
for (i = 0; i < iommu->unit_count; i++) {
/* get the lsb value which should not change when
* changing ttbr0 */
- pt_val = kgsl_iommu_get_pt_lsb(mmu, i,
+ pt_val = kgsl_iommu_get_default_ttbr0(mmu, i,
KGSL_IOMMU_CONTEXT_USER);
- pt_val += pt_base;
- KGSL_IOMMU_SET_CTX_REG(iommu, (&iommu->iommu_units[i]),
- KGSL_IOMMU_CONTEXT_USER, TTBR0, pt_val);
+ pt_base &= KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
+ pt_val &= ~KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
+ pt_val |= pt_base;
+ if (sizeof(phys_addr_t) > sizeof(unsigned long)) {
+ KGSL_IOMMU_SET_CTX_REG_LL(iommu,
+ (&iommu->iommu_units[i]),
+ KGSL_IOMMU_CONTEXT_USER, TTBR0, pt_val);
+ } else {
+ KGSL_IOMMU_SET_CTX_REG(iommu,
+ (&iommu->iommu_units[i]),
+ KGSL_IOMMU_CONTEXT_USER, TTBR0, pt_val);
+ }
mb();
temp = KGSL_IOMMU_GET_CTX_REG(iommu,
@@ -1795,15 +1903,42 @@
}
/* Flush tlb */
if (flags & KGSL_MMUFLAGS_TLBFLUSH) {
+ unsigned long wait_for_flush;
for (i = 0; i < iommu->unit_count; i++) {
KGSL_IOMMU_SET_CTX_REG(iommu, (&iommu->iommu_units[i]),
KGSL_IOMMU_CONTEXT_USER, TLBIALL, 1);
mb();
+ /*
+ * Wait for flush to complete by polling the flush
+ * status bit of TLBSTATUS register for not more than
+ * 2 s. After 2s just exit, at that point the SMMU h/w
+ * may be stuck and will eventually cause GPU to hang
+ * or bring the system down.
+ */
+ if (!msm_soc_version_supports_iommu_v0()) {
+ wait_for_flush = jiffies +
+ msecs_to_jiffies(2000);
+ KGSL_IOMMU_SET_CTX_REG(iommu,
+ (&iommu->iommu_units[i]),
+ KGSL_IOMMU_CONTEXT_USER, TLBSYNC, 0);
+ while (KGSL_IOMMU_GET_CTX_REG(iommu,
+ (&iommu->iommu_units[i]),
+ KGSL_IOMMU_CONTEXT_USER, TLBSTATUS) &
+ (KGSL_IOMMU_CTX_TLBSTATUS_SACTIVE)) {
+ if (time_after(jiffies,
+ wait_for_flush)) {
+ KGSL_DRV_ERR(mmu->device,
+ "Wait limit reached for IOMMU tlb flush\n");
+ break;
+ }
+ cpu_relax();
+ }
+ }
}
}
/* Release GPU-CPU sync Lock here */
- msm_iommu_unlock();
+ _iommu_unlock();
/* Disable smmu clock */
kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
@@ -1816,8 +1951,7 @@
* @ctx_id - The context ID of the IOMMU ctx
* @reg - The register for which address is required
*
- * Return - The number of iommu units which is also the number of register
- * mapped descriptor arrays which the out parameter will have
+ * Return - The gpu address of register which can be used in type3 packet
*/
static unsigned int kgsl_iommu_get_reg_gpuaddr(struct kgsl_mmu *mmu,
int iommu_unit, int ctx_id, int reg)
@@ -1826,10 +1960,25 @@
if (KGSL_IOMMU_GLOBAL_BASE == reg)
return iommu->iommu_units[iommu_unit].reg_map.gpuaddr;
- else
+
+ if (iommu->iommu_reg_list[reg].ctx_reg)
return iommu->iommu_units[iommu_unit].reg_map.gpuaddr +
iommu->iommu_reg_list[reg].reg_offset +
(ctx_id << KGSL_IOMMU_CTX_SHIFT) + iommu->ctx_offset;
+ else
+ return iommu->iommu_units[iommu_unit].reg_map.gpuaddr +
+ iommu->iommu_reg_list[reg].reg_offset;
+}
+/*
+ * kgsl_iommu_hw_halt_supported - Returns whether IOMMU halt command is
+ * supported
+ * @mmu - Pointer to mmu structure
+ * @iommu_unit - The iommu unit for which the property is requested
+ */
+static int kgsl_iommu_hw_halt_supported(struct kgsl_mmu *mmu, int iommu_unit)
+{
+ struct kgsl_iommu *iommu = mmu->priv;
+ return iommu->iommu_units[iommu_unit].iommu_halt_enable;
}
static int kgsl_iommu_get_num_iommu_units(struct kgsl_mmu *mmu)
@@ -1846,14 +1995,17 @@
.mmu_setstate = kgsl_iommu_setstate,
.mmu_device_setstate = kgsl_iommu_default_setstate,
.mmu_pagefault = NULL,
+ .mmu_pagefault_resume = kgsl_iommu_pagefault_resume,
.mmu_get_current_ptbase = kgsl_iommu_get_current_ptbase,
.mmu_enable_clk = kgsl_iommu_enable_clk,
.mmu_disable_clk_on_ts = kgsl_iommu_disable_clk_on_ts,
- .mmu_get_pt_lsb = kgsl_iommu_get_pt_lsb,
+ .mmu_get_default_ttbr0 = kgsl_iommu_get_default_ttbr0,
.mmu_get_reg_gpuaddr = kgsl_iommu_get_reg_gpuaddr,
+ .mmu_get_reg_ahbaddr = kgsl_iommu_get_reg_ahbaddr,
.mmu_get_num_iommu_units = kgsl_iommu_get_num_iommu_units,
.mmu_pt_equal = kgsl_iommu_pt_equal,
.mmu_get_pt_base_addr = kgsl_iommu_get_pt_base_addr,
+ .mmu_hw_halt_supported = kgsl_iommu_hw_halt_supported,
/* These callbacks will be set on some chipsets */
.mmu_setup_pt = NULL,
.mmu_cleanup_pt = NULL,
diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h
index c09bc4b..5c4b17e 100644
--- a/drivers/gpu/msm/kgsl_iommu.h
+++ b/drivers/gpu/msm/kgsl_iommu.h
@@ -46,6 +46,21 @@
#define KGSL_IOMMU_V1_FSYNR0_WNR_MASK 0x00000001
#define KGSL_IOMMU_V1_FSYNR0_WNR_SHIFT 4
+/* TTBR0 register fields */
+#ifdef CONFIG_ARM_LPAE
+#define KGSL_IOMMU_CTX_TTBR0_ADDR_MASK_LPAE 0x000000FFFFFFFFE0ULL
+#define KGSL_IOMMU_CTX_TTBR0_ADDR_MASK KGSL_IOMMU_CTX_TTBR0_ADDR_MASK_LPAE
+#else
+#define KGSL_IOMMU_CTX_TTBR0_ADDR_MASK 0xFFFFC000
+#endif
+
+/* TLBSTATUS register fields */
+#define KGSL_IOMMU_CTX_TLBSTATUS_SACTIVE BIT(0)
+
+/* IMPLDEF_MICRO_MMU_CTRL register fields */
+#define KGSL_IOMMU_IMPLDEF_MICRO_MMU_CTRL_HALT BIT(2)
+#define KGSL_IOMMU_IMPLDEF_MICRO_MMU_CTRL_IDLE BIT(3)
+
enum kgsl_iommu_reg_map {
KGSL_IOMMU_GLOBAL_BASE = 0,
KGSL_IOMMU_CTX_TTBR0,
@@ -57,13 +72,15 @@
KGSL_IOMMU_CTX_V2PUR,
KGSL_IOMMU_CTX_FSYNR0,
KGSL_IOMMU_CTX_FSYNR1,
+ KGSL_IOMMU_CTX_TLBSYNC,
+ KGSL_IOMMU_CTX_TLBSTATUS,
+ KGSL_IOMMU_IMPLDEF_MICRO_MMU_CTRL,
KGSL_IOMMU_REG_MAX
};
struct kgsl_iommu_register_list {
unsigned int reg_offset;
- unsigned int reg_mask;
- unsigned int reg_shift;
+ int ctx_reg;
};
/*
@@ -76,6 +93,20 @@
#define KGSL_IOMMU_MAX_DEVS_PER_UNIT 2
/* Macros to read/write IOMMU registers */
+#define KGSL_IOMMU_SET_CTX_REG_LL(iommu, iommu_unit, ctx, REG, val) \
+ writell_relaxed(val, \
+ iommu_unit->reg_map.hostptr + \
+ iommu->iommu_reg_list[KGSL_IOMMU_CTX_##REG].reg_offset +\
+ (ctx << KGSL_IOMMU_CTX_SHIFT) + \
+ iommu->ctx_offset)
+
+#define KGSL_IOMMU_GET_CTX_REG_LL(iommu, iommu_unit, ctx, REG) \
+ readl_relaxed( \
+ iommu_unit->reg_map.hostptr + \
+ iommu->iommu_reg_list[KGSL_IOMMU_CTX_##REG].reg_offset +\
+ (ctx << KGSL_IOMMU_CTX_SHIFT) + \
+ iommu->ctx_offset)
+
#define KGSL_IOMMU_SET_CTX_REG(iommu, iommu_unit, ctx, REG, val) \
writel_relaxed(val, \
iommu_unit->reg_map.hostptr + \
@@ -91,10 +122,8 @@
iommu->ctx_offset)
/* Gets the lsb value of pagetable */
-#define KGSL_IOMMMU_PT_LSB(iommu, pt_val) \
- (pt_val & \
- ~(iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_mask << \
- iommu->iommu_reg_list[KGSL_IOMMU_CTX_TTBR0].reg_shift))
+#define KGSL_IOMMMU_PT_LSB(iommu, pt_val) \
+ (pt_val & ~(KGSL_IOMMU_CTX_TTBR0_ADDR_MASK))
/* offset at which a nop command is placed in setstate_memory */
#define KGSL_IOMMU_SETSTATE_NOP_OFFSET 1024
@@ -104,8 +133,7 @@
* @dev: Device pointer to iommu context
* @attached: Indicates whether this iommu context is presently attached to
* a pagetable/domain or not
- * @pt_lsb: The LSB of IOMMU_TTBR0 register which is the pagetable
- * register
+ * @default_ttbr0: The TTBR0 value set by iommu driver on start up
* @ctx_id: This iommu units context id. It can be either 0 or 1
* @clk_enabled: If set indicates that iommu clocks of this iommu context
* are on, else the clocks are off
@@ -115,7 +143,7 @@
struct kgsl_iommu_device {
struct device *dev;
bool attached;
- unsigned int pt_lsb;
+ phys_addr_t default_ttbr0;
enum kgsl_iommu_context_id ctx_id;
bool clk_enabled;
struct kgsl_device *kgsldev;
@@ -130,11 +158,18 @@
* @dev_count: Number of IOMMU contexts that are valid in the previous feild
* @reg_map: Memory descriptor which holds the mapped address of this IOMMU
* units register range
+ * @ahb_base - The base address from where IOMMU registers can be accesed from
+ * ahb bus
+ * @iommu_halt_enable: Valid only on IOMMU-v1, when set indicates that the iommu
+ * unit supports halting of the IOMMU, which can be enabled while programming
+ * the IOMMU registers for synchronization
*/
struct kgsl_iommu_unit {
struct kgsl_iommu_device dev[KGSL_IOMMU_MAX_DEVS_PER_UNIT];
unsigned int dev_count;
struct kgsl_memdesc reg_map;
+ unsigned int ahb_base;
+ int iommu_halt_enable;
};
/*
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index 6e41707..12a4b25 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -314,7 +314,7 @@
}
int
-kgsl_mmu_get_ptname_from_ptbase(struct kgsl_mmu *mmu, unsigned int pt_base)
+kgsl_mmu_get_ptname_from_ptbase(struct kgsl_mmu *mmu, phys_addr_t pt_base)
{
struct kgsl_pagetable *pt;
int ptid = -1;
@@ -335,7 +335,7 @@
EXPORT_SYMBOL(kgsl_mmu_get_ptname_from_ptbase);
unsigned int
-kgsl_mmu_log_fault_addr(struct kgsl_mmu *mmu, unsigned int pt_base,
+kgsl_mmu_log_fault_addr(struct kgsl_mmu *mmu, phys_addr_t pt_base,
unsigned int addr)
{
struct kgsl_pagetable *pt;
@@ -372,7 +372,7 @@
status = kgsl_allocate_contiguous(&mmu->setstate_memory, PAGE_SIZE);
if (status)
return status;
- kgsl_sharedmem_set(&mmu->setstate_memory, 0, 0,
+ kgsl_sharedmem_set(device, &mmu->setstate_memory, 0, 0,
mmu->setstate_memory.size);
if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type) {
@@ -410,7 +410,8 @@
static void mh_axi_error(struct kgsl_device *device, const char* type)
{
- unsigned int reg, gpu_err, phys_err, pt_base;
+ unsigned int reg, gpu_err, phys_err;
+ phys_addr_t pt_base;
kgsl_regread(device, MH_AXI_ERROR, ®);
pt_base = kgsl_mmu_get_current_ptbase(&device->mmu);
@@ -423,8 +424,8 @@
kgsl_regwrite(device, MH_DEBUG_CTRL, 45);
kgsl_regread(device, MH_DEBUG_DATA, &phys_err);
KGSL_MEM_CRIT(device,
- "axi %s error: %08x pt %08x gpu %08x phys %08x\n",
- type, reg, pt_base, gpu_err, phys_err);
+ "axi %s error: %08x pt %pa gpu %08x phys %08x\n",
+ type, reg, &pt_base, gpu_err, phys_err);
}
void kgsl_mh_intrcallback(struct kgsl_device *device)
@@ -640,7 +641,10 @@
}
}
- size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
+ /* Add space for the guard page when allocating the mmu VA. */
+ size = memdesc->size;
+ if (kgsl_memdesc_has_guard_page(memdesc))
+ size += PAGE_SIZE;
pool = pagetable->pool;
@@ -738,7 +742,10 @@
return 0;
}
- size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
+ /* Add space for the guard page when freeing the mmu VA. */
+ size = memdesc->size;
+ if (kgsl_memdesc_has_guard_page(memdesc))
+ size += PAGE_SIZE;
start_addr = memdesc->gpuaddr;
end_addr = (memdesc->gpuaddr + size);
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index d7d9516..27cfc40 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -14,7 +14,7 @@
#define __KGSL_MMU_H
#include <mach/iommu.h>
-
+#include "kgsl_iommu.h"
/*
* These defines control the address range for allocations that
* are mapped into all pagetables.
@@ -139,22 +139,27 @@
void (*mmu_device_setstate) (struct kgsl_mmu *mmu,
uint32_t flags);
void (*mmu_pagefault) (struct kgsl_mmu *mmu);
- unsigned int (*mmu_get_current_ptbase)
+ phys_addr_t (*mmu_get_current_ptbase)
+ (struct kgsl_mmu *mmu);
+ void (*mmu_pagefault_resume)
(struct kgsl_mmu *mmu);
void (*mmu_disable_clk_on_ts)
(struct kgsl_mmu *mmu, uint32_t ts, bool ts_valid);
int (*mmu_enable_clk)
(struct kgsl_mmu *mmu, int ctx_id);
- int (*mmu_get_pt_lsb)(struct kgsl_mmu *mmu,
+ phys_addr_t (*mmu_get_default_ttbr0)(struct kgsl_mmu *mmu,
unsigned int unit_id,
enum kgsl_iommu_context_id ctx_id);
unsigned int (*mmu_get_reg_gpuaddr)(struct kgsl_mmu *mmu,
int iommu_unit_num, int ctx_id, int reg);
+ unsigned int (*mmu_get_reg_ahbaddr)(struct kgsl_mmu *mmu,
+ int iommu_unit_num, int ctx_id,
+ enum kgsl_iommu_reg_map reg);
int (*mmu_get_num_iommu_units)(struct kgsl_mmu *mmu);
int (*mmu_pt_equal) (struct kgsl_mmu *mmu,
struct kgsl_pagetable *pt,
- unsigned int pt_base);
- unsigned int (*mmu_get_pt_base_addr)
+ phys_addr_t pt_base);
+ phys_addr_t (*mmu_get_pt_base_addr)
(struct kgsl_mmu *mmu,
struct kgsl_pagetable *pt);
int (*mmu_setup_pt) (struct kgsl_mmu *mmu,
@@ -165,6 +170,7 @@
(struct kgsl_mmu *mmu, unsigned int *cmds);
unsigned int (*mmu_sync_unlock)
(struct kgsl_mmu *mmu, unsigned int *cmds);
+ int (*mmu_hw_halt_supported)(struct kgsl_mmu *mmu, int iommu_unit_num);
};
struct kgsl_mmu_pt_ops {
@@ -224,9 +230,9 @@
void kgsl_setstate(struct kgsl_mmu *mmu, unsigned int context_id,
uint32_t flags);
int kgsl_mmu_get_ptname_from_ptbase(struct kgsl_mmu *mmu,
- unsigned int pt_base);
+ phys_addr_t pt_base);
unsigned int kgsl_mmu_log_fault_addr(struct kgsl_mmu *mmu,
- unsigned int pt_base, unsigned int addr);
+ phys_addr_t pt_base, unsigned int addr);
int kgsl_mmu_pt_get_flags(struct kgsl_pagetable *pt,
enum kgsl_deviceid id);
void kgsl_mmu_ptpool_destroy(void *ptpool);
@@ -242,7 +248,7 @@
* of as wrappers around the actual function
*/
-static inline unsigned int kgsl_mmu_get_current_ptbase(struct kgsl_mmu *mmu)
+static inline phys_addr_t kgsl_mmu_get_current_ptbase(struct kgsl_mmu *mmu)
{
if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_current_ptbase)
return mmu->mmu_ops->mmu_get_current_ptbase(mmu);
@@ -273,7 +279,7 @@
static inline int kgsl_mmu_pt_equal(struct kgsl_mmu *mmu,
struct kgsl_pagetable *pt,
- unsigned int pt_base)
+ phys_addr_t pt_base)
{
if (mmu->mmu_ops && mmu->mmu_ops->mmu_pt_equal)
return mmu->mmu_ops->mmu_pt_equal(mmu, pt, pt_base);
@@ -281,7 +287,7 @@
return 1;
}
-static inline unsigned int kgsl_mmu_get_pt_base_addr(struct kgsl_mmu *mmu,
+static inline phys_addr_t kgsl_mmu_get_pt_base_addr(struct kgsl_mmu *mmu,
struct kgsl_pagetable *pt)
{
if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_pt_base_addr)
@@ -290,12 +296,13 @@
return 0;
}
-static inline int kgsl_mmu_get_pt_lsb(struct kgsl_mmu *mmu,
+static inline phys_addr_t kgsl_mmu_get_default_ttbr0(struct kgsl_mmu *mmu,
unsigned int unit_id,
enum kgsl_iommu_context_id ctx_id)
{
- if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_pt_lsb)
- return mmu->mmu_ops->mmu_get_pt_lsb(mmu, unit_id, ctx_id);
+ if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_default_ttbr0)
+ return mmu->mmu_ops->mmu_get_default_ttbr0(mmu, unit_id,
+ ctx_id);
else
return 0;
}
@@ -337,6 +344,29 @@
return 0;
}
+/*
+ * kgsl_mmu_get_reg_ahbaddr() - Calls the mmu specific function pointer to
+ * return the address that GPU can use to access register
+ * @mmu: Pointer to the device mmu
+ * @iommu_unit_num: There can be multiple iommu units used for graphics.
+ * This parameter is an index to the iommu unit being used
+ * @ctx_id: The context id within the iommu unit
+ * @reg: Register whose address is to be returned
+ *
+ * Returns the ahb address of reg else 0
+ */
+static inline unsigned int kgsl_mmu_get_reg_ahbaddr(struct kgsl_mmu *mmu,
+ int iommu_unit_num,
+ int ctx_id,
+ enum kgsl_iommu_reg_map reg)
+{
+ if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_reg_ahbaddr)
+ return mmu->mmu_ops->mmu_get_reg_ahbaddr(mmu, iommu_unit_num,
+ ctx_id, reg);
+ else
+ return 0;
+}
+
static inline int kgsl_mmu_get_num_iommu_units(struct kgsl_mmu *mmu)
{
if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_num_iommu_units)
@@ -346,6 +376,22 @@
}
/*
+ * kgsl_mmu_hw_halt_supported() - Runtime check for iommu hw halt
+ * @mmu: the mmu
+ *
+ * Returns non-zero if the iommu supports hw halt,
+ * 0 if not.
+ */
+static inline int kgsl_mmu_hw_halt_supported(struct kgsl_mmu *mmu,
+ int iommu_unit_num)
+{
+ if (mmu->mmu_ops && mmu->mmu_ops->mmu_hw_halt_supported)
+ return mmu->mmu_ops->mmu_hw_halt_supported(mmu, iommu_unit_num);
+ else
+ return 0;
+}
+
+/*
* kgsl_mmu_is_perprocess() - Runtime check for per-process
* pagetables.
* @mmu: the mmu
@@ -367,7 +413,7 @@
*/
static inline int kgsl_mmu_use_cpu_map(struct kgsl_mmu *mmu)
{
- return mmu->pt_per_process;
+ return mmu->use_cpu_map;
}
/*
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 5909153..3a2345e 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -18,6 +18,7 @@
#include <mach/msm_iomap.h>
#include <mach/msm_bus.h>
#include <linux/ktime.h>
+#include <linux/delay.h>
#include "kgsl.h"
#include "kgsl_pwrscale.h"
@@ -33,6 +34,16 @@
#define UPDATE_BUSY_VAL 1000000
#define UPDATE_BUSY 50
+/*
+ * Expected delay for post-interrupt processing on A3xx.
+ * The delay may be longer, gradually increase the delay
+ * to compensate. If the GPU isn't done by max delay,
+ * it's working on something other than just the final
+ * command sequence so stop waiting for it to be idle.
+ */
+#define INIT_UDELAY 200
+#define MAX_UDELAY 2000
+
struct clk_pair {
const char *name;
uint map;
@@ -59,8 +70,16 @@
.name = "mem_iface_clk",
.map = KGSL_CLK_MEM_IFACE,
},
+ {
+ .name = "alt_mem_iface_clk",
+ .map = KGSL_CLK_ALT_MEM_IFACE,
+ },
};
+static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
+ int requested_state);
+static void kgsl_pwrctrl_axi(struct kgsl_device *device, int state);
+
/* Update the elapsed time at a particular clock level
* if the device is active(on_time = true).Otherwise
* store it as sleep time.
@@ -463,49 +482,6 @@
pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq);
}
-static int kgsl_pwrctrl_pwrnap_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- char temp[20];
- unsigned long val;
- struct kgsl_device *device = kgsl_device_from_dev(dev);
- struct kgsl_pwrctrl *pwr;
- int rc;
-
- if (device == NULL)
- return 0;
- pwr = &device->pwrctrl;
-
- snprintf(temp, sizeof(temp), "%.*s",
- (int)min(count, sizeof(temp) - 1), buf);
- rc = strict_strtoul(temp, 0, &val);
- if (rc)
- return rc;
-
- mutex_lock(&device->mutex);
-
- if (val == 1)
- pwr->nap_allowed = true;
- else if (val == 0)
- pwr->nap_allowed = false;
-
- mutex_unlock(&device->mutex);
-
- return count;
-}
-
-static int kgsl_pwrctrl_pwrnap_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct kgsl_device *device = kgsl_device_from_dev(dev);
- if (device == NULL)
- return 0;
- return snprintf(buf, PAGE_SIZE, "%d\n", device->pwrctrl.nap_allowed);
-}
-
-
static int kgsl_pwrctrl_idle_timer_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -656,10 +632,90 @@
return snprintf(buf, PAGE_SIZE, "%d\n", device->reset_counter);
}
+static void __force_on(struct kgsl_device *device, int flag, int on)
+{
+ if (on) {
+ switch (flag) {
+ case KGSL_PWRFLAGS_CLK_ON:
+ kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON,
+ KGSL_STATE_ACTIVE);
+ break;
+ case KGSL_PWRFLAGS_AXI_ON:
+ kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON);
+ break;
+ }
+ set_bit(flag, &device->pwrctrl.ctrl_flags);
+ } else {
+ clear_bit(flag, &device->pwrctrl.ctrl_flags);
+ }
+}
+
+static int __force_on_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf, int flag)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ int i = test_bit(flag, &device->pwrctrl.ctrl_flags);
+ return snprintf(buf, PAGE_SIZE, "%d\n", i);
+}
+
+static int __force_on_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count,
+ int flag)
+{
+ char temp[20];
+ unsigned long val;
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ int rc;
+
+ if (device == NULL)
+ return 0;
+
+ snprintf(temp, sizeof(temp), "%.*s",
+ (int)min(count, sizeof(temp) - 1), buf);
+ rc = kstrtoul(temp, 0, &val);
+ if (rc)
+ return rc;
+
+ mutex_lock(&device->mutex);
+ __force_on(device, flag, val);
+ mutex_unlock(&device->mutex);
+
+ return count;
+}
+
+static int kgsl_pwrctrl_force_clk_on_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return __force_on_show(dev, attr, buf, KGSL_PWRFLAGS_CLK_ON);
+}
+
+static int kgsl_pwrctrl_force_clk_on_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return __force_on_store(dev, attr, buf, count, KGSL_PWRFLAGS_CLK_ON);
+}
+
+static int kgsl_pwrctrl_force_bus_on_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return __force_on_show(dev, attr, buf, KGSL_PWRFLAGS_AXI_ON);
+}
+
+static int kgsl_pwrctrl_force_bus_on_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return __force_on_store(dev, attr, buf, count, KGSL_PWRFLAGS_AXI_ON);
+}
+
DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show, kgsl_pwrctrl_gpuclk_store);
DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show,
kgsl_pwrctrl_max_gpuclk_store);
-DEVICE_ATTR(pwrnap, 0664, kgsl_pwrctrl_pwrnap_show, kgsl_pwrctrl_pwrnap_store);
DEVICE_ATTR(idle_timer, 0644, kgsl_pwrctrl_idle_timer_show,
kgsl_pwrctrl_idle_timer_store);
DEVICE_ATTR(gpubusy, 0444, kgsl_pwrctrl_gpubusy_show,
@@ -687,11 +743,16 @@
DEVICE_ATTR(reset_count, 0444,
kgsl_pwrctrl_reset_count_show,
NULL);
+DEVICE_ATTR(force_clk_on, 0644,
+ kgsl_pwrctrl_force_clk_on_show,
+ kgsl_pwrctrl_force_clk_on_store);
+DEVICE_ATTR(force_bus_on, 0644,
+ kgsl_pwrctrl_force_bus_on_show,
+ kgsl_pwrctrl_force_bus_on_store);
static const struct device_attribute *pwrctrl_attr_list[] = {
&dev_attr_gpuclk,
&dev_attr_max_gpuclk,
- &dev_attr_pwrnap,
&dev_attr_idle_timer,
&dev_attr_gpubusy,
&dev_attr_gputop,
@@ -702,6 +763,8 @@
&dev_attr_num_pwrlevels,
&dev_attr_pmqos_latency,
&dev_attr_reset_count,
+ &dev_attr_force_clk_on,
+ &dev_attr_force_bus_on,
NULL
};
@@ -751,11 +814,15 @@
}
}
-void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
+static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
int requested_state)
{
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
int i = 0;
+
+ if (test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->ctrl_flags))
+ return;
+
if (state == KGSL_PWRFLAGS_OFF) {
if (test_and_clear_bit(KGSL_PWRFLAGS_CLK_ON,
&pwr->power_flags)) {
@@ -809,10 +876,13 @@
}
}
-void kgsl_pwrctrl_axi(struct kgsl_device *device, int state)
+static void kgsl_pwrctrl_axi(struct kgsl_device *device, int state)
{
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+ if (test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->ctrl_flags))
+ return;
+
if (state == KGSL_PWRFLAGS_OFF) {
if (test_and_clear_bit(KGSL_PWRFLAGS_AXI_ON,
&pwr->power_flags)) {
@@ -843,7 +913,7 @@
}
}
-void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, int state)
+static void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, int state)
{
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
@@ -946,6 +1016,7 @@
pwr->active_pwrlevel = pdata->init_level;
pwr->default_pwrlevel = pdata->init_level;
+ pwr->init_pwrlevel = pdata->init_level;
for (i = 0; i < pdata->num_levels; i++) {
pwr->pwrlevels[i].gpu_freq =
(pdata->pwrlevel[i].gpu_freq > 0) ?
@@ -975,7 +1046,6 @@
pwr->power_flags = 0;
- pwr->nap_allowed = pdata->nap_allowed;
pwr->idle_needed = pdata->idle_needed;
pwr->interval_timeout = pdata->idle_timeout;
pwr->strtstp_sleepwake = pdata->strtstp_sleepwake;
@@ -1006,7 +1076,6 @@
pwr->pm_qos_latency = 501;
pm_runtime_enable(device->parentdev);
- register_early_suspend(&device->display_off);
return result;
clk_err:
@@ -1026,7 +1095,6 @@
KGSL_PWR_INFO(device, "close device %d\n", device->id);
pm_runtime_disable(device->parentdev);
- unregister_early_suspend(&device->display_off);
clk_put(pwr->ebi1_clk);
@@ -1065,6 +1133,8 @@
*/
void kgsl_idle_check(struct work_struct *work)
{
+ int delay = INIT_UDELAY;
+ int requested_state;
struct kgsl_device *device = container_of(work, struct kgsl_device,
idle_check_ws);
WARN_ON(device == NULL);
@@ -1077,10 +1147,32 @@
if (device->state == KGSL_STATE_ACTIVE
|| device->state == KGSL_STATE_NAP) {
- if (device->active_cnt > 0 || kgsl_pwrctrl_sleep(device) != 0) {
+ /*
+ * If no user is explicitly trying to use the GPU
+ * (active_cnt is zero), then loop with increasing delay,
+ * waiting for the GPU to become idle.
+ */
+ while (!device->active_cnt && delay < MAX_UDELAY) {
+ requested_state = device->requested_state;
+ if (!kgsl_pwrctrl_sleep(device))
+ break;
+ /*
+ * If no new commands have been issued since the
+ * last interrupt, stay in this loop waiting for
+ * the GPU to become idle.
+ */
+ if (!device->pwrctrl.irq_last)
+ break;
+ kgsl_pwrctrl_request_state(device, requested_state);
+ mutex_unlock(&device->mutex);
+ udelay(delay);
+ delay *= 2;
+ mutex_lock(&device->mutex);
+ }
- kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+ if (device->state == KGSL_STATE_ACTIVE) {
mod_timer(&device->idle_timer,
jiffies +
device->pwrctrl.interval_timeout);
@@ -1094,6 +1186,8 @@
kgsl_pwrctrl_busy_time(device, true);
device->pwrctrl.clk_stats.no_nap_cnt = 0;
}
+ } else {
+ device->pwrctrl.irq_last = 0;
}
} else if (device->state & (KGSL_STATE_HUNG |
KGSL_STATE_DUMP_AND_FT)) {
@@ -1110,8 +1204,7 @@
KGSL_PWR_INFO(device, "idle timer expired device %d\n", device->id);
if (device->requested_state != KGSL_STATE_SUSPEND) {
- if (device->pwrctrl.restore_slumber ||
- device->pwrctrl.strtstp_sleepwake)
+ if (device->pwrctrl.strtstp_sleepwake)
kgsl_pwrctrl_request_state(device, KGSL_STATE_SLUMBER);
else
kgsl_pwrctrl_request_state(device, KGSL_STATE_SLEEP);
@@ -1149,6 +1242,7 @@
kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
return -EBUSY;
}
+ del_timer_sync(&device->hang_timer);
kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF, KGSL_STATE_NAP);
kgsl_pwrctrl_set_state(device, KGSL_STATE_NAP);
@@ -1218,6 +1312,7 @@
case KGSL_STATE_NAP:
case KGSL_STATE_SLEEP:
del_timer_sync(&device->idle_timer);
+ del_timer_sync(&device->hang_timer);
/* make sure power is on to stop the device*/
kgsl_pwrctrl_enable(device);
device->ftbl->suspend_context(device);
@@ -1310,6 +1405,8 @@
/* Re-enable HW access */
mod_timer(&device->idle_timer,
jiffies + device->pwrctrl.interval_timeout);
+ mod_timer(&device->hang_timer,
+ (jiffies + msecs_to_jiffies(KGSL_TIMEOUT_PART)));
pm_qos_update_request(&device->pwrctrl.pm_qos_req_dma,
device->pwrctrl.pm_qos_latency);
case KGSL_STATE_ACTIVE:
@@ -1328,8 +1425,10 @@
void kgsl_pwrctrl_enable(struct kgsl_device *device)
{
+ struct kgsl_pwrctrl *pwr = &device->pwrctrl;
/* Order pwrrail/clk sequence based upon platform */
kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_ON);
+ kgsl_pwrctrl_pwrlevel_change(device, pwr->default_pwrlevel);
kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON, KGSL_STATE_ACTIVE);
kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON);
}
@@ -1407,20 +1506,17 @@
BUG_ON(!mutex_is_locked(&device->mutex));
if (device->active_cnt == 0) {
- if (device->requested_state == KGSL_STATE_SUSPEND ||
- device->state == KGSL_STATE_SUSPEND) {
- mutex_unlock(&device->mutex);
- wait_for_completion(&device->hwaccess_gate);
- mutex_lock(&device->mutex);
- } else if (device->state == KGSL_STATE_DUMP_AND_FT) {
- mutex_unlock(&device->mutex);
- wait_for_completion(&device->ft_gate);
- mutex_lock(&device->mutex);
- }
+ mutex_unlock(&device->mutex);
+ wait_for_completion(&device->hwaccess_gate);
+ wait_for_completion(&device->ft_gate);
+ mutex_lock(&device->mutex);
+
ret = kgsl_pwrctrl_wake(device);
}
if (ret == 0)
device->active_cnt++;
+ trace_kgsl_active_count(device,
+ (unsigned long) __builtin_return_address(0));
return ret;
}
EXPORT_SYMBOL(kgsl_active_count_get);
@@ -1449,6 +1545,8 @@
}
device->active_cnt++;
+ trace_kgsl_active_count(device,
+ (unsigned long) __builtin_return_address(0));
return 0;
}
EXPORT_SYMBOL(kgsl_active_count_get_light);
@@ -1471,22 +1569,25 @@
kgsl_pwrscale_idle(device);
if (device->active_cnt > 1) {
device->active_cnt--;
+ trace_kgsl_active_count(device,
+ (unsigned long) __builtin_return_address(0));
return;
}
INIT_COMPLETION(device->suspend_gate);
- if (device->pwrctrl.nap_allowed == true &&
- (device->state == KGSL_STATE_ACTIVE &&
- device->requested_state == KGSL_STATE_NONE)) {
+ if (device->state == KGSL_STATE_ACTIVE &&
+ device->requested_state == KGSL_STATE_NONE) {
kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
- if (kgsl_pwrctrl_sleep(device) != 0)
- mod_timer(&device->idle_timer,
- jiffies
- + device->pwrctrl.interval_timeout);
+ if (kgsl_pwrctrl_sleep(device) && device->pwrctrl.irq_last) {
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
+ queue_work(device->work_queue, &device->idle_check_ws);
+ }
}
device->active_cnt--;
+ trace_kgsl_active_count(device,
+ (unsigned long) __builtin_return_address(0));
if (device->active_cnt == 0)
complete(&device->suspend_gate);
}
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index 0fd64c3..3bf65ee 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -23,7 +23,9 @@
#define KGSL_PWRLEVEL_NOMINAL 1
#define KGSL_PWRLEVEL_LAST_OFFSET 2
-#define KGSL_MAX_CLKS 5
+#define KGSL_PWR_ON 0xFFFF
+
+#define KGSL_MAX_CLKS 6
struct platform_device;
@@ -47,6 +49,8 @@
* @pwrlevels - List of supported power levels
* @active_pwrlevel - The currently active power level
* @thermal_pwrlevel - maximum powerlevel constraint from thermal
+ * @default_pwrlevel - device wake up power level
+ * @init_pwrlevel - device inital power level
* @max_pwrlevel - maximum allowable powerlevel per the user
* @min_pwrlevel - minimum allowable powerlevel per the user
* @num_pwrlevels - number of available power levels
@@ -55,10 +59,8 @@
* @gpu_reg - pointer to the regulator structure for gpu_reg
* @gpu_cx - pointer to the regulator structure for gpu_cx
* @pcl - bus scale identifier
- * @nap_allowed - true if the device supports naps
* @idle_needed - true if the device needs a idle before clock change
* @irq_name - resource name for the IRQ
- * @restore_slumber - Flag to indicate that we are in a suspend/restore sequence
* @clk_stats - structure of clock statistics
* @pm_qos_req_dma - the power management quality of service structure
* @pm_qos_latency - allowed CPU latency in microseconds
@@ -70,10 +72,12 @@
struct clk *ebi1_clk;
struct clk *grp_clks[KGSL_MAX_CLKS];
unsigned long power_flags;
+ unsigned long ctrl_flags;
struct kgsl_pwrlevel pwrlevels[KGSL_MAX_PWRLEVELS];
unsigned int active_pwrlevel;
int thermal_pwrlevel;
unsigned int default_pwrlevel;
+ unsigned int init_pwrlevel;
unsigned int max_pwrlevel;
unsigned int min_pwrlevel;
unsigned int num_pwrlevels;
@@ -82,15 +86,14 @@
struct regulator *gpu_reg;
struct regulator *gpu_cx;
uint32_t pcl;
- unsigned int nap_allowed;
unsigned int idle_needed;
const char *irq_name;
s64 time;
- unsigned int restore_slumber;
struct kgsl_clk_stats clk_stats;
struct pm_qos_request pm_qos_req_dma;
unsigned int pm_qos_latency;
unsigned int step_mul;
+ unsigned int irq_last;
};
void kgsl_pwrctrl_irq(struct kgsl_device *device, int state);
diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c
index afef62e..e5e23f0 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.c
+++ b/drivers/gpu/msm/kgsl_pwrscale.c
@@ -306,6 +306,8 @@
kgsl_pwrctrl_pwrlevel_change(device,
device->pwrctrl.max_pwrlevel);
+ device->pwrctrl.default_pwrlevel =
+ device->pwrctrl.max_pwrlevel;
}
device->pwrscale.policy = NULL;
}
@@ -338,6 +340,8 @@
device->pwrscale.policy = policy;
+ device->pwrctrl.default_pwrlevel =
+ device->pwrctrl.init_pwrlevel;
/* Pwrscale is enabled by default at attach time */
kgsl_pwrscale_enable(device);
diff --git a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c
index 9b2ac70..cfc409c 100644
--- a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c
+++ b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c
@@ -31,6 +31,7 @@
unsigned int no_switch_cnt;
unsigned int skip_cnt;
struct kgsl_power_stats bin;
+ unsigned int idle_dcvs;
};
spinlock_t tz_lock;
@@ -47,24 +48,32 @@
#define SKIP_COUNTER 500
#define TZ_RESET_ID 0x3
#define TZ_UPDATE_ID 0x4
+#define TZ_INIT_ID 0x6
-#ifdef CONFIG_MSM_SCM
/* Trap into the TrustZone, and call funcs there. */
-static int __secure_tz_entry(u32 cmd, u32 val, u32 id)
+static int __secure_tz_entry2(u32 cmd, u32 val1, u32 val2)
{
int ret;
spin_lock(&tz_lock);
+ /* sync memory before sending the commands to tz*/
__iowmb();
- ret = scm_call_atomic2(SCM_SVC_IO, cmd, val, id);
+ ret = scm_call_atomic2(SCM_SVC_IO, cmd, val1, val2);
spin_unlock(&tz_lock);
return ret;
}
-#else
-static int __secure_tz_entry(u32 cmd, u32 val, u32 id)
+
+static int __secure_tz_entry3(u32 cmd, u32 val1, u32 val2,
+ u32 val3)
{
- return 0;
+ int ret;
+ spin_lock(&tz_lock);
+ /* sync memory before sending the commands to tz*/
+ __iowmb();
+ ret = scm_call_atomic3(SCM_SVC_IO, cmd, val1, val2,
+ val3);
+ spin_unlock(&tz_lock);
+ return ret;
}
-#endif /* CONFIG_MSM_SCM */
static ssize_t tz_governor_show(struct kgsl_device *device,
struct kgsl_pwrscale *pwrscale,
@@ -101,8 +110,12 @@
else if (!strncmp(str, "performance", 11))
priv->governor = TZ_GOVERNOR_PERFORMANCE;
- if (priv->governor == TZ_GOVERNOR_PERFORMANCE)
+ if (priv->governor == TZ_GOVERNOR_PERFORMANCE) {
kgsl_pwrctrl_pwrlevel_change(device, pwr->max_pwrlevel);
+ pwr->default_pwrlevel = pwr->max_pwrlevel;
+ } else {
+ pwr->default_pwrlevel = pwr->init_pwrlevel;
+ }
mutex_unlock(&device->mutex);
return count;
@@ -172,11 +185,21 @@
*/
if (priv->bin.busy_time > CEILING) {
val = -1;
- } else {
+ } else if (priv->idle_dcvs) {
idle = priv->bin.total_time - priv->bin.busy_time;
idle = (idle > 0) ? idle : 0;
- val = __secure_tz_entry(TZ_UPDATE_ID, idle, device->id);
+ val = __secure_tz_entry2(TZ_UPDATE_ID, idle, device->id);
+ } else {
+ if (pwr->step_mul > 1)
+ val = __secure_tz_entry3(TZ_UPDATE_ID,
+ (pwr->active_pwrlevel + 1)/2,
+ priv->bin.total_time, priv->bin.busy_time);
+ else
+ val = __secure_tz_entry3(TZ_UPDATE_ID,
+ pwr->active_pwrlevel,
+ priv->bin.total_time, priv->bin.busy_time);
}
+
priv->bin.total_time = 0;
priv->bin.busy_time = 0;
@@ -201,7 +224,7 @@
{
struct tz_priv *priv = pwrscale->priv;
- __secure_tz_entry(TZ_RESET_ID, 0, device->id);
+ __secure_tz_entry2(TZ_RESET_ID, 0, 0);
priv->no_switch_cnt = 0;
priv->bin.total_time = 0;
priv->bin.busy_time = 0;
@@ -210,16 +233,32 @@
#ifdef CONFIG_MSM_SCM
static int tz_init(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale)
{
+ int i = 0, j = 1, ret = 0;
struct tz_priv *priv;
+ struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+ unsigned int tz_pwrlevels[KGSL_MAX_PWRLEVELS + 1];
priv = pwrscale->priv = kzalloc(sizeof(struct tz_priv), GFP_KERNEL);
if (pwrscale->priv == NULL)
return -ENOMEM;
-
+ priv->idle_dcvs = 0;
priv->governor = TZ_GOVERNOR_ONDEMAND;
spin_lock_init(&tz_lock);
kgsl_pwrscale_policy_add_files(device, pwrscale, &tz_attr_group);
-
+ for (i = 0; i < pwr->num_pwrlevels - 1; i++) {
+ if (i == 0)
+ tz_pwrlevels[j] = pwr->pwrlevels[i].gpu_freq;
+ else if (pwr->pwrlevels[i].gpu_freq !=
+ pwr->pwrlevels[i - 1].gpu_freq) {
+ j++;
+ tz_pwrlevels[j] = pwr->pwrlevels[i].gpu_freq;
+ }
+ }
+ tz_pwrlevels[0] = j;
+ ret = scm_call(SCM_SVC_DCVS, TZ_INIT_ID, tz_pwrlevels,
+ sizeof(tz_pwrlevels), NULL, 0);
+ if (ret)
+ priv->idle_dcvs = 1;
return 0;
}
#else
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index 62db513..01f0768 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -65,14 +65,6 @@
mem_entry_max_show), \
}
-
-/*
- * One page allocation for a guard region to protect against over-zealous
- * GPU pre-fetch
- */
-
-static struct page *kgsl_guard_page;
-
/**
* Given a kobj, find the process structure attached to it
*/
@@ -242,6 +234,29 @@
return len;
}
+static int kgsl_drv_full_cache_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ unsigned int thresh;
+ ret = sscanf(buf, "%d", &thresh);
+ if (ret != 1)
+ return count;
+
+ kgsl_driver.full_cache_threshold = thresh;
+
+ return count;
+}
+
+static int kgsl_drv_full_cache_threshold_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ kgsl_driver.full_cache_threshold);
+}
+
DEVICE_ATTR(vmalloc, 0444, kgsl_drv_memstat_show, NULL);
DEVICE_ATTR(vmalloc_max, 0444, kgsl_drv_memstat_show, NULL);
DEVICE_ATTR(page_alloc, 0444, kgsl_drv_memstat_show, NULL);
@@ -251,6 +266,9 @@
DEVICE_ATTR(mapped, 0444, kgsl_drv_memstat_show, NULL);
DEVICE_ATTR(mapped_max, 0444, kgsl_drv_memstat_show, NULL);
DEVICE_ATTR(histogram, 0444, kgsl_drv_histogram_show, NULL);
+DEVICE_ATTR(full_cache_threshold, 0644,
+ kgsl_drv_full_cache_threshold_show,
+ kgsl_drv_full_cache_threshold_store);
static const struct device_attribute *drv_attr_list[] = {
&dev_attr_vmalloc,
@@ -262,6 +280,7 @@
&dev_attr_mapped,
&dev_attr_mapped_max,
&dev_attr_histogram,
+ &dev_attr_full_cache_threshold,
NULL
};
@@ -364,10 +383,6 @@
struct scatterlist *sg;
int sglen = memdesc->sglen;
- /* Don't free the guard page if it was used */
- if (memdesc->priv & KGSL_MEMDESC_GUARD_PAGE)
- sglen--;
-
kgsl_driver.stats.page_alloc -= memdesc->size;
if (memdesc->hostptr) {
@@ -402,10 +417,6 @@
int sglen = memdesc->sglen;
int i, count = 0;
- /* Don't map the guard page if it exists */
- if (memdesc->priv & KGSL_MEMDESC_GUARD_PAGE)
- sglen--;
-
/* create a list of pages to call vmap */
pages = vmalloc(npages * sizeof(struct page *));
if (!pages) {
@@ -564,14 +575,6 @@
sglen_alloc = PAGE_ALIGN(size) >> PAGE_SHIFT;
- /*
- * Add guard page to the end of the allocation when the
- * IOMMU is in use.
- */
-
- if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU)
- sglen_alloc++;
-
memdesc->size = size;
memdesc->pagetable = pagetable;
memdesc->ops = &kgsl_page_alloc_ops;
@@ -622,7 +625,7 @@
gfp_mask |= __GFP_COMP | __GFP_NORETRY |
__GFP_NO_KSWAPD | __GFP_NOWARN;
else
- gfp_mask |= GFP_KERNEL | __GFP_NORETRY;
+ gfp_mask |= GFP_KERNEL;
page = alloc_pages(gfp_mask, get_order(page_size));
@@ -647,26 +650,6 @@
len -= page_size;
}
- /* Add the guard page to the end of the sglist */
-
- if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU) {
- /*
- * It doesn't matter if we use GFP_ZERO here, this never
- * gets mapped, and we only allocate it once in the life
- * of the system
- */
-
- if (kgsl_guard_page == NULL)
- kgsl_guard_page = alloc_page(GFP_KERNEL | __GFP_ZERO |
- __GFP_HIGHMEM);
-
- if (kgsl_guard_page != NULL) {
- sg_set_page(&memdesc->sg[sglen++], kgsl_guard_page,
- PAGE_SIZE, 0);
- memdesc->priv |= KGSL_MEMDESC_GUARD_PAGE;
- }
- }
-
memdesc->sglen = sglen;
/*
@@ -896,7 +879,8 @@
EXPORT_SYMBOL(kgsl_sharedmem_readl);
int
-kgsl_sharedmem_writel(const struct kgsl_memdesc *memdesc,
+kgsl_sharedmem_writel(struct kgsl_device *device,
+ const struct kgsl_memdesc *memdesc,
unsigned int offsetbytes,
uint32_t src)
{
@@ -909,7 +893,8 @@
WARN_ON(offsetbytes + sizeof(uint32_t) > memdesc->size);
if (offsetbytes + sizeof(uint32_t) > memdesc->size)
return -ERANGE;
- kgsl_cffdump_setmem(memdesc->gpuaddr + offsetbytes,
+ kgsl_cffdump_setmem(device,
+ memdesc->gpuaddr + offsetbytes,
src, sizeof(uint32_t));
dst = (uint32_t *)(memdesc->hostptr + offsetbytes);
*dst = src;
@@ -918,14 +903,16 @@
EXPORT_SYMBOL(kgsl_sharedmem_writel);
int
-kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc, unsigned int offsetbytes,
- unsigned int value, unsigned int sizebytes)
+kgsl_sharedmem_set(struct kgsl_device *device,
+ const struct kgsl_memdesc *memdesc, unsigned int offsetbytes,
+ unsigned int value, unsigned int sizebytes)
{
BUG_ON(memdesc == NULL || memdesc->hostptr == NULL);
BUG_ON(offsetbytes + sizebytes > memdesc->size);
- kgsl_cffdump_setmem(memdesc->gpuaddr + offsetbytes, value,
- sizebytes);
+ kgsl_cffdump_setmem(device,
+ memdesc->gpuaddr + offsetbytes, value,
+ sizebytes);
memset(memdesc->hostptr + offsetbytes, value, sizebytes);
return 0;
}
diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h
index 279490f..985b9b8 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.h
+++ b/drivers/gpu/msm/kgsl_sharedmem.h
@@ -55,11 +55,13 @@
uint32_t *dst,
unsigned int offsetbytes);
-int kgsl_sharedmem_writel(const struct kgsl_memdesc *memdesc,
+int kgsl_sharedmem_writel(struct kgsl_device *device,
+ const struct kgsl_memdesc *memdesc,
unsigned int offsetbytes,
uint32_t src);
-int kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc,
+int kgsl_sharedmem_set(struct kgsl_device *device,
+ const struct kgsl_memdesc *memdesc,
unsigned int offsetbytes, unsigned int value,
unsigned int sizebytes);
@@ -154,7 +156,7 @@
static inline int
memdesc_sg_phys(struct kgsl_memdesc *memdesc,
- unsigned int physaddr, unsigned int size)
+ phys_addr_t physaddr, unsigned int size)
{
memdesc->sg = kgsl_sg_alloc(1);
if (memdesc->sg == NULL)
@@ -295,15 +297,4 @@
return ret;
}
-static inline int kgsl_sg_size(struct scatterlist *sg, int sglen)
-{
- int i, size = 0;
- struct scatterlist *s;
-
- for_each_sg(sg, s, sglen, i) {
- size += s->length;
- }
-
- return size;
-}
#endif /* __KGSL_SHAREDMEM_H */
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index abcebfb..d3edbba 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -27,7 +27,7 @@
struct kgsl_snapshot_object {
unsigned int gpuaddr;
- unsigned int ptbase;
+ phys_addr_t ptbase;
unsigned int size;
unsigned int offset;
int type;
@@ -140,6 +140,7 @@
int hang = (int) priv;
int ctxtcount = 0;
int size = sizeof(*header);
+ phys_addr_t temp_ptbase;
/* Figure out how many active contexts there are - these will
* be appended on the end of the structure */
@@ -181,11 +182,14 @@
kgsl_sharedmem_readl(&device->memstore, &header->current_context,
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context));
+
/* Get the current PT base */
- header->ptbase = kgsl_mmu_get_current_ptbase(&device->mmu);
+ temp_ptbase = kgsl_mmu_get_current_ptbase(&device->mmu);
+ /* Truncate to 32 bits in case LPAE is used */
+ header->ptbase = (__u32)temp_ptbase;
/* And the PID for the task leader */
pid = header->pid = kgsl_mmu_get_ptname_from_ptbase(&device->mmu,
- header->ptbase);
+ temp_ptbase);
task = find_task_by_vpid(pid);
@@ -267,7 +271,7 @@
header.size = ALIGN(obj->size, 4) >> 2;
header.gpuaddr = obj->gpuaddr;
- header.ptbase = obj->ptbase;
+ header.ptbase = (__u32)obj->ptbase;
header.type = obj->type;
ret = obj_itr_out(itr, &header, sizeof(header));
@@ -309,7 +313,7 @@
* Return 1 if the object is already in the list - this can save us from
* having to parse the sme thing over again.
*/
-int kgsl_snapshot_have_object(struct kgsl_device *device, unsigned int ptbase,
+int kgsl_snapshot_have_object(struct kgsl_device *device, phys_addr_t ptbase,
unsigned int gpuaddr, unsigned int size)
{
struct kgsl_snapshot_object *obj;
@@ -339,12 +343,16 @@
* size of the object being frozen
*/
-int kgsl_snapshot_get_object(struct kgsl_device *device, unsigned int ptbase,
+int kgsl_snapshot_get_object(struct kgsl_device *device, phys_addr_t ptbase,
unsigned int gpuaddr, unsigned int size, unsigned int type)
{
struct kgsl_mem_entry *entry;
struct kgsl_snapshot_object *obj;
int offset;
+ int ret = -EINVAL;
+
+ if (!gpuaddr)
+ return 0;
entry = kgsl_get_mem_entry(device, ptbase, gpuaddr, size);
@@ -358,7 +366,7 @@
if (entry->memtype != KGSL_MEM_ENTRY_KERNEL) {
KGSL_DRV_ERR(device,
"Only internal GPU buffers can be frozen\n");
- return -EINVAL;
+ goto err_put;
}
/*
@@ -381,7 +389,7 @@
if (size + offset > entry->memdesc.size) {
KGSL_DRV_ERR(device, "Invalid size for GPU buffer %8.8X\n",
gpuaddr);
- return -EINVAL;
+ goto err_put;
}
/* If the buffer is already on the list, skip it */
@@ -390,27 +398,24 @@
/* If the size is different, use the bigger size */
if (obj->size < size)
obj->size = size;
-
- return 0;
+ ret = 0;
+ goto err_put;
}
}
if (kgsl_memdesc_map(&entry->memdesc) == NULL) {
KGSL_DRV_ERR(device, "Unable to map GPU buffer %X\n",
gpuaddr);
- return -EINVAL;
+ goto err_put;
}
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
if (obj == NULL) {
KGSL_DRV_ERR(device, "Unable to allocate memory\n");
- return -EINVAL;
+ goto err_put;
}
- /* Ref count the mem entry */
- kgsl_mem_entry_get(entry);
-
obj->type = type;
obj->entry = entry;
obj->gpuaddr = gpuaddr;
@@ -428,12 +433,15 @@
* 0 so it doesn't get counted twice
*/
- if (entry->memdesc.priv & KGSL_MEMDESC_FROZEN)
- return 0;
+ ret = (entry->memdesc.priv & KGSL_MEMDESC_FROZEN) ? 0
+ : entry->memdesc.size;
entry->memdesc.priv |= KGSL_MEMDESC_FROZEN;
- return entry->memdesc.size;
+ return ret;
+err_put:
+ kgsl_mem_entry_put(entry);
+ return ret;
}
EXPORT_SYMBOL(kgsl_snapshot_get_object);
@@ -532,6 +540,10 @@
void *snapshot;
struct timespec boot;
+ /* increment the hang count (on hang) for good book keeping */
+ if (hang)
+ device->snapshot_faultcount++;
+
/*
* The first hang is always the one we are interested in. To
* avoid a subsequent hang blowing away the first, the snapshot
@@ -670,6 +682,22 @@
return itr.write;
}
+/* Show the total number of hangs since device boot */
+static ssize_t faultcount_show(struct kgsl_device *device, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", device->snapshot_faultcount);
+}
+
+/* Reset the total number of hangs since device boot */
+static ssize_t faultcount_store(struct kgsl_device *device, const char *buf,
+ size_t count)
+{
+ if (device && count > 0)
+ device->snapshot_faultcount = 0;
+
+ return count;
+}
+
/* Show the timestamp of the last collected snapshot */
static ssize_t timestamp_show(struct kgsl_device *device, char *buf)
{
@@ -705,6 +733,7 @@
SNAPSHOT_ATTR(trigger, 0600, NULL, trigger_store);
SNAPSHOT_ATTR(timestamp, 0444, timestamp_show, NULL);
+SNAPSHOT_ATTR(faultcount, 0644, faultcount_show, faultcount_store);
static void snapshot_sysfs_release(struct kobject *kobj)
{
@@ -770,6 +799,7 @@
device->snapshot_maxsize = KGSL_SNAPSHOT_MEMSIZE;
device->snapshot_timestamp = 0;
+ device->snapshot_faultcount = 0;
INIT_LIST_HEAD(&device->snapshot_obj_list);
@@ -787,6 +817,10 @@
goto done;
ret = sysfs_create_file(&device->snapshot_kobj, &attr_timestamp.attr);
+ if (ret)
+ goto done;
+
+ ret = sysfs_create_file(&device->snapshot_kobj, &attr_faultcount.attr);
done:
return ret;
@@ -813,5 +847,6 @@
device->snapshot = NULL;
device->snapshot_maxsize = 0;
device->snapshot_timestamp = 0;
+ device->snapshot_faultcount = 0;
}
EXPORT_SYMBOL(kgsl_device_snapshot_close);
diff --git a/drivers/gpu/msm/kgsl_snapshot.h b/drivers/gpu/msm/kgsl_snapshot.h
index 4db2815..61a3b22 100644
--- a/drivers/gpu/msm/kgsl_snapshot.h
+++ b/drivers/gpu/msm/kgsl_snapshot.h
@@ -320,10 +320,10 @@
unsigned int data, unsigned int start, unsigned int count);
/* Freeze a GPU buffer so it can be dumped in the snapshot */
-int kgsl_snapshot_get_object(struct kgsl_device *device, unsigned int ptbase,
+int kgsl_snapshot_get_object(struct kgsl_device *device, phys_addr_t ptbase,
unsigned int gpuaddr, unsigned int size, unsigned int type);
-int kgsl_snapshot_have_object(struct kgsl_device *device, unsigned int ptbase,
+int kgsl_snapshot_have_object(struct kgsl_device *device, phys_addr_t ptbase,
unsigned int gpuaddr, unsigned int size);
#endif
diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c
index 813305a..2f67405 100644
--- a/drivers/gpu/msm/kgsl_sync.c
+++ b/drivers/gpu/msm/kgsl_sync.c
@@ -12,9 +12,12 @@
*/
#include <linux/file.h>
+#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
+#include <asm/current.h>
+
#include "kgsl_sync.h"
struct sync_pt *kgsl_sync_pt_create(struct sync_timeline *timeline,
@@ -168,7 +171,6 @@
fail_event:
fail_copy_fd:
/* clean up sync_fence_install */
- sync_fence_put(fence);
put_unused_fd(priv.fence_fd);
fail_fd:
/* clean up sync_fence_create */
@@ -180,24 +182,67 @@
return ret;
}
+static unsigned int kgsl_sync_get_timestamp(
+ struct kgsl_sync_timeline *ktimeline, enum kgsl_timestamp_type type)
+{
+ struct kgsl_context *context = idr_find(&ktimeline->device->context_idr,
+ ktimeline->context_id);
+ if (context == NULL)
+ return 0;
+
+ return kgsl_readtimestamp(ktimeline->device, context, type);
+}
+
+static void kgsl_sync_timeline_value_str(struct sync_timeline *sync_timeline,
+ char *str, int size)
+{
+ struct kgsl_sync_timeline *ktimeline =
+ (struct kgsl_sync_timeline *) sync_timeline;
+ unsigned int timestamp_retired = kgsl_sync_get_timestamp(ktimeline,
+ KGSL_TIMESTAMP_RETIRED);
+ snprintf(str, size, "%u retired:%u", ktimeline->last_timestamp,
+ timestamp_retired);
+}
+
+static void kgsl_sync_pt_value_str(struct sync_pt *sync_pt,
+ char *str, int size)
+{
+ struct kgsl_sync_pt *kpt = (struct kgsl_sync_pt *) sync_pt;
+ snprintf(str, size, "%u", kpt->timestamp);
+}
+
static const struct sync_timeline_ops kgsl_sync_timeline_ops = {
.driver_name = "kgsl-timeline",
.dup = kgsl_sync_pt_dup,
.has_signaled = kgsl_sync_pt_has_signaled,
.compare = kgsl_sync_pt_compare,
+ .timeline_value_str = kgsl_sync_timeline_value_str,
+ .pt_value_str = kgsl_sync_pt_value_str,
};
int kgsl_sync_timeline_create(struct kgsl_context *context)
{
struct kgsl_sync_timeline *ktimeline;
+ /* Generate a name which includes the thread name, thread id, process
+ * name, process id, and context id. This makes it possible to
+ * identify the context of a timeline in the sync dump. */
+ char ktimeline_name[sizeof(context->timeline->name)] = {};
+ snprintf(ktimeline_name, sizeof(ktimeline_name),
+ "%s_%.15s(%d)-%.15s(%d)-%d",
+ context->dev_priv->device->name,
+ current->group_leader->comm, current->group_leader->pid,
+ current->comm, current->pid, context->id);
+
context->timeline = sync_timeline_create(&kgsl_sync_timeline_ops,
- (int) sizeof(struct kgsl_sync_timeline), "kgsl-timeline");
+ (int) sizeof(struct kgsl_sync_timeline), ktimeline_name);
if (context->timeline == NULL)
return -EINVAL;
ktimeline = (struct kgsl_sync_timeline *) context->timeline;
ktimeline->last_timestamp = 0;
+ ktimeline->device = context->dev_priv->device;
+ ktimeline->context_id = context->id;
return 0;
}
diff --git a/drivers/gpu/msm/kgsl_sync.h b/drivers/gpu/msm/kgsl_sync.h
index 06b3ad0..63adf06 100644
--- a/drivers/gpu/msm/kgsl_sync.h
+++ b/drivers/gpu/msm/kgsl_sync.h
@@ -19,6 +19,8 @@
struct kgsl_sync_timeline {
struct sync_timeline timeline;
unsigned int last_timestamp;
+ struct kgsl_device *device;
+ u32 context_id;
};
struct kgsl_sync_pt {
diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h
index 5f7ee3c..6917883 100644
--- a/drivers/gpu/msm/kgsl_trace.h
+++ b/drivers/gpu/msm/kgsl_trace.h
@@ -539,6 +539,33 @@
)
);
+TRACE_EVENT(kgsl_mem_sync_full_cache,
+
+ TP_PROTO(unsigned int num_bufs, unsigned int bulk_size,
+ unsigned int op),
+
+ TP_ARGS(num_bufs, bulk_size, op),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, num_bufs)
+ __field(unsigned int, bulk_size)
+ __field(unsigned int, op)
+ ),
+
+ TP_fast_assign(
+ __entry->num_bufs = num_bufs;
+ __entry->bulk_size = bulk_size;
+ __entry->op = op;
+ ),
+
+ TP_printk(
+ "num_bufs=%d bulk_size=%d op=%c%c",
+ __entry->num_bufs, __entry->bulk_size,
+ (__entry->op & KGSL_GPUMEM_CACHE_CLEAN) ? 'c' : '.',
+ (__entry->op & KGSL_GPUMEM_CACHE_INV) ? 'i' : '.'
+ )
+);
+
DECLARE_EVENT_CLASS(kgsl_mem_timestamp_template,
TP_PROTO(struct kgsl_device *device, struct kgsl_mem_entry *mem_entry,
@@ -739,6 +766,30 @@
__entry->id, __entry->ts, __entry->age)
);
+TRACE_EVENT(kgsl_active_count,
+
+ TP_PROTO(struct kgsl_device *device, unsigned long ip),
+
+ TP_ARGS(device, ip),
+
+ TP_STRUCT__entry(
+ __string(device_name, device->name)
+ __field(unsigned int, count)
+ __field(unsigned long, ip)
+ ),
+
+ TP_fast_assign(
+ __assign_str(device_name, device->name);
+ __entry->count = device->active_cnt;
+ __entry->ip = ip;
+ ),
+
+ TP_printk(
+ "d_name=%s active_cnt=%x func=%pf",
+ __get_str(device_name), __entry->count, (void *) __entry->ip
+ )
+);
+
#endif /* _KGSL_TRACE_H */
/* This part must be outside protection */
diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c
index 49265fc..78e2859 100644
--- a/drivers/gpu/msm/z180.c
+++ b/drivers/gpu/msm/z180.c
@@ -216,8 +216,7 @@
}
}
- if ((device->pwrctrl.nap_allowed == true) &&
- (device->requested_state == KGSL_STATE_NONE)) {
+ if (device->requested_state == KGSL_STATE_NONE) {
kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
queue_work(device->work_queue, &device->idle_check_ws);
}
@@ -467,10 +466,10 @@
addmarker(&z180_dev->ringbuffer, z180_dev->current_timestamp);
/* monkey patch the IB so that it jumps back to the ringbuffer */
- kgsl_sharedmem_writel(&entry->memdesc,
+ kgsl_sharedmem_writel(device, &entry->memdesc,
((sizedwords + 1) * sizeof(unsigned int)),
rb_gpuaddr(z180_dev, z180_dev->current_timestamp));
- kgsl_sharedmem_writel(&entry->memdesc,
+ kgsl_sharedmem_writel(device, &entry->memdesc,
((sizedwords + 2) * sizeof(unsigned int)),
nextcnt);
@@ -710,7 +709,7 @@
BUG_ON(offsetwords*sizeof(uint32_t) >= device->reg_len);
reg = (unsigned int *)(device->reg_virt + (offsetwords << 2));
- kgsl_cffdump_regwrite(device->id, offsetwords << 2, value);
+ kgsl_cffdump_regwrite(device, offsetwords << 2, value);
/*ensure previous writes post before this one,
* i.e. act like normal writel() */
wmb();
diff --git a/drivers/gpu/msm/z180_postmortem.c b/drivers/gpu/msm/z180_postmortem.c
index c1e5f07..5d929cf 100644
--- a/drivers/gpu/msm/z180_postmortem.c
+++ b/drivers/gpu/msm/z180_postmortem.c
@@ -118,7 +118,7 @@
int rb_slot_num = -1;
struct z180_device *z180_dev = Z180_DEVICE(device);
struct kgsl_mem_entry *entry = NULL;
- unsigned int pt_base;
+ phys_addr_t pt_base;
unsigned int i;
unsigned int j;
char linebuf[CHARS_PER_LINE];
@@ -168,6 +168,7 @@
KGSL_LOG_DUMP(device,
"Could not map IB to kernel memory, Ringbuffer Slot: %d\n",
rb_slot_num);
+ kgsl_mem_entry_put(entry);
continue;
}
@@ -190,6 +191,7 @@
linebuf);
}
KGSL_LOG_DUMP(device, "IB Dump Finished\n");
+ kgsl_mem_entry_put(entry);
}
}
}
diff --git a/drivers/gud/Makefile b/drivers/gud/Makefile
index 3a16bb7..ef0e083 100644
--- a/drivers/gud/Makefile
+++ b/drivers/gud/Makefile
@@ -10,7 +10,8 @@
mobicore_driver/ops.o \
mobicore_driver/mem.o \
mobicore_driver/api.o \
- mobicore_driver/main.o
+ mobicore_driver/main.o \
+ mobicore_driver/pm.o
mckernelapi-objs := mobicore_kernelapi/main.o \
mobicore_kernelapi/clientlib.o \
diff --git a/drivers/gud/mobicore_driver/build_tag.h b/drivers/gud/mobicore_driver/build_tag.h
index 4abd003..2a7772e 100644
--- a/drivers/gud/mobicore_driver/build_tag.h
+++ b/drivers/gud/mobicore_driver/build_tag.h
@@ -26,4 +26,4 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define MOBICORE_COMPONENT_BUILD_TAG \
- "*** GC_MSM8960_Release_V016 ###"
+ "*** GC_MSM8960_Release_V019 ###"
diff --git a/drivers/gud/mobicore_driver/main.c b/drivers/gud/mobicore_driver/main.c
index a2b3ad7..6f91974 100644
--- a/drivers/gud/mobicore_driver/main.c
+++ b/drivers/gud/mobicore_driver/main.c
@@ -29,6 +29,7 @@
#include <linux/mman.h>
#include <linux/completion.h>
#include <linux/fdtable.h>
+#include <linux/cdev.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include <net/tcp_states.h>
@@ -50,12 +51,20 @@
};
struct device mcd_debug_subname = {
- .init_name = "", /* Set to 'mcd' at mc_init() time */
.driver = &mcd_debug_name
};
struct device *mcd = &mcd_debug_subname;
+/* We need 2 devices for admin and user interface*/
+#define MC_DEV_MAX 2
+
+/* Need to discover a chrdev region for the driver */
+static dev_t mc_dev_admin, mc_dev_user;
+struct cdev mc_admin_cdev, mc_user_cdev;
+/* Device class for the driver assigned major */
+static struct class *mc_device_class;
+
#ifndef FMODE_PATH
#define FMODE_PATH 0x0
#endif
@@ -1205,13 +1214,6 @@
.read = mc_fd_read,
};
-static struct miscdevice mc_admin_device = {
- .name = MC_ADMIN_DEVNODE,
- .mode = (S_IRWXU),
- .minor = 253,
- .fops = &mc_admin_fops,
-};
-
/* function table structure of this device driver. */
static const struct file_operations mc_user_fops = {
.owner = THIS_MODULE,
@@ -1221,30 +1223,80 @@
.mmap = mc_fd_mmap,
};
-static struct miscdevice mc_user_device = {
- .name = MC_USER_DEVNODE,
- .mode = (S_IRWXU | S_IRWXG | S_IRWXO),
- .minor = 254,
- .fops = &mc_user_fops,
-};
+static int create_devices(void)
+{
+ int ret = 0;
+
+ cdev_init(&mc_admin_cdev, &mc_admin_fops);
+ cdev_init(&mc_user_cdev, &mc_user_fops);
+
+ mc_device_class = class_create(THIS_MODULE, "mobicore");
+ if (IS_ERR(mc_device_class)) {
+ MCDRV_DBG_ERROR(mcd, "failed to create device class");
+ ret = PTR_ERR(mc_device_class);
+ goto out;
+ }
+
+ ret = alloc_chrdev_region(&mc_dev_admin, 0, MC_DEV_MAX, "mobicore");
+ if (ret < 0) {
+ MCDRV_DBG_ERROR(mcd, "failed to allocate char dev region\n");
+ goto error;
+ }
+ mc_dev_user = MKDEV(MAJOR(mc_dev_admin), 1);
+
+ MCDRV_DBG_VERBOSE(mcd, "%s: dev %d", "mobicore", MAJOR(mc_dev_region));
+
+ /* First the ADMIN node */
+ ret = cdev_add(&mc_admin_cdev, mc_dev_admin, 1);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR(mcd, "admin device register failed\n");
+ goto error;
+ }
+ mc_admin_cdev.owner = THIS_MODULE;
+ device_create(mc_device_class, NULL, mc_dev_admin, NULL,
+ MC_ADMIN_DEVNODE);
+
+ /* Then the user node */
+
+ ret = cdev_add(&mc_user_cdev, mc_dev_user, 1);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR(mcd, "user device register failed\n");
+ goto error_unregister;
+ }
+ mc_user_cdev.owner = THIS_MODULE;
+ device_create(mc_device_class, NULL, mc_dev_user, NULL,
+ MC_USER_DEVNODE);
+
+ goto out;
+error_unregister:
+ device_destroy(mc_device_class, mc_dev_admin);
+ device_destroy(mc_device_class, mc_dev_user);
+
+ cdev_del(&mc_admin_cdev);
+ cdev_del(&mc_user_cdev);
+ unregister_chrdev_region(mc_dev_admin, MC_DEV_MAX);
+error:
+ class_destroy(mc_device_class);
+out:
+ return ret;
+}
/*
* This function is called the kernel during startup or by a insmod command.
- * This device is installed and registered as miscdevice, then interrupt and
+ * This device is installed and registered as cdev, then interrupt and
* queue handling is set up
*/
static int __init mobicore_init(void)
{
int ret = 0;
-
dev_set_name(mcd, "mcd");
- MCDRV_DBG(mcd, "enter (Build " __TIMESTAMP__ ")\n");
- MCDRV_DBG(mcd, "mcDrvModuleApi version is %i.%i\n",
- MCDRVMODULEAPI_VERSION_MAJOR,
- MCDRVMODULEAPI_VERSION_MINOR);
+ dev_info(mcd, "MobiCore Driver, Build: " __TIMESTAMP__ "\n");
+ dev_info(mcd, "MobiCore mcDrvModuleApi version is %i.%i\n",
+ MCDRVMODULEAPI_VERSION_MAJOR,
+ MCDRVMODULEAPI_VERSION_MINOR);
#ifdef MOBICORE_COMPONENT_BUILD_TAG
- MCDRV_DBG(mcd, "%s\n", MOBICORE_COMPONENT_BUILD_TAG);
+ dev_info(mcd, "MobiCore %s\n", MOBICORE_COMPONENT_BUILD_TAG);
#endif
/* Hardware does not support ARM TrustZone -> Cannot continue! */
if (!has_security_extensions()) {
@@ -1264,6 +1316,10 @@
goto error;
init_completion(&ctx.isr_comp);
+
+ /* initialize event counter for signaling of an IRQ to zero */
+ atomic_set(&ctx.isr_counter, 0);
+
/* set up S-SIQ interrupt handler */
ret = request_irq(MC_INTR_SSIQ, mc_ssiq_isr, IRQF_TRIGGER_RISING,
MC_ADMIN_DEVNODE, &ctx);
@@ -1280,23 +1336,16 @@
}
#endif
- ret = misc_register(&mc_admin_device);
- if (ret != 0) {
- MCDRV_DBG_ERROR(mcd, "admin device register failed\n");
- goto free_isr;
- }
-
- ret = misc_register(&mc_user_device);
- if (ret != 0) {
- MCDRV_DBG_ERROR(mcd, "user device register failed\n");
- goto free_admin;
- }
-
- /* initialize event counter for signaling of an IRQ to zero */
- atomic_set(&ctx.isr_counter, 0);
+ ret = create_devices();
+ if (ret != 0)
+ goto free_pm;
ret = mc_init_l2_tables();
+#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
+ ret = mc_pm_clock_initialize();
+#endif
+
/*
* initialize unique number counter which we can use for
* handles. It is limited to 2^32, but this should be
@@ -1315,10 +1364,12 @@
MCDRV_DBG(mcd, "initialized\n");
return 0;
-free_admin:
- misc_deregister(&mc_admin_device);
+free_pm:
+#ifdef MC_PM_RUNTIME
+ mc_pm_free();
free_isr:
free_irq(MC_INTR_SSIQ, &ctx);
+#endif
err_req_irq:
mc_fastcall_destroy();
error:
@@ -1341,13 +1392,19 @@
mc_pm_free();
#endif
+ device_destroy(mc_device_class, mc_dev_admin);
+ device_destroy(mc_device_class, mc_dev_user);
+ class_destroy(mc_device_class);
+ unregister_chrdev_region(mc_dev_admin, MC_DEV_MAX);
+
free_irq(MC_INTR_SSIQ, &ctx);
- misc_deregister(&mc_admin_device);
- misc_deregister(&mc_user_device);
-
mc_fastcall_destroy();
+#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
+ mc_pm_clock_finalize();
+#endif
+
MCDRV_DBG_VERBOSE(mcd, "exit");
}
@@ -1358,4 +1415,3 @@
MODULE_AUTHOR("Trustonic Limited");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MobiCore driver");
-
diff --git a/drivers/gud/mobicore_driver/ops.c b/drivers/gud/mobicore_driver/ops.c
index 05c80b7..9d4af72 100644
--- a/drivers/gud/mobicore_driver/ops.c
+++ b/drivers/gud/mobicore_driver/ops.c
@@ -60,7 +60,16 @@
{
struct fastcall_work *fc_work =
container_of(work, struct fastcall_work, work);
+
+#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
+ mc_pm_clock_enable();
+#endif
+
smc(fc_work->data);
+
+#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
+ mc_pm_clock_disable();
+#endif
}
void mc_fastcall(void *data)
@@ -114,7 +123,16 @@
{
struct fastcall_work_struct *fc_work =
container_of(work, struct fastcall_work_struct, work);
+
+#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
+ mc_pm_clock_enable();
+#endif
+
smc(fc_work->data);
+
+#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
+ mc_pm_clock_disable();
+#endif
}
void mc_fastcall(void *data)
diff --git a/drivers/gud/mobicore_driver/platforms/msm8960_surf_std/platform.h b/drivers/gud/mobicore_driver/platforms/msm8960_surf_std/platform.h
index 7febcb6..4768f39 100644
--- a/drivers/gud/mobicore_driver/platforms/msm8960_surf_std/platform.h
+++ b/drivers/gud/mobicore_driver/platforms/msm8960_surf_std/platform.h
@@ -43,4 +43,7 @@
*/
#define MC_VM_UNMAP
+/* Enable Power Management for Crypto Engine */
+#define MC_CRYPTO_CLOCK_MANAGEMENT
+
#endif /* _MC_PLATFORM_H_ */
diff --git a/drivers/gud/mobicore_driver/pm.c b/drivers/gud/mobicore_driver/pm.c
new file mode 100644
index 0000000..3ad2015
--- /dev/null
+++ b/drivers/gud/mobicore_driver/pm.c
@@ -0,0 +1,295 @@
+/*
+ * MobiCore Driver Kernel Module.
+ * This module is written as a Linux device driver.
+ * This driver represents the command proxy on the lowest layer, from the
+ * secure world to the non secure world, and vice versa.
+ * This driver is located in the non secure world (Linux).
+ * This driver offers IOCTL commands, for access to the secure world, and has
+ * the interface from the secure world to the normal world.
+ * The access to the driver is possible with a file descriptor,
+ * which has to be created by the fd = open(/dev/mobicore) command.
+ *
+ * <-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ * <-- Copyright Trustonic Limited 2013 -->
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/suspend.h>
+#include <linux/device.h>
+
+#include "main.h"
+#include "pm.h"
+#include "fastcall.h"
+#include "ops.h"
+#include "logging.h"
+#include "debug.h"
+
+#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
+ #include <linux/clk.h>
+ #include <linux/err.h>
+
+ struct clk *mc_ce_iface_clk = NULL;
+ struct clk *mc_ce_core_clk = NULL;
+ struct clk *mc_ce_bus_clk = NULL;
+#endif /* MC_CRYPTO_CLOCK_MANAGEMENT */
+
+#ifdef MC_PM_RUNTIME
+
+static struct mc_context *ctx;
+
+static bool sleep_ready(void)
+{
+ if (!ctx->mcp)
+ return false;
+
+ if (!ctx->mcp->flags.sleep_mode.ReadyToSleep & READY_TO_SLEEP)
+ return false;
+
+ return true;
+}
+
+static void mc_suspend_handler(struct work_struct *work)
+{
+ if (!ctx->mcp)
+ return;
+
+ ctx->mcp->flags.sleep_mode.SleepReq = REQ_TO_SLEEP;
+ _nsiq();
+}
+DECLARE_WORK(suspend_work, mc_suspend_handler);
+
+static inline void dump_sleep_params(struct mc_flags *flags)
+{
+ MCDRV_DBG(mcd, "MobiCore IDLE=%d!", flags->schedule);
+ MCDRV_DBG(mcd,
+ "MobiCore Request Sleep=%d!", flags->sleep_mode.SleepReq);
+ MCDRV_DBG(mcd, "MobiCore Sleep Ready=%d!",
+ flags->sleep_mode.ReadyToSleep);
+}
+
+static int mc_suspend_notifier(struct notifier_block *nb,
+ unsigned long event, void *dummy)
+{
+ struct mc_mcp_buffer *mcp = ctx->mcp;
+ /* We have noting to say if MobiCore is not initialized */
+ if (!mcp)
+ return 0;
+
+#ifdef MC_MEM_TRACES
+ mobicore_log_read();
+#endif
+
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ /*
+ * Make sure we have finished all the work otherwise
+ * we end up in a race condition
+ */
+ cancel_work_sync(&suspend_work);
+ /*
+ * We can't go to sleep if MobiCore is not IDLE
+ * or not Ready to sleep
+ */
+ dump_sleep_params(&mcp->flags);
+ if (!sleep_ready()) {
+ ctx->mcp->flags.sleep_mode.SleepReq = REQ_TO_SLEEP;
+ schedule_work_on(0, &suspend_work);
+ flush_work(&suspend_work);
+ if (!sleep_ready()) {
+ dump_sleep_params(&mcp->flags);
+ ctx->mcp->flags.sleep_mode.SleepReq = 0;
+ MCDRV_DBG_ERROR(mcd, "MobiCore can't SLEEP!");
+ return NOTIFY_BAD;
+ }
+ }
+ break;
+ case PM_POST_SUSPEND:
+ MCDRV_DBG(mcd, "Resume MobiCore system!");
+ ctx->mcp->flags.sleep_mode.SleepReq = 0;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block mc_notif_block = {
+ .notifier_call = mc_suspend_notifier,
+};
+
+#ifdef MC_BL_NOTIFIER
+
+static int bL_switcher_notifier_handler(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ unsigned int mpidr, cpu, cluster;
+ struct mc_mcp_buffer *mcp = ctx->mcp;
+
+ if (!mcp)
+ return 0;
+
+ asm volatile ("mrc\tp15, 0, %0, c0, c0, 5" : "=r" (mpidr));
+ cpu = mpidr & 0x3;
+ cluster = (mpidr >> 8) & 0xf;
+ MCDRV_DBG(mcd, "%s switching!!, cpu: %u, Out=%u\n",
+ (event == SWITCH_ENTER ? "Before" : "After"), cpu, cluster);
+
+ if (cpu != 0)
+ return 0;
+
+ switch (event) {
+ case SWITCH_ENTER:
+ if (!sleep_ready()) {
+ ctx->mcp->flags.sleep_mode.SleepReq = REQ_TO_SLEEP;
+ _nsiq();
+ /* By this time we should be ready for sleep or we are
+ * in the middle of something important */
+ if (!sleep_ready()) {
+ dump_sleep_params(&mcp->flags);
+ MCDRV_DBG(mcd,
+ "MobiCore: Don't allow switch!\n");
+ ctx->mcp->flags.sleep_mode.SleepReq = 0;
+ return -EPERM;
+ }
+ }
+ break;
+ case SWITCH_EXIT:
+ ctx->mcp->flags.sleep_mode.SleepReq = 0;
+ break;
+ default:
+ MCDRV_DBG(mcd, "MobiCore: Unknown switch event!\n");
+ }
+
+ return 0;
+}
+
+static struct notifier_block switcher_nb = {
+ .notifier_call = bL_switcher_notifier_handler,
+};
+#endif
+
+int mc_pm_initialize(struct mc_context *context)
+{
+ int ret = 0;
+
+ ctx = context;
+
+ ret = register_pm_notifier(&mc_notif_block);
+ if (ret)
+ MCDRV_DBG_ERROR(mcd, "device pm register failed\n");
+#ifdef MC_BL_NOTIFIER
+ if (register_bL_swicher_notifier(&switcher_nb))
+ MCDRV_DBG_ERROR(mcd,
+ "Failed to register to bL_switcher_notifier\n");
+#endif
+
+ return ret;
+}
+
+int mc_pm_free(void)
+{
+ int ret = unregister_pm_notifier(&mc_notif_block);
+ if (ret)
+ MCDRV_DBG_ERROR(mcd, "device pm unregister failed\n");
+#ifdef MC_BL_NOTIFIER
+ ret = unregister_bL_swicher_notifier(&switcher_nb);
+ if (ret)
+ MCDRV_DBG_ERROR(mcd, "device bl unregister failed\n");
+#endif
+ return ret;
+}
+
+#endif /* MC_PM_RUNTIME */
+
+#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
+
+int mc_pm_clock_initialize(void)
+{
+ int ret = 0;
+
+ /* Get core clk */
+ mc_ce_core_clk = clk_get(mcd, "core_clk");
+ if (IS_ERR(mc_ce_core_clk)) {
+ ret = PTR_ERR(mc_ce_core_clk);
+ MCDRV_DBG_ERROR(mcd, "cannot get core clock\n");
+ goto error;
+ }
+ /* Get Interface clk */
+ mc_ce_iface_clk = clk_get(mcd, "iface_clk");
+ if (IS_ERR(mc_ce_iface_clk)) {
+ clk_put(mc_ce_core_clk);
+ ret = PTR_ERR(mc_ce_iface_clk);
+ MCDRV_DBG_ERROR(mcd, "cannot get iface clock\n");
+ goto error;
+ }
+ /* Get AXI clk */
+ mc_ce_bus_clk = clk_get(mcd, "bus_clk");
+ if (IS_ERR(mc_ce_bus_clk)) {
+ clk_put(mc_ce_iface_clk);
+ clk_put(mc_ce_core_clk);
+ ret = PTR_ERR(mc_ce_bus_clk);
+ MCDRV_DBG_ERROR(mcd, "cannot get AXI bus clock\n");
+ goto error;
+ }
+ return ret;
+
+error:
+ mc_ce_core_clk = NULL;
+ mc_ce_iface_clk = NULL;
+ mc_ce_bus_clk = NULL;
+
+ return ret;
+}
+
+void mc_pm_clock_finalize(void)
+{
+ if (mc_ce_iface_clk != NULL)
+ clk_put(mc_ce_iface_clk);
+
+ if (mc_ce_core_clk != NULL)
+ clk_put(mc_ce_core_clk);
+
+ if (mc_ce_bus_clk != NULL)
+ clk_put(mc_ce_bus_clk);
+}
+
+int mc_pm_clock_enable(void)
+{
+ int rc = 0;
+
+ rc = clk_prepare_enable(mc_ce_core_clk);
+ if (rc) {
+ MCDRV_DBG_ERROR(mcd, "cannot enable clock\n");
+ } else {
+ rc = clk_prepare_enable(mc_ce_iface_clk);
+ if (rc) {
+ clk_disable_unprepare(mc_ce_core_clk);
+ MCDRV_DBG_ERROR(mcd, "cannot enable clock\n");
+ } else {
+ rc = clk_prepare_enable(mc_ce_bus_clk);
+ if (rc) {
+ clk_disable_unprepare(mc_ce_iface_clk);
+ MCDRV_DBG_ERROR(mcd, "cannot enable clock\n");
+ }
+ }
+ }
+ return rc;
+}
+
+void mc_pm_clock_disable(void)
+{
+ if (mc_ce_iface_clk != NULL)
+ clk_disable_unprepare(mc_ce_iface_clk);
+
+ if (mc_ce_core_clk != NULL)
+ clk_disable_unprepare(mc_ce_core_clk);
+
+ if (mc_ce_bus_clk != NULL)
+ clk_disable_unprepare(mc_ce_bus_clk);
+}
+
+#endif /* MC_CRYPTO_CLOCK_MANAGEMENT */
diff --git a/drivers/gud/mobicore_driver/pm.h b/drivers/gud/mobicore_driver/pm.h
index 3e73b8b..332da34 100644
--- a/drivers/gud/mobicore_driver/pm.h
+++ b/drivers/gud/mobicore_driver/pm.h
@@ -31,5 +31,13 @@
int mc_pm_initialize(struct mc_context *context);
/* Free all Power Management resources*/
int mc_pm_free(void);
+/* Initialize secure crypto clocks */
+int mc_pm_clock_initialize(void);
+/* Free secure crypto clocks */
+void mc_pm_clock_finalize(void);
+/* Enable secure crypto clocks */
+int mc_pm_clock_enable(void);
+/* Disable secure crypto clocks */
+void mc_pm_clock_disable(void);
#endif /* _MC_PM_H_ */
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index 1458bc5..8e350f0 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -130,6 +130,60 @@
{790, 203}
};
+static const struct qpnp_vadc_map_pt adcmap_qrd_btm_threshold[] = {
+ {-200, 1672},
+ {-180, 1656},
+ {-160, 1639},
+ {-140, 1620},
+ {-120, 1599},
+ {-100, 1577},
+ {-80, 1553},
+ {-60, 1527},
+ {-40, 1550},
+ {-20, 1471},
+ {0, 1440},
+ {20, 1408},
+ {40, 1374},
+ {60, 1339},
+ {80, 1303},
+ {100, 1266},
+ {120, 1228},
+ {140, 1190},
+ {160, 1150},
+ {180, 1111},
+ {200, 1071},
+ {220, 1032},
+ {240, 992},
+ {260, 953},
+ {280, 915},
+ {300, 877},
+ {320, 841},
+ {340, 805},
+ {360, 770},
+ {380, 736},
+ {400, 704},
+ {420, 673},
+ {440, 643},
+ {460, 614},
+ {480, 587},
+ {500, 561},
+ {520, 536},
+ {540, 513},
+ {560, 491},
+ {580, 470},
+ {600, 450},
+ {620, 431},
+ {640, 414},
+ {660, 397},
+ {680, 382},
+ {700, 367},
+ {720, 353},
+ {740, 340},
+ {760, 328},
+ {780, 317},
+ {800, 306},
+};
+
/* Voltage to temperature */
static const struct qpnp_vadc_map_pt adcmap_100k_104ef_104fb[] = {
{1758, -40},
@@ -373,7 +427,7 @@
{
struct qpnp_vadc_linear_graph btm_param;
int64_t low_output = 0, high_output = 0;
- int rc = 0;
+ int rc = 0, sign = 0;
rc = qpnp_get_vadc_gain_and_offset(&btm_param, CALIB_ABSOLUTE);
if (rc < 0) {
@@ -384,19 +438,36 @@
/* Convert to Kelvin and account for voltage to be written as 2mV/K */
low_output = (param->low_temp + KELVINMIL_DEGMIL) * 2;
/* Convert to voltage threshold */
- low_output *= btm_param.dy;
- do_div(low_output, btm_param.adc_vref);
+ low_output = (low_output - QPNP_ADC_625_UV) * btm_param.dy;
+ if (low_output < 0) {
+ sign = 1;
+ low_output = -low_output;
+ }
+ do_div(low_output, QPNP_ADC_625_UV);
+ if (sign)
+ low_output = -low_output;
low_output += btm_param.adc_gnd;
+ sign = 0;
/* Convert to Kelvin and account for voltage to be written as 2mV/K */
high_output = (param->high_temp + KELVINMIL_DEGMIL) * 2;
/* Convert to voltage threshold */
- high_output *= btm_param.dy;
- do_div(high_output, btm_param.adc_vref);
+ high_output = (high_output - QPNP_ADC_625_UV) * btm_param.dy;
+ if (high_output < 0) {
+ sign = 1;
+ high_output = -high_output;
+ }
+ do_div(high_output, QPNP_ADC_625_UV);
+ if (sign)
+ high_output = -high_output;
high_output += btm_param.adc_gnd;
- *low_threshold = low_output;
- *high_threshold = high_output;
+ *low_threshold = (uint32_t) low_output;
+ *high_threshold = (uint32_t) high_output;
+ pr_debug("high_temp:%d, low_temp:%d\n", param->high_temp,
+ param->low_temp);
+ pr_debug("adc_code_high:%x, adc_code_low:%x\n", *high_threshold,
+ *low_threshold);
return 0;
}
@@ -446,6 +517,24 @@
}
EXPORT_SYMBOL(qpnp_adc_scale_batt_therm);
+int32_t qpnp_adc_scale_qrd_batt_therm(int32_t adc_code,
+ const struct qpnp_adc_properties *adc_properties,
+ const struct qpnp_vadc_chan_properties *chan_properties,
+ struct qpnp_vadc_result *adc_chan_result)
+{
+ int64_t bat_voltage = 0;
+
+ bat_voltage = qpnp_adc_scale_ratiometric_calib(adc_code,
+ adc_properties, chan_properties);
+
+ return qpnp_adc_map_temp_voltage(
+ adcmap_qrd_btm_threshold,
+ ARRAY_SIZE(adcmap_qrd_btm_threshold),
+ bat_voltage,
+ &adc_chan_result->physical);
+}
+EXPORT_SYMBOL(qpnp_adc_scale_qrd_batt_therm);
+
int32_t qpnp_adc_scale_therm_pu1(int32_t adc_code,
const struct qpnp_adc_properties *adc_properties,
const struct qpnp_vadc_chan_properties *chan_properties,
@@ -637,21 +726,35 @@
uint32_t *low_threshold, uint32_t *high_threshold)
{
struct qpnp_vadc_linear_graph vbatt_param;
- int rc = 0;
+ int rc = 0, sign = 0;
+ int64_t low_thr = 0, high_thr = 0;
rc = qpnp_get_vadc_gain_and_offset(&vbatt_param, CALIB_ABSOLUTE);
if (rc < 0)
return rc;
- *low_threshold = (((param->low_thr/3) - QPNP_ADC_625_UV) *
+ low_thr = (((param->low_thr/3) - QPNP_ADC_625_UV) *
vbatt_param.dy);
- do_div(*low_threshold, QPNP_ADC_625_UV);
- *low_threshold += vbatt_param.adc_gnd;
+ if (low_thr < 0) {
+ sign = 1;
+ low_thr = -low_thr;
+ }
+ do_div(low_thr, QPNP_ADC_625_UV);
+ if (sign)
+ low_thr = -low_thr;
+ *low_threshold = low_thr + vbatt_param.adc_gnd;
- *high_threshold = (((param->high_thr/3) - QPNP_ADC_625_UV) *
+ sign = 0;
+ high_thr = (((param->high_thr/3) - QPNP_ADC_625_UV) *
vbatt_param.dy);
- do_div(*high_threshold, QPNP_ADC_625_UV);
- *high_threshold += vbatt_param.adc_gnd;
+ if (high_thr < 0) {
+ sign = 1;
+ high_thr = -high_thr;
+ }
+ do_div(high_thr, QPNP_ADC_625_UV);
+ if (sign)
+ high_thr = -high_thr;
+ *high_threshold = high_thr + vbatt_param.adc_gnd;
pr_debug("high_volt:%d, low_volt:%d\n", param->high_thr,
param->low_thr);
@@ -876,8 +979,6 @@
init_completion(&adc_qpnp->adc_rslt_completion);
- mutex_init(&adc_qpnp->adc_lock);
-
return 0;
}
EXPORT_SYMBOL(qpnp_adc_get_devicetree_data);
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index 66811bf..de7b0e9 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -95,6 +95,7 @@
#define QPNP_IADC_LSB_OFFSET 0xF3
#define QPNP_IADC_NOMINAL_RSENSE 0xF4
#define QPNP_IADC_ATE_GAIN_CALIB_OFFSET 0xF5
+#define QPNP_INT_TEST_VAL 0xE1
#define QPNP_IADC_ADC_CH_SEL_CTL 0x48
#define QPNP_IADC_ADC_CHX_SEL_SHIFT 3
@@ -126,16 +127,24 @@
#define QPNP_RSENSE_MSB_SIGN_CHECK 0x80
#define QPNP_ADC_COMPLETION_TIMEOUT HZ
+struct qpnp_iadc_comp {
+ bool ext_rsense;
+ u8 id;
+ u8 sys_gain;
+ u8 revision;
+};
+
struct qpnp_iadc_drv {
struct qpnp_adc_drv *adc;
int32_t rsense;
bool external_rsense;
struct device *iadc_hwmon;
bool iadc_initialized;
- int64_t die_temp_calib_offset;
+ int64_t die_temp;
struct delayed_work iadc_work;
struct mutex iadc_vadc_lock;
bool iadc_mode_sel;
+ struct qpnp_iadc_comp iadc_comp;
struct sensor_device_attribute sens_attr[0];
};
@@ -293,6 +302,104 @@
return 0;
}
+static int32_t qpnp_iadc_comp(int64_t *result, struct qpnp_iadc_comp comp,
+ int64_t die_temp)
+{
+ int64_t temp_var = 0, sign_coeff = 0, sys_gain_coeff = 0, old;
+
+ old = *result;
+ *result = *result * 1000000;
+
+ if (comp.revision == QPNP_IADC_VER_3_1) {
+ /* revision 3.1 */
+ if (comp.sys_gain > 127)
+ sys_gain_coeff = -QPNP_COEFF_6 * (comp.sys_gain - 128);
+ else
+ sys_gain_coeff = QPNP_COEFF_6 * comp.sys_gain;
+ } else if (comp.revision != QPNP_IADC_VER_3_0) {
+ /* unsupported revision, do not compensate */
+ *result = old;
+ return 0;
+ }
+
+ if (!comp.ext_rsense) {
+ /* internal rsense */
+ switch (comp.id) {
+ case COMP_ID_TSMC:
+ temp_var = ((QPNP_COEFF_2 * die_temp) -
+ QPNP_COEFF_3_TYPEB);
+ break;
+ case COMP_ID_GF:
+ default:
+ temp_var = ((QPNP_COEFF_2 * die_temp) -
+ QPNP_COEFF_3_TYPEA);
+ break;
+ }
+ temp_var = div64_s64(temp_var, QPNP_COEFF_4);
+ if (comp.revision == QPNP_IADC_VER_3_0)
+ temp_var = QPNP_COEFF_1 * (1000000 - temp_var);
+ else if (comp.revision == QPNP_IADC_VER_3_1)
+ temp_var = 1000000 * (1000000 - temp_var);
+ *result = div64_s64(*result * 1000000, temp_var);
+ }
+
+ sign_coeff = *result < 0 ? QPNP_COEFF_7 : QPNP_COEFF_5;
+ if (comp.ext_rsense) {
+ /* external rsense and current charging */
+ temp_var = div64_s64((-sign_coeff * die_temp) + QPNP_COEFF_8,
+ QPNP_COEFF_4);
+ temp_var = 1000000000 - temp_var;
+ if (comp.revision == QPNP_IADC_VER_3_1) {
+ sys_gain_coeff = (1000000 +
+ div64_s64(sys_gain_coeff, QPNP_COEFF_4));
+ temp_var = div64_s64(temp_var * sys_gain_coeff,
+ 1000000000);
+ }
+ *result = div64_s64(*result, temp_var);
+ }
+ pr_debug("%lld compensated into %lld\n", old, *result);
+
+ return 0;
+}
+
+int32_t qpnp_iadc_comp_result(int64_t *result)
+{
+ struct qpnp_iadc_drv *iadc = qpnp_iadc;
+
+ return qpnp_iadc_comp(result, iadc->iadc_comp, iadc->die_temp);
+}
+EXPORT_SYMBOL(qpnp_iadc_comp_result);
+
+static int32_t qpnp_iadc_comp_info(void)
+{
+ struct qpnp_iadc_drv *iadc = qpnp_iadc;
+ int rc = 0;
+
+ rc = qpnp_iadc_read_reg(QPNP_INT_TEST_VAL, &iadc->iadc_comp.id);
+ if (rc < 0) {
+ pr_err("qpnp adc comp id failed with %d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_iadc_read_reg(QPNP_IADC_REVISION2, &iadc->iadc_comp.revision);
+ if (rc < 0) {
+ pr_err("qpnp adc revision read failed with %d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_iadc_read_reg(QPNP_IADC_ATE_GAIN_CALIB_OFFSET,
+ &iadc->iadc_comp.sys_gain);
+ if (rc < 0)
+ pr_err("full scale read failed with %d\n", rc);
+
+ pr_debug("fab id = %u, revision = %u, sys gain = %u, external_rsense = %d\n",
+ iadc->iadc_comp.id,
+ iadc->iadc_comp.revision,
+ iadc->iadc_comp.sys_gain,
+ iadc->iadc_comp.ext_rsense);
+ return rc;
+}
+
static int32_t qpnp_iadc_configure(enum qpnp_iadc_channels channel,
uint16_t *raw_code, uint32_t mode_sel)
{
@@ -539,13 +646,15 @@
{
struct qpnp_iadc_drv *iadc = qpnp_iadc;
uint8_t rslt_rsense;
- int32_t rc, sign_bit = 0;
+ int32_t rc = 0, sign_bit = 0;
if (!iadc || !iadc->iadc_initialized)
return -EPROBE_DEFER;
- if (iadc->external_rsense)
+ if (iadc->external_rsense) {
*rsense = iadc->rsense;
+ return rc;
+ }
rc = qpnp_iadc_read_reg(QPNP_IADC_NOMINAL_RSENSE, &rslt_rsense);
if (rc < 0) {
@@ -583,12 +692,12 @@
return rc;
die_temp_offset = result_pmic_therm.physical -
- iadc->die_temp_calib_offset;
+ iadc->die_temp;
if (die_temp_offset < 0)
die_temp_offset = -die_temp_offset;
if (die_temp_offset > QPNP_IADC_DIE_TEMP_CALIB_OFFSET) {
- iadc->die_temp_calib_offset =
+ iadc->die_temp =
result_pmic_therm.physical;
rc = qpnp_iadc_calibrate_for_trim();
if (rc)
@@ -640,12 +749,17 @@
(iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw);
result_current = result->result_uv;
result_current *= QPNP_IADC_NANO_VOLTS_FACTOR;
+ /* Intentional fall through. Process the result w/o comp */
do_div(result_current, rsense_u_ohms);
if (sign) {
result->result_uv = -result->result_uv;
result_current = -result_current;
}
+ rc = qpnp_iadc_comp_result(&result_current);
+ if (rc < 0)
+ pr_err("Error during compensating the IADC\n");
+ rc = 0;
result->result_ua = (int32_t) result_current;
fail:
@@ -830,6 +944,8 @@
goto fail;
}
+ mutex_init(&iadc->adc->adc_lock);
+
rc = of_property_read_u32(node, "qcom,rsense",
&iadc->rsense);
if (rc)
@@ -866,6 +982,11 @@
mutex_init(&iadc->iadc_vadc_lock);
INIT_DELAYED_WORK(&iadc->iadc_work, qpnp_iadc_work);
+ rc = qpnp_iadc_comp_info();
+ if (rc) {
+ dev_err(&spmi->dev, "abstracting IADC comp info failed!\n");
+ goto fail;
+ }
iadc->iadc_initialized = true;
rc = qpnp_iadc_calibrate_for_trim();
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index d296a47..4306b1d 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -83,6 +83,15 @@
#define QPNP_VADC_M1_LOW_THR_MSB 0x6a
#define QPNP_VADC_M1_HIGH_THR_LSB 0x6b
#define QPNP_VADC_M1_HIGH_THR_MSB 0x6c
+#define QPNP_VADC_ACCESS 0xd0
+#define QPNP_VADC_ACCESS_DATA 0xa5
+#define QPNP_VADC_PERH_RESET_CTL3 0xda
+#define QPNP_FOLLOW_OTST2_RB BIT(3)
+#define QPNP_FOLLOW_WARM_RB BIT(2)
+#define QPNP_FOLLOW_SHUTDOWN1_RB BIT(1)
+#define QPNP_FOLLOW_SHUTDOWN2_RB BIT(0)
+
+#define QPNP_INT_TEST_VAL 0xE1
#define QPNP_VADC_DATA0 0x60
#define QPNP_VADC_DATA1 0x61
@@ -100,7 +109,8 @@
bool vadc_initialized;
int max_channels_available;
bool vadc_iadc_sync_lock;
- struct sensor_device_attribute sens_attr[0];
+ u8 id;
+ struct sensor_device_attribute sens_attr[0];
};
struct qpnp_vadc_drv *qpnp_vadc;
@@ -112,6 +122,7 @@
[SCALE_XOTHERM] = {qpnp_adc_tdkntcg_therm},
[SCALE_THERM_100K_PULLUP] = {qpnp_adc_scale_therm_pu2},
[SCALE_THERM_150K_PULLUP] = {qpnp_adc_scale_therm_pu1},
+ [SCALE_QRD_BATT_THERM] = {qpnp_adc_scale_qrd_batt_therm},
};
static int32_t qpnp_vadc_read_reg(int16_t reg, u8 *data)
@@ -147,6 +158,40 @@
return 0;
}
+static int32_t qpnp_vadc_warm_rst_configure(void)
+{
+ int rc = 0;
+ u8 data = 0;
+
+ rc = qpnp_vadc_write_reg(QPNP_VADC_ACCESS, QPNP_VADC_ACCESS_DATA);
+ if (rc < 0) {
+ pr_err("VADC write access failed\n");
+ return rc;
+ }
+
+ rc = qpnp_vadc_read_reg(QPNP_VADC_PERH_RESET_CTL3, &data);
+ if (rc < 0) {
+ pr_err("VADC perh reset ctl3 read failed\n");
+ return rc;
+ }
+
+ rc = qpnp_vadc_write_reg(QPNP_VADC_ACCESS, QPNP_VADC_ACCESS_DATA);
+ if (rc < 0) {
+ pr_err("VADC write access failed\n");
+ return rc;
+ }
+
+ data |= QPNP_FOLLOW_WARM_RB;
+
+ rc = qpnp_vadc_write_reg(QPNP_VADC_PERH_RESET_CTL3, data);
+ if (rc < 0) {
+ pr_err("VADC perh reset ctl3 write failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
static int32_t qpnp_vadc_enable(bool state)
{
int rc = 0;
@@ -431,12 +476,88 @@
return 0;
}
-static uint32_t qpnp_vadc_calib_device(void)
+static int32_t qpnp_vbat_sns_comp(int64_t *result, u8 id, int64_t die_temp)
+{
+ int64_t temp_var = 0;
+ int64_t old = *result;
+
+ if (die_temp < 25000)
+ return 0;
+
+ switch (id) {
+ case COMP_ID_TSMC:
+ temp_var = (((die_temp *
+ (-QPNP_VBAT_SNS_COEFF_1_TYPEB))
+ + QPNP_VBAT_SNS_COEFF_2_TYPEB));
+ break;
+ default:
+ case COMP_ID_GF:
+ temp_var = (((die_temp *
+ (-QPNP_VBAT_SNS_COEFF_1_TYPEA))
+ + QPNP_VBAT_SNS_COEFF_2_TYPEA));
+ break;
+ }
+
+ temp_var = div64_s64(temp_var, QPNP_VBAT_SNS_COEFF_3);
+
+ temp_var = 1000000 + temp_var;
+
+ *result = *result * temp_var;
+
+ *result = div64_s64(*result, 1000000);
+ pr_debug("%lld compensated into %lld\n", old, *result);
+
+ return 0;
+}
+
+int32_t qpnp_vbat_sns_comp_result(int64_t *result)
+{
+ struct qpnp_vadc_drv *vadc = qpnp_vadc;
+ struct qpnp_vadc_result die_temp_result;
+ int rc = 0;
+
+ rc = qpnp_vadc_conv_seq_request(ADC_SEQ_NONE,
+ DIE_TEMP, &die_temp_result);
+ if (rc < 0) {
+ pr_err("Error reading die_temp\n");
+ return rc;
+ }
+
+ rc = qpnp_vbat_sns_comp(result, vadc->id,
+ die_temp_result.physical);
+ if (rc < 0)
+ pr_err("Error with vbat compensation\n");
+
+ return rc;
+}
+EXPORT_SYMBOL(qpnp_vbat_sns_comp_result);
+
+static void qpnp_vadc_625mv_channel_sel(uint32_t *ref_channel_sel)
+{
+ struct qpnp_vadc_drv *vadc = qpnp_vadc;
+ uint32_t dt_index = 0;
+
+ /* Check if the buffered 625mV channel exists */
+ while ((vadc->adc->adc_channels[dt_index].channel_num
+ != SPARE1) && (dt_index < vadc->max_channels_available))
+ dt_index++;
+
+ if (dt_index >= vadc->max_channels_available) {
+ pr_debug("Use default 625mV ref channel\n");
+ *ref_channel_sel = REF_625MV;
+ } else {
+ pr_debug("Use buffered 625mV ref channel\n");
+ *ref_channel_sel = SPARE1;
+ }
+}
+
+static int32_t qpnp_vadc_calib_device(void)
{
struct qpnp_vadc_drv *vadc = qpnp_vadc;
struct qpnp_adc_amux_properties conv;
int rc, calib_read_1, calib_read_2, count = 0;
u8 status1 = 0;
+ uint32_t ref_channel_sel = 0;
conv.amux_channel = REF_125V;
conv.decimation = DECIMATION_TYPE2;
@@ -470,7 +591,8 @@
goto calib_fail;
}
- conv.amux_channel = REF_625MV;
+ qpnp_vadc_625mv_channel_sel(&ref_channel_sel);
+ conv.amux_channel = ref_channel_sel;
conv.decimation = DECIMATION_TYPE2;
conv.mode_sel = ADC_OP_NORMAL_MODE << QPNP_VADC_OP_MODE_SHIFT;
conv.hw_settle_time = ADC_CHANNEL_HW_SETTLE_DELAY_0US;
@@ -647,6 +769,7 @@
{
struct qpnp_vadc_drv *vadc = qpnp_vadc;
int rc = 0, scale_type, amux_prescaling, dt_index = 0;
+ uint32_t ref_channel;
if (!vadc || !vadc->vadc_initialized)
return -EPROBE_DEFER;
@@ -666,6 +789,11 @@
vadc->vadc_init_calib = true;
}
+ if (channel == REF_625MV) {
+ qpnp_vadc_625mv_channel_sel(&ref_channel);
+ channel = ref_channel;
+ }
+
vadc->adc->amux_prop->amux_channel = channel;
while ((vadc->adc->adc_channels[dt_index].channel_num
@@ -762,7 +890,34 @@
int32_t qpnp_vadc_read(enum qpnp_vadc_channels channel,
struct qpnp_vadc_result *result)
{
- return qpnp_vadc_conv_seq_request(ADC_SEQ_NONE,
+ struct qpnp_vadc_drv *vadc = qpnp_vadc;
+ enum qpnp_vadc_channels;
+ struct qpnp_vadc_result die_temp_result;
+ int rc = 0;
+
+ if (channel == VBAT_SNS) {
+ rc = qpnp_vadc_conv_seq_request(ADC_SEQ_NONE,
+ channel, result);
+ if (rc < 0) {
+ pr_err("Error reading vbatt\n");
+ return rc;
+ }
+
+ rc = qpnp_vadc_conv_seq_request(ADC_SEQ_NONE,
+ DIE_TEMP, &die_temp_result);
+ if (rc < 0) {
+ pr_err("Error reading die_temp\n");
+ return rc;
+ }
+
+ rc = qpnp_vbat_sns_comp(&result->physical, vadc->id,
+ die_temp_result.physical);
+ if (rc < 0)
+ pr_err("Error with vbat compensation\n");
+
+ return 0;
+ } else
+ return qpnp_vadc_conv_seq_request(ADC_SEQ_NONE,
channel, result);
}
EXPORT_SYMBOL(qpnp_vadc_read);
@@ -942,6 +1097,7 @@
struct device_node *node = spmi->dev.of_node;
struct device_node *child;
int rc, count_adc_channel_list = 0;
+ u8 fab_id = 0;
if (!node)
return -EINVAL;
@@ -982,6 +1138,7 @@
dev_err(&spmi->dev, "failed to read device tree\n");
goto fail;
}
+ mutex_init(&vadc->adc->adc_lock);
rc = devm_request_irq(&spmi->dev, vadc->adc->adc_irq_eoc,
qpnp_vadc_isr, IRQF_TRIGGER_RISING,
@@ -1004,6 +1161,19 @@
vadc->vadc_hwmon = hwmon_device_register(&vadc->adc->spmi->dev);
vadc->vadc_init_calib = false;
vadc->max_channels_available = count_adc_channel_list;
+ rc = qpnp_vadc_read_reg(QPNP_INT_TEST_VAL, &fab_id);
+ if (rc < 0) {
+ pr_err("qpnp adc comp id failed with %d\n", rc);
+ return rc;
+ }
+ vadc->id = fab_id;
+
+ rc = qpnp_vadc_warm_rst_configure();
+ if (rc < 0) {
+ pr_err("Setting perp reset on warm reset failed %d\n", rc);
+ return rc;
+ }
+
vadc->vadc_initialized = true;
vadc->vadc_iadc_sync_lock = false;
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index a77dacb..b96349e 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -37,6 +37,7 @@
#include <linux/of_gpio.h>
#include <mach/board.h>
#include <mach/gpiomux.h>
+#include <mach/msm_bus_board.h>
MODULE_LICENSE("GPL v2");
MODULE_VERSION("0.2");
@@ -142,6 +143,22 @@
.pull = GPIOMUX_PULL_NONE,
};
+/**
+ * qup_i2c_clk_path_vote: data to use bus scaling driver for clock path vote
+ *
+ * @client_hdl when zero, client is not registered with the bus scaling driver,
+ * and bus scaling functionality should not be used. When non zero, it
+ * is a bus scaling client id and may be used to vote for clock path.
+ * @reg_err when true, registration error was detected and an error message was
+ * logged. i2c will attempt to re-register but will log error only once.
+ * once registration succeed, the flag is set to false.
+ */
+struct qup_i2c_clk_path_vote {
+ u32 client_hdl;
+ struct msm_bus_scale_pdata *pdata;
+ bool reg_err;
+};
+
struct qup_i2c_dev {
struct device *dev;
void __iomem *base; /* virtual */
@@ -172,6 +189,7 @@
struct mutex mlock;
void *complete;
int i2c_gpios[ARRAY_SIZE(i2c_rsrcs)];
+ struct qup_i2c_clk_path_vote clk_path_vote;
};
#ifdef DEBUG
@@ -333,11 +351,160 @@
mb();
}
+#define MSM_I2C_CLK_PATH_SUSPEND (0)
+#define MSM_I2C_CLK_PATH_RESUME (1)
+#define MSM_I2C_CLK_PATH_MAX_BW(dev) ((dev->pdata->src_clk_rate * 8) / 1000)
+
+static int i2c_qup_clk_path_init(struct platform_device *pdev,
+ struct qup_i2c_dev *dev)
+{
+ struct msm_bus_vectors *paths = NULL;
+ struct msm_bus_paths *usecases = NULL;
+
+ if (!dev->pdata->master_id)
+ return 0;
+
+ dev_dbg(&pdev->dev, "initialises bus-scaling clock voting");
+
+ paths = devm_kzalloc(&pdev->dev, sizeof(*paths) * 2, GFP_KERNEL);
+ if (!paths) {
+ dev_err(&pdev->dev,
+ "msm_bus_paths.paths memory allocation failed");
+ return -ENOMEM;
+ }
+
+ usecases = devm_kzalloc(&pdev->dev, sizeof(*usecases) * 2, GFP_KERNEL);
+ if (!usecases) {
+ dev_err(&pdev->dev,
+ "msm_bus_scale_pdata.usecases memory allocation failed");
+ goto path_init_err;
+ }
+
+ dev->clk_path_vote.pdata = devm_kzalloc(&pdev->dev,
+ sizeof(*dev->clk_path_vote.pdata),
+ GFP_KERNEL);
+ if (!dev->clk_path_vote.pdata) {
+ dev_err(&pdev->dev,
+ "msm_bus_scale_pdata memory allocation failed");
+ goto path_init_err;
+ }
+
+ paths[MSM_I2C_CLK_PATH_SUSPEND] = (struct msm_bus_vectors) {
+ dev->pdata->master_id, MSM_BUS_SLAVE_EBI_CH0, 0, 0
+ };
+
+ paths[MSM_I2C_CLK_PATH_RESUME] = (struct msm_bus_vectors) {
+ dev->pdata->master_id, MSM_BUS_SLAVE_EBI_CH0, 0,
+ MSM_I2C_CLK_PATH_MAX_BW(dev)
+ };
+
+ usecases[MSM_I2C_CLK_PATH_SUSPEND] = (struct msm_bus_paths) {
+ .num_paths = 1,
+ .vectors = &paths[MSM_I2C_CLK_PATH_SUSPEND],
+ };
+
+ usecases[MSM_I2C_CLK_PATH_RESUME] = (struct msm_bus_paths) {
+ .num_paths = 1,
+ .vectors = &paths[MSM_I2C_CLK_PATH_RESUME],
+ };
+
+ *dev->clk_path_vote.pdata = (struct msm_bus_scale_pdata) {
+ .active_only = dev->pdata->active_only,
+ .name = pdev->name,
+ .num_usecases = 2,
+ .usecase = usecases,
+ };
+
+ return 0;
+
+path_init_err:
+ devm_kfree(&pdev->dev, paths);
+ devm_kfree(&pdev->dev, usecases);
+ devm_kfree(&pdev->dev, dev->clk_path_vote.pdata);
+ dev->clk_path_vote.pdata = NULL;
+ return -ENOMEM;
+}
+
+static void i2c_qup_clk_path_teardown(struct qup_i2c_dev *dev)
+{
+ if (dev->clk_path_vote.client_hdl) {
+ msm_bus_scale_unregister_client(dev->clk_path_vote.client_hdl);
+ dev->clk_path_vote.client_hdl = 0;
+ }
+}
+
+static void i2c_qup_clk_path_vote(struct qup_i2c_dev *dev)
+{
+ if (dev->clk_path_vote.client_hdl)
+ msm_bus_scale_client_update_request(
+ dev->clk_path_vote.client_hdl,
+ MSM_I2C_CLK_PATH_RESUME);
+}
+
+static void i2c_qup_clk_path_unvote(struct qup_i2c_dev *dev)
+{
+ if (dev->clk_path_vote.client_hdl)
+ msm_bus_scale_client_update_request(
+ dev->clk_path_vote.client_hdl,
+ MSM_I2C_CLK_PATH_SUSPEND);
+}
+
+/**
+ * i2c_qup_clk_path_postponed_register: reg with bus-scaling after it is probed
+ *
+ * Workaround: i2c driver may be probed before the bus scaling driver. Thus,
+ * this function should be called not from probe but from a later context.
+ * This function may be called more then once before register succeed. At
+ * this case only one error message will be logged. At boot time all clocks
+ * are on, so earlier i2c transactions should succeed.
+ */
+static void i2c_qup_clk_path_postponed_register(struct qup_i2c_dev *dev)
+{
+ /*
+ * bail out if path voting is diabled (master_id == 0) or if it is
+ * already registered (client_hdl != 0)
+ */
+ if (!dev->pdata->master_id || dev->clk_path_vote.client_hdl)
+ return;
+
+ dev->clk_path_vote.client_hdl = msm_bus_scale_register_client(
+ dev->clk_path_vote.pdata);
+
+ if (dev->clk_path_vote.client_hdl) {
+ if (dev->clk_path_vote.reg_err) {
+ /* log a success message if an error msg was logged */
+ dev->clk_path_vote.reg_err = false;
+ dev_info(dev->dev,
+ "msm_bus_scale_register_client(mstr-id:%d "
+ "actv-only:%d):0x%x",
+ dev->pdata->master_id, dev->pdata->active_only,
+ dev->clk_path_vote.client_hdl);
+ }
+
+ if (dev->pdata->active_only)
+ i2c_qup_clk_path_vote(dev);
+ } else {
+ /* guard to log only one error on multiple failure */
+ if (!dev->clk_path_vote.reg_err) {
+ dev->clk_path_vote.reg_err = true;
+
+ dev_info(dev->dev,
+ "msm_bus_scale_register_client(mstr-id:%d "
+ "actv-only:%d):0",
+ dev->pdata->master_id, dev->pdata->active_only);
+ }
+ }
+}
+
static void
qup_i2c_pwr_mgmt(struct qup_i2c_dev *dev, unsigned int state)
{
dev->pwr_state = state;
if (state != 0) {
+ i2c_qup_clk_path_postponed_register(dev);
+ if (!dev->pdata->active_only)
+ i2c_qup_clk_path_vote(dev);
+
clk_prepare_enable(dev->clk);
if (!dev->pdata->keep_ahb_clk_on)
clk_prepare_enable(dev->pclk);
@@ -347,6 +514,8 @@
qup_config_core_on_en(dev);
if (!dev->pdata->keep_ahb_clk_on)
clk_disable_unprepare(dev->pclk);
+ if (!dev->pdata->active_only)
+ i2c_qup_clk_path_unvote(dev);
}
}
@@ -1099,11 +1268,12 @@
enum msm_i2c_dt_entry_type {
DT_U32,
DT_GPIO,
+ DT_BOOL,
};
struct msm_i2c_dt_to_pdata_map {
const char *dt_name;
- int *ptr_data;
+ void *ptr_data;
enum msm_i2c_dt_entry_status status;
enum msm_i2c_dt_entry_type type;
int default_val;
@@ -1119,28 +1289,42 @@
{"qcom,i2c-bus-freq", &pdata->clk_freq , DT_REQUIRED , DT_U32 , 0},
{"cell-index" , &pdev->id , DT_REQUIRED , DT_U32 , -1},
{"qcom,i2c-src-freq", &pdata->src_clk_rate, DT_SUGGESTED, DT_U32, 0},
+ {"qcom,master-id" , &pdata->master_id , DT_SUGGESTED, DT_U32, 0},
{"qcom,scl-gpio" , gpios , DT_OPTIONAL , DT_GPIO, -1},
{"qcom,sda-gpio" , gpios + 1 , DT_OPTIONAL , DT_GPIO, -1},
+ {"qcom,active-only" , &pdata->active_only , DT_OPTIONAL , DT_BOOL, 0},
{NULL , NULL , 0 , 0 , 0},
};
for (itr = map; itr->dt_name ; ++itr) {
- if (itr->type == DT_GPIO) {
+ switch (itr->type) {
+ case DT_GPIO:
ret = of_get_named_gpio(node, itr->dt_name, 0);
if (ret >= 0) {
- *itr->ptr_data = ret;
+ *((int *) itr->ptr_data) = ret;
ret = 0;
}
- } else {
+ break;
+ case DT_U32:
ret = of_property_read_u32(node, itr->dt_name,
- itr->ptr_data);
+ (u32 *) itr->ptr_data);
+ break;
+ case DT_BOOL:
+ *((bool *) itr->ptr_data) =
+ of_property_read_bool(node, itr->dt_name);
+ ret = 0;
+ break;
+ default:
+ dev_err(&pdev->dev, "%d is an unknown DT entry type\n",
+ itr->type);
+ ret = -EBADE;
}
dev_dbg(&pdev->dev, "DT entry ret:%d name:%s val:%d\n",
- ret, itr->dt_name, *itr->ptr_data);
+ ret, itr->dt_name, *((int *)itr->ptr_data));
if (ret) {
- *itr->ptr_data = itr->default_val;
+ *((int *)itr->ptr_data) = itr->default_val;
if (itr->status < DT_OPTIONAL) {
dev_err(&pdev->dev, "Missing '%s' DT entry\n",
@@ -1326,6 +1510,14 @@
dev->clk_ctl = 0;
dev->pos = 0;
+ ret = i2c_qup_clk_path_init(pdev, dev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to init clock path-voting data structs. err:%d", ret);
+ /* disable i2c_qup_clk_path_xxx() functionality */
+ dev->pdata->master_id = 0;
+ }
+
if (dev->pdata->src_clk_rate <= 0) {
dev_info(&pdev->dev,
"No src_clk_rate specified in platfrom data\n");
@@ -1443,6 +1635,7 @@
err_reset_failed:
clk_disable_unprepare(dev->clk);
clk_disable_unprepare(dev->pclk);
+ i2c_qup_clk_path_teardown(dev);
err_gsbi_failed:
iounmap(dev->base);
err_ioremap_failed:
@@ -1488,6 +1681,11 @@
clk_put(dev->pclk);
}
clk_put(dev->clk);
+
+ if (dev->pdata->active_only)
+ i2c_qup_clk_path_unvote(dev);
+ i2c_qup_clk_path_teardown(dev);
+
if (dev->gsbi)
iounmap(dev->gsbi);
iounmap(dev->base);
diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
index f46139f..97541ff 100644
--- a/drivers/input/misc/kxtj9.c
+++ b/drivers/input/misc/kxtj9.c
@@ -25,6 +25,7 @@
#include <linux/slab.h>
#include <linux/input/kxtj9.h>
#include <linux/input-polldev.h>
+#include <linux/regulator/consumer.h>
#define NAME "kxtj9"
#define G_MAX 8000
@@ -62,6 +63,11 @@
#define RES_CTRL_REG1 1
#define RES_INT_CTRL1 2
#define RESUME_ENTRIES 3
+/* POWER SUPPLY VOLTAGE RANGE */
+#define KXTJ9_VDD_MIN_UV 2000000
+#define KXTJ9_VDD_MAX_UV 3300000
+#define KXTJ9_VIO_MIN_UV 1750000
+#define KXTJ9_VIO_MAX_UV 1950000
/*
* The following table lists the maximum appropriate poll interval for each
@@ -92,6 +98,9 @@
u8 ctrl_reg1;
u8 data_ctrl;
u8 int_ctrl;
+ bool power_enabled;
+ struct regulator *vdd;
+ struct regulator *vio;
};
static int kxtj9_i2c_read(struct kxtj9_data *tj9, u8 addr, u8 *data, int len)
@@ -203,12 +212,130 @@
return 0;
}
-static int kxtj9_device_power_on(struct kxtj9_data *tj9)
+static int kxtj9_power_on(struct kxtj9_data *data, bool on)
{
- if (tj9->pdata.power_on)
- return tj9->pdata.power_on();
+ int rc = 0;
+
+ if (!on && data->power_enabled) {
+ rc = regulator_disable(data->vdd);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator vdd disable failed rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = regulator_disable(data->vio);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator vio disable failed rc=%d\n", rc);
+ regulator_enable(data->vdd);
+ }
+ data->power_enabled = false;
+ } else if (on && !data->power_enabled) {
+ rc = regulator_enable(data->vdd);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator vdd enable failed rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = regulator_enable(data->vio);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator vio enable failed rc=%d\n", rc);
+ regulator_disable(data->vdd);
+ }
+ data->power_enabled = true;
+ } else {
+ dev_warn(&data->client->dev,
+ "Power on=%d. enabled=%d\n",
+ on, data->power_enabled);
+ }
+
+ return rc;
+}
+
+static int kxtj9_power_init(struct kxtj9_data *data, bool on)
+{
+ int rc;
+
+ if (!on) {
+ if (regulator_count_voltages(data->vdd) > 0)
+ regulator_set_voltage(data->vdd, 0, KXTJ9_VDD_MAX_UV);
+
+ regulator_put(data->vdd);
+
+ if (regulator_count_voltages(data->vio) > 0)
+ regulator_set_voltage(data->vio, 0, KXTJ9_VIO_MAX_UV);
+
+ regulator_put(data->vio);
+ } else {
+ data->vdd = regulator_get(&data->client->dev, "vdd");
+ if (IS_ERR(data->vdd)) {
+ rc = PTR_ERR(data->vdd);
+ dev_err(&data->client->dev,
+ "Regulator get failed vdd rc=%d\n", rc);
+ return rc;
+ }
+
+ if (regulator_count_voltages(data->vdd) > 0) {
+ rc = regulator_set_voltage(data->vdd, KXTJ9_VDD_MIN_UV,
+ KXTJ9_VDD_MAX_UV);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator set failed vdd rc=%d\n",
+ rc);
+ goto reg_vdd_put;
+ }
+ }
+
+ data->vio = regulator_get(&data->client->dev, "vio");
+ if (IS_ERR(data->vio)) {
+ rc = PTR_ERR(data->vio);
+ dev_err(&data->client->dev,
+ "Regulator get failed vio rc=%d\n", rc);
+ goto reg_vdd_set;
+ }
+
+ if (regulator_count_voltages(data->vio) > 0) {
+ rc = regulator_set_voltage(data->vio, KXTJ9_VIO_MIN_UV,
+ KXTJ9_VIO_MAX_UV);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator set failed vio rc=%d\n", rc);
+ goto reg_vio_put;
+ }
+ }
+ }
return 0;
+
+reg_vio_put:
+ regulator_put(data->vio);
+reg_vdd_set:
+ if (regulator_count_voltages(data->vdd) > 0)
+ regulator_set_voltage(data->vdd, 0, KXTJ9_VDD_MAX_UV);
+reg_vdd_put:
+ regulator_put(data->vdd);
+ return rc;
+}
+static int kxtj9_device_power_on(struct kxtj9_data *tj9)
+{
+ int err = 0;
+ if (tj9->pdata.power_on) {
+ err = tj9->pdata.power_on();
+ } else {
+ err = kxtj9_power_on(tj9, true);
+ if (err) {
+ dev_err(&tj9->client->dev, "power on failed");
+ goto err_exit;
+ }
+ msleep(20);
+ }
+
+err_exit:
+ dev_dbg(&tj9->client->dev, "soft power on complete err=%d.\n", err);
+ return err;
}
static void kxtj9_device_power_off(struct kxtj9_data *tj9)
@@ -222,6 +349,11 @@
if (tj9->pdata.power_off)
tj9->pdata.power_off();
+ else
+ kxtj9_power_on(tj9, false);
+
+ dev_dbg(&tj9->client->dev, "soft power off complete.\n");
+ return ;
}
static int kxtj9_enable(struct kxtj9_data *tj9)
@@ -489,10 +621,6 @@
{
int retval;
- retval = kxtj9_device_power_on(tj9);
- if (retval < 0)
- return retval;
-
retval = i2c_smbus_read_byte_data(tj9->client, WHO_AM_I);
if (retval < 0) {
dev_err(&tj9->client->dev, "read err int source\n");
@@ -502,7 +630,6 @@
retval = (retval != 0x07 && retval != 0x08) ? -EIO : 0;
out:
- kxtj9_device_power_off(tj9);
return retval;
}
@@ -533,6 +660,7 @@
tj9->client = client;
tj9->pdata = *pdata;
+ tj9->power_enabled = false;
if (pdata->init) {
err = pdata->init();
@@ -540,10 +668,21 @@
goto err_free_mem;
}
+ err = kxtj9_power_init(tj9, true);
+ if (err < 0) {
+ dev_err(&tj9->client->dev, "power init failed! err=%d", err);
+ goto err_pdata_exit;
+ }
+
+ err = kxtj9_device_power_on(tj9);
+ if (err < 0) {
+ dev_err(&client->dev, "power on failed! err=%d\n", err);
+ goto err_power_deinit;
+ }
err = kxtj9_verify(tj9);
if (err < 0) {
dev_err(&client->dev, "device not recognized\n");
- goto err_pdata_exit;
+ goto err_power_off;
}
i2c_set_clientdata(client, tj9);
@@ -558,7 +697,7 @@
err = kxtj9_setup_input_device(tj9);
if (err)
- goto err_pdata_exit;
+ goto err_power_off;
err = request_threaded_irq(client->irq, NULL, kxtj9_isr,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
@@ -577,20 +716,28 @@
} else {
err = kxtj9_setup_polled_device(tj9);
if (err)
- goto err_pdata_exit;
+ goto err_power_off;
}
+ dev_dbg(&client->dev, "%s: kxtj9_probe OK.\n", __func__);
+ kxtj9_device_power_off(tj9);
return 0;
err_free_irq:
free_irq(client->irq, tj9);
err_destroy_input:
input_unregister_device(tj9->input_dev);
+err_power_off:
+ kxtj9_device_power_off(tj9);
+err_power_deinit:
+ kxtj9_power_init(tj9, false);
err_pdata_exit:
if (tj9->pdata.exit)
tj9->pdata.exit();
err_free_mem:
kfree(tj9);
+
+ dev_err(&client->dev, "%s: kxtj9_probe err=%d\n", __func__, err);
return err;
}
@@ -606,6 +753,9 @@
kxtj9_teardown_polled_device(tj9);
}
+ kxtj9_device_power_off(tj9);
+ kxtj9_power_init(tj9, false);
+
if (tj9->pdata.exit)
tj9->pdata.exit();
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 29b269a..479b788 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -2923,13 +2923,32 @@
goto err_free_mem;
}
+ if (gpio_is_valid(pdata->reset_gpio)) {
+ /* configure touchscreen reset out gpio */
+ error = gpio_request(pdata->reset_gpio, "mxt_reset_gpio");
+ if (error) {
+ dev_err(&client->dev, "unable to request gpio [%d]\n",
+ pdata->reset_gpio);
+ goto err_regulator_on;
+ }
+
+ error = gpio_direction_output(pdata->reset_gpio, 0);
+ if (error) {
+ dev_err(&client->dev,
+ "unable to set direction for gpio [%d]\n",
+ pdata->reset_gpio);
+ goto err_reset_gpio_req;
+ }
+ mxt_reset_delay(data);
+ }
+
if (pdata->power_on)
error = pdata->power_on(true);
else
error = mxt_power_on(data, true);
if (error) {
dev_err(&client->dev, "Failed to power on hardware\n");
- goto err_regulator_on;
+ goto err_reset_gpio_req;
}
if (gpio_is_valid(pdata->irq_gpio)) {
@@ -2954,20 +2973,12 @@
}
if (gpio_is_valid(pdata->reset_gpio)) {
- /* configure touchscreen reset out gpio */
- error = gpio_request(pdata->reset_gpio, "mxt_reset_gpio");
- if (error) {
- dev_err(&client->dev, "unable to request gpio [%d]\n",
- pdata->reset_gpio);
- goto err_irq_gpio_req;
- }
-
error = gpio_direction_output(pdata->reset_gpio, 1);
if (error) {
dev_err(&client->dev,
"unable to set direction for gpio [%d]\n",
pdata->reset_gpio);
- goto err_reset_gpio_req;
+ goto err_irq_gpio_req;
}
}
@@ -2982,7 +2993,7 @@
error = mxt_initialize(data);
if (error)
- goto err_reset_gpio_req;
+ goto err_irq_gpio_req;
error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
pdata->irqflags, client->dev.driver->name, data);
@@ -3036,9 +3047,6 @@
free_irq(client->irq, data);
err_free_object:
kfree(data->object_table);
-err_reset_gpio_req:
- if (gpio_is_valid(pdata->reset_gpio))
- gpio_free(pdata->reset_gpio);
err_irq_gpio_req:
if (gpio_is_valid(pdata->irq_gpio))
gpio_free(pdata->irq_gpio);
@@ -3047,6 +3055,9 @@
pdata->power_on(false);
else
mxt_power_on(data, false);
+err_reset_gpio_req:
+ if (gpio_is_valid(pdata->reset_gpio))
+ gpio_free(pdata->reset_gpio);
err_regulator_on:
if (pdata->init_hw)
pdata->init_hw(false);
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c
index f74f2d5..8dbac64 100644
--- a/drivers/input/touchscreen/ft5x06_ts.c
+++ b/drivers/input/touchscreen/ft5x06_ts.c
@@ -3,7 +3,7 @@
* FocalTech ft5x06 TouchScreen driver.
*
* Copyright (c) 2010 Focal tech Ltd.
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -18,19 +18,27 @@
#include <linux/i2c.h>
#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio.h>
+#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/debugfs.h>
#include <linux/input/ft5x06_ts.h>
-#ifdef CONFIG_HAS_EARLYSUSPEND
+#if defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
#include <linux/earlysuspend.h>
/* Early-suspend level */
-#define FT5X06_SUSPEND_LEVEL 1
+#define FT_SUSPEND_LEVEL 1
#endif
#define CFG_MAX_TOUCH_POINTS 5
@@ -51,21 +59,125 @@
#define POINT_READ_BUF (3 + FT_TOUCH_STEP * CFG_MAX_TOUCH_POINTS)
/*register address*/
-#define FT5X06_REG_PMODE 0xA5
-#define FT5X06_REG_FW_VER 0xA6
-#define FT5X06_REG_POINT_RATE 0x88
-#define FT5X06_REG_THGROUP 0x80
+#define FT_REG_DEV_MODE 0x00
+#define FT_DEV_MODE_REG_CAL 0x02
+#define FT_REG_ID 0xA3
+#define FT_REG_PMODE 0xA5
+#define FT_REG_FW_VER 0xA6
+#define FT_REG_POINT_RATE 0x88
+#define FT_REG_THGROUP 0x80
+#define FT_REG_ECC 0xCC
+#define FT_REG_RESET_FW 0x07
/* power register bits*/
-#define FT5X06_PMODE_ACTIVE 0x00
-#define FT5X06_PMODE_MONITOR 0x01
-#define FT5X06_PMODE_STANDBY 0x02
-#define FT5X06_PMODE_HIBERNATE 0x03
+#define FT_PMODE_ACTIVE 0x00
+#define FT_PMODE_MONITOR 0x01
+#define FT_PMODE_STANDBY 0x02
+#define FT_PMODE_HIBERNATE 0x03
+#define FT_FACTORYMODE_VALUE 0x40
+#define FT_WORKMODE_VALUE 0x00
+#define FT_RST_CMD_REG 0xFC
+#define FT_READ_ID_REG 0x90
+#define FT_ERASE_APP_REG 0x61
+#define FT_ERASE_PANEL_REG 0x63
+#define FT_FW_START_REG 0xBF
-#define FT5X06_VTG_MIN_UV 2600000
-#define FT5X06_VTG_MAX_UV 3300000
-#define FT5X06_I2C_VTG_MIN_UV 1800000
-#define FT5X06_I2C_VTG_MAX_UV 1800000
+
+#define FT_VTG_MIN_UV 2600000
+#define FT_VTG_MAX_UV 3300000
+#define FT_I2C_VTG_MIN_UV 1800000
+#define FT_I2C_VTG_MAX_UV 1800000
+
+#define FT_COORDS_ARR_SIZE 4
+#define MAX_BUTTONS 4
+
+#define FT_8BIT_SHIFT 8
+#define FT_4BIT_SHIFT 4
+#define FT_FW_NAME_MAX_LEN 50
+
+#define FT5316_ID 0x0A
+#define FT5306I_ID 0x55
+
+#define FT_UPGRADE_AA 0xAA
+#define FT_UPGRADE_55 0x55
+
+/*upgrade config of FT5606*/
+#define FT5606_UPGRADE_AA_DELAY 50
+#define FT5606_UPGRADE_55_DELAY 10
+#define FT5606_UPGRADE_ID_1 0x79
+#define FT5606_UPGRADE_ID_2 0x06
+#define FT5606_UPGRADE_READID_DELAY 100
+#define FT5606_UPGRADE_EARSE_DELAY 2000
+
+/*upgrade config of FT5316*/
+#define FT5316_UPGRADE_AA_DELAY 50
+#define FT5316_UPGRADE_55_DELAY 30
+#define FT5316_UPGRADE_ID_1 0x79
+#define FT5316_UPGRADE_ID_2 0x07
+#define FT5316_UPGRADE_READID_DELAY 1
+#define FT5316_UPGRADE_EARSE_DELAY 1500
+
+/*upgrade config of FT5x06(x=2,3,4)*/
+#define FT5X06_UPGRADE_AA_DELAY 50
+#define FT5X06_UPGRADE_55_DELAY 30
+#define FT5X06_UPGRADE_ID_1 0x79
+#define FT5X06_UPGRADE_ID_2 0x03
+#define FT5X06_UPGRADE_READID_DELAY 1
+#define FT5X06_UPGRADE_EARSE_DELAY 2000
+
+/*upgrade config of FT6208*/
+#define FT6208_UPGRADE_AA_DELAY 60
+#define FT6208_UPGRADE_55_DELAY 10
+#define FT6208_UPGRADE_ID_1 0x79
+#define FT6208_UPGRADE_ID_2 0x05
+#define FT6208_UPGRADE_READID_DELAY 10
+#define FT6208_UPGRADE_EARSE_DELAY 2000
+
+#define FT_UPGRADE_INFO(x, y) do { \
+ x->delay_55 = y##_UPGRADE_55_DELAY; \
+ x->delay_aa = y##_UPGRADE_AA_DELAY; \
+ x->upgrade_id_1 = y##_UPGRADE_ID_1; \
+ x->upgrade_id_2 = y##_UPGRADE_ID_2; \
+ x->delay_readid = y##_UPGRADE_READID_DELAY; \
+ x->delay_earse_flash = y##_UPGRADE_EARSE_DELAY; \
+ } while (0)
+
+#define FT_FW_MIN_SIZE 8
+#define FT_FW_MAX_SIZE 32768
+#define FT_FW_FILE_VER(x) ((x)->data[(x)->size - 2])
+#define FT_FW_CHECK(x) \
+ (((x)->data[(x)->size - 8] ^ (x)->data[(x)->size - 6]) == 0xFF \
+ && (((x)->data[(x)->size - 7] ^ (x)->data[(x)->size - 5]) == 0xFF \
+ && (((x)->data[(x)->size - 3] ^ (x)->data[(x)->size - 4]) == 0xFF)))
+
+#define FT_MAX_TRIES 5
+#define FT_RETRY_DLY 20
+
+#define FT_MAX_WR_BUF 10
+#define FT_MAX_RD_BUF 2
+#define FT_FW_PKT_LEN 128
+#define FT_FW_PKT_META_LEN 6
+#define FT_FW_PKT_DLY_MS 20
+#define FT_FW_LAST_PKT 0x6ffa
+#define FT_EARSE_DLY_MS 100
+
+#define FT_UPGRADE_LOOP 3
+#define FT_CAL_START 0x04
+#define FT_CAL_FIN 0x00
+#define FT_CAL_STORE 0x05
+#define FT_CAL_RETRY 100
+#define FT_REG_CAL 0x00
+#define FT_CAL_MASK 0x70
+
+#define FT_INFO_MAX_LEN 200
+
+#define FT_STORE_TS_INFO(buf, id, fw_ver) \
+ snprintf(buf, FT_INFO_MAX_LEN, \
+ "controller\t= focaltech\n" \
+ "model\t\t= 0x%x\n" \
+ "fw_ver\t\t= 0x%x\n", id, fw_ver)
+
+#define FT_DEBUG_DIR_NAME "ts_debug"
struct ts_event {
u16 x[CFG_MAX_TOUCH_POINTS]; /*x coordinate */
@@ -77,6 +189,15 @@
u8 touch_point;
};
+struct upgrade_info {
+ u16 delay_aa; /*delay of write FT_UPGRADE_AA */
+ u16 delay_55; /*delay of write FT_UPGRADE_55 */
+ u8 upgrade_id_1; /*upgrade id 1 */
+ u8 upgrade_id_2; /*upgrade id 2 */
+ u16 delay_readid; /*delay of read id */
+ u16 delay_earse_flash; /*delay of earse flash*/
+};
+
struct ft5x06_ts_data {
struct i2c_client *client;
struct input_dev *input_dev;
@@ -84,7 +205,16 @@
const struct ft5x06_ts_platform_data *pdata;
struct regulator *vdd;
struct regulator *vcc_i2c;
-#ifdef CONFIG_HAS_EARLYSUSPEND
+ char fw_name[FT_FW_NAME_MAX_LEN];
+ bool loading_fw;
+ u8 family_id;
+ struct dentry *dir;
+ u16 addr;
+ bool suspended;
+ char *ts_info;
+#if defined(CONFIG_FB)
+ struct notifier_block fb_notif;
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
struct early_suspend early_suspend;
#endif
};
@@ -149,6 +279,21 @@
return ret;
}
+static int ft5x0x_write_reg(struct i2c_client *client, u8 addr, const u8 val)
+{
+ u8 buf[2] = {0};
+
+ buf[0] = addr;
+ buf[1] = val;
+
+ return ft5x06_i2c_write(client, buf, sizeof(buf));
+}
+
+static int ft5x0x_read_reg(struct i2c_client *client, u8 addr, u8 *val)
+{
+ return ft5x06_i2c_read(client, &addr, 1, val, 1);
+}
+
static void ft5x06_report_value(struct ft5x06_ts_data *data)
{
struct ts_event *event = &data->event;
@@ -163,17 +308,22 @@
event->pressure = 0;
}
- input_report_abs(data->input_dev, ABS_MT_POSITION_X,
- event->x[i]);
- input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
- event->y[i]);
- input_report_abs(data->input_dev, ABS_MT_PRESSURE,
- event->pressure);
- input_report_abs(data->input_dev, ABS_MT_TRACKING_ID,
- event->finger_id[i]);
- input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
- event->pressure);
- input_mt_sync(data->input_dev);
+ input_mt_slot(data->input_dev, i);
+ input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER,
+ !!event->pressure);
+
+ if (event->pressure == FT_PRESS) {
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X,
+ event->x[i]);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
+ event->y[i]);
+ input_report_abs(data->input_dev, ABS_MT_PRESSURE,
+ event->pressure);
+ input_report_abs(data->input_dev, ABS_MT_TRACKING_ID,
+ event->finger_id[i]);
+ input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
+ event->pressure);
+ }
}
input_report_key(data->input_dev, BTN_TOUCH, !!fingerdown);
@@ -288,8 +438,8 @@
}
if (regulator_count_voltages(data->vdd) > 0) {
- rc = regulator_set_voltage(data->vdd, FT5X06_VTG_MIN_UV,
- FT5X06_VTG_MAX_UV);
+ rc = regulator_set_voltage(data->vdd, FT_VTG_MIN_UV,
+ FT_VTG_MAX_UV);
if (rc) {
dev_err(&data->client->dev,
"Regulator set_vtg failed vdd rc=%d\n", rc);
@@ -306,8 +456,8 @@
}
if (regulator_count_voltages(data->vcc_i2c) > 0) {
- rc = regulator_set_voltage(data->vcc_i2c, FT5X06_I2C_VTG_MIN_UV,
- FT5X06_I2C_VTG_MAX_UV);
+ rc = regulator_set_voltage(data->vcc_i2c, FT_I2C_VTG_MIN_UV,
+ FT_I2C_VTG_MAX_UV);
if (rc) {
dev_err(&data->client->dev,
"Regulator set_vtg failed vcc_i2c rc=%d\n", rc);
@@ -321,19 +471,19 @@
regulator_put(data->vcc_i2c);
reg_vdd_set_vtg:
if (regulator_count_voltages(data->vdd) > 0)
- regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV);
+ regulator_set_voltage(data->vdd, 0, FT_VTG_MAX_UV);
reg_vdd_put:
regulator_put(data->vdd);
return rc;
pwr_deinit:
if (regulator_count_voltages(data->vdd) > 0)
- regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV);
+ regulator_set_voltage(data->vdd, 0, FT_VTG_MAX_UV);
regulator_put(data->vdd);
if (regulator_count_voltages(data->vcc_i2c) > 0)
- regulator_set_voltage(data->vcc_i2c, 0, FT5X06_I2C_VTG_MAX_UV);
+ regulator_set_voltage(data->vcc_i2c, 0, FT_I2C_VTG_MAX_UV);
regulator_put(data->vcc_i2c);
return 0;
@@ -345,14 +495,26 @@
struct ft5x06_ts_data *data = dev_get_drvdata(dev);
char txbuf[2];
+ if (data->loading_fw) {
+ dev_info(dev, "Firmware loading in process...\n");
+ return 0;
+ }
+
+ if (data->suspended) {
+ dev_info(dev, "Already in suspend state\n");
+ return 0;
+ }
+
disable_irq(data->client->irq);
if (gpio_is_valid(data->pdata->reset_gpio)) {
- txbuf[0] = FT5X06_REG_PMODE;
- txbuf[1] = FT5X06_PMODE_HIBERNATE;
+ txbuf[0] = FT_REG_PMODE;
+ txbuf[1] = FT_PMODE_HIBERNATE;
ft5x06_i2c_write(data->client, txbuf, sizeof(txbuf));
}
+ data->suspended = true;
+
return 0;
}
@@ -360,6 +522,11 @@
{
struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ if (!data->suspended) {
+ dev_info(dev, "Already in awake state\n");
+ return 0;
+ }
+
if (gpio_is_valid(data->pdata->reset_gpio)) {
gpio_set_value_cansleep(data->pdata->reset_gpio, 0);
msleep(FT_RESET_DLY);
@@ -367,10 +534,32 @@
}
enable_irq(data->client->irq);
+ data->suspended = false;
+
return 0;
}
-#ifdef CONFIG_HAS_EARLYSUSPEND
+#if defined(CONFIG_FB)
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct fb_event *evdata = data;
+ int *blank;
+ struct ft5x06_ts_data *ft5x06_data =
+ container_of(self, struct ft5x06_ts_data, fb_notif);
+
+ if (evdata && evdata->data && event == FB_EVENT_BLANK &&
+ ft5x06_data && ft5x06_data->client) {
+ blank = evdata->data;
+ if (*blank == FB_BLANK_UNBLANK)
+ ft5x06_ts_resume(&ft5x06_data->client->dev);
+ else if (*blank == FB_BLANK_POWERDOWN)
+ ft5x06_ts_suspend(&ft5x06_data->client->dev);
+ }
+
+ return 0;
+}
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
static void ft5x06_ts_early_suspend(struct early_suspend *handler)
{
struct ft5x06_ts_data *data = container_of(handler,
@@ -391,23 +580,612 @@
#endif
static const struct dev_pm_ops ft5x06_ts_pm_ops = {
-#ifndef CONFIG_HAS_EARLYSUSPEND
+#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND))
.suspend = ft5x06_ts_suspend,
.resume = ft5x06_ts_resume,
#endif
};
#endif
+static int ft5x06_auto_cal(struct i2c_client *client)
+{
+ u8 temp = 0, i;
+
+ /* set to factory mode */
+ msleep(2 * FT_STARTUP_DLY);
+ ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE);
+ msleep(FT_STARTUP_DLY);
+
+ /* start calibration */
+ ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_START);
+ msleep(2 * FT_STARTUP_DLY);
+ for (i = 0; i < FT_CAL_RETRY; i++) {
+ ft5x0x_read_reg(client, FT_REG_CAL, &temp);
+ /*return to normal mode, calibration finish */
+ if (((temp & FT_CAL_MASK) >> FT_4BIT_SHIFT) == FT_CAL_FIN)
+ break;
+ }
+
+ /*calibration OK */
+ msleep(2 * FT_STARTUP_DLY);
+ ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE);
+ msleep(FT_STARTUP_DLY);
+
+ /* store calibration data */
+ ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_STORE);
+ msleep(2 * FT_STARTUP_DLY);
+
+ /* set to normal mode */
+ ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_WORKMODE_VALUE);
+ msleep(2 * FT_STARTUP_DLY);
+
+ return 0;
+}
+
+static int ft5x06_get_upgrade_info(u8 family_id, struct upgrade_info *info)
+{
+ switch (family_id) {
+ case FT5306I_ID:
+ FT_UPGRADE_INFO(info, FT5X06);
+ break;
+ case FT5316_ID:
+ FT_UPGRADE_INFO(info, FT5316);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ft5x06_fw_upgrade_start(struct i2c_client *client,
+ const u8 *data, u32 data_len)
+{
+ struct ft5x06_ts_data *ts_data = i2c_get_clientdata(client);
+ struct upgrade_info info;
+ u8 w_buf[FT_MAX_WR_BUF] = {0}, r_buf[FT_MAX_RD_BUF] = {0};
+ u8 pkt_buf[FT_FW_PKT_LEN + FT_FW_PKT_META_LEN];
+ int rc, i, j, temp;
+ u32 pkt_num, pkt_len;
+ u8 fw_ecc;
+
+ rc = ft5x06_get_upgrade_info(ts_data->family_id, &info);
+ if (rc < 0) {
+ dev_err(&client->dev, "Cannot get upgrade information!\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < FT_UPGRADE_LOOP; i++) {
+ /* reset - write 0xaa and 0x55 to register 0xfc */
+ ft5x0x_write_reg(client, FT_RST_CMD_REG, FT_UPGRADE_AA);
+ msleep(info.delay_aa);
+
+ ft5x0x_write_reg(client, FT_RST_CMD_REG, FT_UPGRADE_55);
+ msleep(info.delay_55);
+
+ /* Enter upgrade mode */
+ w_buf[0] = FT_UPGRADE_55;
+ w_buf[1] = FT_UPGRADE_AA;
+ do {
+ i++;
+ rc = ft5x06_i2c_write(client, w_buf, 2);
+ msleep(FT_RETRY_DLY);
+ } while (rc <= 0 && i < FT_MAX_TRIES);
+
+ /* check READ_ID */
+ msleep(info.delay_readid);
+ w_buf[0] = FT_READ_ID_REG;
+ w_buf[1] = 0x00;
+ w_buf[2] = 0x00;
+ w_buf[3] = 0x00;
+
+ ft5x06_i2c_read(client, w_buf, 4, r_buf, 2);
+
+ if (r_buf[0] != info.upgrade_id_1
+ || r_buf[1] != info.upgrade_id_2) {
+ dev_err(&client->dev, "Upgrade ID mismatch(%d)\n", i);
+ } else
+ break;
+ }
+
+ if (i >= FT_UPGRADE_LOOP) {
+ dev_err(&client->dev, "Abort upgrade\n");
+ return -EIO;
+ }
+
+ /* erase app and panel paramenter area */
+ w_buf[0] = FT_ERASE_APP_REG;
+ ft5x06_i2c_write(client, w_buf, 1);
+ msleep(info.delay_earse_flash);
+
+ w_buf[0] = FT_ERASE_PANEL_REG;
+ ft5x06_i2c_write(client, w_buf, 1);
+ msleep(FT_EARSE_DLY_MS);
+
+ /* program firmware */
+ data_len = data_len - 8;
+ pkt_num = (data_len) / FT_FW_PKT_LEN;
+ pkt_len = FT_FW_PKT_LEN;
+ pkt_buf[0] = FT_FW_START_REG;
+ pkt_buf[1] = 0x00;
+ fw_ecc = 0;
+
+ for (i = 0; i < pkt_num; i++) {
+ temp = i * FT_FW_PKT_LEN;
+ pkt_buf[2] = (u8) (temp >> FT_8BIT_SHIFT);
+ pkt_buf[3] = (u8) temp;
+ pkt_buf[4] = (u8) (pkt_len >> FT_8BIT_SHIFT);
+ pkt_buf[5] = (u8) pkt_len;
+
+ for (j = 0; j < FT_FW_PKT_LEN; j++) {
+ pkt_buf[6 + j] = data[i * FT_FW_PKT_LEN + j];
+ fw_ecc ^= pkt_buf[6 + j];
+ }
+
+ ft5x06_i2c_write(client, pkt_buf,
+ FT_FW_PKT_LEN + FT_FW_PKT_META_LEN);
+ msleep(FT_FW_PKT_DLY_MS);
+ }
+
+ /* send remaining bytes */
+ if ((data_len) % FT_FW_PKT_LEN > 0) {
+ temp = pkt_num * FT_FW_PKT_LEN;
+ pkt_buf[2] = (u8) (temp >> FT_8BIT_SHIFT);
+ pkt_buf[3] = (u8) temp;
+ temp = (data_len) % FT_FW_PKT_LEN;
+ pkt_buf[4] = (u8) (temp >> FT_8BIT_SHIFT);
+ pkt_buf[5] = (u8) temp;
+
+ for (i = 0; i < temp; i++) {
+ pkt_buf[6 + i] = data[pkt_num * FT_FW_PKT_LEN + i];
+ fw_ecc ^= pkt_buf[6 + i];
+ }
+
+ ft5x06_i2c_write(client, pkt_buf, temp + FT_FW_PKT_META_LEN);
+ msleep(FT_FW_PKT_DLY_MS);
+ }
+
+ /* send the finishing packet */
+ for (i = 0; i < 6; i++) {
+ temp = FT_FW_LAST_PKT + i;
+ pkt_buf[2] = (u8) (temp >> 8);
+ pkt_buf[3] = (u8) temp;
+ temp = 1;
+ pkt_buf[4] = (u8) (temp >> 8);
+ pkt_buf[5] = (u8) temp;
+ pkt_buf[6] = data[data_len + i];
+ fw_ecc ^= pkt_buf[6];
+ ft5x06_i2c_write(client, pkt_buf, temp + FT_FW_PKT_META_LEN);
+ msleep(FT_FW_PKT_DLY_MS);
+ }
+
+ /* verify checksum */
+ w_buf[0] = FT_REG_ECC;
+ ft5x06_i2c_read(client, w_buf, 1, r_buf, 1);
+ if (r_buf[0] != fw_ecc) {
+ dev_err(&client->dev, "ECC error! dev_ecc=%02x fw_ecc=%02x\n",
+ r_buf[0], fw_ecc);
+ return -EIO;
+ }
+
+ /* reset */
+ w_buf[0] = FT_REG_RESET_FW;
+ ft5x06_i2c_write(client, w_buf, 1);
+ msleep(FT_STARTUP_DLY);
+
+ return 0;
+}
+
+static int ft5x06_fw_upgrade(struct device *dev, bool force)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ const struct firmware *fw = NULL;
+ int rc;
+ u8 val = 0;
+
+ rc = request_firmware(&fw, data->fw_name, dev);
+ if (rc < 0) {
+ dev_err(dev, "Request firmware failed - %s (%d)\n",
+ data->fw_name, rc);
+ return rc;
+ }
+
+ if (fw->size < FT_FW_MIN_SIZE || fw->size > FT_FW_MAX_SIZE) {
+ dev_err(dev, "Invalid firmware size (%d)\n", fw->size);
+ rc = -EIO;
+ goto rel_fw;
+ }
+
+ /* check firmware version */
+ rc = ft5x0x_read_reg(data->client, FT_REG_FW_VER, &val);
+ if (rc < 0) {
+ dev_err(dev, "Get firmware version failed\n");
+ goto rel_fw;
+ }
+
+ if (val == FT_FW_FILE_VER(fw) && !force) {
+ dev_err(dev, "No need to update (0x%x)\n", val);
+ rc = -EFAULT;
+ goto rel_fw;
+ }
+
+ dev_info(dev, "upgrade to fw ver 0x%x from 0x%x\n",
+ FT_FW_FILE_VER(fw), val);
+
+ /* start firmware upgrade */
+ if (FT_FW_CHECK(fw)) {
+ rc = ft5x06_fw_upgrade_start(data->client, fw->data, fw->size);
+ if (rc < 0)
+ dev_err(dev, "update failed (%d)\n", rc);
+ else
+ ft5x06_auto_cal(data->client);
+ } else {
+ dev_err(dev, "FW format error\n");
+ rc = -EIO;
+ }
+
+ FT_STORE_TS_INFO(data->ts_info, data->family_id, FT_FW_FILE_VER(fw));
+rel_fw:
+ release_firmware(fw);
+ return rc;
+}
+
+static ssize_t ft5x06_update_fw_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ return snprintf(buf, 2, "%d\n", data->loading_fw);
+}
+
+static ssize_t ft5x06_update_fw_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int rc;
+
+ if (size > 2)
+ return -EINVAL;
+
+ rc = kstrtoul(buf, 10, &val);
+ if (rc != 0)
+ return rc;
+
+ mutex_lock(&data->input_dev->mutex);
+ if (!data->loading_fw && val) {
+ data->loading_fw = true;
+ ft5x06_fw_upgrade(dev, false);
+ data->loading_fw = false;
+ }
+ mutex_unlock(&data->input_dev->mutex);
+
+ return size;
+}
+
+static DEVICE_ATTR(update_fw, 0664, ft5x06_update_fw_show,
+ ft5x06_update_fw_store);
+
+static ssize_t ft5x06_force_update_fw_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int rc;
+
+ if (size > 2)
+ return -EINVAL;
+
+ rc = kstrtoul(buf, 10, &val);
+ if (rc != 0)
+ return rc;
+
+ mutex_lock(&data->input_dev->mutex);
+ if (!data->loading_fw && val) {
+ data->loading_fw = true;
+ ft5x06_fw_upgrade(dev, true);
+ data->loading_fw = false;
+ }
+ mutex_unlock(&data->input_dev->mutex);
+
+ return size;
+}
+
+static DEVICE_ATTR(force_update_fw, 0664, ft5x06_update_fw_show,
+ ft5x06_force_update_fw_store);
+
+static ssize_t ft5x06_fw_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ return snprintf(buf, FT_FW_NAME_MAX_LEN - 1, "%s\n", data->fw_name);
+}
+
+static ssize_t ft5x06_fw_name_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+
+ if (size > FT_FW_NAME_MAX_LEN - 1)
+ return -EINVAL;
+
+ strlcpy(data->fw_name, buf, size);
+ if (data->fw_name[size-1] == '\n')
+ data->fw_name[size-1] = 0;
+
+ return size;
+}
+
+static DEVICE_ATTR(fw_name, 0664, ft5x06_fw_name_show, ft5x06_fw_name_store);
+
+static bool ft5x06_debug_addr_is_valid(int addr)
+{
+ if (addr < 0 || addr > 0xFF) {
+ pr_err("FT reg address is invalid: 0x%x\n", addr);
+ return false;
+ }
+
+ return true;
+}
+
+static int ft5x06_debug_data_set(void *_data, u64 val)
+{
+ struct ft5x06_ts_data *data = _data;
+
+ mutex_lock(&data->input_dev->mutex);
+
+ if (ft5x06_debug_addr_is_valid(data->addr))
+ dev_info(&data->client->dev,
+ "Writing into FT registers not supported\n");
+
+ mutex_unlock(&data->input_dev->mutex);
+
+ return 0;
+}
+
+static int ft5x06_debug_data_get(void *_data, u64 *val)
+{
+ struct ft5x06_ts_data *data = _data;
+ int rc;
+ u8 reg;
+
+ mutex_lock(&data->input_dev->mutex);
+
+ if (ft5x06_debug_addr_is_valid(data->addr)) {
+ rc = ft5x0x_read_reg(data->client, data->addr, ®);
+ if (rc < 0)
+ dev_err(&data->client->dev,
+ "FT read register 0x%x failed (%d)\n",
+ data->addr, rc);
+ else
+ *val = reg;
+ }
+
+ mutex_unlock(&data->input_dev->mutex);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, ft5x06_debug_data_get,
+ ft5x06_debug_data_set, "0x%02llX\n");
+
+static int ft5x06_debug_addr_set(void *_data, u64 val)
+{
+ struct ft5x06_ts_data *data = _data;
+
+ if (ft5x06_debug_addr_is_valid(val)) {
+ mutex_lock(&data->input_dev->mutex);
+ data->addr = val;
+ mutex_unlock(&data->input_dev->mutex);
+ }
+
+ return 0;
+}
+
+static int ft5x06_debug_addr_get(void *_data, u64 *val)
+{
+ struct ft5x06_ts_data *data = _data;
+
+ mutex_lock(&data->input_dev->mutex);
+
+ if (ft5x06_debug_addr_is_valid(data->addr))
+ *val = data->addr;
+
+ mutex_unlock(&data->input_dev->mutex);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, ft5x06_debug_addr_get,
+ ft5x06_debug_addr_set, "0x%02llX\n");
+
+static int ft5x06_debug_suspend_set(void *_data, u64 val)
+{
+ struct ft5x06_ts_data *data = _data;
+
+ mutex_lock(&data->input_dev->mutex);
+
+ if (val)
+ ft5x06_ts_suspend(&data->client->dev);
+ else
+ ft5x06_ts_resume(&data->client->dev);
+
+ mutex_unlock(&data->input_dev->mutex);
+
+ return 0;
+}
+
+static int ft5x06_debug_suspend_get(void *_data, u64 *val)
+{
+ struct ft5x06_ts_data *data = _data;
+
+ mutex_lock(&data->input_dev->mutex);
+ *val = data->suspended;
+ mutex_unlock(&data->input_dev->mutex);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, ft5x06_debug_suspend_get,
+ ft5x06_debug_suspend_set, "%lld\n");
+
+static int ft5x06_debug_dump_info(struct seq_file *m, void *v)
+{
+ struct ft5x06_ts_data *data = m->private;
+
+ seq_printf(m, "%s\n", data->ts_info);
+
+ return 0;
+}
+
+static int debugfs_dump_info_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ft5x06_debug_dump_info, inode->i_private);
+}
+
+static const struct file_operations debug_dump_info_fops = {
+ .owner = THIS_MODULE,
+ .open = debugfs_dump_info_open,
+ .read = seq_read,
+ .release = single_release,
+};
+
+#ifdef CONFIG_OF
+static int ft5x06_get_dt_coords(struct device *dev, char *name,
+ struct ft5x06_ts_platform_data *pdata)
+{
+ u32 coords[FT_COORDS_ARR_SIZE];
+ struct property *prop;
+ struct device_node *np = dev->of_node;
+ int coords_size, rc;
+
+ prop = of_find_property(np, name, NULL);
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+
+ coords_size = prop->length / sizeof(u32);
+ if (coords_size != FT_COORDS_ARR_SIZE) {
+ dev_err(dev, "invalid %s\n", name);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32_array(np, name, coords, coords_size);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read %s\n", name);
+ return rc;
+ }
+
+ if (!strcmp(name, "focaltech,panel-coords")) {
+ pdata->panel_minx = coords[0];
+ pdata->panel_miny = coords[1];
+ pdata->panel_maxx = coords[2];
+ pdata->panel_maxy = coords[3];
+ } else if (!strcmp(name, "focaltech,display-coords")) {
+ pdata->x_min = coords[0];
+ pdata->y_min = coords[1];
+ pdata->x_max = coords[2];
+ pdata->y_max = coords[3];
+ } else {
+ dev_err(dev, "unsupported property %s\n", name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ft5x06_parse_dt(struct device *dev,
+ struct ft5x06_ts_platform_data *pdata)
+{
+ int rc;
+ struct device_node *np = dev->of_node;
+ struct property *prop;
+ u32 temp_val, num_buttons;
+ u32 button_map[MAX_BUTTONS];
+
+ rc = ft5x06_get_dt_coords(dev, "focaltech,panel-coords", pdata);
+ if (rc && (rc != -EINVAL))
+ return rc;
+
+ rc = ft5x06_get_dt_coords(dev, "focaltech,display-coords", pdata);
+ if (rc)
+ return rc;
+
+ pdata->i2c_pull_up = of_property_read_bool(np,
+ "focaltech,i2c-pull-up");
+
+ pdata->no_force_update = of_property_read_bool(np,
+ "focaltech,no-force-update");
+ /* reset, irq gpio info */
+ pdata->reset_gpio = of_get_named_gpio_flags(np, "focaltech,reset-gpio",
+ 0, &pdata->reset_gpio_flags);
+ if (pdata->reset_gpio < 0)
+ return pdata->reset_gpio;
+
+ pdata->irq_gpio = of_get_named_gpio_flags(np, "focaltech,irq-gpio",
+ 0, &pdata->irq_gpio_flags);
+ if (pdata->irq_gpio < 0)
+ return pdata->irq_gpio;
+
+ rc = of_property_read_u32(np, "focaltech,family-id", &temp_val);
+ if (!rc)
+ pdata->family_id = temp_val;
+ else
+ return rc;
+
+ prop = of_find_property(np, "focaltech,button-map", NULL);
+ if (prop) {
+ num_buttons = prop->length / sizeof(temp_val);
+ if (num_buttons > MAX_BUTTONS)
+ return -EINVAL;
+
+ rc = of_property_read_u32_array(np,
+ "focaltech,button-map", button_map,
+ num_buttons);
+ if (rc) {
+ dev_err(dev, "Unable to read key codes\n");
+ return rc;
+ }
+ }
+
+ return 0;
+}
+#else
+static int ft5x06_parse_dt(struct device *dev,
+ struct ft5x06_ts_platform_data *pdata)
+{
+ return -ENODEV;
+}
+#endif
+
static int ft5x06_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- const struct ft5x06_ts_platform_data *pdata = client->dev.platform_data;
+ struct ft5x06_ts_platform_data *pdata;
struct ft5x06_ts_data *data;
struct input_dev *input_dev;
+ struct dentry *temp;
u8 reg_value;
u8 reg_addr;
int err;
+ if (client->dev.of_node) {
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct ft5x06_ts_platform_data), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ err = ft5x06_parse_dt(&client->dev, pdata);
+ if (err)
+ return err;
+ } else
+ pdata = client->dev.platform_data;
+
if (!pdata) {
dev_err(&client->dev, "Invalid pdata\n");
return -EINVAL;
@@ -447,12 +1225,11 @@
__set_bit(BTN_TOUCH, input_dev->keybit);
__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
- input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+ input_mt_init_slots(input_dev, CFG_MAX_TOUCH_POINTS);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min,
pdata->x_max, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min,
pdata->y_max, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0,
- CFG_MAX_TOUCH_POINTS, 0, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, FT_PRESS, 0, 0);
input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, FT_PRESS, 0, 0);
@@ -524,25 +1301,22 @@
/* make sure CTP already finish startup process */
msleep(FT_STARTUP_DLY);
- /*get some register information */
- reg_addr = FT5X06_REG_FW_VER;
+ /* check the controller id */
+ reg_addr = FT_REG_ID;
err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1);
- if (err)
+ if (err < 0) {
dev_err(&client->dev, "version read failed");
+ return err;
+ }
- dev_info(&client->dev, "[FTS] Firmware version = 0x%x\n", reg_value);
+ dev_info(&client->dev, "Device ID = 0x%x\n", reg_value);
- reg_addr = FT5X06_REG_POINT_RATE;
- ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1);
- if (err)
- dev_err(&client->dev, "report rate read failed");
- dev_info(&client->dev, "[FTS] report rate is %dHz.\n", reg_value * 10);
+ if (pdata->family_id != reg_value) {
+ dev_err(&client->dev, "%s:Unsupported controller\n", __func__);
+ goto free_reset_gpio;
+ }
- reg_addr = FT5X06_REG_THGROUP;
- err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1);
- if (err)
- dev_err(&client->dev, "threshold read failed");
- dev_dbg(&client->dev, "[FTS] touch threshold is %d.\n", reg_value * 4);
+ data->family_id = reg_value;
err = request_threaded_irq(client->irq, NULL,
ft5x06_ts_interrupt, pdata->irqflags,
@@ -552,9 +1326,104 @@
goto free_reset_gpio;
}
-#ifdef CONFIG_HAS_EARLYSUSPEND
+ err = device_create_file(&client->dev, &dev_attr_fw_name);
+ if (err) {
+ dev_err(&client->dev, "sys file creation failed\n");
+ goto irq_free;
+ }
+
+ err = device_create_file(&client->dev, &dev_attr_update_fw);
+ if (err) {
+ dev_err(&client->dev, "sys file creation failed\n");
+ goto free_fw_name_sys;
+ }
+
+ err = device_create_file(&client->dev, &dev_attr_force_update_fw);
+ if (err) {
+ dev_err(&client->dev, "sys file creation failed\n");
+ goto free_update_fw_sys;
+ }
+
+ data->dir = debugfs_create_dir(FT_DEBUG_DIR_NAME, NULL);
+ if (data->dir == NULL || IS_ERR(data->dir)) {
+ pr_err("debugfs_create_dir failed(%ld)\n", PTR_ERR(data->dir));
+ err = PTR_ERR(data->dir);
+ goto free_force_update_fw_sys;
+ }
+
+ temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, data->dir, data,
+ &debug_addr_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+ err = PTR_ERR(temp);
+ goto free_debug_dir;
+ }
+
+ temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, data->dir, data,
+ &debug_data_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+ err = PTR_ERR(temp);
+ goto free_debug_dir;
+ }
+
+ temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, data->dir,
+ data, &debug_suspend_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+ err = PTR_ERR(temp);
+ goto free_debug_dir;
+ }
+
+ temp = debugfs_create_file("dump_info", S_IRUSR | S_IWUSR, data->dir,
+ data, &debug_dump_info_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+ err = PTR_ERR(temp);
+ goto free_debug_dir;
+ }
+
+ data->ts_info = kzalloc(FT_INFO_MAX_LEN, GFP_KERNEL);
+ if (!data->ts_info) {
+ dev_err(&client->dev, "Not enough memory\n");
+ goto free_debug_dir;
+ }
+
+ /*get some register information */
+ reg_addr = FT_REG_POINT_RATE;
+ ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1);
+ if (err < 0)
+ dev_err(&client->dev, "report rate read failed");
+
+ dev_info(&client->dev, "report rate = %dHz\n", reg_value * 10);
+
+ reg_addr = FT_REG_THGROUP;
+ err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1);
+ if (err < 0)
+ dev_err(&client->dev, "threshold read failed");
+
+ dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4);
+
+ reg_addr = FT_REG_FW_VER;
+ err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1);
+ if (err < 0)
+ dev_err(&client->dev, "version read failed");
+
+ dev_info(&client->dev, "Firmware version = 0x%x\n", reg_value);
+
+ FT_STORE_TS_INFO(data->ts_info, data->family_id, reg_value);
+
+#if defined(CONFIG_FB)
+ data->fb_notif.notifier_call = fb_notifier_callback;
+
+ err = fb_register_client(&data->fb_notif);
+
+ if (err)
+ dev_err(&client->dev, "Unable to register fb_notifier: %d\n",
+ err);
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
- FT5X06_SUSPEND_LEVEL;
+ FT_SUSPEND_LEVEL;
data->early_suspend.suspend = ft5x06_ts_early_suspend;
data->early_suspend.resume = ft5x06_ts_late_resume;
register_early_suspend(&data->early_suspend);
@@ -562,12 +1431,22 @@
return 0;
+free_debug_dir:
+ debugfs_remove_recursive(data->dir);
+free_force_update_fw_sys:
+ device_remove_file(&client->dev, &dev_attr_force_update_fw);
+free_update_fw_sys:
+ device_remove_file(&client->dev, &dev_attr_update_fw);
+free_fw_name_sys:
+ device_remove_file(&client->dev, &dev_attr_fw_name);
+irq_free:
+ free_irq(client->irq, data);
free_reset_gpio:
if (gpio_is_valid(pdata->reset_gpio))
gpio_free(pdata->reset_gpio);
free_irq_gpio:
if (gpio_is_valid(pdata->irq_gpio))
- gpio_free(pdata->reset_gpio);
+ gpio_free(pdata->irq_gpio);
pwr_off:
if (pdata->power_on)
pdata->power_on(false);
@@ -592,7 +1471,15 @@
{
struct ft5x06_ts_data *data = i2c_get_clientdata(client);
-#ifdef CONFIG_HAS_EARLYSUSPEND
+ debugfs_remove_recursive(data->dir);
+ device_remove_file(&client->dev, &dev_attr_force_update_fw);
+ device_remove_file(&client->dev, &dev_attr_update_fw);
+ device_remove_file(&client->dev, &dev_attr_fw_name);
+
+#if defined(CONFIG_FB)
+ if (fb_unregister_client(&data->fb_notif))
+ dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n");
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
unregister_early_suspend(&data->early_suspend);
#endif
free_irq(client->irq, data);
@@ -601,7 +1488,7 @@
gpio_free(data->pdata->reset_gpio);
if (gpio_is_valid(data->pdata->irq_gpio))
- gpio_free(data->pdata->reset_gpio);
+ gpio_free(data->pdata->irq_gpio);
if (data->pdata->power_on)
data->pdata->power_on(false);
@@ -614,6 +1501,7 @@
ft5x06_power_init(data, false);
input_unregister_device(data->input_dev);
+ kfree(data->ts_info);
kfree(data);
return 0;
@@ -626,12 +1514,22 @@
MODULE_DEVICE_TABLE(i2c, ft5x06_ts_id);
+#ifdef CONFIG_OF
+static struct of_device_id ft5x06_match_table[] = {
+ { .compatible = "focaltech,5x06",},
+ { },
+};
+#else
+#define ft5x06_match_table NULL
+#endif
+
static struct i2c_driver ft5x06_ts_driver = {
.probe = ft5x06_ts_probe,
.remove = __devexit_p(ft5x06_ts_remove),
.driver = {
.name = "ft5x06_ts",
.owner = THIS_MODULE,
+ .of_match_table = ft5x06_match_table,
#ifdef CONFIG_PM
.pm = &ft5x06_ts_pm_ops,
#endif
diff --git a/drivers/input/touchscreen/gen_vkeys.c b/drivers/input/touchscreen/gen_vkeys.c
index fcda6c9..efddf61 100644
--- a/drivers/input/touchscreen/gen_vkeys.c
+++ b/drivers/input/touchscreen/gen_vkeys.c
@@ -24,6 +24,8 @@
#define HEIGHT_SCALE_NUM 8
#define HEIGHT_SCALE_DENOM 10
+#define VKEY_Y_OFFSET_DEFAULT 0
+
/* numerator and denomenator for border equations */
#define BORDER_ADJUST_NUM 3
#define BORDER_ADJUST_DENOM 4
@@ -59,7 +61,7 @@
{
struct device_node *np = dev->of_node;
struct property *prop;
- int rc;
+ int rc, val;
rc = of_property_read_string(np, "label", &pdata->name);
if (rc) {
@@ -105,6 +107,15 @@
return -EINVAL;
}
}
+
+ pdata->y_offset = VKEY_Y_OFFSET_DEFAULT;
+ rc = of_property_read_u32(np, "qcom,y-offset", &val);
+ if (!rc)
+ pdata->y_offset = val;
+ else if (rc != -EINVAL) {
+ dev_err(dev, "Failed to read y position offset\n");
+ return rc;
+ }
return 0;
}
@@ -147,7 +158,7 @@
width = ((pdata->disp_maxx - (border * (pdata->num_keys - 1)))
/ pdata->num_keys);
height = (pdata->panel_maxy - pdata->disp_maxy);
- center_y = pdata->disp_maxy + (height / 2);
+ center_y = pdata->disp_maxy + (height / 2) + pdata->y_offset;
height = height * HEIGHT_SCALE_NUM / HEIGHT_SCALE_DENOM;
x2 -= border * BORDER_ADJUST_NUM / BORDER_ADJUST_DENOM;
diff --git a/drivers/input/touchscreen/synaptics_fw_update.c b/drivers/input/touchscreen/synaptics_fw_update.c
index 986c062..349b020 100644
--- a/drivers/input/touchscreen/synaptics_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_fw_update.c
@@ -25,6 +25,7 @@
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/firmware.h>
+#include <linux/string.h>
#include <linux/input/synaptics_dsx.h>
#include "synaptics_i2c_rmi4.h"
@@ -34,14 +35,7 @@
#define FORCE_UPDATE false
#define INSIDE_FIRMWARE_UPDATE
-#define CHECKSUM_OFFSET 0x00
-#define BOOTLOADER_VERSION_OFFSET 0x07
-#define IMAGE_SIZE_OFFSET 0x08
-#define CONFIG_SIZE_OFFSET 0x0C
-#define PRODUCT_ID_OFFSET 0x10
-#define PRODUCT_INFO_OFFSET 0x1E
#define FW_IMAGE_OFFSET 0x100
-#define PRODUCT_ID_SIZE 10
#define BOOTLOADER_ID_OFFSET 0
#define FLASH_PROPERTIES_OFFSET 2
@@ -59,10 +53,14 @@
#define BLOCK_NUMBER_OFFSET 0
#define BLOCK_DATA_OFFSET 2
-#define UI_CONFIG_AREA 0x00
-#define PERM_CONFIG_AREA 0x01
-#define BL_CONFIG_AREA 0x02
-#define DISP_CONFIG_AREA 0x03
+#define NAME_BUFFER_SIZE 128
+
+enum falsh_config_area {
+ UI_CONFIG_AREA = 0x00,
+ PERM_CONFIG_AREA = 0x01,
+ BL_CONFIG_AREA = 0x02,
+ DISP_CONFIG_AREA = 0x03
+};
enum flash_command {
CMD_WRITE_FW_BLOCK = 0x2,
@@ -70,9 +68,10 @@
CMD_READ_CONFIG_BLOCK = 0x5,
CMD_WRITE_CONFIG_BLOCK = 0x6,
CMD_ERASE_CONFIG = 0x7,
+ CMD_READ_SENSOR_ID = 0x8,
CMD_ERASE_BL_CONFIG = 0x9,
CMD_ERASE_DISP_CONFIG = 0xA,
- CMD_ENABLE_FLASH_PROG = 0xF,
+ CMD_ENABLE_FLASH_PROG = 0xF
};
enum flash_area {
@@ -81,6 +80,11 @@
CONFIG_AREA
};
+enum image_file_option {
+ OPTION_BUILD_INFO = 0,
+ OPTION_CONTAIN_BOOTLOADER = 1,
+};
+
#define SLEEP_MODE_NORMAL (0x00)
#define SLEEP_MODE_SENSOR_SLEEP (0x01)
#define SLEEP_MODE_RESERVED0 (0x02)
@@ -91,6 +95,8 @@
#define ERASE_WAIT_MS (5 * 1000)
#define RESET_WAIT_MS (500)
+#define POLLING_MODE 0
+
#define SLEEP_TIME_US 50
static ssize_t fwu_sysfs_show_image(struct file *data_file,
@@ -101,6 +107,9 @@
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count);
+static ssize_t fwu_sysfs_force_reflash_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
@@ -139,6 +148,40 @@
static int fwu_wait_for_idle(int timeout_ms);
+struct image_header_data {
+ union {
+ struct {
+ /* 0x00-0x0F */
+ unsigned char file_checksum[4];
+ unsigned char reserved_04;
+ unsigned char reserved_05;
+ unsigned char options_firmware_id:1;
+ unsigned char options_contain_bootloader:1;
+ unsigned char options_reserved:6;
+ unsigned char bootloader_version;
+ unsigned char firmware_size[4];
+ unsigned char config_size[4];
+ /* 0x10-0x1F */
+ unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE];
+ unsigned char reserved_1a;
+ unsigned char reserved_1b;
+ unsigned char reserved_1c;
+ unsigned char reserved_1d;
+ unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE];
+ /* 0x20-0x2F */
+ unsigned char reserved_20_2f[0x10];
+ /* 0x30-0x3F */
+ unsigned char ds_firmware_id[0x10];
+ /* 0x40-0x4F */
+ unsigned char ds_customize_info[10];
+ unsigned char reserved_4a_4f[6];
+ /* 0x50-0x53*/
+ unsigned char firmware_id[4];
+ } __packed;
+ unsigned char data[54];
+ };
+};
+
struct image_header {
unsigned int checksum;
unsigned int image_size;
@@ -147,6 +190,8 @@
unsigned char bootloader_version;
unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE];
+ unsigned int firmware_id;
+ bool is_contain_build_info;
};
struct pdt_properties {
@@ -245,6 +290,7 @@
struct f34_flash_properties flash_properties;
struct workqueue_struct *fwu_workqueue;
struct delayed_work fwu_work;
+ char *firmware_name;
};
static struct bin_attribute dev_attr_data = {
@@ -258,7 +304,10 @@
};
static struct device_attribute attrs[] = {
- __ATTR(doreflash, S_IWUGO,
+ __ATTR(force_update_fw, S_IWUGO,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_force_reflash_store),
+ __ATTR(update_fw, S_IWUGO,
synaptics_rmi4_show_error,
fwu_sysfs_do_reflash_store),
__ATTR(writeconfig, S_IWUGO,
@@ -308,18 +357,36 @@
(unsigned int)ptr[3] * 0x1000000;
}
+static unsigned int extract_uint_be(const unsigned char *ptr)
+{
+ return (unsigned int)ptr[3] +
+ (unsigned int)ptr[2] * 0x100 +
+ (unsigned int)ptr[1] * 0x10000 +
+ (unsigned int)ptr[0] * 0x1000000;
+}
+
static void parse_header(struct image_header *header,
const unsigned char *fw_image)
{
- header->checksum = extract_uint(&fw_image[CHECKSUM_OFFSET]);
- header->bootloader_version = fw_image[BOOTLOADER_VERSION_OFFSET];
- header->image_size = extract_uint(&fw_image[IMAGE_SIZE_OFFSET]);
- header->config_size = extract_uint(&fw_image[CONFIG_SIZE_OFFSET]);
- memcpy(header->product_id, &fw_image[PRODUCT_ID_OFFSET],
- SYNAPTICS_RMI4_PRODUCT_ID_SIZE);
- header->product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE] = 0;
- memcpy(header->product_info, &fw_image[PRODUCT_INFO_OFFSET],
- SYNAPTICS_RMI4_PRODUCT_INFO_SIZE);
+ struct image_header_data *data = (struct image_header_data *)fw_image;
+ header->checksum = extract_uint(data->file_checksum);
+ header->bootloader_version = data->bootloader_version;
+ header->image_size = extract_uint(data->firmware_size);
+ header->config_size = extract_uint(data->config_size);
+ memcpy(header->product_id, data->product_id,
+ sizeof(data->product_id));
+ header->product_id[sizeof(data->product_info)] = 0;
+ memcpy(header->product_info, data->product_info,
+ sizeof(data->product_info));
+
+ header->is_contain_build_info =
+ (data->options_firmware_id == (1 << OPTION_BUILD_INFO));
+ if (header->is_contain_build_info) {
+ header->firmware_id = extract_uint(data->firmware_id);
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "%s Firwmare build id %d\n", __func__,
+ header->firmware_id);
+ }
#ifdef DEBUG_FW_UPDATE
dev_info(&fwu->rmi4_data->i2c_client->dev,
@@ -516,6 +583,9 @@
int count = 0;
int timeout_count = ((timeout_ms * 1000) / SLEEP_TIME_US) + 1;
do {
+ #if POLLING_MODE
+ fwu_read_f34_flash_status();
+ #endif
if (fwu->flash_control.command == 0x00)
return 0;
@@ -533,7 +603,7 @@
return -ETIMEDOUT;
}
-static enum flash_area fwu_go_nogo(void)
+static enum flash_area fwu_go_nogo(struct image_header *header)
{
int retval = 0;
int index = 0;
@@ -569,7 +639,6 @@
goto exit;
}
-
/* device firmware id */
retval = fwu->fn_ptr->read(fwu->rmi4_data,
fwu->f01_fd.query_base_addr + 18,
@@ -577,37 +646,45 @@
sizeof(firmware_id));
if (retval < 0) {
dev_err(&i2c_client->dev,
- "Failed to read firmware ID (code %d).\n", retval);
+ "%s: Failed to read firmware ID (code %d).\n",
+ __func__, retval);
goto exit;
}
firmware_id[3] = 0;
deviceFirmwareID = extract_uint(firmware_id);
/* .img firmware id */
- strptr = strnstr(fwu->rmi4_data->fw_image_name, "PR",
- sizeof(fwu->rmi4_data->fw_image_name));
- if (!strptr) {
+ if (header->is_contain_build_info) {
dev_err(&i2c_client->dev,
- "No valid PR number (PRxxxxxxx)" \
- "found in image file name...\n");
- goto exit;
+ "%s: Image option contains build info.\n",
+ __func__);
+ imageFirmwareID = header->firmware_id;
+ } else {
+ strptr = strnstr(fwu->firmware_name, "PR",
+ sizeof(fwu->firmware_name));
+ if (!strptr) {
+ dev_err(&i2c_client->dev,
+ "No valid PR number (PRxxxxxxx)" \
+ "found in image file name...\n");
+ goto exit;
+ }
+
+ strptr += 2;
+ while (strptr[index] >= '0' && strptr[index] <= '9') {
+ imagePR[index] = strptr[index];
+ index++;
+ }
+ imagePR[index] = 0;
+
+ retval = kstrtoul(imagePR, 10, &imageFirmwareID);
+ if (retval == -EINVAL) {
+ dev_err(&i2c_client->dev,
+ "invalid image firmware id...\n");
+ goto exit;
+ }
}
- strptr += 2;
- while (strptr[index] >= '0' && strptr[index] <= '9') {
- imagePR[index] = strptr[index];
- index++;
- }
- imagePR[index] = 0;
-
- retval = kstrtoul(imagePR, 10, &imageFirmwareID);
- if (retval == -EINVAL) {
- dev_err(&i2c_client->dev,
- "invalid image firmware id...\n");
- goto exit;
- }
-
- dev_info(&i2c_client->dev,
+ dev_dbg(&i2c_client->dev,
"Device firmware id %d, .img firmware id %d\n",
deviceFirmwareID,
(unsigned int)imageFirmwareID);
@@ -617,7 +694,8 @@
} else if (imageFirmwareID < deviceFirmwareID) {
flash_area = NONE;
dev_info(&i2c_client->dev,
- "Img fw is older than device fw. Skip fw update.\n");
+ "%s: Img fw is older than device fw. Skip fw update.\n",
+ __func__);
goto exit;
}
@@ -628,24 +706,29 @@
sizeof(config_id));
if (retval < 0) {
dev_err(&i2c_client->dev,
- "Failed to read config ID (code %d).\n", retval);
+ "%s: Failed to read config ID (code %d).\n",
+ __func__, retval);
flash_area = NONE;
goto exit;
}
- deviceConfigID = extract_uint(config_id);
+ deviceConfigID = extract_uint_be(config_id);
- dev_info(&i2c_client->dev,
+ dev_dbg(&i2c_client->dev,
"Device config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
config_id[0], config_id[1], config_id[2], config_id[3]);
/* .img config id */
- dev_info(&i2c_client->dev,
+ dev_dbg(&i2c_client->dev,
".img config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
fwu->config_data[0],
fwu->config_data[1],
fwu->config_data[2],
fwu->config_data[3]);
- imageConfigID = extract_uint(fwu->config_data);
+ imageConfigID = extract_uint_be(fwu->config_data);
+
+ dev_dbg(&i2c_client->dev,
+ "%s: Device config ID %d, .img config ID %d\n",
+ __func__, deviceConfigID, imageConfigID);
if (imageConfigID > deviceConfigID) {
flash_area = CONFIG_AREA;
@@ -656,10 +739,10 @@
kfree(imagePR);
if (flash_area == NONE)
dev_info(&i2c_client->dev,
- "Nothing needs to be updated\n");
+ "%s: Nothing needs to be updated\n", __func__);
else
dev_info(&i2c_client->dev,
- "Update %s block\n",
+ "%s: Update %s block\n", __func__,
flash_area == UI_FIRMWARE ? "UI FW" : "CONFIG");
return flash_area;
}
@@ -1210,7 +1293,7 @@
static int fwu_start_reflash(void)
{
- int retval;
+ int retval = 0;
struct image_header header;
const unsigned char *fw_image;
const struct firmware *fw_entry = NULL;
@@ -1229,18 +1312,30 @@
if (fwu->ext_data_source)
fw_image = fwu->ext_data_source;
else {
- dev_dbg(&fwu->rmi4_data->i2c_client->dev,
- "%s: Requesting firmware image %s\n",
- __func__, fwu->rmi4_data->fw_image_name);
+ fwu->firmware_name = kcalloc(NAME_BUFFER_SIZE,
+ sizeof(char), GFP_KERNEL);
+ if (!fwu->firmware_name) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s Failed to allocate firmware name (%d).\n",
+ __func__, NAME_BUFFER_SIZE);
+ retval = -ENOMEM;
+ goto memory_exit;
+ }
+
+ snprintf(fwu->firmware_name, NAME_BUFFER_SIZE, "%s",
+ fwu->rmi4_data->fw_image_name);
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Requesting firmware image %s\n",
+ __func__, fwu->firmware_name);
retval = request_firmware(&fw_entry,
- fwu->rmi4_data->fw_image_name,
+ fwu->firmware_name,
&fwu->rmi4_data->i2c_client->dev);
if (retval != 0) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"%s: Firmware image %s not available\n",
__func__,
- fwu->rmi4_data->fw_image_name);
+ fwu->firmware_name);
retval = -EINVAL;
goto exit;
}
@@ -1264,7 +1359,7 @@
if (fwu->ext_data_source)
flash_area = UI_FIRMWARE;
else
- flash_area = fwu_go_nogo();
+ flash_area = fwu_go_nogo(&header);
switch (flash_area) {
case NONE:
@@ -1318,6 +1413,8 @@
pr_notice("%s: End of reflash process\n", __func__);
exit:
+ kfree(fwu->firmware_name);
+memory_exit:
return retval;
}
@@ -1371,6 +1468,40 @@
return count;
}
+static ssize_t fwu_sysfs_force_reflash_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned int input;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ if (sscanf(buf, "%u", &input) != 1) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ if (input != 1) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ fwu->force_update = true;
+ retval = synaptics_fw_updater(fwu->ext_data_source);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to do reflash\n",
+ __func__);
+ goto exit;
+ }
+
+ retval = count;
+
+exit:
+ kfree(fwu->ext_data_source);
+ fwu->ext_data_source = NULL;
+ return retval;
+}
+
static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
index 4e2b1a4..b9dd4ae 100644
--- a/drivers/input/touchscreen/synaptics_i2c_rmi4.c
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
@@ -103,20 +103,31 @@
static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data);
-#ifdef CONFIG_HAS_EARLYSUSPEND
+#ifdef CONFIG_PM
+static int synaptics_rmi4_suspend(struct device *dev);
+
+static int synaptics_rmi4_resume(struct device *dev);
+
static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t synaptics_rmi4_full_pm_cycle_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t synaptics_rmi4_mode_suspend_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t synaptics_rmi4_mode_resume_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+#if defined(CONFIG_FB)
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data);
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
static void synaptics_rmi4_early_suspend(struct early_suspend *h);
static void synaptics_rmi4_late_resume(struct early_suspend *h);
-
-static int synaptics_rmi4_suspend(struct device *dev);
-
-static int synaptics_rmi4_resume(struct device *dev);
+#endif
#endif
static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev,
@@ -228,10 +239,16 @@
};
static struct device_attribute attrs[] = {
-#ifdef CONFIG_HAS_EARLYSUSPEND
+#ifdef CONFIG_PM
__ATTR(full_pm_cycle, (S_IRUGO | S_IWUGO),
synaptics_rmi4_full_pm_cycle_show,
synaptics_rmi4_full_pm_cycle_store),
+ __ATTR(mode_suspend, S_IWUGO,
+ synaptics_rmi4_show_error,
+ synaptics_rmi4_mode_suspend_store),
+ __ATTR(mode_resume, S_IWUGO,
+ synaptics_rmi4_show_error,
+ synaptics_rmi4_mode_resume_store),
#endif
__ATTR(reset, S_IWUGO,
synaptics_rmi4_show_error,
@@ -259,8 +276,7 @@
static bool exp_fn_inited;
static struct mutex exp_fn_list_mutex;
static struct list_head exp_fn_list;
-
-#ifdef CONFIG_HAS_EARLYSUSPEND
+#ifdef CONFIG_PM
static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -283,6 +299,64 @@
return count;
}
+
+static ssize_t synaptics_rmi4_mode_suspend_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int input;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ if (sscanf(buf, "%u", &input) != 1)
+ return -EINVAL;
+
+ synaptics_rmi4_suspend(&(rmi4_data->input_dev->dev));
+
+ return count;
+}
+
+static ssize_t synaptics_rmi4_mode_resume_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int input;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ if (sscanf(buf, "%u", &input) != 1)
+ return -EINVAL;
+
+ synaptics_rmi4_resume(&(rmi4_data->input_dev->dev));
+
+ return count;
+}
+
+#ifdef CONFIG_FB
+static void configure_sleep(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval = 0;
+
+ rmi4_data->fb_notif.notifier_call = fb_notifier_callback;
+
+ retval = fb_register_client(&rmi4_data->fb_notif);
+ if (retval)
+ dev_err(&rmi4_data->i2c_client->dev,
+ "Unable to register fb_notifier: %d\n", retval);
+ return;
+}
+#elif defined CONFIG_HAS_EARLYSUSPEND
+static void configure_sleep(struct synaptics_rmi4_data *rmi4_data)
+{
+ rmi4_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ rmi4_data->early_suspend.suspend = synaptics_rmi4_early_suspend;
+ rmi4_data->early_suspend.resume = synaptics_rmi4_late_resume;
+ register_early_suspend(&rmi4_data->early_suspend);
+
+ return;
+}
+#else
+static void configure_sleep(struct synaptics_rmi4_data *rmi4_data)
+{
+ return;
+}
+#endif
#endif
static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev,
@@ -997,8 +1071,6 @@
rmi4_pdata->i2c_pull_up = of_property_read_bool(np,
"synaptics,i2c-pull-up");
- rmi4_pdata->regulator_en = of_property_read_bool(np,
- "synaptics,reg-en");
rmi4_pdata->x_flip = of_property_read_bool(np, "synaptics,x-flip");
rmi4_pdata->y_flip = of_property_read_bool(np, "synaptics,y-flip");
@@ -1081,17 +1153,25 @@
bool enable)
{
int retval = 0;
- unsigned char intr_status;
+ unsigned char *intr_status;
if (enable) {
if (rmi4_data->irq_enabled)
return retval;
+ intr_status = kzalloc(rmi4_data->num_of_intr_regs, GFP_KERNEL);
+ if (!intr_status) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to alloc memory\n",
+ __func__);
+ return -ENOMEM;
+ }
/* Clear interrupts first */
retval = synaptics_rmi4_i2c_read(rmi4_data,
rmi4_data->f01_data_base_addr + 1,
- &intr_status,
+ intr_status,
rmi4_data->num_of_intr_regs);
+ kfree(intr_status);
if (retval < 0)
return retval;
@@ -1823,25 +1903,23 @@
if (on == false)
goto hw_shutdown;
- if (rmi4_data->board->regulator_en) {
- rmi4_data->vdd = regulator_get(&rmi4_data->i2c_client->dev,
- "vdd");
- if (IS_ERR(rmi4_data->vdd)) {
- dev_err(&rmi4_data->i2c_client->dev,
- "%s: Failed to get vdd regulator\n",
- __func__);
- return PTR_ERR(rmi4_data->vdd);
- }
+ rmi4_data->vdd = regulator_get(&rmi4_data->i2c_client->dev,
+ "vdd");
+ if (IS_ERR(rmi4_data->vdd)) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to get vdd regulator\n",
+ __func__);
+ return PTR_ERR(rmi4_data->vdd);
+ }
- if (regulator_count_voltages(rmi4_data->vdd) > 0) {
- retval = regulator_set_voltage(rmi4_data->vdd,
- RMI4_VTG_MIN_UV, RMI4_VTG_MAX_UV);
- if (retval) {
- dev_err(&rmi4_data->i2c_client->dev,
- "regulator set_vtg failed retval=%d\n",
- retval);
- goto err_set_vtg_vdd;
- }
+ if (regulator_count_voltages(rmi4_data->vdd) > 0) {
+ retval = regulator_set_voltage(rmi4_data->vdd,
+ RMI4_VTG_MIN_UV, RMI4_VTG_MAX_UV);
+ if (retval) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "regulator set_vtg failed retval =%d\n",
+ retval);
+ goto err_set_vtg_vdd;
}
}
@@ -1861,7 +1939,7 @@
RMI4_I2C_VTG_MIN_UV, RMI4_I2C_VTG_MAX_UV);
if (retval) {
dev_err(&rmi4_data->i2c_client->dev,
- "reg set i2c vtg failed retval=%d\n",
+ "reg set i2c vtg failed retval =%d\n",
retval);
goto err_set_vtg_i2c;
}
@@ -1873,22 +1951,18 @@
if (rmi4_data->board->i2c_pull_up)
regulator_put(rmi4_data->vcc_i2c);
err_get_vtg_i2c:
- if (rmi4_data->board->regulator_en)
- if (regulator_count_voltages(rmi4_data->vdd) > 0)
- regulator_set_voltage(rmi4_data->vdd, 0,
- RMI4_VTG_MAX_UV);
+ if (regulator_count_voltages(rmi4_data->vdd) > 0)
+ regulator_set_voltage(rmi4_data->vdd, 0,
+ RMI4_VTG_MAX_UV);
err_set_vtg_vdd:
- if (rmi4_data->board->regulator_en)
- regulator_put(rmi4_data->vdd);
+ regulator_put(rmi4_data->vdd);
return retval;
hw_shutdown:
- if (rmi4_data->board->regulator_en) {
- if (regulator_count_voltages(rmi4_data->vdd) > 0)
- regulator_set_voltage(rmi4_data->vdd, 0,
- RMI4_VTG_MAX_UV);
- regulator_put(rmi4_data->vdd);
- }
+ if (regulator_count_voltages(rmi4_data->vdd) > 0)
+ regulator_set_voltage(rmi4_data->vdd, 0,
+ RMI4_VTG_MAX_UV);
+ regulator_put(rmi4_data->vdd);
if (rmi4_data->board->i2c_pull_up) {
if (regulator_count_voltages(rmi4_data->vcc_i2c) > 0)
regulator_set_voltage(rmi4_data->vcc_i2c, 0,
@@ -1905,23 +1979,21 @@
if (on == false)
goto power_off;
- if (rmi4_data->board->regulator_en) {
- retval = reg_set_optimum_mode_check(rmi4_data->vdd,
- RMI4_ACTIVE_LOAD_UA);
- if (retval < 0) {
- dev_err(&rmi4_data->i2c_client->dev,
- "Regulator vdd set_opt failed rc=%d\n",
- retval);
- return retval;
- }
+ retval = reg_set_optimum_mode_check(rmi4_data->vdd,
+ RMI4_ACTIVE_LOAD_UA);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "Regulator vdd set_opt failed rc=%d\n",
+ retval);
+ return retval;
+ }
- retval = regulator_enable(rmi4_data->vdd);
- if (retval) {
- dev_err(&rmi4_data->i2c_client->dev,
- "Regulator vdd enable failed rc=%d\n",
- retval);
- goto error_reg_en_vdd;
- }
+ retval = regulator_enable(rmi4_data->vdd);
+ if (retval) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "Regulator vdd enable failed rc=%d\n",
+ retval);
+ goto error_reg_en_vdd;
}
if (rmi4_data->board->i2c_pull_up) {
@@ -1948,18 +2020,14 @@
if (rmi4_data->board->i2c_pull_up)
reg_set_optimum_mode_check(rmi4_data->vdd, 0);
error_reg_opt_i2c:
- if (rmi4_data->board->regulator_en)
- regulator_disable(rmi4_data->vdd);
+ regulator_disable(rmi4_data->vdd);
error_reg_en_vdd:
- if (rmi4_data->board->regulator_en)
- reg_set_optimum_mode_check(rmi4_data->vdd, 0);
+ reg_set_optimum_mode_check(rmi4_data->vdd, 0);
return retval;
power_off:
- if (rmi4_data->board->regulator_en) {
- reg_set_optimum_mode_check(rmi4_data->vdd, 0);
- regulator_disable(rmi4_data->vdd);
- }
+ reg_set_optimum_mode_check(rmi4_data->vdd, 0);
+ regulator_disable(rmi4_data->vdd);
if (rmi4_data->board->i2c_pull_up) {
reg_set_optimum_mode_check(rmi4_data->vcc_i2c, 0);
regulator_disable(rmi4_data->vcc_i2c);
@@ -2194,12 +2262,7 @@
goto err_register_input;
}
-#ifdef CONFIG_HAS_EARLYSUSPEND
- rmi4_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
- rmi4_data->early_suspend.suspend = synaptics_rmi4_early_suspend;
- rmi4_data->early_suspend.resume = synaptics_rmi4_late_resume;
- register_early_suspend(&rmi4_data->early_suspend);
-#endif
+ configure_sleep(rmi4_data);
if (!exp_fn_inited) {
mutex_init(&exp_fn_list_mutex);
@@ -2437,7 +2500,27 @@
return;
}
-#ifdef CONFIG_HAS_EARLYSUSPEND
+#if defined(CONFIG_FB)
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct fb_event *evdata = data;
+ int *blank;
+ struct synaptics_rmi4_data *rmi4_data =
+ container_of(self, struct synaptics_rmi4_data, fb_notif);
+
+ if (evdata && evdata->data && event == FB_EVENT_BLANK &&
+ rmi4_data && rmi4_data->i2c_client) {
+ blank = evdata->data;
+ if (*blank == FB_BLANK_UNBLANK)
+ synaptics_rmi4_resume(&(rmi4_data->input_dev->dev));
+ else if (*blank == FB_BLANK_POWERDOWN)
+ synaptics_rmi4_suspend(&(rmi4_data->input_dev->dev));
+ }
+
+ return 0;
+}
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
/**
* synaptics_rmi4_early_suspend()
*
@@ -2492,6 +2575,75 @@
}
#endif
+static int synaptics_rmi4_regulator_lpm(struct synaptics_rmi4_data *rmi4_data,
+ bool on)
+{
+ int retval;
+
+ if (on == false)
+ goto regulator_hpm;
+
+ retval = reg_set_optimum_mode_check(rmi4_data->vdd, RMI4_LPM_LOAD_UA);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "Regulator vcc_ana set_opt failed rc=%d\n",
+ retval);
+ goto fail_regulator_lpm;
+ }
+
+ if (rmi4_data->board->i2c_pull_up) {
+ retval = reg_set_optimum_mode_check(rmi4_data->vcc_i2c,
+ RMI4_I2C_LPM_LOAD_UA);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "Regulator vcc_i2c set_opt failed rc=%d\n",
+ retval);
+ goto fail_regulator_lpm;
+ }
+ }
+
+ return 0;
+
+regulator_hpm:
+
+ retval = reg_set_optimum_mode_check(rmi4_data->vdd,
+ RMI4_ACTIVE_LOAD_UA);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "Regulator vcc_ana set_opt failed rc=%d\n",
+ retval);
+ goto fail_regulator_hpm;
+ }
+
+ if (rmi4_data->board->i2c_pull_up) {
+ retval = reg_set_optimum_mode_check(rmi4_data->vcc_i2c,
+ RMI4_I2C_LOAD_UA);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "Regulator vcc_i2c set_opt failed rc=%d\n",
+ retval);
+ goto fail_regulator_hpm;
+ }
+ }
+
+ return 0;
+
+fail_regulator_lpm:
+ reg_set_optimum_mode_check(rmi4_data->vdd, RMI4_ACTIVE_LOAD_UA);
+ if (rmi4_data->board->i2c_pull_up)
+ reg_set_optimum_mode_check(rmi4_data->vcc_i2c,
+ RMI4_I2C_LOAD_UA);
+
+ return retval;
+
+fail_regulator_hpm:
+ reg_set_optimum_mode_check(rmi4_data->vdd, RMI4_LPM_LOAD_UA);
+ if (rmi4_data->board->i2c_pull_up)
+ reg_set_optimum_mode_check(rmi4_data->vcc_i2c,
+ RMI4_I2C_LPM_LOAD_UA);
+ return retval;
+}
+
/**
* synaptics_rmi4_suspend()
*
@@ -2505,6 +2657,7 @@
static int synaptics_rmi4_suspend(struct device *dev)
{
struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ int retval;
if (!rmi4_data->sensor_sleep) {
rmi4_data->touch_stopped = true;
@@ -2513,6 +2666,12 @@
synaptics_rmi4_sensor_sleep(rmi4_data);
}
+ retval = synaptics_rmi4_regulator_lpm(rmi4_data, true);
+ if (retval < 0) {
+ dev_err(dev, "failed to enter low power mode\n");
+ return retval;
+ }
+
return 0;
}
@@ -2529,6 +2688,13 @@
static int synaptics_rmi4_resume(struct device *dev)
{
struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ int retval;
+
+ retval = synaptics_rmi4_regulator_lpm(rmi4_data, false);
+ if (retval < 0) {
+ dev_err(dev, "failed to enter active power mode\n");
+ return retval;
+ }
synaptics_rmi4_sensor_wake(rmi4_data);
rmi4_data->touch_stopped = false;
@@ -2537,10 +2703,15 @@
return 0;
}
+#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND))
static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = {
.suspend = synaptics_rmi4_suspend,
.resume = synaptics_rmi4_resume,
};
+#else
+static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = {
+};
+#endif
#endif
static const struct i2c_device_id synaptics_rmi4_id_table[] = {
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi4.h b/drivers/input/touchscreen/synaptics_i2c_rmi4.h
index 16b1f8f..681b95c 100644
--- a/drivers/input/touchscreen/synaptics_i2c_rmi4.h
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi4.h
@@ -24,10 +24,14 @@
#define SYNAPTICS_DS4 (1 << 0)
#define SYNAPTICS_DS5 (1 << 1)
#define SYNAPTICS_DSX_DRIVER_PRODUCT SYNAPTICS_DS4
-#define SYNAPTICS_DSX_DRIVER_VERSION 0x1004
+#define SYNAPTICS_DSX_DRIVER_VERSION 0x1005
#include <linux/version.h>
-#ifdef CONFIG_HAS_EARLYSUSPEND
+
+#ifdef CONFIG_FB
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#elif defined CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
@@ -227,6 +231,13 @@
unsigned char *data, unsigned short length);
int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable);
int (*reset_device)(struct synaptics_rmi4_data *rmi4_data);
+#ifdef CONFIG_FB
+ struct notifier_block fb_notif;
+#else
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+#endif
};
enum exp_fn {
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index f35f0e7..6324dff 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -14,12 +14,32 @@
if IOMMU_SUPPORT
# MSM IOMMU support
+
+# MSM_IOMMU always gets selected by whoever wants it.
config MSM_IOMMU
- bool "MSM IOMMU Support"
- depends on ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_APQ8064 || ARCH_MSM8974 || ARCH_MPQ8092 || ARCH_MSM8610 || ARCH_MSM8226 || ARCH_MSMZINC
+ bool
+
+# MSM IOMMUv0 support
+config MSM_IOMMU_V0
+ bool "MSM IOMMUv0 Support"
+ depends on ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_APQ8064 || ARCH_MSM8610
select IOMMU_API
+ select MSM_IOMMU
help
- Support for the IOMMUs found on certain Qualcomm SOCs.
+ Support for the IOMMUs (v0) found on certain Qualcomm SOCs.
+ These IOMMUs allow virtualization of the address space used by most
+ cores within the multimedia subsystem.
+
+ If unsure, say N here.
+
+# MSM IOMMUv1 support
+config MSM_IOMMU_V1
+ bool "MSM IOMMUv1 Support"
+ depends on ARCH_MSM8974 || ARCH_MPQ8092 || ARCH_MSM8226 || ARCH_APQ8084
+ select IOMMU_API
+ select MSM_IOMMU
+ help
+ Support for the IOMMUs (v1) found on certain Qualcomm SOCs.
These IOMMUs allow virtualization of the address space used by most
cores within the multimedia subsystem.
@@ -28,7 +48,7 @@
# MSM IOMMU CPU-GPU sync programming support
config MSM_IOMMU_GPU_SYNC
bool "MSM IOMMU CPU-GPU Sync Support"
- depends on (ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_APQ8064 || ARCH_MSM8930) && MSM_IOMMU && MSM_REMOTE_SPINLOCK_SFPB
+ depends on (ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_APQ8064 || ARCH_MSM8930) && MSM_IOMMU_V0 && MSM_REMOTE_SPINLOCK_SFPB
help
Say Y here if you want to synchronize access to IOMMU configuration
port between CPU and GPU. CPU will grab a remote spinlock before
@@ -55,6 +75,20 @@
section mappings and TLB misses should be quite infrequent.
Most people can probably say Y here.
+config IOMMU_NON_SECURE
+ bool "Turns on programming of secure SMMU by kernel"
+ depends on MSM_IOMMU
+ help
+ Say Y here if you want the kernel to program all SMMUs regardless of
+ whether SMMUs are secure or not. A secure SMMU is an SMMU that has
+ its global address space programmed by the secure environment. In
+ addition some of the context banks might be owned/programmed by the
+ secure environment for a secure SMMU. Enabling this feature can be
+ used during testing when the secure environment is not available
+ and the kernel needs to program all the SMMUs.
+
+ If unsure, say N here.
+
# AMD IOMMU support
config AMD_IOMMU
bool "AMD IOMMU support"
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 096b53e..29a8b39 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -1,9 +1,14 @@
obj-$(CONFIG_IOMMU_API) += iommu.o
-obj-$(CONFIG_MSM_IOMMU) += msm_iommu-v0.o msm_iommu_dev-v0.o
-ifdef CONFIG_OF
-obj-$(CONFIG_MSM_IOMMU) += msm_iommu-v1.o msm_iommu_dev-v1.o msm_iommu_pagetable.o msm_iommu_sec.o
+obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
+obj-$(CONFIG_MSM_IOMMU_V0) += msm_iommu-v0.o msm_iommu_dev-v0.o
+obj-$(CONFIG_MSM_IOMMU_V1) += msm_iommu-v1.o msm_iommu_dev-v1.o msm_iommu_pagetable.o msm_iommu_sec.o
+obj-$(CONFIG_MSM_IOMMU_PMON) += msm_iommu_perfmon.o
+ifdef CONFIG_MSM_IOMMU_V0
+obj-$(CONFIG_MSM_IOMMU_PMON) += msm_iommu_perfmon-v0.o
endif
-obj-$(CONFIG_MSM_IOMMU_PMON) += msm_iommu_perfmon.o msm_iommu_perfmon-v0.o msm_iommu_perfmon-v1.o
+ifdef CONFIG_MSM_IOMMU_V1
+obj-$(CONFIG_MSM_IOMMU_PMON) += msm_iommu_perfmon-v1.o
+endif
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
obj-$(CONFIG_DMAR_TABLE) += dmar.o
diff --git a/drivers/iommu/msm_iommu-v0.c b/drivers/iommu/msm_iommu-v0.c
index c0a4720..49bfdb8 100644
--- a/drivers/iommu/msm_iommu-v0.c
+++ b/drivers/iommu/msm_iommu-v0.c
@@ -31,7 +31,8 @@
#include <mach/iommu_hw-v0.h>
#include <mach/msm_iommu_priv.h>
#include <mach/iommu.h>
-#include <mach/msm_smsm.h>
+#include <mach/msm_smem.h>
+#include <mach/msm_bus.h>
#define MRC(reg, processor, op1, crn, crm, op2) \
__asm__ __volatile__ ( \
@@ -51,9 +52,8 @@
#define MSM_IOMMU_ATTR_CACHED_WB_NWA 0x2
#define MSM_IOMMU_ATTR_CACHED_WT 0x3
-struct bus_type msm_iommu_sec_bus_type = {
- .name = "msm_iommu_sec_bus",
-};
+static int msm_iommu_unmap_range(struct iommu_domain *domain, unsigned int va,
+ unsigned int len);
static inline void clean_pte(unsigned long *start, unsigned long *end,
int redirect)
@@ -132,6 +132,20 @@
return msm_iommu_remote_lock.lock;
}
+static int apply_bus_vote(struct msm_iommu_drvdata *drvdata, unsigned int vote)
+{
+ int ret = 0;
+
+ if (drvdata->bus_client) {
+ ret = msm_bus_scale_client_update_request(drvdata->bus_client,
+ vote);
+ if (ret)
+ pr_err("%s: Failed to vote for bus: %d\n", __func__,
+ vote);
+ }
+ return ret;
+}
+
static int __enable_clocks(struct msm_iommu_drvdata *drvdata)
{
int ret;
@@ -145,12 +159,26 @@
if (ret)
clk_disable_unprepare(drvdata->pclk);
}
+
+ if (ret)
+ goto fail;
+
+ if (drvdata->aclk) {
+ ret = clk_prepare_enable(drvdata->aclk);
+ if (ret) {
+ clk_disable_unprepare(drvdata->clk);
+ clk_disable_unprepare(drvdata->pclk);
+ }
+ }
+
fail:
return ret;
}
static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
{
+ if (drvdata->aclk)
+ clk_disable_unprepare(drvdata->aclk);
if (drvdata->clk)
clk_disable_unprepare(drvdata->clk);
clk_disable_unprepare(drvdata->pclk);
@@ -167,6 +195,11 @@
/* No need to do anything. IOMMUv0 is always on. */
}
+static void *_iommu_lock_initialize(void)
+{
+ return msm_iommu_lock_initialize();
+}
+
static void _iommu_lock_acquire(void)
{
msm_iommu_lock();
@@ -180,12 +213,13 @@
struct iommu_access_ops iommu_access_ops_v0 = {
.iommu_power_on = __enable_regulators,
.iommu_power_off = __disable_regulators,
+ .iommu_bus_vote = apply_bus_vote,
.iommu_clk_on = __enable_clocks,
.iommu_clk_off = __disable_clocks,
+ .iommu_lock_initialize = _iommu_lock_initialize,
.iommu_lock_acquire = _iommu_lock_acquire,
.iommu_lock_release = _iommu_lock_release,
};
-EXPORT_SYMBOL(iommu_access_ops_v0);
static int __flush_iotlb_va(struct iommu_domain *domain, unsigned int va)
{
@@ -483,6 +517,11 @@
goto unlock;
}
+ ret = apply_bus_vote(iommu_drvdata, 1);
+
+ if (ret)
+ goto unlock;
+
ret = __enable_clocks(iommu_drvdata);
if (ret)
goto unlock;
@@ -549,6 +588,9 @@
msm_iommu_remote_spin_unlock();
__disable_clocks(iommu_drvdata);
+
+ apply_bus_vote(iommu_drvdata, 0);
+
list_del_init(&ctx_drvdata->attached_elm);
ctx_drvdata->attached_domain = NULL;
unlock:
@@ -953,6 +995,7 @@
int prot)
{
unsigned int pa;
+ unsigned int start_va = va;
unsigned int offset = 0;
unsigned long *fl_table;
unsigned long *fl_pte;
@@ -1026,12 +1069,6 @@
chunk_offset = 0;
sg = sg_next(sg);
pa = get_phys_addr(sg);
- if (pa == 0) {
- pr_debug("No dma address for sg %p\n",
- sg);
- ret = -EINVAL;
- goto fail;
- }
}
continue;
}
@@ -1085,12 +1122,6 @@
chunk_offset = 0;
sg = sg_next(sg);
pa = get_phys_addr(sg);
- if (pa == 0) {
- pr_debug("No dma address for sg %p\n",
- sg);
- ret = -EINVAL;
- goto fail;
- }
}
}
@@ -1103,6 +1134,8 @@
__flush_iotlb(domain);
fail:
mutex_unlock(&msm_iommu_lock);
+ if (ret && offset > 0)
+ msm_iommu_unmap_range(domain, start_va, offset);
return ret;
}
@@ -1243,7 +1276,7 @@
return 0;
}
-static void print_ctx_regs(void __iomem *base, int ctx)
+static void __print_ctx_regs(void __iomem *base, int ctx)
{
unsigned int fsr = GET_FSR(base, ctx);
pr_err("FAR = %08x PAR = %08x\n",
@@ -1309,7 +1342,7 @@
pr_err("name = %s\n", drvdata->name);
pr_err("context = %s (%d)\n", ctx_drvdata->name, num);
pr_err("Interesting registers:\n");
- print_ctx_regs(base, num);
+ __print_ctx_regs(base, num);
}
SET_FSR(base, num, fsr);
diff --git a/drivers/iommu/msm_iommu-v1.c b/drivers/iommu/msm_iommu-v1.c
index 8a26003..653487b 100644
--- a/drivers/iommu/msm_iommu-v1.c
+++ b/drivers/iommu/msm_iommu-v1.c
@@ -32,6 +32,7 @@
#include <mach/iommu.h>
#include <mach/msm_iommu_priv.h>
#include <mach/iommu_perfmon.h>
+#include <mach/msm_bus.h>
#include "msm_iommu_pagetable.h"
/* bitmap of the page sizes currently supported */
@@ -41,15 +42,18 @@
static int __enable_regulators(struct msm_iommu_drvdata *drvdata)
{
- int ret = regulator_enable(drvdata->gdsc);
- if (ret)
- goto fail;
+ int ret = 0;
+ if (drvdata->gdsc) {
+ ret = regulator_enable(drvdata->gdsc);
+ if (ret)
+ goto fail;
- if (drvdata->alt_gdsc)
- ret = regulator_enable(drvdata->alt_gdsc);
+ if (drvdata->alt_gdsc)
+ ret = regulator_enable(drvdata->alt_gdsc);
- if (ret)
- regulator_disable(drvdata->gdsc);
+ if (ret)
+ regulator_disable(drvdata->gdsc);
+ }
fail:
return ret;
}
@@ -59,7 +63,22 @@
if (drvdata->alt_gdsc)
regulator_disable(drvdata->alt_gdsc);
- regulator_disable(drvdata->gdsc);
+ if (drvdata->gdsc)
+ regulator_disable(drvdata->gdsc);
+}
+
+static int apply_bus_vote(struct msm_iommu_drvdata *drvdata, unsigned int vote)
+{
+ int ret = 0;
+
+ if (drvdata->bus_client) {
+ ret = msm_bus_scale_client_update_request(drvdata->bus_client,
+ vote);
+ if (ret)
+ pr_err("%s: Failed to vote for bus: %d\n", __func__,
+ vote);
+ }
+ return ret;
}
static int __enable_clocks(struct msm_iommu_drvdata *drvdata)
@@ -116,12 +135,12 @@
struct iommu_access_ops iommu_access_ops_v1 = {
.iommu_power_on = __enable_regulators,
.iommu_power_off = __disable_regulators,
+ .iommu_bus_vote = apply_bus_vote,
.iommu_clk_on = __enable_clocks,
.iommu_clk_off = __disable_clocks,
.iommu_lock_acquire = _iommu_lock_acquire,
.iommu_lock_release = _iommu_lock_release,
};
-EXPORT_SYMBOL(iommu_access_ops_v1);
void iommu_halt(const struct msm_iommu_drvdata *iommu_drvdata)
{
@@ -240,12 +259,46 @@
mb();
}
+#ifdef CONFIG_IOMMU_NON_SECURE
+static void __reset_iommu_secure(void __iomem *base)
+{
+ SET_NSACR(base, 0);
+ SET_NSCR2(base, 0);
+ SET_NSGFAR(base, 0);
+ SET_NSGFSRRESTORE(base, 0);
+ mb();
+}
+
+static void __program_iommu_secure(void __iomem *base)
+{
+ SET_NSCR0_SMCFCFG(base, 1);
+ SET_NSCR0_USFCFG(base, 1);
+ SET_NSCR0_STALLD(base, 1);
+ SET_NSCR0_GCFGFIE(base, 1);
+ SET_NSCR0_GCFGFRE(base, 1);
+ SET_NSCR0_GFIE(base, 1);
+ SET_NSCR0_GFRE(base, 1);
+ SET_NSCR0_CLIENTPD(base, 0);
+}
+
+#else
+static inline void __reset_iommu_secure(void __iomem *base)
+{
+}
+
+static inline void __program_iommu_secure(void __iomem *base)
+{
+}
+
+#endif
+
/*
* May only be called for non-secure iommus
*/
static void __program_iommu(void __iomem *base)
{
__reset_iommu(base);
+ __reset_iommu_secure(base);
SET_CR0_SMCFCFG(base, 1);
SET_CR0_USFCFG(base, 1);
@@ -256,6 +309,8 @@
SET_CR0_GFRE(base, 1);
SET_CR0_CLIENTPD(base, 0);
+ __program_iommu_secure(base);
+
mb(); /* Make sure writes complete before returning */
}
@@ -513,6 +568,10 @@
if (ret)
goto fail;
+ ret = apply_bus_vote(iommu_drvdata, 1);
+ if (ret)
+ goto fail;
+
ret = __enable_clocks(iommu_drvdata);
if (ret) {
__disable_regulators(iommu_drvdata);
@@ -602,6 +661,8 @@
__disable_clocks(iommu_drvdata);
+ apply_bus_vote(iommu_drvdata, 0);
+
__disable_regulators(iommu_drvdata);
list_del_init(&ctx_drvdata->attached_elm);
@@ -761,10 +822,12 @@
return 0;
}
-static void print_ctx_regs(void __iomem *base, int ctx, unsigned int fsr)
+void print_ctx_regs(struct msm_iommu_context_regs *regs)
{
+ uint32_t fsr = regs->fsr;
+
pr_err("FAR = %08x PAR = %08x\n",
- GET_FAR(base, ctx), GET_PAR(base, ctx));
+ regs->far, regs->par);
pr_err("FSR = %08x [%s%s%s%s%s%s%s%s%s]\n", fsr,
(fsr & 0x02) ? "TF " : "",
(fsr & 0x04) ? "AFF " : "",
@@ -777,13 +840,31 @@
(fsr & 0x80000000) ? "MULTI " : "");
pr_err("FSYNR0 = %08x FSYNR1 = %08x\n",
- GET_FSYNR0(base, ctx), GET_FSYNR1(base, ctx));
+ regs->fsynr0, regs->fsynr1);
pr_err("TTBR0 = %08x TTBR1 = %08x\n",
- GET_TTBR0(base, ctx), GET_TTBR1(base, ctx));
+ regs->ttbr0, regs->ttbr1);
pr_err("SCTLR = %08x ACTLR = %08x\n",
- GET_SCTLR(base, ctx), GET_ACTLR(base, ctx));
+ regs->sctlr, regs->actlr);
pr_err("PRRR = %08x NMRR = %08x\n",
- GET_PRRR(base, ctx), GET_NMRR(base, ctx));
+ regs->prrr, regs->nmrr);
+}
+
+static void __print_ctx_regs(void __iomem *base, int ctx, unsigned int fsr)
+{
+ struct msm_iommu_context_regs regs = {
+ .far = GET_FAR(base, ctx),
+ .par = GET_PAR(base, ctx),
+ .fsr = fsr,
+ .fsynr0 = GET_FSYNR0(base, ctx),
+ .fsynr1 = GET_FSYNR1(base, ctx),
+ .ttbr0 = GET_TTBR0(base, ctx),
+ .ttbr1 = GET_TTBR1(base, ctx),
+ .sctlr = GET_SCTLR(base, ctx),
+ .actlr = GET_ACTLR(base, ctx),
+ .prrr = GET_PRRR(base, ctx),
+ .nmrr = GET_NMRR(base, ctx),
+ };
+ print_ctx_regs(®s);
}
irqreturn_t msm_iommu_fault_handler_v2(int irq, void *dev_id)
@@ -804,6 +885,19 @@
ctx_drvdata = dev_get_drvdata(&pdev->dev);
BUG_ON(!ctx_drvdata);
+ if (!drvdata->ctx_attach_count) {
+ pr_err("Unexpected IOMMU page fault!\n");
+ pr_err("name = %s\n", drvdata->name);
+ pr_err("Power is OFF. Unable to read page fault information\n");
+ /*
+ * We cannot determine which context bank caused the issue so
+ * we just return handled here to ensure IRQ handler code is
+ * happy
+ */
+ ret = IRQ_HANDLED;
+ goto fail;
+ }
+
ret = __enable_clocks(drvdata);
if (ret) {
ret = IRQ_NONE;
@@ -826,7 +920,7 @@
pr_err("context = %s (%d)\n", ctx_drvdata->name,
ctx_drvdata->num);
pr_err("Interesting registers:\n");
- print_ctx_regs(drvdata->base, ctx_drvdata->num, fsr);
+ __print_ctx_regs(drvdata->base, ctx_drvdata->num, fsr);
}
SET_FSR(drvdata->base, ctx_drvdata->num, fsr);
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
new file mode 100644
index 0000000..ebecd11
--- /dev/null
+++ b/drivers/iommu/msm_iommu.c
@@ -0,0 +1,97 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/export.h>
+#include <linux/iommu.h>
+#include <mach/iommu.h>
+
+static DEFINE_MUTEX(iommu_list_lock);
+static LIST_HEAD(iommu_list);
+
+static struct iommu_access_ops *iommu_access_ops;
+
+struct bus_type msm_iommu_sec_bus_type = {
+ .name = "msm_iommu_sec_bus",
+};
+
+void msm_set_iommu_access_ops(struct iommu_access_ops *ops)
+{
+ iommu_access_ops = ops;
+}
+
+struct iommu_access_ops *msm_get_iommu_access_ops()
+{
+ BUG_ON(iommu_access_ops == NULL);
+ return iommu_access_ops;
+}
+EXPORT_SYMBOL(msm_get_iommu_access_ops);
+
+void msm_iommu_add_drv(struct msm_iommu_drvdata *drv)
+{
+ mutex_lock(&iommu_list_lock);
+ list_add(&drv->list, &iommu_list);
+ mutex_unlock(&iommu_list_lock);
+}
+
+void msm_iommu_remove_drv(struct msm_iommu_drvdata *drv)
+{
+ mutex_lock(&iommu_list_lock);
+ list_del(&drv->list);
+ mutex_unlock(&iommu_list_lock);
+}
+
+static int find_iommu_ctx(struct device *dev, void *data)
+{
+ struct msm_iommu_ctx_drvdata *c;
+
+ c = dev_get_drvdata(dev);
+ if (!c || !c->name)
+ return 0;
+
+ return !strcmp(data, c->name);
+}
+
+static struct device *find_context(struct device *dev, const char *name)
+{
+ return device_find_child(dev, (void *)name, find_iommu_ctx);
+}
+
+struct device *msm_iommu_get_ctx(const char *ctx_name)
+{
+ struct msm_iommu_drvdata *drv;
+ struct device *dev = NULL;
+
+ mutex_lock(&iommu_list_lock);
+ list_for_each_entry(drv, &iommu_list, list) {
+ dev = find_context(drv->dev, ctx_name);
+ if (dev)
+ break;
+ }
+ mutex_unlock(&iommu_list_lock);
+
+ put_device(dev);
+
+ if (!dev || !dev_get_drvdata(dev)) {
+ pr_debug("Could not find context <%s>\n", ctx_name);
+ dev = ERR_PTR(-EPROBE_DEFER);
+ }
+
+ return dev;
+}
+EXPORT_SYMBOL(msm_iommu_get_ctx);
+
diff --git a/drivers/iommu/msm_iommu_dev-v0.c b/drivers/iommu/msm_iommu_dev-v0.c
index 7ae0b21..2d0fba2 100644
--- a/drivers/iommu/msm_iommu_dev-v0.c
+++ b/drivers/iommu/msm_iommu_dev-v0.c
@@ -30,60 +30,10 @@
#include <mach/iommu_perfmon.h>
#include <mach/iommu_hw-v0.h>
#include <mach/iommu.h>
+#include <mach/msm_bus.h>
-static DEFINE_MUTEX(iommu_list_lock);
-static LIST_HEAD(iommu_list);
-
-void msm_iommu_add_drv(struct msm_iommu_drvdata *drv)
-{
- mutex_lock(&iommu_list_lock);
- list_add(&drv->list, &iommu_list);
- mutex_unlock(&iommu_list_lock);
-}
-
-void msm_iommu_remove_drv(struct msm_iommu_drvdata *drv)
-{
- mutex_lock(&iommu_list_lock);
- list_del(&drv->list);
- mutex_unlock(&iommu_list_lock);
-}
-
-static int find_iommu_ctx(struct device *dev, void *data)
-{
- struct msm_iommu_ctx_drvdata *c;
-
- c = dev_get_drvdata(dev);
- if (!c || !c->name)
- return 0;
-
- return !strcmp(data, c->name);
-}
-
-static struct device *find_context(struct device *dev, const char *name)
-{
- return device_find_child(dev, (void *)name, find_iommu_ctx);
-}
-
-struct device *msm_iommu_get_ctx(const char *ctx_name)
-{
- struct msm_iommu_drvdata *drv;
- struct device *dev = NULL;
-
- mutex_lock(&iommu_list_lock);
- list_for_each_entry(drv, &iommu_list, list) {
- dev = find_context(drv->dev, ctx_name);
- if (dev)
- break;
- }
- mutex_unlock(&iommu_list_lock);
-
- if (!dev || !dev_get_drvdata(dev))
- pr_err("Could not find context <%s>\n", ctx_name);
- put_device(dev);
-
- return dev;
-}
-EXPORT_SYMBOL(msm_iommu_get_ctx);
+static struct of_device_id msm_iommu_v0_ctx_match_table[];
+static struct iommu_access_ops *msm_iommu_access_ops;
static void msm_iommu_reset(void __iomem *base, void __iomem *glb_base, int ncb)
{
@@ -128,68 +78,24 @@
mb();
}
-static int msm_iommu_parse_dt(struct platform_device *pdev,
- struct msm_iommu_drvdata *drvdata)
-{
-#ifdef CONFIG_OF_DEVICE
- struct device_node *child;
- struct resource *r;
- u32 glb_offset = 0;
- int ret;
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r) {
- pr_err("%s: Missing property reg\n", __func__);
- return -EINVAL;
- }
- drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
- if (!drvdata->base) {
- pr_err("%s: Unable to ioremap %pr\n", __func__, r);
- return -ENOMEM;
- }
- drvdata->glb_base = drvdata->base;
-
- if (!of_property_read_u32(pdev->dev.of_node, "qcom,glb-offset",
- &glb_offset)) {
- drvdata->glb_base += glb_offset;
- } else {
- pr_err("%s: Missing property qcom,glb-offset\n", __func__);
- return -EINVAL;
- }
-
- for_each_child_of_node(pdev->dev.of_node, child) {
- drvdata->ncb++;
- if (!of_platform_device_create(child, NULL, &pdev->dev))
- pr_err("Failed to create %s device\n", child->name);
- }
-
- ret = of_property_read_string(pdev->dev.of_node, "label",
- &drvdata->name);
- if (ret) {
- pr_err("%s: Missing property label\n", __func__);
- return -EINVAL;
- }
- drvdata->sec_id = -1;
- drvdata->ttbr_split = 0;
-#endif
- return 0;
-}
-
static int __get_clocks(struct platform_device *pdev,
- struct msm_iommu_drvdata *drvdata)
+ struct msm_iommu_drvdata *drvdata,
+ int needs_alt_core_clk)
{
int ret = 0;
- drvdata->pclk = clk_get(&pdev->dev, "iface_clk");
+ drvdata->pclk = devm_clk_get(&pdev->dev, "iface_clk");
if (IS_ERR(drvdata->pclk)) {
ret = PTR_ERR(drvdata->pclk);
drvdata->pclk = NULL;
- pr_err("Unable to get %s clock for %s IOMMU device\n",
- dev_name(&pdev->dev), drvdata->name);
+ if (ret != -EPROBE_DEFER) {
+ pr_err("Unable to get %s clock for %s IOMMU device\n",
+ dev_name(&pdev->dev), drvdata->name);
+ }
goto fail;
}
- drvdata->clk = clk_get(&pdev->dev, "core_clk");
+ drvdata->clk = devm_clk_get(&pdev->dev, "core_clk");
if (!IS_ERR(drvdata->clk)) {
if (clk_get_rate(drvdata->clk) == 0) {
@@ -199,42 +105,146 @@
} else {
drvdata->clk = NULL;
}
+
+ if (needs_alt_core_clk) {
+ drvdata->aclk = devm_clk_get(&pdev->dev, "alt_core_clk");
+ if (IS_ERR(drvdata->aclk)) {
+ ret = PTR_ERR(drvdata->aclk);
+ goto fail;
+ }
+ }
+
+ if (drvdata->aclk && clk_get_rate(drvdata->aclk) == 0) {
+ ret = clk_round_rate(drvdata->aclk, 1000);
+ clk_set_rate(drvdata->aclk, ret);
+ }
+
return 0;
fail:
return ret;
}
-static void __put_clocks(struct msm_iommu_drvdata *drvdata)
-{
- if (drvdata->clk)
- clk_put(drvdata->clk);
- clk_put(drvdata->pclk);
-}
+#ifdef CONFIG_OF_DEVICE
-static int __enable_clocks(struct msm_iommu_drvdata *drvdata)
+static int __get_bus_vote_client(struct platform_device *pdev,
+ struct msm_iommu_drvdata *drvdata)
{
- int ret;
+ int ret = 0;
+ struct msm_bus_scale_pdata *bs_table;
+ const char *dummy;
- ret = clk_prepare_enable(drvdata->pclk);
+ /* Check whether bus scaling has been specified for this node */
+ ret = of_property_read_string(pdev->dev.of_node, "qcom,msm-bus,name",
+ &dummy);
if (ret)
- goto fail;
+ return 0;
- if (drvdata->clk) {
- ret = clk_prepare_enable(drvdata->clk);
- if (ret)
- clk_disable_unprepare(drvdata->pclk);
+ bs_table = msm_bus_cl_get_pdata(pdev);
+
+ if (bs_table) {
+ drvdata->bus_client = msm_bus_scale_register_client(bs_table);
+ if (IS_ERR(&drvdata->bus_client)) {
+ pr_err("%s(): Bus client register failed.\n", __func__);
+ ret = -EINVAL;
+ }
}
-fail:
return ret;
}
-static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
+static void __put_bus_vote_client(struct msm_iommu_drvdata *drvdata)
{
- if (drvdata->clk)
- clk_disable_unprepare(drvdata->clk);
- clk_disable_unprepare(drvdata->pclk);
+ msm_bus_scale_unregister_client(drvdata->bus_client);
+ drvdata->bus_client = 0;
}
+static int msm_iommu_parse_dt(struct platform_device *pdev,
+ struct msm_iommu_drvdata *drvdata)
+{
+ struct device_node *child;
+ struct resource *r;
+ u32 glb_offset = 0;
+ int ret = 0;
+ int needs_alt_core_clk;
+
+ ret = __get_bus_vote_client(pdev, drvdata);
+
+ if (ret)
+ goto fail;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ pr_err("%s: Missing property reg\n", __func__);
+ ret = -EINVAL;
+ goto fail;
+ }
+ drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+ if (!drvdata->base) {
+ pr_err("%s: Unable to ioremap %pr\n", __func__, r);
+ ret = -ENOMEM;
+ goto fail;
+ }
+ drvdata->glb_base = drvdata->base;
+
+ if (!of_property_read_u32(pdev->dev.of_node, "qcom,glb-offset",
+ &glb_offset)) {
+ drvdata->glb_base += glb_offset;
+ } else {
+ pr_err("%s: Missing property qcom,glb-offset\n", __func__);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ for_each_child_of_node(pdev->dev.of_node, child)
+ drvdata->ncb++;
+
+ ret = of_property_read_string(pdev->dev.of_node, "label",
+ &drvdata->name);
+ if (ret) {
+ pr_err("%s: Missing property label\n", __func__);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ needs_alt_core_clk = of_property_read_bool(pdev->dev.of_node,
+ "qcom,needs-alt-core-clk");
+
+ ret = __get_clocks(pdev, drvdata, needs_alt_core_clk);
+
+ if (ret)
+ goto fail;
+
+ drvdata->sec_id = -1;
+ drvdata->ttbr_split = 0;
+
+ ret = of_platform_populate(pdev->dev.of_node,
+ msm_iommu_v0_ctx_match_table,
+ NULL, &pdev->dev);
+ if (ret) {
+ pr_err("Failed to create iommu context device\n");
+ goto fail;
+ }
+
+ return ret;
+
+fail:
+ __put_bus_vote_client(drvdata);
+ return ret;
+}
+
+#else
+static int msm_iommu_parse_dt(struct platform_device *pdev,
+ struct msm_iommu_drvdata *drvdata)
+{
+ return 0;
+}
+
+static void __put_bus_vote_client(struct msm_iommu_drvdata *drvdata)
+{
+
+}
+
+#endif
+
/*
* Do a basic check of the IOMMU by performing an ATS operation
* on context bank 0.
@@ -331,7 +341,7 @@
if (!drvdata) {
ret = -ENOMEM;
- goto fail;
+ goto fail_mem;
}
if (pdev->dev.of_node) {
@@ -342,6 +352,11 @@
struct resource *r, *r2;
resource_size_t len;
+ ret = __get_clocks(pdev, drvdata, 0);
+
+ if (ret)
+ goto fail;
+
r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"physbase");
@@ -352,7 +367,8 @@
len = resource_size(r);
- r2 = request_mem_region(r->start, len, r->name);
+ r2 = devm_request_mem_region(&pdev->dev, r->start,
+ len, r->name);
if (!r2) {
pr_err("Could not request memory region: %pr\n", r);
ret = -EBUSY;
@@ -382,12 +398,7 @@
drvdata->dev = &pdev->dev;
- ret = __get_clocks(pdev, drvdata);
-
- if (ret)
- goto fail;
-
- __enable_clocks(drvdata);
+ msm_iommu_access_ops->iommu_clk_on(drvdata);
msm_iommu_reset(drvdata->base, drvdata->glb_base, drvdata->ncb);
@@ -395,14 +406,14 @@
if (ret)
goto fail_clk;
+ msm_iommu_access_ops->iommu_clk_off(drvdata);
+
pr_info("device %s mapped at %p, with %d ctx banks\n",
drvdata->name, drvdata->base, drvdata->ncb);
msm_iommu_add_drv(drvdata);
platform_set_drvdata(pdev, drvdata);
- __disable_clocks(drvdata);
-
pmon_info = msm_iommu_pm_alloc(&pdev->dev);
if (pmon_info != NULL) {
ret = msm_iommu_pmon_parse_dt(pdev, pmon_info);
@@ -411,7 +422,7 @@
pr_info("%s: pmon not available.\n", drvdata->name);
} else {
pmon_info->iommu.base = drvdata->base;
- pmon_info->iommu.ops = &iommu_access_ops_v0;
+ pmon_info->iommu.ops = msm_iommu_access_ops;
pmon_info->iommu.hw_ops = iommu_pm_get_hw_ops_v0();
pmon_info->iommu.iommu_name = drvdata->name;
pmon_info->iommu.always_on = 1;
@@ -430,9 +441,10 @@
return 0;
fail_clk:
- __disable_clocks(drvdata);
- __put_clocks(drvdata);
+ msm_iommu_access_ops->iommu_clk_off(drvdata);
fail:
+ __put_bus_vote_client(drvdata);
+fail_mem:
return ret;
}
@@ -440,12 +452,13 @@
{
struct msm_iommu_drvdata *drv = NULL;
+ msm_iommu_pm_iommu_unregister(&pdev->dev);
+ msm_iommu_pm_free(&pdev->dev);
+
drv = platform_get_drvdata(pdev);
if (drv) {
+ __put_bus_vote_client(drv);
msm_iommu_remove_drv(drv);
- if (drv->clk)
- clk_put(drv->clk);
- clk_put(drv->pclk);
platform_set_drvdata(pdev, NULL);
}
return 0;
@@ -461,26 +474,28 @@
irq = platform_get_irq(pdev, 0);
if (irq > 0) {
- ret = request_threaded_irq(irq, NULL,
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
msm_iommu_fault_handler,
IRQF_ONESHOT | IRQF_SHARED,
"msm_iommu_nonsecure_irq", ctx_drvdata);
if (ret) {
pr_err("Request IRQ %d failed with ret=%d\n", irq, ret);
- return ret;
+ goto out;
}
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
pr_err("Could not find reg property for context bank\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
ret = of_address_to_resource(pdev->dev.parent->of_node, 0, &rp);
if (ret) {
pr_err("of_address_to_resource failed\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
/* Calculate the context bank number using the base addresses. CB0
@@ -491,29 +506,34 @@
if (of_property_read_string(pdev->dev.of_node, "label",
&ctx_drvdata->name)) {
pr_err("Could not find label property\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
if (!of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-mids",
&nmid_array_size)) {
pr_err("Could not find iommu-ctx-mids property\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
if (nmid_array_size >= sizeof(ctx_drvdata->sids)) {
pr_err("Too many mids defined - array size: %u, mids size: %u\n",
nmid_array_size, sizeof(ctx_drvdata->sids));
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
nmid = nmid_array_size / sizeof(*ctx_drvdata->sids);
if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-ctx-mids",
ctx_drvdata->sids, nmid)) {
pr_err("Could not find iommu-ctx-mids property\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
ctx_drvdata->nsid = nmid;
- return 0;
+out:
+ return ret;
}
static void __program_m2v_tables(struct msm_iommu_drvdata *drvdata,
@@ -563,7 +583,7 @@
drvdata = dev_get_drvdata(pdev->dev.parent);
if (!drvdata) {
- ret = -ENODEV;
+ ret = -EPROBE_DEFER;
goto fail;
}
@@ -581,8 +601,10 @@
if (pdev->dev.of_node) {
ret = msm_iommu_ctx_parse_dt(pdev, ctx_drvdata);
- if (ret)
+ if (ret) {
+ platform_set_drvdata(pdev, NULL);
goto fail;
+ }
} else if (pdev->dev.platform_data) {
struct msm_iommu_ctx_dev *c = pdev->dev.platform_data;
@@ -604,7 +626,8 @@
goto fail;
}
- ret = request_threaded_irq(irq, NULL, msm_iommu_fault_handler,
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ msm_iommu_fault_handler,
IRQF_ONESHOT | IRQF_SHARED,
"msm_iommu_nonsecure_irq", ctx_drvdata);
@@ -618,9 +641,9 @@
goto fail;
}
- __enable_clocks(drvdata);
+ msm_iommu_access_ops->iommu_clk_on(drvdata);
__program_m2v_tables(drvdata, ctx_drvdata);
- __disable_clocks(drvdata);
+ msm_iommu_access_ops->iommu_clk_off(drvdata);
dev_info(&pdev->dev, "context %s using bank %d\n", ctx_drvdata->name,
ctx_drvdata->num);
@@ -650,15 +673,15 @@
.remove = __devexit_p(msm_iommu_remove),
};
-static struct of_device_id msm_iommu_ctx_match_table[] = {
- { .name = "qcom,iommu-ctx", },
+static struct of_device_id msm_iommu_v0_ctx_match_table[] = {
+ { .compatible = "qcom,msm-smmu-v0-ctx", },
{}
};
static struct platform_driver msm_iommu_ctx_driver = {
.driver = {
.name = "msm_iommu_ctx",
- .of_match_table = msm_iommu_ctx_match_table,
+ .of_match_table = msm_iommu_v0_ctx_match_table,
},
.probe = msm_iommu_ctx_probe,
.remove = __devexit_p(msm_iommu_ctx_remove),
@@ -667,6 +690,11 @@
static int __init msm_iommu_driver_init(void)
{
int ret;
+
+ if (msm_soc_version_supports_iommu_v0()) {
+ msm_set_iommu_access_ops(&iommu_access_ops_v0);
+ msm_iommu_access_ops = msm_get_iommu_access_ops();
+ }
ret = platform_driver_register(&msm_iommu_driver);
if (ret != 0) {
pr_err("Failed to register IOMMU driver\n");
diff --git a/drivers/iommu/msm_iommu_dev-v1.c b/drivers/iommu/msm_iommu_dev-v1.c
index 418a086..119a126 100644
--- a/drivers/iommu/msm_iommu_dev-v1.c
+++ b/drivers/iommu/msm_iommu_dev-v1.c
@@ -28,6 +28,9 @@
#include <mach/iommu_hw-v1.h>
#include <mach/iommu.h>
#include <mach/iommu_perfmon.h>
+#include <mach/msm_bus.h>
+
+static struct of_device_id msm_iommu_v1_ctx_match_table[];
static int msm_iommu_parse_bfb_settings(struct platform_device *pdev,
struct msm_iommu_drvdata *drvdata)
@@ -84,6 +87,63 @@
return 0;
}
+static int __get_bus_vote_client(struct platform_device *pdev,
+ struct msm_iommu_drvdata *drvdata)
+{
+ int ret = 0;
+ struct msm_bus_scale_pdata *bs_table;
+ const char *dummy;
+
+ /* Check whether bus scaling has been specified for this node */
+ ret = of_property_read_string(pdev->dev.of_node, "qcom,msm-bus,name",
+ &dummy);
+ if (ret)
+ return 0;
+
+ bs_table = msm_bus_cl_get_pdata(pdev);
+
+ if (bs_table) {
+ drvdata->bus_client = msm_bus_scale_register_client(bs_table);
+ if (IS_ERR(&drvdata->bus_client)) {
+ pr_err("%s(): Bus client register failed.\n", __func__);
+ ret = -EINVAL;
+ }
+ }
+ return ret;
+}
+
+static void __put_bus_vote_client(struct msm_iommu_drvdata *drvdata)
+{
+ msm_bus_scale_unregister_client(drvdata->bus_client);
+ drvdata->bus_client = 0;
+}
+
+#ifdef CONFIG_IOMMU_NON_SECURE
+static inline void get_secure_id(struct device_node *node,
+ struct msm_iommu_drvdata *drvdata)
+{
+}
+
+static inline void get_secure_ctx(struct device_node *node,
+ struct msm_iommu_ctx_drvdata *ctx_drvdata)
+{
+ ctx_drvdata->secure_context = 0;
+}
+#else
+static void get_secure_id(struct device_node *node,
+ struct msm_iommu_drvdata *drvdata)
+{
+ of_property_read_u32(node, "qcom,iommu-secure-id", &drvdata->sec_id);
+}
+
+static void get_secure_ctx(struct device_node *node,
+ struct msm_iommu_ctx_drvdata *ctx_drvdata)
+{
+ ctx_drvdata->secure_context =
+ of_property_read_bool(node, "qcom,secure-context");
+}
+#endif
+
static int msm_iommu_parse_dt(struct platform_device *pdev,
struct msm_iommu_drvdata *drvdata)
{
@@ -92,17 +152,18 @@
struct resource *r;
drvdata->dev = &pdev->dev;
- msm_iommu_add_drv(drvdata);
+
+ ret = __get_bus_vote_client(pdev, drvdata);
+
+ if (ret)
+ goto fail;
ret = msm_iommu_parse_bfb_settings(pdev, drvdata);
if (ret)
goto fail;
- for_each_child_of_node(pdev->dev.of_node, child) {
+ for_each_child_of_node(pdev->dev.of_node, child)
drvdata->ncb++;
- if (!of_platform_device_create(child, NULL, &pdev->dev))
- pr_err("Failed to create %s device\n", child->name);
- }
drvdata->asid = devm_kzalloc(&pdev->dev, drvdata->ncb * sizeof(int),
GFP_KERNEL);
@@ -119,8 +180,7 @@
goto fail;
drvdata->sec_id = -1;
- of_property_read_u32(pdev->dev.of_node, "qcom,iommu-secure-id",
- &drvdata->sec_id);
+ get_secure_id(pdev->dev.of_node, drvdata);
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clk_base");
if (r) {
@@ -137,8 +197,15 @@
drvdata->halt_enabled = of_property_read_bool(pdev->dev.of_node,
"qcom,iommu-enable-halt");
- return 0;
+ ret = of_platform_populate(pdev->dev.of_node,
+ msm_iommu_v1_ctx_match_table,
+ NULL, &pdev->dev);
+ if (ret)
+ pr_err("Failed to create iommu context device\n");
+
+ msm_iommu_add_drv(drvdata);
fail:
+ __put_bus_vote_client(drvdata);
return ret;
}
@@ -222,13 +289,19 @@
drvdata->glb_base = drvdata->base;
- drvdata->gdsc = devm_regulator_get(&pdev->dev, "vdd");
- if (IS_ERR(drvdata->gdsc))
- return -EINVAL;
+ if (of_get_property(pdev->dev.of_node, "vdd-supply", NULL)) {
- drvdata->alt_gdsc = devm_regulator_get(&pdev->dev, "qcom,alt-vdd");
- if (IS_ERR(drvdata->alt_gdsc))
- drvdata->alt_gdsc = NULL;
+ drvdata->gdsc = devm_regulator_get(&pdev->dev, "vdd");
+ if (IS_ERR(drvdata->gdsc))
+ return PTR_ERR(drvdata->gdsc);
+
+ drvdata->alt_gdsc = devm_regulator_get(&pdev->dev,
+ "qcom,alt-vdd");
+ if (IS_ERR(drvdata->alt_gdsc))
+ drvdata->alt_gdsc = NULL;
+ } else {
+ pr_debug("Warning: No regulator specified for IOMMU\n");
+ }
drvdata->pclk = devm_clk_get(&pdev->dev, "iface_clk");
if (IS_ERR(drvdata->pclk))
@@ -265,8 +338,6 @@
platform_set_drvdata(pdev, drvdata);
- msm_iommu_sec_set_access_ops(&iommu_access_ops_v1);
-
pmon_info = msm_iommu_pm_alloc(&pdev->dev);
if (pmon_info != NULL) {
ret = msm_iommu_pmon_parse_dt(pdev, pmon_info);
@@ -275,7 +346,7 @@
pr_info("%s: pmon not available.\n", drvdata->name);
} else {
pmon_info->iommu.base = drvdata->base;
- pmon_info->iommu.ops = &iommu_access_ops_v1;
+ pmon_info->iommu.ops = msm_get_iommu_access_ops();
pmon_info->iommu.hw_ops = iommu_pm_get_hw_ops_v1();
pmon_info->iommu.iommu_name = drvdata->name;
ret = msm_iommu_pm_iommu_register(pmon_info);
@@ -301,10 +372,8 @@
drv = platform_get_drvdata(pdev);
if (drv) {
+ __put_bus_vote_client(drv);
msm_iommu_remove_drv(drv);
- if (drv->clk)
- clk_put(drv->clk);
- clk_put(drv->pclk);
platform_set_drvdata(pdev, NULL);
}
return 0;
@@ -314,34 +383,48 @@
struct msm_iommu_ctx_drvdata *ctx_drvdata)
{
struct resource *r, rp;
- int irq, ret;
+ int irq = 0, ret = 0;
u32 nsid;
- ctx_drvdata->secure_context = of_property_read_bool(pdev->dev.of_node,
- "qcom,secure-context");
+ get_secure_ctx(pdev->dev.of_node, ctx_drvdata);
- if (!ctx_drvdata->secure_context) {
- irq = platform_get_irq(pdev, 0);
+ if (ctx_drvdata->secure_context) {
+ irq = platform_get_irq(pdev, 1);
if (irq > 0) {
- ret = request_threaded_irq(irq, NULL,
- msm_iommu_fault_handler_v2,
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ msm_iommu_secure_fault_handler_v2,
IRQF_ONESHOT | IRQF_SHARED,
- "msm_iommu_nonsecure_irq", pdev);
+ "msm_iommu_secure_irq", pdev);
if (ret) {
pr_err("Request IRQ %d failed with ret=%d\n",
irq, ret);
return ret;
}
}
+ } else {
+ irq = platform_get_irq(pdev, 0);
+ if (irq > 0) {
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ msm_iommu_fault_handler_v2,
+ IRQF_ONESHOT | IRQF_SHARED,
+ "msm_iommu_nonsecure_irq", pdev);
+ if (ret) {
+ pr_err("Request IRQ %d failed with ret=%d\n",
+ irq, ret);
+ goto out;
+ }
+ }
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r)
- return -EINVAL;
+ if (!r) {
+ ret = -EINVAL;
+ goto out;
+ }
ret = of_address_to_resource(pdev->dev.parent->of_node, 0, &rp);
if (ret)
- return -EINVAL;
+ goto out;
/* Calculate the context bank number using the base addresses. The
* first 8 pages belong to the global address space which is followed
@@ -354,21 +437,26 @@
&ctx_drvdata->name))
ctx_drvdata->name = dev_name(&pdev->dev);
- if (!of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-sids", &nsid))
- return -EINVAL;
-
- if (nsid >= sizeof(ctx_drvdata->sids))
- return -EINVAL;
+ if (!of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-sids", &nsid)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (nsid >= sizeof(ctx_drvdata->sids)) {
+ ret = -EINVAL;
+ goto out;
+ }
if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-ctx-sids",
ctx_drvdata->sids,
nsid / sizeof(*ctx_drvdata->sids))) {
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
ctx_drvdata->nsid = nsid;
ctx_drvdata->asid = -1;
- return 0;
+out:
+ return ret;
}
static int __devinit msm_iommu_ctx_probe(struct platform_device *pdev)
@@ -386,12 +474,14 @@
ctx_drvdata->pdev = pdev;
INIT_LIST_HEAD(&ctx_drvdata->attached_elm);
- platform_set_drvdata(pdev, ctx_drvdata);
ret = msm_iommu_ctx_parse_dt(pdev, ctx_drvdata);
- if (!ret)
+ if (!ret) {
+ platform_set_drvdata(pdev, ctx_drvdata);
+
dev_info(&pdev->dev, "context %s using bank %d\n",
ctx_drvdata->name, ctx_drvdata->num);
+ }
return ret;
}
@@ -416,15 +506,15 @@
.remove = __devexit_p(msm_iommu_remove),
};
-static struct of_device_id msm_iommu_ctx_match_table[] = {
- { .name = "qcom,iommu-ctx", },
+static struct of_device_id msm_iommu_v1_ctx_match_table[] = {
+ { .compatible = "qcom,msm-smmu-v1-ctx", },
{}
};
static struct platform_driver msm_iommu_ctx_driver = {
.driver = {
.name = "msm_iommu_ctx_v1",
- .of_match_table = msm_iommu_ctx_match_table,
+ .of_match_table = msm_iommu_v1_ctx_match_table,
},
.probe = msm_iommu_ctx_probe,
.remove = __devexit_p(msm_iommu_ctx_remove),
@@ -434,6 +524,10 @@
{
int ret;
+ if (!msm_soc_version_supports_iommu_v0()) {
+ msm_set_iommu_access_ops(&iommu_access_ops_v1);
+ msm_iommu_sec_set_access_ops(&iommu_access_ops_v1);
+ }
ret = platform_driver_register(&msm_iommu_driver);
if (ret != 0) {
pr_err("Failed to register IOMMU driver\n");
diff --git a/drivers/iommu/msm_iommu_pagetable.c b/drivers/iommu/msm_iommu_pagetable.c
index b62bb76..b871a5a 100644
--- a/drivers/iommu/msm_iommu_pagetable.c
+++ b/drivers/iommu/msm_iommu_pagetable.c
@@ -21,6 +21,7 @@
#include <mach/iommu.h>
#include <mach/msm_iommu_priv.h>
+#include <trace/events/kmem.h>
#include "msm_iommu_pagetable.h"
/* Sharability attributes of MSM IOMMU mappings */
@@ -431,6 +432,7 @@
struct scatterlist *sg, unsigned int len, int prot)
{
phys_addr_t pa;
+ unsigned int start_va = va;
unsigned int offset = 0;
unsigned long *fl_pte;
unsigned long fl_offset;
@@ -470,6 +472,8 @@
chunk_size = SZ_1M;
/* 64k or 4k determined later */
+ trace_iommu_map_range(va, pa, sg->length, chunk_size);
+
/* for 1M and 16M, only first level entries are required */
if (chunk_size >= SZ_1M) {
if (chunk_size == SZ_16M) {
@@ -495,12 +499,6 @@
chunk_offset = 0;
sg = sg_next(sg);
pa = get_phys_addr(sg);
- if (pa == 0) {
- pr_debug("No dma address for sg %p\n",
- sg);
- ret = -EINVAL;
- goto fail;
- }
}
continue;
}
@@ -534,6 +532,9 @@
else
chunk_size = SZ_4K;
+ trace_iommu_map_range(va, pa, sg->length,
+ chunk_size);
+
if (chunk_size == SZ_4K) {
sl_4k(&sl_table[sl_offset], pa, pgprot4k);
sl_offset++;
@@ -553,12 +554,6 @@
chunk_offset = 0;
sg = sg_next(sg);
pa = get_phys_addr(sg);
- if (pa == 0) {
- pr_debug("No dma address for sg %p\n",
- sg);
- ret = -EINVAL;
- goto fail;
- }
}
}
@@ -569,6 +564,9 @@
}
fail:
+ if (ret && offset > 0)
+ msm_iommu_pagetable_unmap_range(pt, start_va, offset);
+
return ret;
}
diff --git a/drivers/iommu/msm_iommu_perfmon.c b/drivers/iommu/msm_iommu_perfmon.c
index a11d794..958c6ca 100644
--- a/drivers/iommu/msm_iommu_perfmon.c
+++ b/drivers/iommu/msm_iommu_perfmon.c
@@ -257,6 +257,7 @@
dev_get_drvdata(iommu->iommu_dev);
iommu->ops->iommu_power_on(iommu_drvdata);
+ iommu->ops->iommu_bus_vote(iommu_drvdata, 1);
iommu->ops->iommu_clk_on(iommu_drvdata);
/* Reset counters in HW */
@@ -311,6 +312,7 @@
iommu->ops->iommu_lock_release();
iommu->ops->iommu_clk_off(iommu_drvdata);
+ iommu->ops->iommu_bus_vote(iommu_drvdata, 0);
iommu->ops->iommu_power_off(iommu_drvdata);
pr_info("%s: TLB performance monitoring turned OFF\n",
diff --git a/drivers/iommu/msm_iommu_sec.c b/drivers/iommu/msm_iommu_sec.c
index 74d8b48..a17a4e8 100644
--- a/drivers/iommu/msm_iommu_sec.c
+++ b/drivers/iommu/msm_iommu_sec.c
@@ -38,6 +38,7 @@
/* bitmap of the page sizes currently supported */
#define MSM_IOMMU_PGSIZES (SZ_4K | SZ_64K | SZ_1M | SZ_16M)
+/* commands for SCM_SVC_MP */
#define IOMMU_SECURE_CFG 2
#define IOMMU_SECURE_PTBL_SIZE 3
#define IOMMU_SECURE_PTBL_INIT 4
@@ -47,6 +48,9 @@
#define IOMMU_SECURE_UNMAP2 0x0C
#define IOMMU_TLBINVAL_FLAG 0x00000001
+/* commands for SCM_SVC_UTIL */
+#define IOMMU_DUMP_SMMU_FAULT_REGS 0X0C
+
static struct iommu_access_ops *iommu_access_ops;
struct msm_scm_paddr_list {
@@ -73,11 +77,154 @@
unsigned int flags;
};
+struct msm_scm_fault_regs_dump {
+ uint32_t dump_size;
+ uint32_t fsr_addr;
+ uint32_t fsr;
+ uint32_t far0_addr;
+ uint32_t far0;
+ uint32_t far1_addr;
+ uint32_t far1;
+ uint32_t par0_addr;
+ uint32_t par0;
+ uint32_t par1_addr;
+ uint32_t par1;
+ uint32_t fsyn0_addr;
+ uint32_t fsyn0;
+ uint32_t fsyn1_addr;
+ uint32_t fsyn1;
+ uint32_t ttbr0_addr;
+ uint32_t ttbr0;
+ uint32_t ttbr1_addr;
+ uint32_t ttbr1;
+ uint32_t ttbcr_addr;
+ uint32_t ttbcr;
+ uint32_t sctlr_addr;
+ uint32_t sctlr;
+ uint32_t actlr_addr;
+ uint32_t actlr;
+ uint32_t prrr_addr;
+ uint32_t prrr;
+ uint32_t nmrr_addr;
+ uint32_t nmrr;
+};
+
void msm_iommu_sec_set_access_ops(struct iommu_access_ops *access_ops)
{
iommu_access_ops = access_ops;
}
+static int msm_iommu_dump_fault_regs(int smmu_id, int cb_num,
+ struct msm_scm_fault_regs_dump *regs)
+{
+ int ret;
+
+ struct msm_scm_fault_regs_dump_req {
+ uint32_t id;
+ uint32_t cb_num;
+ phys_addr_t buff;
+ uint32_t len;
+ } req_info;
+ int resp;
+
+ req_info.id = smmu_id;
+ req_info.cb_num = cb_num;
+ req_info.buff = virt_to_phys(regs);
+ req_info.len = sizeof(*regs);
+
+ ret = scm_call(SCM_SVC_UTIL, IOMMU_DUMP_SMMU_FAULT_REGS,
+ &req_info, sizeof(req_info), &resp, 1);
+
+ return ret;
+}
+
+irqreturn_t msm_iommu_secure_fault_handler_v2(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct msm_iommu_drvdata *drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ struct msm_scm_fault_regs_dump *regs;
+ int tmp, ret = IRQ_HANDLED;
+
+ iommu_access_ops->iommu_lock_acquire();
+
+ BUG_ON(!pdev);
+
+ drvdata = dev_get_drvdata(pdev->dev.parent);
+ BUG_ON(!drvdata);
+
+ ctx_drvdata = dev_get_drvdata(&pdev->dev);
+ BUG_ON(!ctx_drvdata);
+
+ regs = kmalloc(sizeof(*regs), GFP_KERNEL);
+ if (!regs) {
+ pr_err("%s: Couldn't allocate memory\n", __func__);
+ goto lock_release;
+ }
+
+ if (!drvdata->ctx_attach_count) {
+ pr_err("Unexpected IOMMU page fault from secure context bank!\n");
+ pr_err("name = %s\n", drvdata->name);
+ pr_err("Power is OFF. Unable to read page fault information\n");
+ /*
+ * We cannot determine which context bank caused the issue so
+ * we just return handled here to ensure IRQ handler code is
+ * happy
+ */
+ goto free_regs;
+ }
+
+ iommu_access_ops->iommu_clk_on(drvdata);
+ tmp = msm_iommu_dump_fault_regs(drvdata->sec_id,
+ ctx_drvdata->num, regs);
+ iommu_access_ops->iommu_clk_off(drvdata);
+
+ if (tmp) {
+ pr_err("%s: Couldn't dump fault registers!\n", __func__);
+ goto free_regs;
+ } else if (regs->fsr) {
+ struct msm_iommu_context_regs ctx_regs = {
+ .far = regs->far0,
+ .par = regs->par0,
+ .fsr = regs->fsr,
+ .fsynr0 = regs->fsyn0,
+ .fsynr1 = regs->fsyn1,
+ .ttbr0 = regs->ttbr0,
+ .ttbr1 = regs->ttbr1,
+ .sctlr = regs->sctlr,
+ .actlr = regs->actlr,
+ .prrr = regs->prrr,
+ .nmrr = regs->nmrr,
+ };
+
+ if (!ctx_drvdata->attached_domain) {
+ pr_err("Bad domain in interrupt handler\n");
+ tmp = -ENOSYS;
+ } else {
+ tmp = report_iommu_fault(ctx_drvdata->attached_domain,
+ &ctx_drvdata->pdev->dev,
+ regs->far0, 0);
+ }
+
+ /* if the fault wasn't handled by someone else: */
+ if (tmp == -ENOSYS) {
+ pr_err("Unexpected IOMMU page fault from secure context bank!\n");
+ pr_err("name = %s\n", drvdata->name);
+ pr_err("context = %s (%d)\n", ctx_drvdata->name,
+ ctx_drvdata->num);
+ pr_err("Interesting registers:\n");
+ print_ctx_regs(&ctx_regs);
+ }
+ } else {
+ ret = IRQ_NONE;
+ }
+free_regs:
+ kfree(regs);
+lock_release:
+ iommu_access_ops->iommu_lock_release();
+ return ret;
+}
+
static int msm_iommu_sec_ptbl_init(void)
{
struct device_node *np;
diff --git a/drivers/leds/leds-pm8xxx.c b/drivers/leds/leds-pm8xxx.c
index c3a5564..aef6295 100644
--- a/drivers/leds/leds-pm8xxx.c
+++ b/drivers/leds/leds-pm8xxx.c
@@ -133,6 +133,11 @@
_rgb_led_blue << PM8XXX_ID_RGB_LED_BLUE, \
}
+#define PM8XXX_PWM_CURRENT_4MA 4
+#define PM8XXX_PWM_CURRENT_8MA 8
+#define PM8XXX_PWM_CURRENT_12MA 12
+
+
/**
* supported_leds - leds supported for each PMIC version
* @version - version of PMIC
@@ -795,7 +800,7 @@
static int pm8xxx_led_pwm_configure(struct pm8xxx_led_data *led)
{
- int start_idx, idx_len, duty_us, rc;
+ int start_idx, idx_len, duty_us, rc, flags;
led->pwm_dev = pwm_request(led->pwm_channel,
led->cdev.name);
@@ -808,6 +813,22 @@
return -ENODEV;
}
+ flags = PM8XXX_LED_PWM_FLAGS;
+ switch (led->max_current) {
+ case PM8XXX_PWM_CURRENT_4MA:
+ flags |= PM_PWM_BANK_LO;
+ break;
+ case PM8XXX_PWM_CURRENT_8MA:
+ flags |= PM_PWM_BANK_HI;
+ break;
+ case PM8XXX_PWM_CURRENT_12MA:
+ flags |= (PM_PWM_BANK_LO | PM_PWM_BANK_HI);
+ break;
+ default:
+ flags |= (PM_PWM_BANK_LO | PM_PWM_BANK_HI);
+ break;
+ }
+
if (led->pwm_duty_cycles != NULL) {
start_idx = led->pwm_duty_cycles->start_idx;
idx_len = led->pwm_duty_cycles->num_duty_pcts;
@@ -825,7 +846,7 @@
led->pwm_duty_cycles->duty_pcts,
led->pwm_duty_cycles->duty_ms,
start_idx, idx_len, 0, 0,
- PM8XXX_LED_PWM_FLAGS);
+ flags);
} else {
duty_us = led->pwm_period_us;
rc = pwm_config(led->pwm_dev, duty_us, led->pwm_period_us);
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index 3667296..4835d62 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -23,6 +23,7 @@
#include <linux/spmi.h>
#include <linux/qpnp/pwm.h>
#include <linux/workqueue.h>
+#include <linux/regulator/consumer.h>
#define WLED_MOD_EN_REG(base, n) (base + 0x60 + n*0x10)
#define WLED_IDAC_DLY_REG(base, n) (WLED_MOD_EN_REG(base, n) + 0x01)
@@ -93,6 +94,7 @@
#define FLASH_LED_STROBE_CTRL(base) (base + 0x47)
#define FLASH_LED_UNLOCK_SECURE(base) (base + 0xD0)
#define FLASH_LED_TORCH(base) (base + 0xE4)
+#define FLASH_FAULT_DETECT(base) (base + 0x51)
#define FLASH_MAX_LEVEL 0x4F
#define FLASH_NO_MASK 0x00
@@ -106,10 +108,9 @@
#define FLASH_TMR_MASK 0x03
#define FLASH_TMR_WATCHDOG 0x03
#define FLASH_TMR_SAFETY 0x00
-
+#define FLASH_FAULT_DETECT_MASK 0X80
#define FLASH_HW_VREG_OK 0x80
#define FLASH_VREG_MASK 0xC0
-
#define FLASH_STARTUP_DLY_MASK 0x02
#define FLASH_ENABLE_ALL 0xE0
@@ -120,6 +121,7 @@
#define FLASH_ENABLE_LED_0 0x40
#define FLASH_ENABLE_LED_1 0x20
#define FLASH_INIT_MASK 0xE0
+#define FLASH_SELFCHECK_ENABLE 0x80
#define FLASH_STROBE_SW 0xC0
#define FLASH_STROBE_HW 0xC4
@@ -130,7 +132,7 @@
#define FLASH_CURRENT_PRGM_MIN 1
#define FLASH_CURRENT_PRGM_SHIFT 1
#define FLASH_CURRENT_MAX 0x4F
-#define FLASH_CURRENT_TORCH 0x0F
+#define FLASH_CURRENT_TORCH 0x07
#define FLASH_DURATION_200ms 0x13
#define FLASH_CLAMP_200mA 0x0F
@@ -165,12 +167,15 @@
#define LED_MPP_EN_CTRL(base) (base + 0x46)
#define LED_MPP_SINK_CTRL(base) (base + 0x4C)
-#define LED_MPP_CURRENT_DEFAULT 10
+#define LED_MPP_CURRENT_DEFAULT 5
+#define LED_MPP_CURRENT_PER_SETTING 5
#define LED_MPP_SOURCE_SEL_DEFAULT LED_MPP_MODE_ENABLE
#define LED_MPP_SINK_MASK 0x07
#define LED_MPP_MODE_MASK 0x7F
#define LED_MPP_EN_MASK 0x80
+#define LED_MPP_SRC_MASK 0x0F
+#define LED_MPP_MODE_CTRL_MASK 0x70
#define LED_MPP_MODE_SINK (0x06 << 4)
#define LED_MPP_MODE_ENABLE 0x01
@@ -180,6 +185,20 @@
#define LED_MPP_EN_DISABLE 0x00
#define MPP_SOURCE_DTEST1 0x08
+
+#define KPDBL_MAX_LEVEL LED_FULL
+#define KPDBL_ROW_SRC_SEL(base) (base + 0x40)
+#define KPDBL_ENABLE(base) (base + 0x46)
+#define KPDBL_ROW_SRC(base) (base + 0xE5)
+
+#define KPDBL_ROW_SRC_SEL_VAL_MASK 0x0F
+#define KPDBL_ROW_SCAN_EN_MASK 0x80
+#define KPDBL_ROW_SCAN_VAL_MASK 0x0F
+#define KPDBL_ROW_SCAN_EN_SHIFT 7
+#define KPDBL_MODULE_EN 0x80
+#define KPDBL_MODULE_DIS 0x00
+#define KPDBL_MODULE_EN_MASK 0x80
+
/**
* enum qpnp_leds - QPNP supported led ids
* @QPNP_ID_WLED - White led backlight
@@ -192,6 +211,7 @@
QPNP_ID_RGB_GREEN,
QPNP_ID_RGB_BLUE,
QPNP_ID_LED_MPP,
+ QPNP_ID_KPDBL,
QPNP_ID_MAX,
};
@@ -237,9 +257,10 @@
DELAY_128us,
};
-enum rgb_mode {
- RGB_MODE_PWM = 0,
- RGB_MODE_LPG,
+enum led_mode {
+ PWM_MODE = 0,
+ LPG_MODE,
+ MANUAL_MODE,
};
static u8 wled_debug_regs[] = {
@@ -267,6 +288,29 @@
0x40, 0x41, 0x42, 0x45, 0x46, 0x4c,
};
+static u8 kpdbl_debug_regs[] = {
+ 0x40, 0x46, 0xb1, 0xb3, 0xb4, 0xe5,
+};
+
+/**
+ * pwm_config_data - pwm configuration data
+ * @lut_params - lut parameters to be used by pwm driver
+ * @pwm_device - pwm device
+ * @pwm_channel - pwm channel to be configured for led
+ * @pwm_period_us - period for pwm, in us
+ * @mode - mode the led operates in
+ */
+struct pwm_config_data {
+ struct lut_params lut_params;
+ struct pwm_device *pwm_dev;
+ int pwm_channel;
+ u32 pwm_period_us;
+ struct pwm_duty_cycles *duty_cycles;
+ u8 mode;
+ u8 enable;
+ bool use_blink;
+};
+
/**
* wled_config_data - wled configuration data
* @num_strings - number of wled strings supported
@@ -292,12 +336,18 @@
/**
* mpp_config_data - mpp configuration data
+ * @pwm_cfg - device pwm configuration
* @current_setting - current setting, 5ma-40ma in 5ma increments
+ * @source_sel - source selection
+ * @mode_ctrl - mode control
+ * @pwm_mode - pwm mode in use
*/
struct mpp_config_data {
+ struct pwm_config_data *pwm_cfg;
u8 current_setting;
u8 source_sel;
u8 mode_ctrl;
+ u8 pwm_mode;
};
/**
@@ -314,6 +364,9 @@
* @second_addr - address of secondary flash to be written
* @safety_timer - enable safety timer or watchdog timer
* @torch_enable - enable flash LED torch mode
+ * @regulator_get - regulator attached or not
+ * @flash_on - flash status, on or off
+ * @flash_boost_reg - boost regulator for flash
*/
struct flash_config_data {
u8 current_prgm;
@@ -328,23 +381,32 @@
u16 second_addr;
bool safety_timer;
bool torch_enable;
+ bool regulator_get;
+ bool flash_on;
+ struct regulator *flash_boost_reg;
+};
+
+/**
+ * kpdbl_config_data - kpdbl configuration data
+ * @pwm_cfg - device pwm configuration
+ * @row_src_sel_val - select source, 0 for vph_pwr and 1 for vbst
+ * @row_scan_en - enable row scan
+ * @row_scan_val - map to enable needed rows
+ */
+struct kpdbl_config_data {
+ struct pwm_config_data *pwm_cfg;
+ u32 row_src_sel_val;
+ u32 row_scan_en;
+ u32 row_scan_val;
};
/**
* rgb_config_data - rgb configuration data
- * @lut_params - lut parameters to be used by pwm driver
- * @pwm_device - pwm device
- * @pwm_channel - pwm channel to be configured for led
- * @pwm_period_us - period for pwm, in us
- * @mode - mode the led operates in
+ * @pwm_cfg - device pwm configuration
+ * @enable - bits to enable led
*/
struct rgb_config_data {
- struct lut_params lut_params;
- struct pwm_device *pwm_dev;
- int pwm_channel;
- u32 pwm_period_us;
- struct pwm_duty_cycles *duty_cycles;
- u8 mode;
+ struct pwm_config_data *pwm_cfg;
u8 enable;
};
@@ -372,6 +434,7 @@
spinlock_t lock;
struct wled_config_data *wled_cfg;
struct flash_config_data *flash_cfg;
+ struct kpdbl_config_data *kpdbl_cfg;
struct rgb_config_data *rgb_cfg;
struct mpp_config_data *mpp_cfg;
int max_current;
@@ -414,7 +477,8 @@
led->spmi_dev->sid,
led->base + regs[i],
&val, sizeof(val));
- pr_debug("0x%x = 0x%x\n", led->base + regs[i], val);
+ pr_debug("%s: 0x%x = 0x%x\n", led->cdev.name,
+ led->base + regs[i], val);
}
pr_debug("===== %s LED register dump end =====\n", led->cdev.name);
}
@@ -499,23 +563,33 @@
static int qpnp_mpp_set(struct qpnp_led_data *led)
{
int rc, val;
+ int duty_us;
if (led->cdev.brightness) {
- val = (led->cdev.brightness * LED_MPP_SINK_MASK) / LED_FULL;
- rc = qpnp_led_masked_write(led,
- LED_MPP_SINK_CTRL(led->base),
- LED_MPP_SINK_MASK, val);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Failed to write led enable reg\n");
- return rc;
+ if (led->mpp_cfg->pwm_mode == PWM_MODE) {
+ pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
+ duty_us = (led->mpp_cfg->pwm_cfg->pwm_period_us *
+ led->cdev.brightness) / LED_FULL;
+ /*config pwm for brightness scaling*/
+ rc = pwm_config(led->mpp_cfg->pwm_cfg->pwm_dev,
+ duty_us,
+ led->mpp_cfg->pwm_cfg->pwm_period_us);
+ if (rc < 0) {
+ dev_err(&led->spmi_dev->dev, "Failed to " \
+ "configure pwm for new values\n");
+ return rc;
+ }
}
- val = led->mpp_cfg->source_sel | led->mpp_cfg->mode_ctrl;
+ if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
+ pwm_enable(led->mpp_cfg->pwm_cfg->pwm_dev);
+
+ val = (led->mpp_cfg->source_sel & LED_MPP_SRC_MASK) |
+ (led->mpp_cfg->mode_ctrl & LED_MPP_MODE_CTRL_MASK);
rc = qpnp_led_masked_write(led,
- LED_MPP_MODE_CTRL(led->base), LED_MPP_MODE_MASK,
- val);
+ LED_MPP_MODE_CTRL(led->base), LED_MPP_MODE_MASK,
+ val);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Failed to write led mode reg\n");
@@ -525,13 +599,15 @@
rc = qpnp_led_masked_write(led,
LED_MPP_EN_CTRL(led->base), LED_MPP_EN_MASK,
LED_MPP_EN_ENABLE);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Failed to write led enable " \
- "reg\n");
- return rc;
- }
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Failed to write led enable " \
+ "reg\n");
+ return rc;
+ }
} else {
+ if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
+ pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
rc = qpnp_led_masked_write(led,
LED_MPP_MODE_CTRL(led->base),
LED_MPP_MODE_MASK,
@@ -592,18 +668,10 @@
return rc;
}
- qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
- FLASH_CURRENT_MASK, FLASH_CURRENT_TORCH);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Max current reg write failed(%d)\n",
- rc);
- return rc;
- }
-
rc = qpnp_led_masked_write(led,
led->flash_cfg->current_addr,
- FLASH_CURRENT_MASK, FLASH_CURRENT_TORCH);
+ FLASH_CURRENT_MASK,
+ led->flash_cfg->current_prgm);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Current reg write failed(%d)\n", rc);
@@ -612,7 +680,8 @@
rc = qpnp_led_masked_write(led,
led->flash_cfg->second_addr,
- FLASH_CURRENT_MASK, FLASH_CURRENT_TORCH);
+ FLASH_CURRENT_MASK,
+ led->flash_cfg->current_prgm);
if (rc) {
dev_err(&led->spmi_dev->dev,
"2nd Current reg write failed(%d)\n",
@@ -620,6 +689,16 @@
return rc;
}
+ qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
+ FLASH_CURRENT_MASK,
+ led->max_current);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Max current reg write failed(%d)\n",
+ rc);
+ return rc;
+ }
+
rc = qpnp_led_masked_write(led,
FLASH_ENABLE_CONTROL(led->base),
FLASH_ENABLE_MODULE_MASK, FLASH_ENABLE_MODULE);
@@ -629,9 +708,22 @@
return rc;
}
} else {
+ /* Set flash safety timer */
rc = qpnp_led_masked_write(led,
- FLASH_MAX_CURR(led->base),
- FLASH_CURRENT_MASK, FLASH_CURRENT_MAX);
+ FLASH_SAFETY_TIMER(led->base),
+ FLASH_SAFETY_TIMER_MASK,
+ led->flash_cfg->duration);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Safety timer reg write failed(%d)\n",
+ rc);
+ return rc;
+ }
+
+ /* Set max current */
+ rc = qpnp_led_masked_write(led,
+ FLASH_MAX_CURR(led->base), FLASH_CURRENT_MASK,
+ FLASH_MAX_LEVEL);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Max current reg write failed(%d)\n",
@@ -639,6 +731,18 @@
return rc;
}
+ /* Set clamp current */
+ rc = qpnp_led_masked_write(led,
+ FLASH_CLAMP_CURR(led->base),
+ FLASH_CURRENT_MASK,
+ led->flash_cfg->clamp_curr);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Clamp current reg write failed(%d)\n",
+ rc);
+ return rc;
+ }
+
/* Write 0x80 to MODULE_ENABLE before writing 0xE0
* in order to avoid reg value goes from 0x00 to
* 0xE0. This causes a hardware bug.
@@ -674,16 +778,6 @@
}
rc = qpnp_led_masked_write(led,
- FLASH_CLAMP_CURR(led->base),
- FLASH_CURRENT_MASK, FLASH_CURRENT_TORCH);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Clamp Current reg write failed(%d)\n",
- rc);
- return rc;
- }
-
- rc = qpnp_led_masked_write(led,
FLASH_ENABLE_CONTROL(led->base),
FLASH_ENABLE_MASK, FLASH_ENABLE_ALL);
if (rc) {
@@ -722,25 +816,16 @@
if (rc) {
dev_err(&led->spmi_dev->dev,
"Secure reg write failed(%d)\n", rc);
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_LED_TORCH(led->base),
- FLASH_TORCH_MASK, FLASH_LED_TORCH_DISABLE);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Torch reg write failed(%d)\n", rc);
return rc;
}
rc = qpnp_led_masked_write(led,
- FLASH_SAFETY_TIMER(led->base),
- FLASH_SAFETY_TIMER_MASK,
- led->flash_cfg->duration);
+ FLASH_LED_TORCH(led->base),
+ FLASH_TORCH_MASK,
+ FLASH_LED_TORCH_DISABLE);
if (rc) {
dev_err(&led->spmi_dev->dev,
- "Safety timer reg write failed(%d)\n",
- rc);
+ "Torch reg write failed(%d)\n", rc);
return rc;
}
}
@@ -770,20 +855,57 @@
return 0;
}
+static int qpnp_kpdbl_set(struct qpnp_led_data *led)
+{
+ int duty_us;
+ int rc;
+
+ if (led->cdev.brightness) {
+ rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
+ KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
+ duty_us = (led->kpdbl_cfg->pwm_cfg->pwm_period_us *
+ led->cdev.brightness) / KPDBL_MAX_LEVEL;
+ rc = pwm_config(led->kpdbl_cfg->pwm_cfg->pwm_dev, duty_us,
+ led->kpdbl_cfg->pwm_cfg->pwm_period_us);
+ if (rc < 0) {
+ dev_err(&led->spmi_dev->dev, "pwm config failed\n");
+ return rc;
+ }
+ rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
+ if (rc < 0) {
+ dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
+ return rc;
+ }
+ } else {
+ pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
+ rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
+ KPDBL_MODULE_EN_MASK, KPDBL_MODULE_DIS);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Failed to write led enable reg\n");
+ return rc;
+ }
+ }
+
+ qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
+
+ return 0;
+}
+
static int qpnp_rgb_set(struct qpnp_led_data *led)
{
int duty_us;
int rc;
if (led->cdev.brightness) {
- if (led->rgb_cfg->mode == RGB_MODE_PWM) {
- duty_us = (led->rgb_cfg->pwm_period_us *
+ if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
+ duty_us = (led->rgb_cfg->pwm_cfg->pwm_period_us *
led->cdev.brightness) / LED_FULL;
- rc = pwm_config(led->rgb_cfg->pwm_dev, duty_us,
- led->rgb_cfg->pwm_period_us);
+ rc = pwm_config(led->rgb_cfg->pwm_cfg->pwm_dev, duty_us,
+ led->rgb_cfg->pwm_cfg->pwm_period_us);
if (rc < 0) {
- dev_err(&led->spmi_dev->dev, "Failed to " \
- "configure pwm for new values\n");
+ dev_err(&led->spmi_dev->dev,
+ "pwm config failed\n");
return rc;
}
}
@@ -795,9 +917,14 @@
"Failed to write led enable reg\n");
return rc;
}
- rc = pwm_enable(led->rgb_cfg->pwm_dev);
+
+ rc = pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev);
+ if (rc < 0) {
+ dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
+ return rc;
+ }
} else {
- pwm_disable(led->rgb_cfg->pwm_dev);
+ pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
rc = qpnp_led_masked_write(led,
RGB_LED_EN_CTL(led->base),
led->rgb_cfg->enable, RGB_LED_DISABLE);
@@ -816,8 +943,9 @@
static void qpnp_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
- int rc;
+ int rc, i;
struct qpnp_led_data *led;
+ struct qpnp_led_data *led_array;
led = container_of(led_cdev, struct qpnp_led_data, cdev);
if (value < LED_OFF || value > led->cdev.max_brightness) {
@@ -825,6 +953,33 @@
return;
}
+ if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1) {
+ if (!led->flash_cfg->flash_on && value > 0) {
+ led_array = dev_get_drvdata(&led->spmi_dev->dev);
+ if (!led_array) {
+ dev_err(&led->spmi_dev->dev,
+ "Unable to unable to get array\n");
+ return;
+ }
+
+ for (i = 0; i < led->num_leds; i++) {
+ if (led_array[i].flash_cfg->regulator_get) {
+ rc = regulator_enable(led_array[i].\
+ flash_cfg->\
+ flash_boost_reg);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Regulator enable" \
+ "failed(%d)\n",
+ rc);
+ return;
+ }
+ }
+ }
+ led->flash_cfg->flash_on = true;
+ }
+ }
+
spin_lock(&led->lock);
led->cdev.brightness = value;
@@ -856,11 +1011,43 @@
dev_err(&led->spmi_dev->dev,
"MPP set brightness failed (%d)\n", rc);
break;
+ case QPNP_ID_KPDBL:
+ rc = qpnp_kpdbl_set(led);
+ if (rc < 0)
+ dev_err(&led->spmi_dev->dev,
+ "KPDBL set brightness failed (%d)\n", rc);
+ break;
default:
dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
break;
}
spin_unlock(&led->lock);
+
+ if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1) {
+ if (led->flash_cfg->flash_on && !value) {
+ led_array = dev_get_drvdata(&led->spmi_dev->dev);
+ if (!led_array) {
+ dev_err(&led->spmi_dev->dev,
+ "Unable to get LED array\n");
+ return;
+ }
+
+ for (i = 0; i < led->num_leds; i++) {
+ if (led_array[i].flash_cfg->regulator_get) {
+ rc = regulator_disable(led_array[i]\
+ .flash_cfg->flash_boost_reg);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Unable to disable" \
+ " regulator(%d)\n",
+ rc);
+ return;
+ }
+ }
+ }
+ led->flash_cfg->flash_on = false;
+ }
+ }
}
static int __devinit qpnp_led_set_max_brightness(struct qpnp_led_data *led)
@@ -881,6 +1068,9 @@
case QPNP_ID_LED_MPP:
led->cdev.max_brightness = MPP_MAX_LEVEL;
break;
+ case QPNP_ID_KPDBL:
+ led->cdev.max_brightness = KPDBL_MAX_LEVEL;
+ break;
default:
dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
return -EINVAL;
@@ -1094,8 +1284,122 @@
return count;
}
+static int qpnp_pwm_init(struct pwm_config_data *pwm_cfg,
+ struct spmi_device *spmi_dev,
+ const char *name)
+{
+ int rc, start_idx, idx_len;
+
+ if (pwm_cfg->pwm_channel != -1) {
+ pwm_cfg->pwm_dev =
+ pwm_request(pwm_cfg->pwm_channel, name);
+
+ if (IS_ERR_OR_NULL(pwm_cfg->pwm_dev)) {
+ dev_err(&spmi_dev->dev,
+ "could not acquire PWM Channel %d, " \
+ "error %ld\n",
+ pwm_cfg->pwm_channel,
+ PTR_ERR(pwm_cfg->pwm_dev));
+ pwm_cfg->pwm_dev = NULL;
+ return -ENODEV;
+ }
+
+ if (pwm_cfg->mode == LPG_MODE) {
+ start_idx =
+ pwm_cfg->duty_cycles->start_idx;
+ idx_len =
+ pwm_cfg->duty_cycles->num_duty_pcts;
+
+ if (idx_len >= PWM_LUT_MAX_SIZE &&
+ start_idx) {
+ dev_err(&spmi_dev->dev,
+ "Wrong LUT size or index\n");
+ return -EINVAL;
+ }
+ if ((start_idx + idx_len) >
+ PWM_LUT_MAX_SIZE) {
+ dev_err(&spmi_dev->dev,
+ "Exceed LUT limit\n");
+ return -EINVAL;
+ }
+ rc = pwm_lut_config(pwm_cfg->pwm_dev,
+ PM_PWM_PERIOD_MIN, /* ignored by hardware */
+ pwm_cfg->duty_cycles->duty_pcts,
+ pwm_cfg->lut_params);
+ if (rc < 0) {
+ dev_err(&spmi_dev->dev, "Failed to " \
+ "configure pwm LUT\n");
+ return rc;
+ }
+ }
+ } else {
+ dev_err(&spmi_dev->dev,
+ "Invalid PWM channel\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void led_blink(struct qpnp_led_data *led,
+ struct pwm_config_data *pwm_cfg)
+{
+ u8 previous_mode;
+
+ previous_mode = pwm_cfg->mode;
+ if (pwm_cfg->use_blink) {
+ if (led->cdev.brightness) {
+ if (led->id == QPNP_ID_LED_MPP)
+ led->mpp_cfg->pwm_mode = LPG_MODE;
+ pwm_cfg->mode = LPG_MODE;
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ if (led->id == QPNP_ID_LED_MPP)
+ led->mpp_cfg->pwm_mode = previous_mode;
+ pwm_cfg->mode = previous_mode;
+ } else {
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ }
+ }
+}
+
+static ssize_t blink_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_led_data *led;
+ unsigned long blinking;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret = -EINVAL;
+
+ ret = kstrtoul(buf, 10, &blinking);
+ if (ret)
+ return ret;
+ led = container_of(led_cdev, struct qpnp_led_data, cdev);
+ led->cdev.brightness = blinking ? led->cdev.max_brightness : 0;
+
+ switch (led->id) {
+ case QPNP_ID_LED_MPP:
+ led_blink(led, led->mpp_cfg->pwm_cfg);
+ break;
+ case QPNP_ID_RGB_RED:
+ case QPNP_ID_RGB_GREEN:
+ case QPNP_ID_RGB_BLUE:
+ led_blink(led, led->rgb_cfg->pwm_cfg);
+ break;
+ default:
+ dev_err(&led->spmi_dev->dev, "Invalid LED id type for blink\n");
+ return -EINVAL;
+ }
+ return count;
+}
+
static DEVICE_ATTR(led_mode, 0664, NULL, led_mode_store);
static DEVICE_ATTR(strobe, 0664, NULL, led_strobe_type_store);
+static DEVICE_ATTR(blink, 0664, NULL, blink_store);
static struct attribute *led_attrs[] = {
&dev_attr_led_mode.attr,
@@ -1107,10 +1411,21 @@
.attrs = led_attrs,
};
+static struct attribute *blink_attrs[] = {
+ &dev_attr_blink.attr,
+ NULL
+};
+
+static const struct attribute_group blink_attr_group = {
+ .attrs = blink_attrs,
+};
+
static int __devinit qpnp_flash_init(struct qpnp_led_data *led)
{
int rc;
+ led->flash_cfg->flash_on = false;
+
rc = qpnp_led_masked_write(led,
FLASH_LED_STROBE_CTRL(led->base),
FLASH_STROBE_MASK, FLASH_DISABLE_ALL);
@@ -1119,52 +1434,7 @@
"LED %d flash write failed(%d)\n", led->id, rc);
return rc;
}
- rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
- FLASH_INIT_MASK, FLASH_ENABLE_MODULE);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Enable reg write failed(%d)\n", rc);
- return rc;
- }
- /* Set flash safety timer */
- rc = qpnp_led_masked_write(led, FLASH_SAFETY_TIMER(led->base),
- FLASH_SAFETY_TIMER_MASK, led->flash_cfg->duration);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Safety timer reg write failed(%d)\n", rc);
- return rc;
- }
-
- /* Set max current */
- rc = qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
- FLASH_CURRENT_MASK, FLASH_MAX_LEVEL);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Max current reg write failed(%d)\n", rc);
- return rc;
- }
- /* Set clamp current */
- rc = qpnp_led_masked_write(led, FLASH_CLAMP_CURR(led->base),
- FLASH_CURRENT_MASK, led->flash_cfg->clamp_curr);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Clamp current reg write failed(%d)\n", rc);
- return rc;
- }
-
- /* Set timer control - safety or watchdog */
- if (led->flash_cfg->safety_timer)
- rc = qpnp_led_masked_write(led, FLASH_LED_TMR_CTRL(led->base),
- FLASH_TMR_MASK, FLASH_TMR_SAFETY);
- else
- rc = qpnp_led_masked_write(led, FLASH_LED_TMR_CTRL(led->base),
- FLASH_TMR_MASK, FLASH_TMR_WATCHDOG);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "LED timer ctrl reg write failed(%d)\n", rc);
- return rc;
- }
/* Set headroom */
rc = qpnp_led_masked_write(led, FLASH_HEADROOM(led->base),
FLASH_HEADROOM_MASK, led->flash_cfg->headroom);
@@ -1174,6 +1444,47 @@
return rc;
}
+ /* Set startup delay */
+ rc = qpnp_led_masked_write(led,
+ FLASH_STARTUP_DELAY(led->base), FLASH_STARTUP_DLY_MASK,
+ led->flash_cfg->startup_dly);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Startup delay reg write failed(%d)\n", rc);
+ return rc;
+ }
+
+ /* Set timer control - safety or watchdog */
+ if (led->flash_cfg->safety_timer) {
+ rc = qpnp_led_masked_write(led,
+ FLASH_LED_TMR_CTRL(led->base),
+ FLASH_TMR_MASK, FLASH_TMR_SAFETY);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "LED timer ctrl reg write failed(%d)\n",
+ rc);
+ return rc;
+ }
+ }
+
+ /* Set Vreg force */
+ rc = qpnp_led_masked_write(led, FLASH_VREG_OK_FORCE(led->base),
+ FLASH_VREG_MASK, FLASH_HW_VREG_OK);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Vreg OK reg write failed(%d)\n", rc);
+ return rc;
+ }
+
+ /* Set self fault check */
+ rc = qpnp_led_masked_write(led, FLASH_FAULT_DETECT(led->base),
+ FLASH_FAULT_DETECT_MASK, FLASH_SELFCHECK_ENABLE);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Fault detect reg write failed(%d)\n", rc);
+ return rc;
+ }
+
/* Set mask enable */
rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
FLASH_MASK_REG_MASK, FLASH_MASK_1);
@@ -1183,32 +1494,7 @@
return rc;
}
- /* Set startup delay */
- rc = qpnp_led_masked_write(led, FLASH_STARTUP_DELAY(led->base),
- FLASH_STARTUP_DLY_MASK, led->flash_cfg->startup_dly);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Startup delay reg write failed(%d)\n", rc);
- return rc;
- }
-
- rc = qpnp_led_masked_write(led, FLASH_VREG_OK_FORCE(led->base),
- FLASH_VREG_MASK, FLASH_HW_VREG_OK);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Vreg OK reg write failed(%d)\n", rc);
- return rc;
- }
-
- /* Set led current and disable module */
- rc = qpnp_led_masked_write(led, led->flash_cfg->current_addr,
- FLASH_CURRENT_MASK, led->flash_cfg->current_prgm);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Current reg write failed(%d)\n", rc);
- return rc;
- }
-
+ /* Disable flash LED module */
rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
FLASH_ENABLE_MODULE_MASK, FLASH_DISABLE_ALL);
if (rc) {
@@ -1217,7 +1503,6 @@
return rc;
}
- led->flash_cfg->torch_enable = false;
led->flash_cfg->strobe_type = 0;
/* dump flash registers */
@@ -1226,9 +1511,72 @@
return 0;
}
+static int __devinit qpnp_kpdbl_init(struct qpnp_led_data *led)
+{
+ int rc;
+ u8 val;
+
+ /* enable row source selct */
+ rc = qpnp_led_masked_write(led, KPDBL_ROW_SRC_SEL(led->base),
+ KPDBL_ROW_SRC_SEL_VAL_MASK, led->kpdbl_cfg->row_src_sel_val);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Enable row src sel write failed(%d)\n", rc);
+ return rc;
+ }
+
+ /* row source */
+ rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
+ KPDBL_ROW_SRC(led->base), &val, 1);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Unable to read from addr=%x, rc(%d)\n",
+ KPDBL_ROW_SRC(led->base), rc);
+ return rc;
+ }
+
+ val &= ~KPDBL_ROW_SCAN_VAL_MASK;
+ val |= led->kpdbl_cfg->row_scan_val;
+
+ led->kpdbl_cfg->row_scan_en <<= KPDBL_ROW_SCAN_EN_SHIFT;
+ val &= ~KPDBL_ROW_SCAN_EN_MASK;
+ val |= led->kpdbl_cfg->row_scan_en;
+
+ rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
+ KPDBL_ROW_SRC(led->base), &val, 1);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Unable to write to addr=%x, rc(%d)\n",
+ KPDBL_ROW_SRC(led->base), rc);
+ return rc;
+ }
+
+ /* enable module */
+ rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
+ KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Enable module write failed(%d)\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_pwm_init(led->kpdbl_cfg->pwm_cfg, led->spmi_dev,
+ led->cdev.name);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm\n");
+ return rc;
+ }
+
+ /* dump kpdbl registers */
+ qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
+
+ return 0;
+}
+
static int __devinit qpnp_rgb_init(struct qpnp_led_data *led)
{
- int rc, start_idx, idx_len;
+ int rc;
rc = qpnp_led_masked_write(led, RGB_LED_SRC_SEL(led->base),
RGB_LED_SRC_MASK, RGB_LED_SOURCE_VPH_PWR);
@@ -1238,55 +1586,13 @@
return rc;
}
- if (led->rgb_cfg->pwm_channel != -1) {
- led->rgb_cfg->pwm_dev =
- pwm_request(led->rgb_cfg->pwm_channel,
- led->cdev.name);
-
- if (IS_ERR_OR_NULL(led->rgb_cfg->pwm_dev)) {
- dev_err(&led->spmi_dev->dev,
- "could not acquire PWM Channel %d, " \
- "error %ld\n",
- led->rgb_cfg->pwm_channel,
- PTR_ERR(led->rgb_cfg->pwm_dev));
- led->rgb_cfg->pwm_dev = NULL;
- return -ENODEV;
- }
-
- if (led->rgb_cfg->mode == RGB_MODE_LPG) {
- start_idx =
- led->rgb_cfg->duty_cycles->start_idx;
- idx_len =
- led->rgb_cfg->duty_cycles->num_duty_pcts;
-
- if (idx_len >= PWM_LUT_MAX_SIZE &&
- start_idx) {
- dev_err(&led->spmi_dev->dev,
- "Wrong LUT size or index\n");
- return -EINVAL;
- }
- if ((start_idx + idx_len) >
- PWM_LUT_MAX_SIZE) {
- dev_err(&led->spmi_dev->dev,
- "Exceed LUT limit\n");
- return -EINVAL;
- }
- rc = pwm_lut_config(led->rgb_cfg->pwm_dev,
- PM_PWM_PERIOD_MIN, /* ignored by hardware */
- led->rgb_cfg->duty_cycles->duty_pcts,
- led->rgb_cfg->lut_params);
- if (rc < 0) {
- dev_err(&led->spmi_dev->dev, "Failed to " \
- "configure pwm LUT\n");
- return rc;
- }
- }
- } else {
+ rc = qpnp_pwm_init(led->rgb_cfg->pwm_cfg, led->spmi_dev,
+ led->cdev.name);
+ if (rc) {
dev_err(&led->spmi_dev->dev,
- "Invalid PWM channel\n");
- return -EINVAL;
+ "Failed to initialize pwm\n");
+ return rc;
}
-
/* Initialize led for use in auto trickle charging mode */
rc = qpnp_led_masked_write(led, RGB_LED_ATC_CTL(led->base),
led->rgb_cfg->enable, led->rgb_cfg->enable);
@@ -1294,9 +1600,39 @@
return 0;
}
+static int __devinit qpnp_mpp_init(struct qpnp_led_data *led)
+{
+ int rc, val;
+
+ val = (led->mpp_cfg->current_setting / LED_MPP_CURRENT_PER_SETTING) - 1;
+
+ if (val < 0)
+ val = 0;
+
+ rc = qpnp_led_masked_write(led, LED_MPP_SINK_CTRL(led->base),
+ LED_MPP_SINK_MASK, val);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Failed to write led enable reg\n");
+ return rc;
+ }
+
+ if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
+ rc = qpnp_pwm_init(led->mpp_cfg->pwm_cfg, led->spmi_dev,
+ led->cdev.name);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm\n");
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
static int __devinit qpnp_led_initialize(struct qpnp_led_data *led)
{
- int rc;
+ int rc = 0;
switch (led->id) {
case QPNP_ID_WLED:
@@ -1321,13 +1657,23 @@
"RGB initialize failed(%d)\n", rc);
break;
case QPNP_ID_LED_MPP:
+ rc = qpnp_mpp_init(led);
+ if (rc)
+ dev_err(&led->spmi_dev->dev,
+ "MPP initialize failed(%d)\n", rc);
+ break;
+ case QPNP_ID_KPDBL:
+ rc = qpnp_kpdbl_init(led);
+ if (rc)
+ dev_err(&led->spmi_dev->dev,
+ "KPDBL initialize failed(%d)\n", rc);
break;
default:
dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
return -EINVAL;
}
- return 0;
+ return rc;
}
static int __devinit qpnp_get_common_configs(struct qpnp_led_data *led,
@@ -1439,7 +1785,7 @@
}
static int __devinit qpnp_get_config_flash(struct qpnp_led_data *led,
- struct device_node *node)
+ struct device_node *node, bool *reg_set)
{
int rc;
u32 val;
@@ -1456,11 +1802,39 @@
led->flash_cfg->current_addr = FLASH_LED_0_CURR(led->base);
led->flash_cfg->second_addr = FLASH_LED_1_CURR(led->base);
led->flash_cfg->trigger_flash = FLASH_LED_0_OUTPUT;
+ if (!*reg_set) {
+ led->flash_cfg->flash_boost_reg =
+ regulator_get(&led->spmi_dev->dev,
+ "flash_boost");
+ if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
+ rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
+ dev_err(&led->spmi_dev->dev,
+ "Regulator get failed(%d)\n", rc);
+ return rc;
+ }
+ led->flash_cfg->regulator_get = true;
+ *reg_set = true;
+ } else
+ led->flash_cfg->regulator_get = false;
} else if (led->id == QPNP_ID_FLASH1_LED1) {
led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
led->flash_cfg->current_addr = FLASH_LED_1_CURR(led->base);
led->flash_cfg->second_addr = FLASH_LED_0_CURR(led->base);
led->flash_cfg->trigger_flash = FLASH_LED_1_OUTPUT;
+ if (!*reg_set) {
+ led->flash_cfg->flash_boost_reg =
+ regulator_get(&led->spmi_dev->dev,
+ "flash_boost");
+ if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
+ rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
+ dev_err(&led->spmi_dev->dev,
+ "Regulator get failed(%d)\n", rc);
+ return rc;
+ }
+ led->flash_cfg->regulator_get = true;
+ *reg_set = true;
+ } else
+ led->flash_cfg->regulator_get = false;
} else {
dev_err(&led->spmi_dev->dev, "Unknown flash LED name given\n");
return -EINVAL;
@@ -1477,7 +1851,7 @@
if (!rc)
led->flash_cfg->headroom = (u8) val;
else if (rc == -EINVAL)
- led->flash_cfg->headroom = HEADROOM_300mV;
+ led->flash_cfg->headroom = HEADROOM_500mV;
else
return rc;
@@ -1502,23 +1876,226 @@
if (!rc)
led->flash_cfg->startup_dly = (u8) val;
else if (rc == -EINVAL)
- led->flash_cfg->startup_dly = DELAY_32us;
+ led->flash_cfg->startup_dly = DELAY_128us;
else
return rc;
led->flash_cfg->safety_timer =
of_property_read_bool(node, "qcom,safety-timer");
+ led->flash_cfg->torch_enable =
+ of_property_read_bool(node, "qcom,torch-enable");
+
+ return 0;
+}
+
+static int __devinit qpnp_get_config_pwm(struct pwm_config_data *pwm_cfg,
+ struct spmi_device *spmi_dev,
+ struct device_node *node)
+{
+ struct property *prop;
+ int rc, i;
+ u32 val;
+ u8 *temp_cfg;
+
+ rc = of_property_read_u32(node, "qcom,pwm-channel", &val);
+ if (!rc)
+ pwm_cfg->pwm_channel = val;
+ else
+ return rc;
+
+ if (pwm_cfg->mode == PWM_MODE) {
+ rc = of_property_read_u32(node, "qcom,pwm-us", &val);
+ if (!rc)
+ pwm_cfg->pwm_period_us = val;
+ else
+ return rc;
+ }
+
+ pwm_cfg->use_blink =
+ of_property_read_bool(node, "qcom,use-blink");
+
+ if (pwm_cfg->mode == LPG_MODE || pwm_cfg->use_blink) {
+ pwm_cfg->duty_cycles =
+ devm_kzalloc(&spmi_dev->dev,
+ sizeof(struct pwm_duty_cycles), GFP_KERNEL);
+ if (!pwm_cfg->duty_cycles) {
+ dev_err(&spmi_dev->dev,
+ "Unable to allocate memory\n");
+ rc = -ENOMEM;
+ goto bad_lpg_params;
+ }
+
+ prop = of_find_property(node, "qcom,duty-pcts",
+ &pwm_cfg->duty_cycles->num_duty_pcts);
+ if (!prop) {
+ dev_err(&spmi_dev->dev, "Looking up property " \
+ "node qcom,duty-pcts failed\n");
+ rc = -ENODEV;
+ goto bad_lpg_params;
+ } else if (!pwm_cfg->duty_cycles->num_duty_pcts) {
+ dev_err(&spmi_dev->dev, "Invalid length of " \
+ "duty pcts\n");
+ rc = -EINVAL;
+ goto bad_lpg_params;
+ }
+
+ pwm_cfg->duty_cycles->duty_pcts =
+ devm_kzalloc(&spmi_dev->dev,
+ sizeof(int) * pwm_cfg->duty_cycles->num_duty_pcts,
+ GFP_KERNEL);
+ if (!pwm_cfg->duty_cycles->duty_pcts) {
+ dev_err(&spmi_dev->dev,
+ "Unable to allocate memory\n");
+ rc = -ENOMEM;
+ goto bad_lpg_params;
+ }
+
+ temp_cfg = devm_kzalloc(&spmi_dev->dev,
+ pwm_cfg->duty_cycles->num_duty_pcts *
+ sizeof(u8), GFP_KERNEL);
+ if (!temp_cfg) {
+ dev_err(&spmi_dev->dev, "Failed to allocate " \
+ "memory for duty pcts\n");
+ rc = -ENOMEM;
+ goto bad_lpg_params;
+ }
+
+ memcpy(temp_cfg, prop->value,
+ pwm_cfg->duty_cycles->num_duty_pcts);
+
+ for (i = 0; i < pwm_cfg->duty_cycles->num_duty_pcts; i++)
+ pwm_cfg->duty_cycles->duty_pcts[i] =
+ (int) temp_cfg[i];
+
+ rc = of_property_read_u32(node, "qcom,start-idx", &val);
+ if (!rc) {
+ pwm_cfg->lut_params.start_idx = val;
+ pwm_cfg->duty_cycles->start_idx = val;
+ } else
+ goto bad_lpg_params;
+
+ pwm_cfg->lut_params.lut_pause_hi = 0;
+ rc = of_property_read_u32(node, "qcom,pause-hi", &val);
+ if (!rc)
+ pwm_cfg->lut_params.lut_pause_hi = val;
+ else if (rc != -EINVAL)
+ goto bad_lpg_params;
+
+ pwm_cfg->lut_params.lut_pause_lo = 0;
+ rc = of_property_read_u32(node, "qcom,pause-lo", &val);
+ if (!rc)
+ pwm_cfg->lut_params.lut_pause_lo = val;
+ else if (rc != -EINVAL)
+ goto bad_lpg_params;
+
+ pwm_cfg->lut_params.ramp_step_ms =
+ QPNP_LUT_RAMP_STEP_DEFAULT;
+ rc = of_property_read_u32(node, "qcom,ramp-step-ms", &val);
+ if (!rc)
+ pwm_cfg->lut_params.ramp_step_ms = val;
+ else if (rc != -EINVAL)
+ goto bad_lpg_params;
+
+ pwm_cfg->lut_params.flags = QPNP_LED_PWM_FLAGS;
+ rc = of_property_read_u32(node, "qcom,lut-flags", &val);
+ if (!rc)
+ pwm_cfg->lut_params.flags = (u8) val;
+ else if (rc != -EINVAL)
+ goto bad_lpg_params;
+
+ pwm_cfg->lut_params.idx_len =
+ pwm_cfg->duty_cycles->num_duty_pcts;
+ }
+ return 0;
+
+bad_lpg_params:
+ pwm_cfg->use_blink = false;
+ if (pwm_cfg->mode == PWM_MODE) {
+ dev_err(&spmi_dev->dev, "LPG parameters not set for" \
+ " blink mode, defaulting to PWM mode\n");
+ return 0;
+ }
+ return rc;
+};
+
+static int qpnp_led_get_mode(const char *mode)
+{
+ if (strncmp(mode, "manual", strlen(mode)) == 0)
+ return MANUAL_MODE;
+ else if (strncmp(mode, "pwm", strlen(mode)) == 0)
+ return PWM_MODE;
+ else if (strncmp(mode, "lpg", strlen(mode)) == 0)
+ return LPG_MODE;
+ else
+ return -EINVAL;
+};
+
+static int __devinit qpnp_get_config_kpdbl(struct qpnp_led_data *led,
+ struct device_node *node)
+{
+ int rc;
+ u32 val;
+ u8 led_mode;
+ const char *mode;
+
+ led->kpdbl_cfg = devm_kzalloc(&led->spmi_dev->dev,
+ sizeof(struct kpdbl_config_data), GFP_KERNEL);
+ if (!led->kpdbl_cfg) {
+ dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+ rc = of_property_read_string(node, "qcom,mode", &mode);
+ if (!rc) {
+ led_mode = qpnp_led_get_mode(mode);
+ if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
+ dev_err(&led->spmi_dev->dev, "Selected mode not " \
+ "supported for kpdbl.\n");
+ return -EINVAL;
+ }
+ led->kpdbl_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
+ sizeof(struct pwm_config_data),
+ GFP_KERNEL);
+ if (!led->kpdbl_cfg->pwm_cfg) {
+ dev_err(&led->spmi_dev->dev,
+ "Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+ led->kpdbl_cfg->pwm_cfg->mode = led_mode;
+ } else
+ return rc;
+
+ rc = qpnp_get_config_pwm(led->kpdbl_cfg->pwm_cfg, led->spmi_dev, node);
+ if (rc < 0)
+ return rc;
+
+ rc = of_property_read_u32(node, "qcom,row-src-sel-val", &val);
+ if (!rc)
+ led->kpdbl_cfg->row_src_sel_val = val;
+ else
+ return rc;
+
+ rc = of_property_read_u32(node, "qcom,row-scan-val", &val);
+ if (!rc)
+ led->kpdbl_cfg->row_scan_val = val;
+ else
+ return rc;
+
+ rc = of_property_read_u32(node, "qcom,row-scan-en", &val);
+ if (!rc)
+ led->kpdbl_cfg->row_scan_en = val;
+ else
+ return rc;
+
return 0;
}
static int __devinit qpnp_get_config_rgb(struct qpnp_led_data *led,
struct device_node *node)
{
- struct property *prop;
- int rc, i;
- u32 val;
- u8 *temp_cfg;
+ int rc;
+ u8 led_mode;
+ const char *mode;
led->rgb_cfg = devm_kzalloc(&led->spmi_dev->dev,
sizeof(struct rgb_config_data), GFP_KERNEL);
@@ -1536,113 +2113,29 @@
else
return -EINVAL;
- rc = of_property_read_u32(node, "qcom,mode", &val);
- if (!rc)
- led->rgb_cfg->mode = (u8) val;
- else
- return rc;
-
- rc = of_property_read_u32(node, "qcom,pwm-channel", &val);
- if (!rc)
- led->rgb_cfg->pwm_channel = val;
- else
- return rc;
-
- if (led->rgb_cfg->mode == RGB_MODE_PWM) {
- rc = of_property_read_u32(node, "qcom,pwm-us", &val);
- if (!rc)
- led->rgb_cfg->pwm_period_us = val;
- else
- return rc;
- }
-
- if (led->rgb_cfg->mode == RGB_MODE_LPG) {
- led->rgb_cfg->duty_cycles =
- devm_kzalloc(&led->spmi_dev->dev,
- sizeof(struct pwm_duty_cycles), GFP_KERNEL);
- if (!led->rgb_cfg->duty_cycles) {
- dev_err(&led->spmi_dev->dev,
- "Unable to allocate memory\n");
- return -ENOMEM;
- }
-
- prop = of_find_property(node, "qcom,duty-pcts",
- &led->rgb_cfg->duty_cycles->num_duty_pcts);
- if (!prop) {
- dev_err(&led->spmi_dev->dev, "Looking up property " \
- "node qcom,duty-pcts failed\n");
- return -ENODEV;
- } else if (!led->rgb_cfg->duty_cycles->num_duty_pcts) {
- dev_err(&led->spmi_dev->dev, "Invalid length of " \
- "duty pcts\n");
+ rc = of_property_read_string(node, "qcom,mode", &mode);
+ if (!rc) {
+ led_mode = qpnp_led_get_mode(mode);
+ if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
+ dev_err(&led->spmi_dev->dev, "Selected mode not " \
+ "supported for rgb.\n");
return -EINVAL;
}
-
- led->rgb_cfg->duty_cycles->duty_pcts =
- devm_kzalloc(&led->spmi_dev->dev,
- sizeof(int) * led->rgb_cfg->duty_cycles->num_duty_pcts,
- GFP_KERNEL);
- if (!led->rgb_cfg->duty_cycles->duty_pcts) {
+ led->rgb_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
+ sizeof(struct pwm_config_data),
+ GFP_KERNEL);
+ if (!led->rgb_cfg->pwm_cfg) {
dev_err(&led->spmi_dev->dev,
"Unable to allocate memory\n");
return -ENOMEM;
}
+ led->rgb_cfg->pwm_cfg->mode = led_mode;
+ } else
+ return rc;
- temp_cfg = devm_kzalloc(&led->spmi_dev->dev,
- led->rgb_cfg->duty_cycles->num_duty_pcts *
- sizeof(u8), GFP_KERNEL);
- if (!temp_cfg) {
- dev_err(&led->spmi_dev->dev, "Failed to allocate " \
- "memory for duty pcts\n");
- return -ENOMEM;
- }
-
- memcpy(temp_cfg, prop->value,
- led->rgb_cfg->duty_cycles->num_duty_pcts);
-
- for (i = 0; i < led->rgb_cfg->duty_cycles->num_duty_pcts; i++)
- led->rgb_cfg->duty_cycles->duty_pcts[i] =
- (int) temp_cfg[i];
-
- rc = of_property_read_u32(node, "qcom,start-idx", &val);
- if (!rc) {
- led->rgb_cfg->lut_params.start_idx = val;
- led->rgb_cfg->duty_cycles->start_idx = val;
- } else
- return rc;
-
- led->rgb_cfg->lut_params.lut_pause_hi = 0;
- rc = of_property_read_u32(node, "qcom,pause-hi", &val);
- if (!rc)
- led->rgb_cfg->lut_params.lut_pause_hi = val;
- else if (rc != -EINVAL)
- return rc;
-
- led->rgb_cfg->lut_params.lut_pause_lo = 0;
- rc = of_property_read_u32(node, "qcom,pause-lo", &val);
- if (!rc)
- led->rgb_cfg->lut_params.lut_pause_lo = val;
- else if (rc != -EINVAL)
- return rc;
-
- led->rgb_cfg->lut_params.ramp_step_ms =
- QPNP_LUT_RAMP_STEP_DEFAULT;
- rc = of_property_read_u32(node, "qcom,ramp-step-ms", &val);
- if (!rc)
- led->rgb_cfg->lut_params.ramp_step_ms = val;
- else if (rc != -EINVAL)
- return rc;
-
- led->rgb_cfg->lut_params.flags = QPNP_LED_PWM_FLAGS;
- rc = of_property_read_u32(node, "qcom,lut-flags", &val);
- if (!rc)
- led->rgb_cfg->lut_params.flags = val;
- else if (rc != -EINVAL)
- return rc;
-
- led->rgb_cfg->lut_params.idx_len =
- led->rgb_cfg->duty_cycles->num_duty_pcts;
- }
+ rc = qpnp_get_config_pwm(led->rgb_cfg->pwm_cfg, led->spmi_dev, node);
+ if (rc < 0)
+ return rc;
return 0;
}
@@ -1652,6 +2145,8 @@
{
int rc;
u32 val;
+ u8 led_mode;
+ const char *mode;
led->mpp_cfg = devm_kzalloc(&led->spmi_dev->dev,
sizeof(struct mpp_config_data), GFP_KERNEL);
@@ -1681,6 +2176,33 @@
else if (rc != -EINVAL)
return rc;
+ rc = of_property_read_string(node, "qcom,mode", &mode);
+ if (!rc) {
+ led_mode = qpnp_led_get_mode(mode);
+ led->mpp_cfg->pwm_mode = led_mode;
+ if (led_mode == MANUAL_MODE)
+ return MANUAL_MODE;
+ else if (led_mode == -EINVAL) {
+ dev_err(&led->spmi_dev->dev, "Selected mode not " \
+ "supported for mpp.\n");
+ return -EINVAL;
+ }
+ led->mpp_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
+ sizeof(struct pwm_config_data),
+ GFP_KERNEL);
+ if (!led->mpp_cfg->pwm_cfg) {
+ dev_err(&led->spmi_dev->dev,
+ "Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+ led->mpp_cfg->pwm_cfg->mode = led_mode;
+ } else
+ return rc;
+
+ rc = qpnp_get_config_pwm(led->mpp_cfg->pwm_cfg, led->spmi_dev, node);
+ if (rc < 0)
+ return rc;
+
return 0;
}
@@ -1691,6 +2213,7 @@
struct device_node *node, *temp;
int rc, i, num_leds = 0, parsed_leds = 0;
const char *led_label;
+ bool regulator_probe = false;
node = spmi->dev.of_node;
if (node == NULL)
@@ -1773,7 +2296,9 @@
}
} else if (strncmp(led_label, "flash", sizeof("flash"))
== 0) {
- rc = qpnp_get_config_flash(led, temp);
+ if (!of_find_property(node, "flash_boost-supply", NULL))
+ regulator_probe = true;
+ rc = qpnp_get_config_flash(led, temp, ®ulator_probe);
if (rc < 0) {
dev_err(&led->spmi_dev->dev,
"Unable to read flash config data\n");
@@ -1793,6 +2318,13 @@
"Unable to read mpp config data\n");
goto fail_id_check;
}
+ } else if (strncmp(led_label, "kpdbl", sizeof("kpdbl")) == 0) {
+ rc = qpnp_get_config_kpdbl(led, temp);
+ if (rc < 0) {
+ dev_err(&led->spmi_dev->dev,
+ "Unable to read kpdbl config data\n");
+ goto fail_id_check;
+ }
} else {
dev_err(&led->spmi_dev->dev, "No LED matching label\n");
rc = -EINVAL;
@@ -1825,16 +2357,35 @@
}
+ if (led->id == QPNP_ID_LED_MPP) {
+ if (!led->mpp_cfg->pwm_cfg)
+ break;
+ if (led->mpp_cfg->pwm_cfg->use_blink) {
+ rc = sysfs_create_group(&led->cdev.dev->kobj,
+ &blink_attr_group);
+ if (rc)
+ goto fail_id_check;
+ }
+ } else if ((led->id == QPNP_ID_RGB_RED) ||
+ (led->id == QPNP_ID_RGB_GREEN) ||
+ (led->id == QPNP_ID_RGB_BLUE)) {
+ if (led->rgb_cfg->pwm_cfg->use_blink) {
+ rc = sysfs_create_group(&led->cdev.dev->kobj,
+ &blink_attr_group);
+ if (rc)
+ goto fail_id_check;
+ }
+ }
+
/* configure default state */
if (led->default_on) {
led->cdev.brightness = led->cdev.max_brightness;
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
if (led->turn_off_delay_ms > 0)
qpnp_led_turn_off(led);
} else
led->cdev.brightness = LED_OFF;
- qpnp_led_set(&led->cdev, led->cdev.brightness);
-
parsed_leds++;
}
dev_set_drvdata(&spmi->dev, led_array);
@@ -1858,6 +2409,9 @@
break;
case QPNP_ID_FLASH1_LED0:
case QPNP_ID_FLASH1_LED1:
+ if (led_array[i].flash_cfg->regulator_get)
+ regulator_put(led_array[i].flash_cfg-> \
+ flash_boost_reg);
sysfs_remove_group(&led_array[i].cdev.dev->kobj,
&led_attr_group);
break;
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
index 89500f9..2c2b339 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -75,7 +75,8 @@
DMX_FIFO_ERROR, /* Receiver FIFO overrun */
DMX_MISSED_ERROR, /* Receiver missed packet */
DMX_OK_DECODER_BUF, /* Received OK, new ES data in decoder buffer */
- DMX_OK_IDX /* Received OK, new index event */
+ DMX_OK_IDX, /* Received OK, new index event */
+ DMX_OK_SCRAMBLING_STATUS, /* Received OK, new scrambling status */
} ;
@@ -135,6 +136,7 @@
} marker;
struct dmx_index_event_info idx_event;
+ struct dmx_scrambling_status_event_info scrambling_bits;
};
};
@@ -246,7 +248,11 @@
struct dmx_secure_mode *sec_mode);
int (*oob_command) (struct dmx_ts_feed *feed,
struct dmx_oob_command *cmd);
-
+ int (*ts_insertion_init)(struct dmx_ts_feed *feed);
+ int (*ts_insertion_terminate)(struct dmx_ts_feed *feed);
+ int (*ts_insertion_insert_buffer)(struct dmx_ts_feed *feed,
+ char *data, size_t size);
+ int (*get_scrambling_bits)(struct dmx_ts_feed *feed, u8 *value);
};
/*--------------------------------------------------------------------------*/
@@ -297,6 +303,7 @@
struct dmx_secure_mode *sec_mode);
int (*oob_command) (struct dmx_section_feed *feed,
struct dmx_oob_command *cmd);
+ int (*get_scrambling_bits)(struct dmx_section_feed *feed, u8 *value);
};
/*--------------------------------------------------------------------------*/
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index 5e7a09e..6734da8 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -34,6 +34,8 @@
#include <linux/uaccess.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
#include "dmxdev.h"
static int debug;
@@ -416,7 +418,33 @@
return 0;
}
-static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src,
+static inline int dvb_dmxdev_check_data(struct dmxdev_filter *filter,
+ struct dvb_ringbuffer *src)
+{
+ int data_status_change;
+
+ if (filter)
+ if (mutex_lock_interruptible(&filter->mutex))
+ return -ERESTARTSYS;
+
+ if (!src->data ||
+ !dvb_ringbuffer_empty(src) ||
+ src->error ||
+ (filter &&
+ (filter->state != DMXDEV_STATE_GO) &&
+ (filter->state != DMXDEV_STATE_DONE)))
+ data_status_change = 1;
+ else
+ data_status_change = 0;
+
+ if (filter)
+ mutex_unlock(&filter->mutex);
+
+ return data_status_change;
+}
+
+static ssize_t dvb_dmxdev_buffer_read(struct dmxdev_filter *filter,
+ struct dvb_ringbuffer *src,
int non_blocking, char __user *buf,
size_t count, loff_t *ppos)
{
@@ -439,9 +467,26 @@
break;
}
- ret = wait_event_interruptible(src->queue, (!src->data) ||
- !dvb_ringbuffer_empty(src) ||
- (src->error != 0));
+ if (filter) {
+ if ((filter->state == DMXDEV_STATE_DONE) &&
+ dvb_ringbuffer_empty(src))
+ break;
+
+ mutex_unlock(&filter->mutex);
+ }
+
+ ret = wait_event_interruptible(src->queue,
+ dvb_dmxdev_check_data(filter, src));
+
+ if (filter) {
+ if (mutex_lock_interruptible(&filter->mutex))
+ return -ERESTARTSYS;
+
+ if ((filter->state != DMXDEV_STATE_GO) &&
+ (filter->state != DMXDEV_STATE_DONE))
+ return -ENODEV;
+ }
+
if (ret < 0)
break;
@@ -1108,9 +1153,9 @@
if (dmxdev->exit)
return -ENODEV;
- res = dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,
- file->f_flags & O_NONBLOCK,
- buf, count, ppos);
+ res = dvb_dmxdev_buffer_read(NULL, &dmxdev->dvr_buffer,
+ file->f_flags & O_NONBLOCK,
+ buf, count, ppos);
if (res > 0) {
dvb_dmxdev_notify_data_read(dmxdev->dvr_feed, res);
@@ -1751,6 +1796,223 @@
return 0;
}
+static int dvb_dmxdev_get_scrambling_bits(struct dmxdev_filter *filter,
+ struct dmx_scrambling_bits *scrambling_bits)
+{
+ struct dmxdev_feed *feed;
+
+ if (!scrambling_bits ||
+ (filter->state != DMXDEV_STATE_GO))
+ return -EINVAL;
+
+ if (filter->type == DMXDEV_TYPE_SEC) {
+ if (filter->feed.sec.feed->get_scrambling_bits)
+ return filter->feed.sec.feed->get_scrambling_bits(
+ filter->feed.sec.feed,
+ &scrambling_bits->value);
+ return -EINVAL;
+ }
+
+ list_for_each_entry(feed, &filter->feed.ts, next) {
+ if (feed->pid == scrambling_bits->pid) {
+ if (feed->ts->get_scrambling_bits)
+ return feed->ts->get_scrambling_bits(feed->ts,
+ &scrambling_bits->value);
+ return -EINVAL;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static void dvb_dmxdev_ts_insertion_work(struct work_struct *worker)
+{
+ struct ts_insertion_buffer *ts_buffer =
+ container_of(worker, struct ts_insertion_buffer, dwork.work);
+ struct dmxdev_feed *feed;
+ size_t free_bytes;
+ struct dmx_ts_feed *ts;
+
+ mutex_lock(&ts_buffer->dmxdevfilter->mutex);
+
+ if (ts_buffer->abort ||
+ (ts_buffer->dmxdevfilter->state != DMXDEV_STATE_GO)) {
+ mutex_unlock(&ts_buffer->dmxdevfilter->mutex);
+ return;
+ }
+
+ feed = list_first_entry(&ts_buffer->dmxdevfilter->feed.ts,
+ struct dmxdev_feed, next);
+ ts = feed->ts;
+ free_bytes = dvb_ringbuffer_free(&ts_buffer->dmxdevfilter->buffer);
+
+ mutex_unlock(&ts_buffer->dmxdevfilter->mutex);
+
+ if (ts_buffer->size < free_bytes)
+ ts->ts_insertion_insert_buffer(ts,
+ ts_buffer->buffer, ts_buffer->size);
+
+ if (ts_buffer->repetition_time && !ts_buffer->abort)
+ schedule_delayed_work(&ts_buffer->dwork,
+ msecs_to_jiffies(ts_buffer->repetition_time));
+}
+
+static void dvb_dmxdev_queue_ts_insertion(
+ struct ts_insertion_buffer *ts_buffer)
+{
+ size_t tsp_size;
+
+ if (ts_buffer->dmxdevfilter->dmx_tsp_format == DMX_TSP_FORMAT_188)
+ tsp_size = 188;
+ else
+ tsp_size = 192;
+
+ if (ts_buffer->size % tsp_size) {
+ printk(KERN_ERR "%s: Wrong buffer alignment, size=%d, tsp_size=%d\n",
+ __func__, ts_buffer->size, tsp_size);
+ return;
+ }
+
+ ts_buffer->abort = 0;
+ schedule_delayed_work(&ts_buffer->dwork, 0);
+}
+
+static void dvb_dmxdev_cancel_ts_insertion(
+ struct ts_insertion_buffer *ts_buffer)
+{
+ /*
+ * This function assumes it is called while mutex
+ * of demux filter is taken. Since work in workqueue
+ * captures the filter's mutex to protect against the DB,
+ * mutex needs to be released before waiting for the work
+ * to get finished otherwise work in workqueue will
+ * never be finished.
+ */
+ if (!mutex_is_locked(&ts_buffer->dmxdevfilter->mutex)) {
+ printk(KERN_ERR "%s: mutex is not locked!\n", __func__);
+ return;
+ }
+
+ ts_buffer->abort = 1;
+
+ mutex_unlock(&ts_buffer->dmxdevfilter->mutex);
+ cancel_delayed_work_sync(&ts_buffer->dwork);
+ mutex_lock(&ts_buffer->dmxdevfilter->mutex);
+}
+
+static int dvb_dmxdev_set_ts_insertion(struct dmxdev_filter *dmxdevfilter,
+ struct dmx_set_ts_insertion *params)
+{
+ int ret = 0;
+ int first_buffer;
+ struct dmxdev_feed *feed;
+ struct ts_insertion_buffer *ts_buffer;
+
+ if (!params ||
+ !params->size ||
+ !(dmxdevfilter->dev->capabilities & DMXDEV_CAP_TS_INSERTION) ||
+ (dmxdevfilter->state < DMXDEV_STATE_SET) ||
+ (dmxdevfilter->type != DMXDEV_TYPE_PES) ||
+ ((dmxdevfilter->params.pes.output != DMX_OUT_TS_TAP) &&
+ (dmxdevfilter->params.pes.output != DMX_OUT_TSDEMUX_TAP)))
+ return -EINVAL;
+
+ ts_buffer = vmalloc(sizeof(struct ts_insertion_buffer));
+ if (!ts_buffer)
+ return -ENOMEM;
+
+ ts_buffer->buffer = vmalloc(params->size);
+ if (!ts_buffer->buffer) {
+ vfree(ts_buffer);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(ts_buffer->buffer,
+ params->ts_packets, params->size)) {
+ vfree(ts_buffer->buffer);
+ vfree(ts_buffer);
+ return -EFAULT;
+ }
+
+ if (params->repetition_time &&
+ params->repetition_time < DMX_MIN_INSERTION_REPETITION_TIME)
+ params->repetition_time = DMX_MIN_INSERTION_REPETITION_TIME;
+
+ ts_buffer->size = params->size;
+ ts_buffer->identifier = params->identifier;
+ ts_buffer->repetition_time = params->repetition_time;
+ ts_buffer->dmxdevfilter = dmxdevfilter;
+ INIT_DELAYED_WORK(&ts_buffer->dwork, dvb_dmxdev_ts_insertion_work);
+
+ first_buffer = list_empty(&dmxdevfilter->insertion_buffers);
+ list_add_tail(&ts_buffer->next, &dmxdevfilter->insertion_buffers);
+
+ if (dmxdevfilter->state != DMXDEV_STATE_GO)
+ return 0;
+
+ feed = list_first_entry(&dmxdevfilter->feed.ts,
+ struct dmxdev_feed, next);
+
+ if (first_buffer && feed->ts->ts_insertion_init)
+ ret = feed->ts->ts_insertion_init(feed->ts);
+
+ if (!ret) {
+ dvb_dmxdev_queue_ts_insertion(ts_buffer);
+ } else {
+ list_del(&ts_buffer->next);
+ vfree(ts_buffer->buffer);
+ vfree(ts_buffer);
+ }
+
+ return ret;
+}
+
+static int dvb_dmxdev_abort_ts_insertion(struct dmxdev_filter *dmxdevfilter,
+ struct dmx_abort_ts_insertion *params)
+{
+ int ret = 0;
+ int found_buffer;
+ struct dmxdev_feed *feed;
+ struct ts_insertion_buffer *ts_buffer, *tmp;
+
+ if (!params ||
+ !(dmxdevfilter->dev->capabilities & DMXDEV_CAP_TS_INSERTION) ||
+ (dmxdevfilter->state < DMXDEV_STATE_SET) ||
+ (dmxdevfilter->type != DMXDEV_TYPE_PES) ||
+ ((dmxdevfilter->params.pes.output != DMX_OUT_TS_TAP) &&
+ (dmxdevfilter->params.pes.output != DMX_OUT_TSDEMUX_TAP)))
+ return -EINVAL;
+
+ found_buffer = 0;
+ list_for_each_entry_safe(ts_buffer, tmp,
+ &dmxdevfilter->insertion_buffers, next) {
+ if (ts_buffer->identifier == params->identifier) {
+ list_del(&ts_buffer->next);
+ found_buffer = 1;
+ break;
+ }
+ }
+
+ if (!found_buffer)
+ return -EINVAL;
+
+ if (dmxdevfilter->state == DMXDEV_STATE_GO) {
+ dvb_dmxdev_cancel_ts_insertion(ts_buffer);
+ if (list_empty(&dmxdevfilter->insertion_buffers)) {
+ feed = list_first_entry(&dmxdevfilter->feed.ts,
+ struct dmxdev_feed, next);
+ if (feed->ts->ts_insertion_terminate)
+ ret = feed->ts->ts_insertion_terminate(
+ feed->ts);
+ }
+ }
+
+ vfree(ts_buffer->buffer);
+ vfree(ts_buffer);
+
+ return ret;
+}
+
static int dvb_dmxdev_ts_fullness_callback(struct dmx_ts_feed *filter,
int required_space)
{
@@ -2006,6 +2268,9 @@
dvb_ringbuffer_flush(&dmxdevfilter->buffer);
dvb_dmxdev_notify_data_read(dmxdevfilter, flush_len);
dmxdevfilter->buffer.error = 0;
+ } else if (event->type == DMX_EVENT_SECTION_TIMEOUT) {
+ /* clear buffer error now that user was notified */
+ dmxdevfilter->buffer.error = 0;
}
/*
@@ -2036,10 +2301,13 @@
static void dvb_dmxdev_filter_timeout(unsigned long data)
{
struct dmxdev_filter *dmxdevfilter = (struct dmxdev_filter *)data;
+ struct dmx_filter_event event;
dmxdevfilter->buffer.error = -ETIMEDOUT;
spin_lock_irq(&dmxdevfilter->dev->lock);
dmxdevfilter->state = DMXDEV_STATE_TIMEDOUT;
+ event.type = DMX_EVENT_SECTION_TIMEOUT;
+ dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
spin_unlock_irq(&dmxdevfilter->dev->lock);
wake_up_all(&dmxdevfilter->buffer.queue);
}
@@ -2280,6 +2548,13 @@
dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
spin_unlock(&dmxdevfilter->dev->lock);
wake_up_all(&dmxdevfilter->buffer.queue);
+ } else if (dmx_data_ready->status == DMX_OK_SCRAMBLING_STATUS) {
+ event.type = DMX_EVENT_SCRAMBLING_STATUS_CHANGE;
+ event.params.scrambling_status =
+ dmx_data_ready->scrambling_bits;
+ dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&dmxdevfilter->buffer.queue);
} else {
spin_unlock(&dmxdevfilter->dev->lock);
}
@@ -2396,6 +2671,16 @@
return 0;
}
+ if (dmx_data_ready->status == DMX_OK_SCRAMBLING_STATUS) {
+ event.type = DMX_EVENT_SCRAMBLING_STATUS_CHANGE;
+ event.params.scrambling_status =
+ dmx_data_ready->scrambling_bits;
+ dvb_dmxdev_add_event(events, &event);
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&buffer->queue);
+ return 0;
+ }
+
if (dmx_data_ready->status == DMX_OK_DECODER_BUF) {
event.type = DMX_EVENT_NEW_ES_DATA;
event.params.es_data.buf_handle = dmx_data_ready->buf.handle;
@@ -2612,6 +2897,7 @@
{
struct dmxdev_feed *feed;
struct dmx_demux *demux;
+ struct ts_insertion_buffer *ts_buffer;
if (dmxdevfilter->state < DMXDEV_STATE_GO)
return 0;
@@ -2631,6 +2917,18 @@
case DMXDEV_TYPE_PES:
dvb_dmxdev_feed_stop(dmxdevfilter);
demux = dmxdevfilter->dev->demux;
+
+ if (!list_empty(&dmxdevfilter->insertion_buffers)) {
+ feed = list_first_entry(&dmxdevfilter->feed.ts,
+ struct dmxdev_feed, next);
+
+ list_for_each_entry(ts_buffer,
+ &dmxdevfilter->insertion_buffers, next)
+ dvb_dmxdev_cancel_ts_insertion(ts_buffer);
+ if (feed->ts->ts_insertion_terminate)
+ feed->ts->ts_insertion_terminate(feed->ts);
+ }
+
list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) {
demux->release_ts_feed(demux, feed->ts);
feed->ts = NULL;
@@ -2971,6 +3269,29 @@
}
dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
+
+ if ((filter->type == DMXDEV_TYPE_PES) &&
+ !list_empty(&filter->insertion_buffers)) {
+ struct ts_insertion_buffer *ts_buffer;
+
+ feed = list_first_entry(&filter->feed.ts,
+ struct dmxdev_feed, next);
+
+ ret = 0;
+ if (feed->ts->ts_insertion_init)
+ ret = feed->ts->ts_insertion_init(feed->ts);
+ if (!ret) {
+ list_for_each_entry(ts_buffer,
+ &filter->insertion_buffers, next)
+ dvb_dmxdev_queue_ts_insertion(
+ ts_buffer);
+ } else {
+ printk(KERN_ERR
+ "%s: ts_insertion_init failed, err %d\n",
+ __func__, ret);
+ }
+ }
+
return 0;
}
@@ -3017,6 +3338,8 @@
dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
init_timer(&dmxdevfilter->timer);
+ INIT_LIST_HEAD(&dmxdevfilter->insertion_buffers);
+
dmxdevfilter->dmx_tsp_format = DMX_TSP_FORMAT_188;
dvbdev->users++;
@@ -3027,6 +3350,8 @@
static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev,
struct dmxdev_filter *dmxdevfilter)
{
+ struct ts_insertion_buffer *ts_buffer, *tmp;
+
mutex_lock(&dmxdev->mutex);
mutex_lock(&dmxdevfilter->mutex);
@@ -3034,6 +3359,13 @@
dvb_dmxdev_filter_reset(dmxdevfilter);
+ list_for_each_entry_safe(ts_buffer, tmp,
+ &dmxdevfilter->insertion_buffers, next) {
+ list_del(&ts_buffer->next);
+ vfree(ts_buffer->buffer);
+ vfree(ts_buffer);
+ }
+
if (dmxdevfilter->buffer.data) {
void *mem = dmxdevfilter->buffer.data;
@@ -3098,12 +3430,20 @@
static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev,
struct dmxdev_filter *filter, u16 pid)
{
+ int feed_count;
struct dmxdev_feed *feed, *tmp;
if ((filter->type != DMXDEV_TYPE_PES) ||
(filter->state < DMXDEV_STATE_SET))
return -EINVAL;
+ feed_count = 0;
+ list_for_each_entry(tmp, &filter->feed.ts, next)
+ feed_count++;
+
+ if (feed_count <= 1)
+ return -EINVAL;
+
list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) {
if (feed->pid == pid) {
if (feed->ts != NULL) {
@@ -3283,7 +3623,7 @@
hcount = 3 + dfil->todo;
if (hcount > count)
hcount = count;
- result = dvb_dmxdev_buffer_read(&dfil->buffer,
+ result = dvb_dmxdev_buffer_read(dfil, &dfil->buffer,
file->f_flags & O_NONBLOCK,
buf, hcount, ppos);
if (result < 0) {
@@ -3304,7 +3644,7 @@
}
if (count > dfil->todo)
count = dfil->todo;
- result = dvb_dmxdev_buffer_read(&dfil->buffer,
+ result = dvb_dmxdev_buffer_read(dfil, &dfil->buffer,
file->f_flags & O_NONBLOCK,
buf, count, ppos);
if (result < 0)
@@ -3333,9 +3673,10 @@
if (dmxdevfilter->type == DMXDEV_TYPE_SEC)
ret = dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos);
else
- ret = dvb_dmxdev_buffer_read(&dmxdevfilter->buffer,
- file->f_flags & O_NONBLOCK,
- buf, count, ppos);
+ ret = dvb_dmxdev_buffer_read(dmxdevfilter,
+ &dmxdevfilter->buffer,
+ file->f_flags & O_NONBLOCK,
+ buf, count, ppos);
if (ret > 0) {
dvb_dmxdev_notify_data_read(dmxdevfilter, ret);
@@ -3625,6 +3966,33 @@
mutex_unlock(&dmxdevfilter->mutex);
break;
+ case DMX_SET_TS_INSERTION:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_set_ts_insertion(dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_ABORT_TS_INSERTION:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_abort_ts_insertion(dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_GET_SCRAMBLING_BITS:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_get_scrambling_bits(dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
default:
ret = -EINVAL;
break;
@@ -3880,6 +4248,7 @@
struct dmxdev_filter *filter;
int active_count = 0;
struct dmx_buffer_status buffer_status;
+ struct dmx_scrambling_bits scrambling_bits;
const char *pes_feeds[] = {"DEC", "PES", "DVR", "REC"};
if (!dmxdev)
@@ -3896,23 +4265,32 @@
seq_printf(s, "type: SEC, ");
seq_printf(s, "PID %04d ",
filter->params.sec.pid);
+ scrambling_bits.pid = filter->params.sec.pid;
} else {
seq_printf(s, "type: %s, ",
pes_feeds[filter->params.pes.output]);
seq_printf(s, "PID: %04d ",
filter->params.pes.pid);
+ scrambling_bits.pid = filter->params.pes.pid;
}
+ dvb_dmxdev_get_scrambling_bits(filter,
+ &scrambling_bits);
+
if (0 == dvb_dmxdev_get_buffer_status(
filter, &buffer_status)) {
seq_printf(s, "size: %08d, ",
buffer_status.size);
seq_printf(s, "fullness: %08d, ",
buffer_status.fullness);
- seq_printf(s, "error: %d\n",
+ seq_printf(s, "error: %d, ",
buffer_status.error);
+ seq_printf(s, "scramble: %d\n",
+ scrambling_bits.value);
+
} else {
- seq_printf(s, "\n");
+ seq_printf(s, "scramble: %d\n",
+ scrambling_bits.value);
}
}
}
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
index 2ed99ae..49e5e1b 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -114,6 +114,32 @@
struct dmx_filter_event queue[DMX_EVENT_QUEUE_SIZE];
};
+#define DMX_MIN_INSERTION_REPETITION_TIME 25 /* in msec */
+struct ts_insertion_buffer {
+ /* work scheduled for insertion of this buffer */
+ struct delayed_work dwork;
+
+ struct list_head next;
+
+ /* buffer holding TS packets for insertion */
+ char *buffer;
+
+ /* buffer size */
+ size_t size;
+
+ /* buffer ID from user */
+ u32 identifier;
+
+ /* repetition time for the buffer insertion */
+ u32 repetition_time;
+
+ /* the recording filter to which this buffer belongs */
+ struct dmxdev_filter *dmxdevfilter;
+
+ /* indication whether insertion should be aborted */
+ int abort;
+};
+
struct dmxdev_filter {
union {
struct dmx_section_filter *sec;
@@ -145,6 +171,9 @@
enum dmx_tsp_format_t dmx_tsp_format;
u32 rec_chunk_size;
+ /* list of buffers used for insertion (struct ts_insertion_buffer) */
+ struct list_head insertion_buffers;
+
/* End-of-stream indication has been received */
int eos_state;
@@ -170,6 +199,8 @@
#define DMXDEV_CAP_PULL_MODE 0x02
#define DMXDEV_CAP_INDEXING 0x04
#define DMXDEV_CAP_EXTERNAL_BUFFS_ONLY 0x08
+#define DMXDEV_CAP_TS_INSERTION 0x10
+
enum dmx_playback_mode_t playback_mode;
dmx_source_t source;
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index 3f3d222..bd4344b 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -164,6 +164,11 @@
return ((buf[1] & 0x1f) << 8) + buf[2];
}
+static inline u16 ts_scrambling_ctrl(const u8 *buf)
+{
+ return (buf[3] >> 6) & 0x3;
+}
+
static inline u8 payload(const u8 *tsp)
{
if (!(tsp[3] & 0x10)) // no payload?
@@ -437,6 +442,66 @@
}
EXPORT_SYMBOL(dvb_dmx_video_pattern_search);
+/**
+ * dvb_dmx_notify_section_event() - Notify demux event for all filters of a
+ * specified section feed.
+ *
+ * @feed: dvb_demux_feed object
+ * @event: demux event to notify
+ * @should_lock: specifies whether the function should lock the demux
+ *
+ * Caller is responsible for locking the demux properly, either by doing the
+ * locking itself and setting 'should_lock' to 0, or have the function do it
+ * by setting 'should_lock' to 1.
+ */
+int dvb_dmx_notify_section_event(struct dvb_demux_feed *feed,
+ struct dmx_data_ready *event, int should_lock)
+{
+ struct dvb_demux_filter *f;
+
+ if (feed == NULL || event == NULL || feed->type != DMX_TYPE_SEC)
+ return -EINVAL;
+
+ if (!should_lock && !spin_is_locked(&feed->demux->lock))
+ return -EINVAL;
+
+ if (should_lock)
+ spin_lock(&feed->demux->lock);
+
+ f = feed->filter;
+ while (f && feed->feed.sec.is_filtering) {
+ feed->data_ready_cb.sec(&f->filter, event);
+ f = f->next;
+ }
+
+ if (should_lock)
+ spin_unlock(&feed->demux->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(dvb_dmx_notify_section_event);
+
+static int dvb_dmx_check_pes_end(struct dvb_demux_feed *feed)
+{
+ struct dmx_data_ready data;
+
+ if (!feed->pusi_seen)
+ return 0;
+
+ data.status = DMX_OK_PES_END;
+ data.data_length = 0;
+ data.pes_end.start_gap = 0;
+ data.pes_end.actual_length = feed->peslen;
+ data.pes_end.disc_indicator_set = 0;
+ data.pes_end.pes_length_mismatch = 0;
+ data.pes_end.stc = 0;
+ data.pes_end.tei_counter = feed->pes_tei_counter;
+ data.pes_end.cont_err_counter = feed->pes_cont_err_counter;
+ data.pes_end.ts_packets_num = feed->pes_ts_packets_num;
+
+ return feed->data_ready_cb.ts(&feed->feed.ts, &data);
+}
+
static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed,
const u8 *buf)
{
@@ -444,7 +509,6 @@
int p;
int ccok;
u8 cc;
- struct dmx_data_ready data;
if (count == 0)
return -1;
@@ -462,24 +526,7 @@
/* PUSI ? */
if (buf[1] & 0x40) {
- if (feed->pusi_seen) {
- /* We had seen PUSI before, this means
- * that previous PES can be closed now.
- */
- data.status = DMX_OK_PES_END;
- data.data_length = 0;
- data.pes_end.start_gap = 0;
- data.pes_end.actual_length = feed->peslen;
- data.pes_end.disc_indicator_set = 0;
- data.pes_end.pes_length_mismatch = 0;
- data.pes_end.stc = 0;
- data.pes_end.tei_counter = feed->pes_tei_counter;
- data.pes_end.cont_err_counter =
- feed->pes_cont_err_counter;
- data.pes_end.ts_packets_num = feed->pes_ts_packets_num;
- feed->data_ready_cb.ts(&feed->feed.ts, &data);
- }
-
+ dvb_dmx_check_pes_end(feed);
feed->pusi_seen = 1;
feed->peslen = 0;
feed->pes_tei_counter = 0;
@@ -1284,6 +1331,31 @@
static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,
const u8 *buf, const u8 timestamp[TIMESTAMP_LEN])
{
+ u16 pid = ts_pid(buf);
+ u8 scrambling_bits = ts_scrambling_ctrl(buf);
+ struct dmx_data_ready dmx_data_ready;
+
+ /*
+ * Notify on scrambling status change only when we move
+ * from clear (0) to non-clear and vise-versa
+ */
+ if ((scrambling_bits && !feed->scrambling_bits) ||
+ (!scrambling_bits && feed->scrambling_bits)) {
+ dmx_data_ready.status = DMX_OK_SCRAMBLING_STATUS;
+ dmx_data_ready.data_length = 0;
+ dmx_data_ready.scrambling_bits.pid = pid;
+ dmx_data_ready.scrambling_bits.old_value =
+ feed->scrambling_bits;
+ dmx_data_ready.scrambling_bits.new_value = scrambling_bits;
+
+ if (feed->type == DMX_TYPE_SEC)
+ dvb_dmx_notify_section_event(feed, &dmx_data_ready, 0);
+ else
+ feed->data_ready_cb.ts(&feed->feed.ts, &dmx_data_ready);
+ }
+
+ feed->scrambling_bits = scrambling_bits;
+
switch (feed->type) {
case DMX_TYPE_TS:
if (!feed->feed.ts.is_filtering)
@@ -1543,9 +1615,11 @@
if (pktsize == 192) {
if (leadingbytes)
- memcpy(timestamp, &buf[p], TIMESTAMP_LEN);
+ memcpy(timestamp, &demux->tsbuf[p],
+ TIMESTAMP_LEN);
else
- memcpy(timestamp, &buf[188], TIMESTAMP_LEN);
+ memcpy(timestamp, &demux->tsbuf[188],
+ TIMESTAMP_LEN);
} else {
memset(timestamp, 0, TIMESTAMP_LEN);
}
@@ -2044,6 +2118,7 @@
}
feed->first_cc = 1;
+ feed->scrambling_bits = 0;
if ((feed->ts_type & TS_PACKET) &&
!(feed->ts_type & TS_PAYLOAD_ONLY)) {
@@ -2250,7 +2325,9 @@
struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
struct dmx_data_ready data;
struct dvb_demux *dvbdmx = feed->demux;
- int ret;
+ int ret = 0;
+ int secure_non_rec = feed->secure_mode.is_secured &&
+ !dvb_dmx_is_rec_feed(feed);
mutex_lock(&dvbdmx->mutex);
@@ -2259,13 +2336,14 @@
return -EINVAL;
}
- /* Decoder feeds are handled by plug-in */
- if (feed->ts_type & TS_DECODER) {
+ /* Decoder & non-recording secure feeds are handled by plug-in */
+ if ((feed->ts_type & TS_DECODER) || secure_non_rec) {
if (feed->demux->oob_command)
ret = feed->demux->oob_command(feed, cmd);
- else
- ret = 0;
+ }
+ if (!(feed->ts_type & (TS_PAYLOAD_ONLY | TS_PACKET)) ||
+ secure_non_rec) {
mutex_unlock(&dvbdmx->mutex);
return ret;
}
@@ -2274,44 +2352,9 @@
switch (cmd->type) {
case DMX_OOB_CMD_EOS:
- if (feed->ts_type & TS_PAYLOAD_ONLY) {
- if (feed->secure_mode.is_secured) {
- /* Secure feeds are handled by plug-in */
- if (feed->demux->oob_command)
- ret = feed->demux->oob_command(feed,
- cmd);
- else
- ret = 0;
- break;
- }
+ if (feed->ts_type & TS_PAYLOAD_ONLY)
+ dvb_dmx_check_pes_end(feed);
- /* Close last PES on non-secure feeds */
- if (feed->pusi_seen) {
- data.status = DMX_OK_PES_END;
- data.pes_end.start_gap = 0;
- data.pes_end.actual_length =
- feed->peslen;
- data.pes_end.disc_indicator_set = 0;
- data.pes_end.pes_length_mismatch = 0;
- data.pes_end.stc = 0;
- data.pes_end.tei_counter =
- feed->pes_tei_counter;
- data.pes_end.cont_err_counter =
- feed->pes_cont_err_counter;
- data.pes_end.ts_packets_num =
- feed->pes_ts_packets_num;
-
- feed->peslen = 0;
- feed->pes_tei_counter = 0;
- feed->pes_ts_packets_num = 0;
- feed->pes_cont_err_counter = 0;
-
- ret = feed->data_ready_cb.ts(&feed->feed.ts,
- &data);
- if (ret)
- break;
- }
- }
data.status = DMX_OK_EOS;
ret = feed->data_ready_cb.ts(&feed->feed.ts, &data);
break;
@@ -2331,6 +2374,44 @@
return ret;
}
+static int dvbdmx_ts_get_scrambling_bits(struct dmx_ts_feed *ts_feed,
+ u8 *value)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *demux = feed->demux;
+
+ spin_lock(&demux->lock);
+
+ if (!ts_feed->is_filtering) {
+ spin_unlock(&demux->lock);
+ return -EINVAL;
+ }
+
+ *value = feed->scrambling_bits;
+ spin_unlock(&demux->lock);
+
+ return 0;
+}
+
+static int dvbdmx_ts_insertion_insert_buffer(struct dmx_ts_feed *ts_feed,
+ char *data, size_t size)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *demux = feed->demux;
+
+ spin_lock(&demux->lock);
+ if (!ts_feed->is_filtering) {
+ spin_unlock(&demux->lock);
+ return 0;
+ }
+
+ feed->cb.ts(data, size, NULL, 0, ts_feed, DMX_OK);
+
+ spin_unlock(&demux->lock);
+
+ return 0;
+}
+
static int dmx_ts_set_tsp_out_format(
struct dmx_ts_feed *ts_feed,
enum dmx_tsp_format_t tsp_format)
@@ -2401,6 +2482,11 @@
(*ts_feed)->notify_data_read = NULL;
(*ts_feed)->set_secure_mode = dmx_ts_set_secure_mode;
(*ts_feed)->oob_command = dvbdmx_ts_feed_oob_cmd;
+ (*ts_feed)->get_scrambling_bits = dvbdmx_ts_get_scrambling_bits;
+ (*ts_feed)->ts_insertion_init = NULL;
+ (*ts_feed)->ts_insertion_terminate = NULL;
+ (*ts_feed)->ts_insertion_insert_buffer =
+ dvbdmx_ts_insertion_insert_buffer;
if (!(feed->filter = dvb_dmx_filter_alloc(demux))) {
feed->state = DMX_STATE_FREE;
@@ -2563,6 +2649,7 @@
dvbdmxfeed->feed.sec.secbufp = 0;
dvbdmxfeed->feed.sec.seclen = 0;
dvbdmxfeed->first_cc = 1;
+ dvbdmxfeed->scrambling_bits = 0;
if (!dvbdmx->start_feed) {
mutex_unlock(&dvbdmx->mutex);
@@ -2686,7 +2773,7 @@
struct dvb_demux_feed *feed = (struct dvb_demux_feed *)section_feed;
struct dvb_demux *dvbdmx = feed->demux;
struct dmx_data_ready data;
- int ret;
+ int ret = 0;
data.data_length = 0;
@@ -2711,13 +2798,11 @@
switch (cmd->type) {
case DMX_OOB_CMD_EOS:
data.status = DMX_OK_EOS;
- ret = feed->data_ready_cb.sec(&feed->filter->filter, &data);
break;
case DMX_OOB_CMD_MARKER:
data.status = DMX_OK_MARKER;
data.marker.id = cmd->params.marker.id;
- ret = feed->data_ready_cb.sec(&feed->filter->filter, &data);
break;
default:
@@ -2725,10 +2810,32 @@
break;
}
+ if (!ret)
+ ret = dvb_dmx_notify_section_event(feed, &data, 1);
+
mutex_unlock(&dvbdmx->mutex);
return ret;
}
+static int dvbdmx_section_get_scrambling_bits(
+ struct dmx_section_feed *section_feed, u8 *value)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)section_feed;
+ struct dvb_demux *demux = feed->demux;
+
+ spin_lock(&demux->lock);
+
+ if (!section_feed->is_filtering) {
+ spin_unlock(&demux->lock);
+ return -EINVAL;
+ }
+
+ *value = feed->scrambling_bits;
+ spin_unlock(&demux->lock);
+
+ return 0;
+}
+
static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,
struct dmx_section_feed **feed,
dmx_section_cb callback)
@@ -2769,6 +2876,7 @@
(*feed)->notify_data_read = NULL;
(*feed)->set_secure_mode = dmx_section_set_secure_mode;
(*feed)->oob_command = dvbdmx_section_feed_oob_cmd;
+ (*feed)->get_scrambling_bits = dvbdmx_section_get_scrambling_bits;
mutex_unlock(&dvbdmx->mutex);
return 0;
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index 879aad2..aeafa57 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -187,6 +187,8 @@
int first_cc;
int pusi_seen; /* prevents feeding of garbage from previous section */
+ u8 scrambling_bits;
+
struct dvb_demux_rec_info *rec_info;
u64 prev_tsp_num;
u64 prev_stc;
@@ -323,6 +325,8 @@
u64 curr_match_tsp, u64 prev_match_tsp,
u64 curr_pusi_tsp, u64 prev_pusi_tsp);
void dvb_dmx_notify_idx_events(struct dvb_demux_feed *feed);
+int dvb_dmx_notify_section_event(struct dvb_demux_feed *feed,
+ struct dmx_data_ready *event, int should_lock);
/**
* dvb_dmx_is_video_feed - Returns whether the PES feed
diff --git a/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_core.c b/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_core.c
index 84f7307..9370fc9 100644
--- a/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_core.c
+++ b/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013, 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
@@ -142,7 +142,7 @@
buf_p = msm_gemini_hw_pingpong_active_buffer(&we_pingpong_buf);
if (buf_p) {
buf_p->framedone_len = msm_gemini_hw_encode_output_size();
- GMN_DBG("%s:%d] framedone_len %d\n", __func__, __LINE__,
+ pr_debug("%s:%d] framedone_len %d\n", __func__, __LINE__,
buf_p->framedone_len);
}
diff --git a/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_hw.c b/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_hw.c
index 0cbb101..79c533e 100644
--- a/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_hw.c
+++ b/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_hw.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010,2013 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
@@ -297,6 +297,8 @@
struct msm_gemini_hw_cmd *hw_cmd_p;
+ pr_debug("%s:%d] pingpong index %d", __func__, __LINE__,
+ pingpong_index);
if (pingpong_index == 0) {
hw_cmd_p = &hw_cmd_we_ping_update[0];
@@ -486,40 +488,38 @@
return is_copy_to_user;
}
-void msm_gemini_hw_region_dump(int size)
+#ifdef MSM_GMN_DBG_DUMP
+void msm_gemini_io_dump(int size)
{
- uint32_t *p;
- uint8_t *p8;
-
- if (size > gemini_region_size)
- GMN_PR_ERR("%s:%d] wrong region dump size\n",
- __func__, __LINE__);
-
- p = (uint32_t *) gemini_region_base;
- while (size >= 16) {
- GMN_DBG("0x%08X] %08X %08X %08X %08X\n",
- gemini_region_size - size,
- readl(p), readl(p+1), readl(p+2), readl(p+3));
- p += 4;
- size -= 16;
- }
-
- if (size > 0) {
- uint32_t d;
- GMN_DBG("0x%08X] ", gemini_region_size - size);
- while (size >= 4) {
- GMN_DBG("%08X ", readl(p++));
- size -= 4;
+ char line_str[128], *p_str;
+ void __iomem *addr = gemini_region_base;
+ int i;
+ u32 *p = (u32 *) addr;
+ u32 data;
+ pr_info("%s: %p %d reg_size %d\n", __func__, addr, size,
+ gemini_region_size);
+ line_str[0] = '\0';
+ p_str = line_str;
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+ snprintf(p_str, 12, "%08x: ", (u32) p);
+ p_str += 10;
}
-
- d = readl(p);
- p8 = (uint8_t *) &d;
- while (size) {
- GMN_DBG("%02X", *p8++);
- size--;
+ data = readl_relaxed(p++);
+ snprintf(p_str, 12, "%08x ", data);
+ p_str += 9;
+ if ((i + 1) % 4 == 0) {
+ pr_info("%s\n", line_str);
+ line_str[0] = '\0';
+ p_str = line_str;
}
-
- GMN_DBG("\n");
}
+ if (line_str[0] != '\0')
+ pr_info("%s\n", line_str);
}
+#else
+void msm_gemini_io_dump(int size)
+{
+}
+#endif
diff --git a/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_hw.h b/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_hw.h
index 1c8de19..aa6c4aa1 100644
--- a/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_hw.h
+++ b/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_hw.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013, 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
@@ -95,7 +95,7 @@
int msm_gemini_hw_wait(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us);
void msm_gemini_hw_delay(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us);
int msm_gemini_hw_exec_cmds(struct msm_gemini_hw_cmd *hw_cmd_p, int m_cmds);
-void msm_gemini_hw_region_dump(int size);
+void msm_gemini_io_dump(int size);
#define MSM_GEMINI_PIPELINE_CLK_128MHZ 128 /* 8MP 128MHz */
#define MSM_GEMINI_PIPELINE_CLK_140MHZ 140 /* 9MP 140MHz */
diff --git a/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_hw_reg.h b/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_hw_reg.h
index ea13d68..2fe6038 100644
--- a/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_hw_reg.h
+++ b/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_hw_reg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010, 2013, 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
@@ -171,6 +171,6 @@
#define HWIO_JPEG_IRQ_STATUS_RMSK 0xffffffff
#define HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_ADDR (GEMINI_REG_BASE + 0x00000034)
-#define HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_RMSK 0xffffff
+#define HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_RMSK 0xffffffff
#endif /* MSM_GEMINI_HW_REG_H */
diff --git a/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_sync.c b/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_sync.c
index ed2222a..50c7284 100644
--- a/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_sync.c
+++ b/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_sync.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013, 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
@@ -16,6 +16,8 @@
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <media/msm_gemini.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
#include "msm_gemini_sync.h"
#include "msm_gemini_core.h"
#include "msm_gemini_platform.h"
@@ -23,6 +25,9 @@
static int release_buf;
+/* size is based on 4k page size */
+static const int g_max_out_size = 0x7ff000;
+
/*************** queue helper ****************/
inline void msm_gemini_q_init(char const *name, struct msm_gemini_q *q_p)
{
@@ -180,7 +185,7 @@
{
int rc = 0;
- GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ pr_debug("%s:%d] buf_in %p", __func__, __LINE__, buf_in);
if (buf_in) {
buf_in->vbuf.framedone_len = buf_in->framedone_len;
@@ -266,19 +271,88 @@
/*************** output queue ****************/
+int msm_gemini_get_out_buffer(struct msm_gemini_device *pgmn_dev,
+ struct msm_gemini_hw_buf *p_outbuf)
+{
+ int buf_size = 0;
+ int bytes_remaining = 0;
+ if (pgmn_dev->out_offset >= pgmn_dev->out_buf.y_len) {
+ GMN_PR_ERR("%s:%d] no more buffers", __func__, __LINE__);
+ return -EINVAL;
+ }
+ bytes_remaining = pgmn_dev->out_buf.y_len - pgmn_dev->out_offset;
+ buf_size = min(bytes_remaining, pgmn_dev->max_out_size);
+
+ pgmn_dev->out_frag_cnt++;
+ pr_debug("%s:%d] buf_size[%d] %d", __func__, __LINE__,
+ pgmn_dev->out_frag_cnt, buf_size);
+ p_outbuf->y_len = buf_size;
+ p_outbuf->y_buffer_addr = pgmn_dev->out_buf.y_buffer_addr +
+ pgmn_dev->out_offset;
+ pgmn_dev->out_offset += buf_size;
+ return 0;
+}
+
+int msm_gemini_outmode_single_we_pingpong_irq(
+ struct msm_gemini_device *pgmn_dev,
+ struct msm_gemini_core_buf *buf_in)
+{
+ int rc = 0;
+ struct msm_gemini_core_buf out_buf;
+ int frame_done = buf_in &&
+ buf_in->vbuf.type == MSM_GEMINI_EVT_FRAMEDONE;
+ pr_debug("%s:%d] framedone %d", __func__, __LINE__, frame_done);
+ if (!pgmn_dev->out_buf_set) {
+ pr_err("%s:%d] output buffer not set",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ if (frame_done) {
+ /* send the buffer back */
+ pgmn_dev->out_buf.vbuf.framedone_len = buf_in->framedone_len;
+ pgmn_dev->out_buf.vbuf.type = MSM_GEMINI_EVT_FRAMEDONE;
+ rc = msm_gemini_q_in_buf(&pgmn_dev->output_rtn_q,
+ &pgmn_dev->out_buf);
+ if (rc) {
+ pr_err("%s:%d] cannot queue the output buffer",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ rc = msm_gemini_q_wakeup(&pgmn_dev->output_rtn_q);
+ /* reset the output buffer since the ownership is
+ transferred to the rtn queue */
+ if (!rc)
+ pgmn_dev->out_buf_set = 0;
+ } else {
+ /* configure ping/pong */
+ rc = msm_gemini_get_out_buffer(pgmn_dev, &out_buf);
+ if (rc)
+ msm_gemini_core_we_buf_reset(&out_buf);
+ else
+ msm_gemini_core_we_buf_update(&out_buf);
+ }
+ return rc;
+}
+
int msm_gemini_we_pingpong_irq(struct msm_gemini_device *pgmn_dev,
struct msm_gemini_core_buf *buf_in)
{
int rc = 0;
struct msm_gemini_core_buf *buf_out;
- GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ pr_debug("%s:%d] Enter mode %d", __func__, __LINE__,
+ pgmn_dev->out_mode);
+
+ if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_SINGLE)
+ return msm_gemini_outmode_single_we_pingpong_irq(pgmn_dev,
+ buf_in);
+
if (buf_in) {
- GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ pr_debug("%s:%d] 0x%08x %d\n", __func__, __LINE__,
(int) buf_in->y_buffer_addr, buf_in->y_len);
rc = msm_gemini_q_in_buf(&pgmn_dev->output_rtn_q, buf_in);
} else {
- GMN_DBG("%s:%d] no output return buffer\n", __func__,
+ pr_debug("%s:%d] no output return buffer\n", __func__,
__LINE__);
rc = -1;
return rc;
@@ -291,7 +365,7 @@
kfree(buf_out);
} else {
msm_gemini_core_we_buf_reset(buf_in);
- GMN_DBG("%s:%d] no output buffer\n", __func__, __LINE__);
+ pr_debug("%s:%d] no output buffer\n", __func__, __LINE__);
rc = -2;
}
@@ -339,6 +413,43 @@
return 0;
}
+int msm_gemini_set_output_buf(struct msm_gemini_device *pgmn_dev,
+ void __user *arg)
+{
+ struct msm_gemini_buf buf_cmd;
+
+ if (pgmn_dev->out_buf_set) {
+ pr_err("%s:%d] outbuffer buffer already provided",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_gemini_buf))) {
+ pr_err("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ GMN_DBG("%s:%d] output addr 0x%08x len %d", __func__, __LINE__,
+ (int) buf_cmd.vaddr,
+ buf_cmd.y_len);
+
+ pgmn_dev->out_buf.y_buffer_addr = msm_gemini_platform_v2p(
+ buf_cmd.fd,
+ buf_cmd.y_len,
+ &pgmn_dev->out_buf.file,
+ &pgmn_dev->out_buf.handle);
+ if (!pgmn_dev->out_buf.y_buffer_addr) {
+ pr_err("%s:%d] cannot map the output address",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ pgmn_dev->out_buf.y_len = buf_cmd.y_len;
+ pgmn_dev->out_buf.vbuf = buf_cmd;
+ pgmn_dev->out_buf_set = 1;
+
+ return 0;
+}
+
int msm_gemini_output_buf_enqueue(struct msm_gemini_device *pgmn_dev,
void __user *arg)
{
@@ -456,6 +567,7 @@
struct msm_gemini_core_buf *buf_p;
struct msm_gemini_buf buf_cmd;
int rc = 0;
+ struct msm_bus_scale_pdata *p_bus_scale_data = NULL;
if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_gemini_buf))) {
GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
@@ -484,9 +596,9 @@
return rc;
}
} else {
- buf_p->y_buffer_addr = msm_gemini_platform_v2p(buf_cmd.fd,
- buf_cmd.y_len + buf_cmd.cbcr_len, &buf_p->file,
- &buf_p->handle) + buf_cmd.offset + buf_cmd.y_off;
+ buf_p->y_buffer_addr = msm_gemini_platform_v2p(buf_cmd.fd,
+ buf_cmd.y_len + buf_cmd.cbcr_len, &buf_p->file,
+ &buf_p->handle) + buf_cmd.offset + buf_cmd.y_off;
}
buf_p->y_len = buf_cmd.y_len;
@@ -504,6 +616,30 @@
return -1;
}
buf_p->vbuf = buf_cmd;
+ buf_p->vbuf.type = MSM_GEMINI_EVT_RESET;
+
+ /* Set bus vectors */
+ p_bus_scale_data = (struct msm_bus_scale_pdata *)
+ pgmn_dev->pdev->dev.platform_data;
+ if (pgmn_dev->bus_perf_client &&
+ (MSM_GMN_OUTMODE_SINGLE == pgmn_dev->out_mode)) {
+ int rc;
+ struct msm_bus_paths *path = &(p_bus_scale_data->usecase[1]);
+ GMN_DBG("%s:%d] Update bus bandwidth", __func__, __LINE__);
+ if (pgmn_dev->op_mode & MSM_GEMINI_MODE_OFFLINE_ENCODE) {
+ path->vectors[0].ab = (buf_p->y_len + buf_p->cbcr_len) *
+ 15 * 2;
+ path->vectors[0].ib = path->vectors[0].ab;
+ path->vectors[1].ab = 0;
+ path->vectors[1].ib = 0;
+ }
+ rc = msm_bus_scale_client_update_request(
+ pgmn_dev->bus_perf_client, 1);
+ if (rc < 0) {
+ GMN_PR_ERR("%s:%d] update_request fails %d",
+ __func__, __LINE__, rc);
+ }
+ }
msm_gemini_q_in(&pgmn_dev->input_buf_q, buf_p);
@@ -545,6 +681,9 @@
int __msm_gemini_open(struct msm_gemini_device *pgmn_dev)
{
int rc;
+ struct msm_bus_scale_pdata *p_bus_scale_data =
+ (struct msm_bus_scale_pdata *)pgmn_dev->pdev->dev.
+ platform_data;
mutex_lock(&pgmn_dev->lock);
if (pgmn_dev->open_count) {
@@ -576,7 +715,23 @@
msm_gemini_q_cleanup(&pgmn_dev->input_rtn_q);
msm_gemini_q_cleanup(&pgmn_dev->input_buf_q);
msm_gemini_core_init();
+ pgmn_dev->out_mode = MSM_GMN_OUTMODE_FRAGMENTED;
+ pgmn_dev->out_buf_set = 0;
+ pgmn_dev->out_offset = 0;
+ pgmn_dev->max_out_size = g_max_out_size;
+ pgmn_dev->out_frag_cnt = 0;
+ pgmn_dev->bus_perf_client = 0;
+ if (p_bus_scale_data) {
+ GMN_DBG("%s:%d] register bus client", __func__, __LINE__);
+ pgmn_dev->bus_perf_client =
+ msm_bus_scale_register_client(p_bus_scale_data);
+ if (!pgmn_dev->bus_perf_client) {
+ GMN_PR_ERR("%s:%d] bus client register failed",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ }
GMN_DBG("%s:%d] success\n", __func__, __LINE__);
return rc;
}
@@ -593,13 +748,23 @@
pgmn_dev->open_count--;
mutex_unlock(&pgmn_dev->lock);
- msm_gemini_core_release(release_buf);
+ if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_FRAGMENTED) {
+ msm_gemini_core_release(release_buf);
+ } else if (pgmn_dev->out_buf_set) {
+ msm_gemini_platform_p2v(pgmn_dev->out_buf.file,
+ &pgmn_dev->out_buf.handle);
+ }
msm_gemini_q_cleanup(&pgmn_dev->evt_q);
msm_gemini_q_cleanup(&pgmn_dev->output_rtn_q);
msm_gemini_outbuf_q_cleanup(&pgmn_dev->output_buf_q);
msm_gemini_q_cleanup(&pgmn_dev->input_rtn_q);
msm_gemini_outbuf_q_cleanup(&pgmn_dev->input_buf_q);
+ if (pgmn_dev->bus_perf_client) {
+ msm_bus_scale_unregister_client(pgmn_dev->bus_perf_client);
+ pgmn_dev->bus_perf_client = 0;
+ }
+
if (pgmn_dev->open_count)
GMN_PR_ERR(KERN_ERR "%s: multiple opens\n", __func__);
@@ -699,29 +864,63 @@
}
}
- for (i = 0; i < 2; i++) {
- buf_out_free[i] = msm_gemini_q_out(&pgmn_dev->output_buf_q);
+ if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_FRAGMENTED) {
+ for (i = 0; i < 2; i++) {
+ buf_out_free[i] =
+ msm_gemini_q_out(&pgmn_dev->output_buf_q);
- if (buf_out_free[i]) {
- msm_gemini_core_we_buf_update(buf_out_free[i]);
- } else if (i == 1) {
- /* set the pong to same address as ping */
- buf_out_free[0]->y_len >>= 1;
- buf_out_free[0]->y_buffer_addr +=
- buf_out_free[0]->y_len;
- msm_gemini_core_we_buf_update(buf_out_free[0]);
- /* since ping and pong are same buf release only once*/
- release_buf = 0;
- } else {
- GMN_DBG("%s:%d] no output buffer\n",
- __func__, __LINE__);
- break;
+ if (buf_out_free[i]) {
+ msm_gemini_core_we_buf_update(buf_out_free[i]);
+ } else if (i == 1) {
+ /* set the pong to same address as ping */
+ buf_out_free[0]->y_len >>= 1;
+ buf_out_free[0]->y_buffer_addr +=
+ buf_out_free[0]->y_len;
+ msm_gemini_core_we_buf_update(buf_out_free[0]);
+ /*
+ * since ping and pong are same buf
+ * release only once
+ */
+ release_buf = 0;
+ } else {
+ GMN_DBG("%s:%d] no output buffer\n",
+ __func__, __LINE__);
+ break;
+ }
}
+ for (i = 0; i < 2; i++)
+ kfree(buf_out_free[i]);
+ } else {
+ struct msm_gemini_core_buf out_buf;
+ /*
+ * Since the same buffer is fragmented, p2v need not be
+ * called for all the buffers
+ */
+ release_buf = 0;
+ if (!pgmn_dev->out_buf_set) {
+ GMN_PR_ERR("%s:%d] output buffer not set",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ /* configure ping */
+ rc = msm_gemini_get_out_buffer(pgmn_dev, &out_buf);
+ if (rc) {
+ GMN_PR_ERR("%s:%d] no output buffer for ping",
+ __func__, __LINE__);
+ return rc;
+ }
+ msm_gemini_core_we_buf_update(&out_buf);
+ /* configure pong */
+ rc = msm_gemini_get_out_buffer(pgmn_dev, &out_buf);
+ if (rc) {
+ GMN_DBG("%s:%d] no output buffer for pong",
+ __func__, __LINE__);
+ /* fall through to configure same buffer */
+ }
+ msm_gemini_core_we_buf_update(&out_buf);
+ msm_gemini_io_dump(0x150);
}
- for (i = 0; i < 2; i++)
- kfree(buf_out_free[i]);
-
rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, arg);
GMN_DBG("%s:%d]\n", __func__, __LINE__);
return rc;
@@ -746,12 +945,22 @@
return rc;
}
-int msm_gemini_ioctl_test_dump_region(struct msm_gemini_device *pgmn_dev,
- unsigned long arg)
+int msm_gemini_ioctl_set_outmode(struct msm_gemini_device *pgmn_dev,
+ void * __user arg)
{
- GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
- msm_gemini_hw_region_dump(arg);
- return 0;
+ int rc = 0;
+ enum msm_gmn_out_mode mode;
+
+ if (copy_from_user(&mode, arg, sizeof(mode))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ GMN_DBG("%s:%d] mode %d", __func__, __LINE__, mode);
+
+ if ((mode == MSM_GMN_OUTMODE_FRAGMENTED)
+ || (mode == MSM_GMN_OUTMODE_SINGLE))
+ pgmn_dev->out_mode = mode;
+ return rc;
}
long __msm_gemini_ioctl(struct msm_gemini_device *pgmn_dev,
@@ -790,8 +999,12 @@
break;
case MSM_GMN_IOCTL_OUTPUT_BUF_ENQUEUE:
- rc = msm_gemini_output_buf_enqueue(pgmn_dev,
- (void __user *) arg);
+ if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_FRAGMENTED)
+ rc = msm_gemini_output_buf_enqueue(pgmn_dev,
+ (void __user *) arg);
+ else
+ rc = msm_gemini_set_output_buf(pgmn_dev,
+ (void __user *) arg);
break;
case MSM_GMN_IOCTL_OUTPUT_GET:
@@ -818,8 +1031,8 @@
rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, (void __user *) arg);
break;
- case MSM_GMN_IOCTL_TEST_DUMP_REGION:
- rc = msm_gemini_ioctl_test_dump_region(pgmn_dev, arg);
+ case MSM_GMN_IOCTL_SET_MODE:
+ rc = msm_gemini_ioctl_set_outmode(pgmn_dev, (void __user *)arg);
break;
default:
diff --git a/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_sync.h b/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_sync.h
index d1a43e1..88e9615 100644
--- a/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_sync.h
+++ b/drivers/media/platform/msm/camera_v1/gemini/msm_gemini_sync.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010,2013, 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
@@ -74,6 +74,16 @@
struct msm_gemini_q input_buf_q;
struct v4l2_subdev subdev;
+ enum msm_gmn_out_mode out_mode;
+
+ /* single out mode parameters */
+ struct msm_gemini_hw_buf out_buf;
+ int out_offset;
+ int out_buf_set;
+ int max_out_size;
+ int out_frag_cnt;
+
+ uint32_t bus_perf_client;
};
int __msm_gemini_open(struct msm_gemini_device *pgmn_dev);
diff --git a/drivers/media/platform/msm/camera_v2/Kconfig b/drivers/media/platform/msm/camera_v2/Kconfig
index d9552e2..525a545 100644
--- a/drivers/media/platform/msm/camera_v2/Kconfig
+++ b/drivers/media/platform/msm/camera_v2/Kconfig
@@ -36,6 +36,16 @@
This config macro is required targets based on 8960,
8930 and 8064 platforms.
+config MSM_CSI22_HEADER
+ bool "Qualcomm MSM CSI 2.2 Header"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for CSI drivers to include 2.2
+ header. This header has register macros and its
+ values and bit mask for register configuration bits
+ This config macro is required targets based on 8610
+ platform.
+
config MSM_CSI30_HEADER
bool "Qualcomm MSM CSI 3.0 Header"
depends on MSMB_CAMERA
@@ -82,6 +92,15 @@
of any CID of CSID can be routed to of of pixel or raw
data interface in VFE.
+config MSM_ISPIF_V1
+ bool "Qualcomm MSM Image Signal Processing interface support"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for Image Signal Processing interface module.
+ This module acts as a crossbar between CSID and VFE. Output
+ of any CID of MSM_CSI22_HEADER can be routed to of pixel
+ or raw data interface in VFE.
+
config S5K3L1YX
bool "Sensor S5K3L1YX (BAYER 12M)"
depends on MSMB_CAMERA
@@ -153,3 +172,8 @@
This module serves as the common driver
for the JPEG 1.0 encoder and decoder.
+config MSM_GEMINI
+ tristate "Qualcomm MSM Gemini JPEG engine support"
+ depends on MSMB_CAMERA && (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_MSM8960)
+ ---help---
+ Enables support for the Gemini JPEG encoder engine for 8x60.
diff --git a/drivers/media/platform/msm/camera_v2/Makefile b/drivers/media/platform/msm/camera_v2/Makefile
index a1c5ea5..02eb3dd 100644
--- a/drivers/media/platform/msm/camera_v2/Makefile
+++ b/drivers/media/platform/msm/camera_v2/Makefile
@@ -16,3 +16,4 @@
obj-$(CONFIG_MSMB_JPEG) += jpeg_10/
obj-$(CONFIG_MSMB_CAMERA) += msm_buf_mgr/
obj-$(CONFIG_MSMB_CAMERA) += pproc/
+obj-$(CONFIG_MSMB_CAMERA) += gemini/
diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c
index 71087d9..08fa7dd 100644
--- a/drivers/media/platform/msm/camera_v2/camera/camera.c
+++ b/drivers/media/platform/msm/camera_v2/camera/camera.c
@@ -539,7 +539,6 @@
rc = msm_create_session(pvdev->vdev->num, pvdev->vdev);
if (rc < 0)
goto session_fail;
-
rc = msm_create_command_ack_q(pvdev->vdev->num, 0);
if (rc < 0)
goto command_ack_q_fail;
@@ -595,17 +594,23 @@
struct v4l2_event event;
struct msm_video_device *pvdev = video_drvdata(filep);
struct camera_v4l2_private *sp = fh_to_private(filep->private_data);
-
BUG_ON(!pvdev);
atomic_sub_return(1, &pvdev->opened);
if (atomic_read(&pvdev->opened) == 0) {
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_DEL_STREAM, -1, &event);
+
+ /* Donot wait, imaging server may have crashed */
+ msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+
camera_pack_event(filep, MSM_CAMERA_DEL_SESSION, 0, -1, &event);
/* Donot wait, imaging server may have crashed */
msm_post_event(&event, -1);
+ msm_delete_command_ack_q(pvdev->vdev->num, 0);
/* This should take care of both normal close
* and application crashes */
@@ -617,7 +622,6 @@
/* Donot wait, imaging server may have crashed */
msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
-
msm_delete_command_ack_q(pvdev->vdev->num,
sp->stream_id);
diff --git a/drivers/media/platform/msm/camera_v2/gemini/Makefile b/drivers/media/platform/msm/camera_v2/gemini/Makefile
new file mode 100644
index 0000000..74d7294
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/Makefile
@@ -0,0 +1,5 @@
+GCC_VERSION := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+ccflags-y += -Idrivers/media/video/msm
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+
+obj-$(CONFIG_MSM_GEMINI) += msm_gemini_dev.o msm_gemini_sync.o msm_gemini_core.o msm_gemini_hw.o msm_gemini_platform.o
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_common.h b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_common.h
new file mode 100644
index 0000000..eefad6d
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_common.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2010,2013, 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.
+ */
+
+#ifndef MSM_GEMINI_COMMON_H
+#define MSM_GEMINI_COMMON_H
+
+#define MSM_GEMINI_DEBUG
+#ifdef MSM_GEMINI_DEBUG
+#define GMN_DBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define GMN_DBG(fmt, args...) do { } while (0)
+#endif
+
+#define GMN_PR_ERR pr_err
+
+enum GEMINI_MODE {
+ GEMINI_MODE_DISABLE,
+ GEMINI_MODE_OFFLINE,
+ GEMINI_MODE_REALTIME,
+ GEMINI_MODE_REALTIME_ROTATION
+};
+
+enum GEMINI_ROTATION {
+ GEMINI_ROTATION_0,
+ GEMINI_ROTATION_90,
+ GEMINI_ROTATION_180,
+ GEMINI_ROTATION_270
+};
+
+#endif /* MSM_GEMINI_COMMON_H */
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_core.c b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_core.c
new file mode 100644
index 0000000..88fa9e7
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_core.c
@@ -0,0 +1,250 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "msm_gemini_hw.h"
+#include "msm_gemini_core.h"
+#include "msm_gemini_platform.h"
+#include "msm_gemini_common.h"
+
+static struct msm_gemini_hw_pingpong fe_pingpong_buf;
+static struct msm_gemini_hw_pingpong we_pingpong_buf;
+static int we_pingpong_index;
+static int reset_done_ack;
+static spinlock_t reset_lock;
+static wait_queue_head_t reset_wait;
+
+int msm_gemini_core_reset(uint8_t op_mode, void *base, int size)
+{
+ unsigned long flags;
+ int rc = 0;
+ int tm = 500;
+ memset(&fe_pingpong_buf, 0, sizeof(fe_pingpong_buf));
+ fe_pingpong_buf.is_fe = 1;
+ we_pingpong_index = 0;
+ memset(&we_pingpong_buf, 0, sizeof(we_pingpong_buf));
+ spin_lock_irqsave(&reset_lock, flags);
+ reset_done_ack = 0;
+ msm_gemini_hw_reset(base, size);
+ spin_unlock_irqrestore(&reset_lock, flags);
+ rc = wait_event_interruptible_timeout(
+ reset_wait,
+ reset_done_ack,
+ msecs_to_jiffies(tm));
+
+ if (!reset_done_ack) {
+ GMN_DBG("%s: reset ACK failed %d", __func__, rc);
+ return -EBUSY;
+ }
+
+ GMN_DBG("%s: reset_done_ack rc %d", __func__, rc);
+ spin_lock_irqsave(&reset_lock, flags);
+ reset_done_ack = 0;
+ spin_unlock_irqrestore(&reset_lock, flags);
+
+ if (op_mode == MSM_GEMINI_MODE_REALTIME_ENCODE) {
+ /* Nothing needed for fe buffer cfg, config we only */
+ msm_gemini_hw_we_buffer_cfg(1);
+ } else {
+ /* Nothing needed for fe buffer cfg, config we only */
+ msm_gemini_hw_we_buffer_cfg(0);
+ }
+
+ /* @todo wait for reset done irq */
+
+ return 0;
+}
+
+void msm_gemini_core_release(int release_buf)
+{
+ int i = 0;
+ for (i = 0; i < 2; i++) {
+ if (we_pingpong_buf.buf_status[i] && release_buf)
+ msm_gemini_platform_p2v(we_pingpong_buf.buf[i].file,
+ &we_pingpong_buf.buf[i].handle);
+ we_pingpong_buf.buf_status[i] = 0;
+ }
+}
+
+void msm_gemini_core_init(void)
+{
+ init_waitqueue_head(&reset_wait);
+ spin_lock_init(&reset_lock);
+}
+
+int msm_gemini_core_fe_start(void)
+{
+ msm_gemini_hw_fe_start();
+ return 0;
+}
+
+/* fetch engine */
+int msm_gemini_core_fe_buf_update(struct msm_gemini_core_buf *buf)
+{
+ GMN_DBG("%s:%d] 0x%08x %d 0x%08x %d\n", __func__, __LINE__,
+ (int) buf->y_buffer_addr, buf->y_len,
+ (int) buf->cbcr_buffer_addr, buf->cbcr_len);
+ return msm_gemini_hw_pingpong_update(&fe_pingpong_buf, buf);
+}
+
+void *msm_gemini_core_fe_pingpong_irq(int gemini_irq_status, void *context)
+{
+ return msm_gemini_hw_pingpong_irq(&fe_pingpong_buf);
+}
+
+/* write engine */
+int msm_gemini_core_we_buf_update(struct msm_gemini_core_buf *buf)
+{
+ int rc;
+ GMN_DBG("%s:%d] 0x%08x 0x%08x %d\n", __func__, __LINE__,
+ (int) buf->y_buffer_addr, (int) buf->cbcr_buffer_addr,
+ buf->y_len);
+ we_pingpong_buf.buf_status[we_pingpong_index] = 0;
+ we_pingpong_index = (we_pingpong_index + 1)%2;
+ rc = msm_gemini_hw_pingpong_update(&we_pingpong_buf, buf);
+ return 0;
+}
+
+int msm_gemini_core_we_buf_reset(struct msm_gemini_hw_buf *buf)
+{
+ int i;
+ for (i = 0; i < 2; i++) {
+ if (we_pingpong_buf.buf[i].y_buffer_addr
+ == buf->y_buffer_addr)
+ we_pingpong_buf.buf_status[i] = 0;
+ }
+ return 0;
+}
+
+void *msm_gemini_core_we_pingpong_irq(int gemini_irq_status, void *context)
+{
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+
+ return msm_gemini_hw_pingpong_irq(&we_pingpong_buf);
+}
+
+void *msm_gemini_core_framedone_irq(int gemini_irq_status, void *context)
+{
+ struct msm_gemini_hw_buf *buf_p;
+
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+
+ buf_p = msm_gemini_hw_pingpong_active_buffer(&we_pingpong_buf);
+ if (buf_p) {
+ buf_p->framedone_len = msm_gemini_hw_encode_output_size();
+ GMN_DBG("%s:%d] framedone_len %d\n", __func__, __LINE__,
+ buf_p->framedone_len);
+ }
+
+ return buf_p;
+}
+
+void *msm_gemini_core_reset_ack_irq(int gemini_irq_status, void *context)
+{
+ /* @todo return the status back to msm_gemini_core_reset */
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+ return NULL;
+}
+
+void *msm_gemini_core_err_irq(int gemini_irq_status, void *context)
+{
+ GMN_PR_ERR("%s:%d]\n", __func__, gemini_irq_status);
+ return NULL;
+}
+
+static int (*msm_gemini_irq_handler) (int, void *, void *);
+
+irqreturn_t msm_gemini_core_irq(int irq_num, void *context)
+{
+ void *data = NULL;
+ unsigned long flags;
+ int gemini_irq_status;
+
+ GMN_DBG("%s:%d] irq_num = %d\n", __func__, __LINE__, irq_num);
+
+ spin_lock_irqsave(&reset_lock, flags);
+ reset_done_ack = 1;
+ spin_unlock_irqrestore(&reset_lock, flags);
+ gemini_irq_status = msm_gemini_hw_irq_get_status();
+
+ GMN_DBG("%s:%d] gemini_irq_status = %0x\n", __func__, __LINE__,
+ gemini_irq_status);
+
+ /* For reset and framedone IRQs, clear all bits */
+ if (gemini_irq_status & 0x400) {
+ wake_up(&reset_wait);
+ msm_gemini_hw_irq_clear(HWIO_JPEG_IRQ_CLEAR_RMSK,
+ JPEG_IRQ_CLEAR_ALL);
+ } else if (gemini_irq_status & 0x1) {
+ msm_gemini_hw_irq_clear(HWIO_JPEG_IRQ_CLEAR_RMSK,
+ JPEG_IRQ_CLEAR_ALL);
+ } else {
+ msm_gemini_hw_irq_clear(HWIO_JPEG_IRQ_CLEAR_RMSK,
+ gemini_irq_status);
+ }
+
+ if (msm_gemini_hw_irq_is_frame_done(gemini_irq_status)) {
+ data = msm_gemini_core_framedone_irq(gemini_irq_status,
+ context);
+ if (msm_gemini_irq_handler)
+ msm_gemini_irq_handler(
+ MSM_GEMINI_HW_MASK_COMP_FRAMEDONE,
+ context, data);
+ }
+
+ if (msm_gemini_hw_irq_is_fe_pingpong(gemini_irq_status)) {
+ data = msm_gemini_core_fe_pingpong_irq(gemini_irq_status,
+ context);
+ if (msm_gemini_irq_handler)
+ msm_gemini_irq_handler(MSM_GEMINI_HW_MASK_COMP_FE,
+ context, data);
+ }
+
+ if (msm_gemini_hw_irq_is_we_pingpong(gemini_irq_status) &&
+ !msm_gemini_hw_irq_is_frame_done(gemini_irq_status)) {
+ data = msm_gemini_core_we_pingpong_irq(gemini_irq_status,
+ context);
+ if (msm_gemini_irq_handler)
+ msm_gemini_irq_handler(MSM_GEMINI_HW_MASK_COMP_WE,
+ context, data);
+ }
+
+ if (msm_gemini_hw_irq_is_reset_ack(gemini_irq_status)) {
+ data = msm_gemini_core_reset_ack_irq(gemini_irq_status,
+ context);
+ if (msm_gemini_irq_handler)
+ msm_gemini_irq_handler(
+ MSM_GEMINI_HW_MASK_COMP_RESET_ACK,
+ context, data);
+ }
+
+ /* Unexpected/unintended HW interrupt */
+ if (msm_gemini_hw_irq_is_err(gemini_irq_status)) {
+ data = msm_gemini_core_err_irq(gemini_irq_status, context);
+ if (msm_gemini_irq_handler)
+ msm_gemini_irq_handler(MSM_GEMINI_HW_MASK_COMP_ERR,
+ context, data);
+ }
+
+ return IRQ_HANDLED;
+}
+
+void msm_gemini_core_irq_install(int (*irq_handler) (int, void *, void *))
+{
+ msm_gemini_irq_handler = irq_handler;
+}
+
+void msm_gemini_core_irq_remove(void)
+{
+ msm_gemini_irq_handler = NULL;
+}
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_core.h b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_core.h
new file mode 100644
index 0000000..3aac25a
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_core.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2010,2013, 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.
+ */
+
+#ifndef MSM_GEMINI_CORE_H
+#define MSM_GEMINI_CORE_H
+
+#include <linux/interrupt.h>
+#include "msm_gemini_hw.h"
+
+#define msm_gemini_core_buf msm_gemini_hw_buf
+
+irqreturn_t msm_gemini_core_irq(int irq_num, void *context);
+
+void msm_gemini_core_irq_install(int (*irq_handler) (int, void *, void *));
+void msm_gemini_core_irq_remove(void);
+
+int msm_gemini_core_fe_buf_update(struct msm_gemini_core_buf *buf);
+int msm_gemini_core_we_buf_update(struct msm_gemini_core_buf *buf);
+int msm_gemini_core_we_buf_reset(struct msm_gemini_hw_buf *buf);
+
+int msm_gemini_core_reset(uint8_t op_mode, void *base, int size);
+int msm_gemini_core_fe_start(void);
+
+void msm_gemini_core_release(int);
+void msm_gemini_core_init(void);
+#endif /* MSM_GEMINI_CORE_H */
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_dev.c b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_dev.c
new file mode 100644
index 0000000..13c1e11
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_dev.c
@@ -0,0 +1,265 @@
+/* Copyright (c) 2010-2011,2013 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <media/msm_gemini.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <mach/board.h>
+#include "../msm.h"
+#include "msm_gemini_sync.h"
+#include "msm_gemini_common.h"
+
+#define MSM_GEMINI_NAME "gemini"
+#define MSM_GEMINI_DRV_NAME "msm_gemini"
+
+static int msm_gemini_open(struct inode *inode, struct file *filp)
+{
+ int rc;
+
+ struct msm_gemini_device *pgmn_dev = container_of(inode->i_cdev,
+ struct msm_gemini_device, cdev);
+ filp->private_data = pgmn_dev;
+
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+
+ rc = __msm_gemini_open(pgmn_dev);
+
+ GMN_DBG("%s:%d] %s open_count = %d\n", __func__, __LINE__,
+ filp->f_path.dentry->d_name.name, pgmn_dev->open_count);
+
+ return rc;
+}
+
+static int msm_gemini_release(struct inode *inode, struct file *filp)
+{
+ int rc;
+
+ struct msm_gemini_device *pgmn_dev = filp->private_data;
+
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+
+ rc = __msm_gemini_release(pgmn_dev);
+
+ GMN_DBG("%s:%d] %s open_count = %d\n", __func__, __LINE__,
+ filp->f_path.dentry->d_name.name, pgmn_dev->open_count);
+ return rc;
+}
+
+static long msm_gemini_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc;
+ struct msm_gemini_device *pgmn_dev = filp->private_data;
+
+ GMN_DBG("%s:%d] cmd=%d pgmn_dev=0x%x arg=0x%x\n", __func__,
+ __LINE__, _IOC_NR(cmd), (uint32_t)pgmn_dev, (uint32_t)arg);
+
+ rc = __msm_gemini_ioctl(pgmn_dev, cmd, arg);
+
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+ return rc;
+}
+
+static const struct file_operations msm_gemini_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_gemini_open,
+ .release = msm_gemini_release,
+ .unlocked_ioctl = msm_gemini_ioctl,
+};
+
+static struct class *msm_gemini_class;
+static dev_t msm_gemini_devno;
+static struct msm_gemini_device *msm_gemini_device_p;
+
+int msm_gemini_subdev_init(struct v4l2_subdev *gemini_sd)
+{
+ int rc;
+ struct msm_gemini_device *pgmn_dev =
+ (struct msm_gemini_device *)gemini_sd->host_priv;
+
+ GMN_DBG("%s:%d: gemini_sd=0x%x pgmn_dev=0x%x\n",
+ __func__, __LINE__, (uint32_t)gemini_sd, (uint32_t)pgmn_dev);
+ rc = __msm_gemini_open(pgmn_dev);
+ GMN_DBG("%s:%d: rc=%d\n",
+ __func__, __LINE__, rc);
+ return rc;
+}
+
+static long msm_gemini_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ long rc;
+ struct msm_gemini_device *pgmn_dev =
+ (struct msm_gemini_device *)sd->host_priv;
+
+ GMN_DBG("%s: cmd=%d\n", __func__, cmd);
+
+ GMN_DBG("%s: pgmn_dev 0x%x", __func__, (uint32_t)pgmn_dev);
+
+ GMN_DBG("%s: Calling __msm_gemini_ioctl\n", __func__);
+
+ rc = __msm_gemini_ioctl(pgmn_dev, cmd, (unsigned long)arg);
+ GMN_DBG("%s: X\n", __func__);
+ return rc;
+}
+
+void msm_gemini_subdev_release(struct v4l2_subdev *gemini_sd)
+{
+ int rc;
+ struct msm_gemini_device *pgmn_dev =
+ (struct msm_gemini_device *)gemini_sd->host_priv;
+ GMN_DBG("%s:pgmn_dev=0x%x", __func__, (uint32_t)pgmn_dev);
+ rc = __msm_gemini_release(pgmn_dev);
+ GMN_DBG("%s:rc=%d", __func__, rc);
+}
+
+static const struct v4l2_subdev_core_ops msm_gemini_subdev_core_ops = {
+ .ioctl = msm_gemini_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_gemini_subdev_ops = {
+ .core = &msm_gemini_subdev_core_ops,
+};
+
+static int msm_gemini_init(struct platform_device *pdev)
+{
+ int rc = -1;
+ struct device *dev;
+
+ GMN_DBG("%s:\n", __func__);
+ msm_gemini_device_p = __msm_gemini_init(pdev);
+ if (msm_gemini_device_p == NULL) {
+ GMN_PR_ERR("%s: initialization failed\n", __func__);
+ goto fail;
+ }
+
+ v4l2_subdev_init(&msm_gemini_device_p->subdev, &msm_gemini_subdev_ops);
+ v4l2_set_subdev_hostdata(&msm_gemini_device_p->subdev,
+ msm_gemini_device_p);
+ GMN_DBG("%s: msm_gemini_device_p 0x%x", __func__,
+ (uint32_t)msm_gemini_device_p);
+ GMN_DBG("%s:gemini: platform_set_drvdata\n", __func__);
+ platform_set_drvdata(pdev, &msm_gemini_device_p->subdev);
+
+ rc = alloc_chrdev_region(&msm_gemini_devno, 0, 1, MSM_GEMINI_NAME);
+ if (rc < 0) {
+ GMN_PR_ERR("%s: failed to allocate chrdev\n", __func__);
+ goto fail_1;
+ }
+
+ if (!msm_gemini_class) {
+ msm_gemini_class = class_create(THIS_MODULE, MSM_GEMINI_NAME);
+ if (IS_ERR(msm_gemini_class)) {
+ rc = PTR_ERR(msm_gemini_class);
+ GMN_PR_ERR("%s: create device class failed\n",
+ __func__);
+ goto fail_2;
+ }
+ }
+
+ dev = device_create(msm_gemini_class, NULL,
+ MKDEV(MAJOR(msm_gemini_devno), MINOR(msm_gemini_devno)), NULL,
+ "%s%d", MSM_GEMINI_NAME, 0);
+
+ if (IS_ERR(dev)) {
+ GMN_PR_ERR("%s: error creating device\n", __func__);
+ rc = -ENODEV;
+ goto fail_3;
+ }
+
+ cdev_init(&msm_gemini_device_p->cdev, &msm_gemini_fops);
+ msm_gemini_device_p->cdev.owner = THIS_MODULE;
+ msm_gemini_device_p->cdev.ops =
+ (const struct file_operations *) &msm_gemini_fops;
+ rc = cdev_add(&msm_gemini_device_p->cdev, msm_gemini_devno, 1);
+ if (rc < 0) {
+ GMN_PR_ERR("%s: error adding cdev\n", __func__);
+ rc = -ENODEV;
+ goto fail_4;
+ }
+
+ GMN_DBG("%s %s: success\n", __func__, MSM_GEMINI_NAME);
+
+ return rc;
+
+fail_4:
+ device_destroy(msm_gemini_class, msm_gemini_devno);
+
+fail_3:
+ class_destroy(msm_gemini_class);
+
+fail_2:
+ unregister_chrdev_region(msm_gemini_devno, 1);
+
+fail_1:
+ __msm_gemini_exit(msm_gemini_device_p);
+
+fail:
+ return rc;
+}
+
+static void msm_gemini_exit(void)
+{
+ cdev_del(&msm_gemini_device_p->cdev);
+ device_destroy(msm_gemini_class, msm_gemini_devno);
+ class_destroy(msm_gemini_class);
+ unregister_chrdev_region(msm_gemini_devno, 1);
+
+ __msm_gemini_exit(msm_gemini_device_p);
+}
+
+static int __msm_gemini_probe(struct platform_device *pdev)
+{
+ return msm_gemini_init(pdev);
+}
+
+static int __msm_gemini_remove(struct platform_device *pdev)
+{
+ msm_gemini_exit();
+ return 0;
+}
+
+static struct platform_driver msm_gemini_driver = {
+ .probe = __msm_gemini_probe,
+ .remove = __msm_gemini_remove,
+ .driver = {
+ .name = MSM_GEMINI_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_gemini_driver_init(void)
+{
+ int rc;
+ rc = platform_driver_register(&msm_gemini_driver);
+ return rc;
+}
+
+static void __exit msm_gemini_driver_exit(void)
+{
+ platform_driver_unregister(&msm_gemini_driver);
+}
+
+MODULE_DESCRIPTION("MSM Gemini JPEG driver");
+MODULE_VERSION("msm gemini 0.1");
+
+module_init(msm_gemini_driver_init);
+module_exit(msm_gemini_driver_exit);
+
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw.c b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw.c
new file mode 100644
index 0000000..96470fd
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw.c
@@ -0,0 +1,520 @@
+/* Copyright (c) 2010,2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include "msm_gemini_hw.h"
+#include "msm_gemini_common.h"
+
+
+static void *gemini_region_base;
+static uint32_t gemini_region_size;
+
+int msm_gemini_hw_pingpong_update(struct msm_gemini_hw_pingpong *pingpong_hw,
+ struct msm_gemini_hw_buf *buf)
+{
+ int buf_free_index = -1;
+
+ if (!pingpong_hw->buf_status[0])
+ buf_free_index = 0;
+ else if (!pingpong_hw->buf_status[1])
+ buf_free_index = 1;
+ else {
+ GMN_PR_ERR("%s:%d: pingpong buffer busy\n", __func__, __LINE__);
+ return -EBUSY;
+ }
+
+ pingpong_hw->buf[buf_free_index] = *buf;
+ pingpong_hw->buf_status[buf_free_index] = 1;
+
+ if (pingpong_hw->is_fe)
+ msm_gemini_hw_fe_buffer_update(
+ &pingpong_hw->buf[buf_free_index], buf_free_index);
+ else
+ msm_gemini_hw_we_buffer_update(
+ &pingpong_hw->buf[buf_free_index], buf_free_index);
+ return 0;
+}
+
+void *msm_gemini_hw_pingpong_irq(struct msm_gemini_hw_pingpong *pingpong_hw)
+{
+ struct msm_gemini_hw_buf *buf_p = NULL;
+
+ if (pingpong_hw->buf_status[pingpong_hw->buf_active_index]) {
+ buf_p = &pingpong_hw->buf[pingpong_hw->buf_active_index];
+ pingpong_hw->buf_status[pingpong_hw->buf_active_index] = 0;
+ }
+
+ pingpong_hw->buf_active_index = !pingpong_hw->buf_active_index;
+
+ return (void *) buf_p;
+}
+
+void *msm_gemini_hw_pingpong_active_buffer(
+ struct msm_gemini_hw_pingpong *pingpong_hw)
+{
+ struct msm_gemini_hw_buf *buf_p = NULL;
+
+ if (pingpong_hw->buf_status[pingpong_hw->buf_active_index])
+ buf_p = &pingpong_hw->buf[pingpong_hw->buf_active_index];
+
+ return (void *) buf_p;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_irq_get_status[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_READ, 1, HWIO_JPEG_IRQ_STATUS_ADDR,
+ HWIO_JPEG_IRQ_STATUS_RMSK, {0} },
+};
+
+int msm_gemini_hw_irq_get_status(void)
+{
+ uint32_t n_irq_status = 0;
+ n_irq_status = msm_gemini_hw_read(&hw_cmd_irq_get_status[0]);
+ return n_irq_status;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_encode_output_size[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_READ, 1,
+ HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_ADDR,
+ HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_RMSK, {0} },
+};
+
+long msm_gemini_hw_encode_output_size(void)
+{
+ long encode_output_size;
+
+ encode_output_size = msm_gemini_hw_read(&hw_cmd_encode_output_size[0]);
+
+ return encode_output_size;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_irq_clear[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_CLEAR_ADDR,
+ HWIO_JPEG_IRQ_CLEAR_RMSK, {JPEG_IRQ_CLEAR_ALL} },
+};
+
+void msm_gemini_hw_irq_clear(uint32_t mask, uint32_t data)
+{
+ GMN_DBG("%s:%d] mask %0x data %0x", __func__, __LINE__, mask, data);
+ hw_cmd_irq_clear[0].mask = mask;
+ hw_cmd_irq_clear[0].data = data;
+ msm_gemini_hw_write(&hw_cmd_irq_clear[0]);
+}
+
+struct msm_gemini_hw_cmd hw_cmd_fe_ping_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_BUFFER_CFG_ADDR,
+ HWIO_JPEG_FE_BUFFER_CFG_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_Y_PING_ADDR_ADDR,
+ HWIO_JPEG_FE_Y_PING_ADDR_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CBCR_PING_ADDR_ADDR,
+ HWIO_JPEG_FE_CBCR_PING_ADDR_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CMD_ADDR,
+ HWIO_JPEG_FE_CMD_RMSK, {JPEG_FE_CMD_BUFFERRELOAD} },
+};
+
+struct msm_gemini_hw_cmd hw_cmd_fe_pong_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_BUFFER_CFG_ADDR,
+ HWIO_JPEG_FE_BUFFER_CFG_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_Y_PONG_ADDR_ADDR,
+ HWIO_JPEG_FE_Y_PONG_ADDR_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CBCR_PONG_ADDR_ADDR,
+ HWIO_JPEG_FE_CBCR_PONG_ADDR_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CMD_ADDR,
+ HWIO_JPEG_FE_CMD_RMSK, {JPEG_FE_CMD_BUFFERRELOAD} },
+};
+
+void msm_gemini_hw_fe_buffer_update(struct msm_gemini_hw_buf *p_input,
+ uint8_t pingpong_index)
+{
+ uint32_t n_reg_val = 0;
+
+ struct msm_gemini_hw_cmd *hw_cmd_p;
+
+ if (pingpong_index == 0) {
+ hw_cmd_p = &hw_cmd_fe_ping_update[0];
+ n_reg_val = ((((p_input->num_of_mcu_rows - 1) <<
+ HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_SHFT) &
+ HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_BMSK) |
+ (((p_input->num_of_mcu_rows - 1) <<
+ HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_SHFT) &
+ HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_BMSK));
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = ((p_input->y_buffer_addr <<
+ HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_SHFT) &
+ HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = ((p_input->cbcr_buffer_addr<<
+ HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_SHFT) &
+ HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ msm_gemini_hw_write(hw_cmd_p);
+ } else if (pingpong_index == 1) {
+ hw_cmd_p = &hw_cmd_fe_pong_update[0];
+ n_reg_val = ((((p_input->num_of_mcu_rows - 1) <<
+ HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_SHFT) &
+ HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_BMSK) |
+ (((p_input->num_of_mcu_rows - 1) <<
+ HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_SHFT) &
+ HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_BMSK));
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = ((p_input->y_buffer_addr <<
+ HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_SHFT) &
+ HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = ((p_input->cbcr_buffer_addr<<
+ HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_SHFT) &
+ HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ msm_gemini_hw_write(hw_cmd_p);
+ } else {
+ /* shall not get to here */
+ }
+
+ return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_fe_start[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CMD_ADDR,
+ HWIO_JPEG_FE_CMD_RMSK, {JPEG_OFFLINE_CMD_START} },
+};
+
+void msm_gemini_hw_fe_start(void)
+{
+ msm_gemini_hw_write(&hw_cmd_fe_start[0]);
+
+ return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_we_buffer_cfg[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_THRESHOLD_ADDR,
+ HWIO_JPEG_WE_Y_THRESHOLD_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_UB_CFG_ADDR,
+ HWIO_JPEG_WE_Y_UB_CFG_RMSK, {JPEG_WE_YUB_ENCODE} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_CBCR_THRESHOLD_ADDR,
+ HWIO_JPEG_WE_CBCR_THRESHOLD_RMSK, {0} },
+};
+
+/*
+ * first dimension is WE_ASSERT_STALL_TH and WE_DEASSERT_STALL_TH
+ * second dimension is for offline and real-time settings
+ */
+static const uint32_t GEMINI_WE_Y_THRESHOLD[2][2] = {
+ { 0x00000190, 0x000001ff },
+ { 0x0000016a, 0x000001ff }
+};
+
+/*
+ * first dimension is WE_ASSERT_STALL_TH and WE_DEASSERT_STALL_TH
+ * second dimension is for offline and real-time settings
+ */
+static const uint32_t GEMINI_WE_CBCR_THRESHOLD[2][2] = {
+ { 0x00000190, 0x000001ff },
+ { 0x0000016a, 0x000001ff }
+};
+
+void msm_gemini_hw_we_buffer_cfg(uint8_t is_realtime)
+{
+ uint32_t n_reg_val = 0;
+
+ struct msm_gemini_hw_cmd *hw_cmd_p = &hw_cmd_we_buffer_cfg[0];
+
+ n_reg_val = (((GEMINI_WE_Y_THRESHOLD[1][is_realtime] <<
+ HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT) &
+ HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK) |
+ ((GEMINI_WE_Y_THRESHOLD[0][is_realtime] <<
+ HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_SHFT) &
+ HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_BMSK));
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ /* @todo maybe not for realtime? */
+ n_reg_val = (((GEMINI_WE_CBCR_THRESHOLD[1][is_realtime] <<
+ HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT) &
+ HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK) |
+ ((GEMINI_WE_CBCR_THRESHOLD[0][is_realtime] <<
+ HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_SHFT) &
+ HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_BMSK));
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p);
+
+ return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_we_ping_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PING_BUFFER_CFG_ADDR,
+ HWIO_JPEG_WE_Y_PING_BUFFER_CFG_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PING_ADDR_ADDR,
+ HWIO_JPEG_WE_Y_PING_ADDR_RMSK, {0} },
+};
+
+struct msm_gemini_hw_cmd hw_cmd_we_pong_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_ADDR,
+ HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PONG_ADDR_ADDR,
+ HWIO_JPEG_WE_Y_PONG_ADDR_RMSK, {0} },
+};
+
+void msm_gemini_hw_we_buffer_update(struct msm_gemini_hw_buf *p_input,
+ uint8_t pingpong_index)
+{
+ uint32_t n_reg_val = 0;
+
+ struct msm_gemini_hw_cmd *hw_cmd_p;
+
+ GMN_DBG("%s:%d] pingpong index %d", __func__, __LINE__,
+ pingpong_index);
+ if (pingpong_index == 0) {
+ hw_cmd_p = &hw_cmd_we_ping_update[0];
+
+ n_reg_val = ((p_input->y_len <<
+ HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT) &
+ HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = p_input->y_buffer_addr;
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+ } else if (pingpong_index == 1) {
+ hw_cmd_p = &hw_cmd_we_pong_update[0];
+
+ n_reg_val = ((p_input->y_len <<
+ HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT) &
+ HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = p_input->y_buffer_addr;
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+ } else {
+ /* shall not get to here */
+ }
+
+ return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_reset[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_MASK_ADDR,
+ HWIO_JPEG_IRQ_MASK_RMSK, {JPEG_IRQ_DISABLE_ALL} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_CLEAR_ADDR,
+ HWIO_JPEG_IRQ_MASK_RMSK, {JPEG_IRQ_CLEAR_ALL} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_MASK_ADDR,
+ HWIO_JPEG_IRQ_MASK_RMSK, {JPEG_IRQ_ALLSOURCES_ENABLE} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_RESET_CMD_ADDR,
+ HWIO_JPEG_RESET_CMD_RMSK, {JPEG_RESET_DEFAULT} },
+};
+
+void msm_gemini_hw_init(void *base, int size)
+{
+ gemini_region_base = base;
+ gemini_region_size = size;
+}
+
+void msm_gemini_hw_reset(void *base, int size)
+{
+ struct msm_gemini_hw_cmd *hw_cmd_p;
+
+ hw_cmd_p = &hw_cmd_reset[0];
+
+ msm_gemini_hw_write(hw_cmd_p++);
+ msm_gemini_hw_write(hw_cmd_p++);
+ msm_gemini_hw_write(hw_cmd_p++);
+ msm_gemini_hw_write(hw_cmd_p);
+}
+
+uint32_t msm_gemini_hw_read(struct msm_gemini_hw_cmd *hw_cmd_p)
+{
+ uint32_t *paddr;
+ uint32_t data;
+
+ paddr = gemini_region_base + hw_cmd_p->offset;
+
+ data = readl_relaxed(paddr);
+ data &= hw_cmd_p->mask;
+
+ GMN_DBG("%s:%d] type-%d n-%d offset-0x%4x mask-0x%8x data-0x%8x\n",
+ __func__, __LINE__, hw_cmd_p->type, hw_cmd_p->n,
+ hw_cmd_p->offset, hw_cmd_p->mask, data);
+ return data;
+}
+
+void msm_gemini_hw_write(struct msm_gemini_hw_cmd *hw_cmd_p)
+{
+ uint32_t *paddr;
+ uint32_t old_data, new_data;
+
+ /* type, repeat n times, offset, mask, data or pdata */
+ GMN_DBG("%s:%d] type-%d n-%d offset-0x%4x mask-0x%8x data-0x%8x\n",
+ __func__, __LINE__, hw_cmd_p->type, hw_cmd_p->n,
+ hw_cmd_p->offset, hw_cmd_p->mask, hw_cmd_p->data);
+
+ paddr = gemini_region_base + hw_cmd_p->offset;
+
+ if (hw_cmd_p->mask == 0xffffffff) {
+ old_data = 0;
+ } else {
+ old_data = readl_relaxed(paddr);
+ old_data &= ~hw_cmd_p->mask;
+ }
+
+ new_data = hw_cmd_p->data & hw_cmd_p->mask;
+ new_data |= old_data;
+ writel_relaxed(new_data, paddr);
+}
+
+int msm_gemini_hw_wait(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us)
+{
+ int tm = hw_cmd_p->n;
+ uint32_t data;
+ uint32_t wait_data = hw_cmd_p->data & hw_cmd_p->mask;
+
+ data = msm_gemini_hw_read(hw_cmd_p);
+ if (data != wait_data) {
+ while (tm) {
+ udelay(m_us);
+ data = msm_gemini_hw_read(hw_cmd_p);
+ if (data == wait_data)
+ break;
+ tm--;
+ }
+ }
+ hw_cmd_p->data = data;
+ return tm;
+}
+
+void msm_gemini_hw_delay(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us)
+{
+ int tm = hw_cmd_p->n;
+ while (tm) {
+ udelay(m_us);
+ tm--;
+ }
+}
+
+int msm_gemini_hw_exec_cmds(struct msm_gemini_hw_cmd *hw_cmd_p, int m_cmds)
+{
+ int is_copy_to_user = -1;
+ uint32_t data;
+
+ while (m_cmds--) {
+ if (hw_cmd_p->offset > gemini_region_size) {
+ GMN_PR_ERR("%s:%d] %d exceed hw region %d\n", __func__,
+ __LINE__, hw_cmd_p->offset, gemini_region_size);
+ return -EFAULT;
+ }
+
+ switch (hw_cmd_p->type) {
+ case MSM_GEMINI_HW_CMD_TYPE_READ:
+ hw_cmd_p->data = msm_gemini_hw_read(hw_cmd_p);
+ is_copy_to_user = 1;
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_WRITE:
+ msm_gemini_hw_write(hw_cmd_p);
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_WRITE_OR:
+ data = msm_gemini_hw_read(hw_cmd_p);
+ hw_cmd_p->data = (hw_cmd_p->data & hw_cmd_p->mask) |
+ data;
+ msm_gemini_hw_write(hw_cmd_p);
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_UWAIT:
+ msm_gemini_hw_wait(hw_cmd_p, 1);
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_MWAIT:
+ msm_gemini_hw_wait(hw_cmd_p, 1000);
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_UDELAY:
+ /* Userspace driver provided delay duration */
+ msm_gemini_hw_delay(hw_cmd_p, 1);
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_MDELAY:
+ /* Userspace driver provided delay duration */
+ msm_gemini_hw_delay(hw_cmd_p, 1000);
+ break;
+
+ default:
+ GMN_PR_ERR("wrong hw command type\n");
+ break;
+ }
+
+ hw_cmd_p++;
+ }
+ return is_copy_to_user;
+}
+
+#ifdef MSM_GMN_DBG_DUMP
+void msm_gemini_io_dump(int size)
+{
+ char line_str[128], *p_str;
+ void __iomem *addr = gemini_region_base;
+ int i;
+ u32 *p = (u32 *) addr;
+ u32 data;
+ pr_info("%s: %p %d reg_size %d\n", __func__, addr, size,
+ gemini_region_size);
+ line_str[0] = '\0';
+ p_str = line_str;
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+ snprintf(p_str, 12, "%08x: ", (u32) p);
+ p_str += 10;
+ }
+ data = readl_relaxed(p++);
+ snprintf(p_str, 12, "%08x ", data);
+ p_str += 9;
+ if ((i + 1) % 4 == 0) {
+ pr_info("%s\n", line_str);
+ line_str[0] = '\0';
+ p_str = line_str;
+ }
+ }
+ if (line_str[0] != '\0')
+ pr_info("%s\n", line_str);
+}
+#else
+void msm_gemini_io_dump(int size)
+{
+
+}
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw.h b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw.h
new file mode 100644
index 0000000..84eed72
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw.h
@@ -0,0 +1,104 @@
+/* Copyright (c) 2010-2013, 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.
+ */
+
+#ifndef MSM_GEMINI_HW_H
+#define MSM_GEMINI_HW_H
+
+#include <linux/msm_ion.h>
+#include <media/msm_gemini.h>
+#include <mach/iommu_domains.h>
+#include "msm_gemini_hw_reg.h"
+
+struct msm_gemini_hw_buf {
+ struct msm_gemini_buf vbuf;
+ struct file *file;
+ uint32_t framedone_len;
+ uint32_t y_buffer_addr;
+ uint32_t y_len;
+ uint32_t cbcr_buffer_addr;
+ uint32_t cbcr_len;
+ uint32_t num_of_mcu_rows;
+ struct ion_handle *handle;
+};
+
+struct msm_gemini_hw_pingpong {
+ uint8_t is_fe; /* 1: fe; 0: we */
+ struct msm_gemini_hw_buf buf[2];
+ int buf_status[2];
+ int buf_active_index;
+};
+
+int msm_gemini_hw_pingpong_update(struct msm_gemini_hw_pingpong *pingpong_hw,
+ struct msm_gemini_hw_buf *buf);
+void *msm_gemini_hw_pingpong_irq(struct msm_gemini_hw_pingpong *pingpong_hw);
+void *msm_gemini_hw_pingpong_active_buffer(struct msm_gemini_hw_pingpong
+ *pingpong_hw);
+
+void msm_gemini_hw_irq_clear(uint32_t, uint32_t);
+int msm_gemini_hw_irq_get_status(void);
+long msm_gemini_hw_encode_output_size(void);
+#define MSM_GEMINI_HW_MASK_COMP_FRAMEDONE \
+ MSM_GEMINI_HW_IRQ_STATUS_FRAMEDONE_MASK
+#define MSM_GEMINI_HW_MASK_COMP_FE \
+ MSM_GEMINI_HW_IRQ_STATUS_FE_RD_DONE_MASK
+#define MSM_GEMINI_HW_MASK_COMP_WE \
+ (MSM_GEMINI_HW_IRQ_STATUS_WE_Y_PINGPONG_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_PINGPONG_MASK)
+#define MSM_GEMINI_HW_MASK_COMP_RESET_ACK \
+ MSM_GEMINI_HW_IRQ_STATUS_RESET_ACK_MASK
+#define MSM_GEMINI_HW_MASK_COMP_ERR \
+ (MSM_GEMINI_HW_IRQ_STATUS_FE_RTOVF_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_FE_VFE_OVERFLOW_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_WE_Y_BUFFER_OVERFLOW_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_BUFFER_OVERFLOW_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_WE_CH0_DATAFIFO_OVERFLOW_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_WE_CH1_DATAFIFO_OVERFLOW_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_BUS_ERROR_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_VIOLATION_MASK)
+
+#define msm_gemini_hw_irq_is_frame_done(gemini_irq_status) \
+ (gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_FRAMEDONE)
+#define msm_gemini_hw_irq_is_fe_pingpong(gemini_irq_status) \
+ (gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_FE)
+#define msm_gemini_hw_irq_is_we_pingpong(gemini_irq_status) \
+ (gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_WE)
+#define msm_gemini_hw_irq_is_reset_ack(gemini_irq_status) \
+ (gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_RESET_ACK)
+#define msm_gemini_hw_irq_is_err(gemini_irq_status) \
+ (gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_ERR)
+
+void msm_gemini_hw_fe_buffer_update(struct msm_gemini_hw_buf *p_input,
+ uint8_t pingpong_index);
+void msm_gemini_hw_we_buffer_update(struct msm_gemini_hw_buf *p_input,
+ uint8_t pingpong_index);
+
+void msm_gemini_hw_we_buffer_cfg(uint8_t is_realtime);
+
+void msm_gemini_hw_fe_start(void);
+void msm_gemini_hw_clk_cfg(void);
+
+void msm_gemini_hw_reset(void *base, int size);
+void msm_gemini_hw_irq_cfg(void);
+void msm_gemini_hw_init(void *base, int size);
+
+uint32_t msm_gemini_hw_read(struct msm_gemini_hw_cmd *hw_cmd_p);
+void msm_gemini_hw_write(struct msm_gemini_hw_cmd *hw_cmd_p);
+int msm_gemini_hw_wait(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us);
+void msm_gemini_hw_delay(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us);
+int msm_gemini_hw_exec_cmds(struct msm_gemini_hw_cmd *hw_cmd_p, int m_cmds);
+void msm_gemini_io_dump(int size);
+
+#define MSM_GEMINI_PIPELINE_CLK_128MHZ 128 /* 8MP 128MHz */
+#define MSM_GEMINI_PIPELINE_CLK_140MHZ 140 /* 9MP 140MHz */
+#define MSM_GEMINI_PIPELINE_CLK_200MHZ 153 /* 12MP 153MHz */
+
+#endif /* MSM_GEMINI_HW_H */
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw_reg.h b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw_reg.h
new file mode 100644
index 0000000..4f05650
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw_reg.h
@@ -0,0 +1,176 @@
+/* Copyright (c) 2010,2013, 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.
+ */
+
+#ifndef MSM_GEMINI_HW_REG_H
+#define MSM_GEMINI_HW_REG_H
+
+#define GEMINI_REG_BASE 0
+
+#define MSM_GEMINI_HW_IRQ_MASK_ADDR 0x00000014
+#define MSM_GEMINI_HW_IRQ_MASK_RMSK 0xffffffff
+#define MSM_GEMINI_HW_IRQ_MASK_SHFT 0
+#define MSM_GEMINI_HW_IRQ_DISABLE 0
+#define MSM_GEMINI_HW_IRQ_ENABLE 0xffffffff
+
+#define MSM_GEMINI_HW_IRQ_CLEAR_ADDR 0x00000018
+#define MSM_GEMINI_HW_IRQ_CLEAR_RMSK 0xffffffff
+#define MSM_GEMINI_HW_IRQ_CLEAR_SHFT 0
+#define MSM_GEMINI_HW_IRQ_CLEAR 0xffffffff
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FRAMEDONE_MASK 0x00000001
+#define MSM_GEMINI_HW_IRQ_STATUS_FRAMEDONE_SHIFT 0x00000000
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RD_DONE_MASK 0x00000002
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RD_DONE_SHIFT 0x00000001
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RTOVF_MASK 0x00000004
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RTOVF_SHIFT 0x00000002
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_VFE_OVERFLOW_MASK 0x00000008
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_VFE_OVERFLOW_SHIFT 0x00000003
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_PINGPONG_MASK 0x00000010
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_PINGPONG_SHIFT 0x00000004
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_PINGPONG_MASK 0x00000020
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_PINGPONG_SHIFT 0x00000005
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_BUFFER_OVERFLOW_MASK 0x00000040
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_BUFFER_OVERFLOW_SHIFT 0x00000006
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_BUFFER_OVERFLOW_MASK 0x00000080
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_BUFFER_OVERFLOW_SHIFT 0x00000007
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH0_DATAFIFO_OVERFLOW_MASK 0x00000100
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH0_DATAFIFO_OVERFLOW_SHIFT 0x00000008
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH1_DATAFIFO_OVERFLOW_MASK 0x00000200
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH1_DATAFIFO_OVERFLOW_SHIFT 0x00000009
+
+#define MSM_GEMINI_HW_IRQ_STATUS_RESET_ACK_MASK 0x00000400
+#define MSM_GEMINI_HW_IRQ_STATUS_RESET_ACK_SHIFT 0x0000000a
+
+#define MSM_GEMINI_HW_IRQ_STATUS_BUS_ERROR_MASK 0x00000800
+#define MSM_GEMINI_HW_IRQ_STATUS_BUS_ERROR_SHIFT 0x0000000b
+
+#define MSM_GEMINI_HW_IRQ_STATUS_VIOLATION_MASK 0x00001000
+#define MSM_GEMINI_HW_IRQ_STATUS_VIOLATION_SHIFT 0x0000000c
+
+#define JPEG_BUS_CMD_HALT_REQ 0x00000001
+
+#define JPEG_REALTIME_CMD_STOP_FB 0x00000000
+#define JPEG_REALTIME_CMD_STOP_IM 0x00000003
+#define JPEG_REALTIME_CMD_START 0x00000001
+
+#define JPEG_OFFLINE_CMD_START 0x00000003
+
+#define JPEG_DMI_CFG_DISABLE 0x00000000
+#define JPEG_DMI_ADDR_START 0x00000000
+
+#define JPEG_FE_CMD_BUFFERRELOAD 0x00000001
+
+#define JPEG_WE_YUB_ENCODE 0x01ff0000
+
+#define JPEG_RESET_DEFAULT 0x0004ffff /* cfff? */
+
+#define JPEG_IRQ_DISABLE_ALL 0x00000000
+#define JPEG_IRQ_CLEAR_ALL 0xffffffff
+#define JPEG_IRQ_ALLSOURCES_ENABLE 0xffffffff
+
+#define HWIO_JPEG_FE_BUFFER_CFG_ADDR (GEMINI_REG_BASE + 0x00000080)
+#define HWIO_JPEG_FE_BUFFER_CFG_RMSK 0x1fff1fff
+
+#define HWIO_JPEG_FE_Y_PING_ADDR_ADDR (GEMINI_REG_BASE + 0x00000084)
+#define HWIO_JPEG_FE_Y_PING_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_Y_PONG_ADDR_ADDR (GEMINI_REG_BASE + 0x00000088)
+#define HWIO_JPEG_FE_Y_PONG_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_ADDR (GEMINI_REG_BASE + 0x0000008c)
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_ADDR (GEMINI_REG_BASE + 0x00000090)
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_CMD_ADDR (GEMINI_REG_BASE + 0x00000094)
+#define HWIO_JPEG_FE_CMD_RMSK 0x3
+
+#define HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_BMSK 0x1fff0000
+#define HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_SHFT 0x10
+#define HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_BMSK 0x1fff
+#define HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_SHFT 0
+
+#define HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_WE_Y_THRESHOLD_ADDR (GEMINI_REG_BASE + 0x000000c0)
+#define HWIO_JPEG_WE_Y_THRESHOLD_RMSK 0x1ff01ff
+
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_ADDR (GEMINI_REG_BASE + 0x000000c4)
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_RMSK 0x1ff01ff
+
+#define HWIO_JPEG_WE_Y_UB_CFG_ADDR (GEMINI_REG_BASE + 0x000000e8)
+#define HWIO_JPEG_WE_Y_UB_CFG_RMSK 0x1ff01ff
+
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK 0x1ff0000
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT 0x10
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_BMSK 0x1ff
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_SHFT 0
+
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK 0x1ff0000
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT 0x10
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_BMSK 0x1ff
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_SHFT 0
+
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_ADDR (GEMINI_REG_BASE + 0x000000c8)
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_RMSK 0x7fffff
+
+#define HWIO_JPEG_WE_Y_PING_ADDR_ADDR (GEMINI_REG_BASE + 0x000000d8)
+#define HWIO_JPEG_WE_Y_PING_ADDR_RMSK 0xfffffff8
+
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_ADDR (GEMINI_REG_BASE + 0x000000cc)
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_RMSK 0x7fffff
+
+#define HWIO_JPEG_WE_Y_PONG_ADDR_ADDR (GEMINI_REG_BASE + 0x000000dc)
+#define HWIO_JPEG_WE_Y_PONG_ADDR_RMSK 0xfffffff8
+
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK 0x7fffff
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT 0
+
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK 0x7fffff
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT 0
+
+#define HWIO_JPEG_IRQ_MASK_ADDR (GEMINI_REG_BASE + 0x00000014)
+#define HWIO_JPEG_IRQ_MASK_RMSK 0xffffffff
+
+#define HWIO_JPEG_IRQ_CLEAR_ADDR (GEMINI_REG_BASE + 0x00000018)
+#define HWIO_JPEG_IRQ_CLEAR_RMSK 0xffffffff
+
+#define HWIO_JPEG_RESET_CMD_ADDR (GEMINI_REG_BASE + 0x00000004)
+#define HWIO_JPEG_RESET_CMD_RMSK 0xe004ffff
+
+#define HWIO_JPEG_IRQ_STATUS_ADDR (GEMINI_REG_BASE + 0x0000001c)
+#define HWIO_JPEG_IRQ_STATUS_RMSK 0xffffffff
+
+#define HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_ADDR (GEMINI_REG_BASE + 0x00000034)
+#define HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_RMSK 0xffffffff
+
+#endif /* MSM_GEMINI_HW_REG_H */
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_platform.c b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_platform.c
new file mode 100644
index 0000000..f442068
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_platform.c
@@ -0,0 +1,283 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/pm_qos.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/android_pmem.h>
+#include <mach/clk.h>
+#include <mach/camera2.h>
+#include <mach/iommu_domains.h>
+#include "msm_gemini_platform.h"
+#include "msm_gemini_sync.h"
+#include "msm_gemini_common.h"
+#include "msm_gemini_hw.h"
+#include "msm_camera_io_util.h"
+
+/* AXI rate in KHz */
+#define MSM_SYSTEM_BUS_RATE 160000
+struct ion_client *gemini_client;
+
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+void msm_gemini_platform_p2v(struct file *file,
+ struct ion_handle **ionhandle)
+{
+ ion_unmap_iommu(gemini_client, *ionhandle, CAMERA_DOMAIN, GEN_POOL);
+ ion_free(gemini_client, *ionhandle);
+ *ionhandle = NULL;
+}
+#else
+void msm_gemini_platform_p2v(struct file *file,
+ struct ion_handle **ionhandle)
+{
+
+}
+#endif
+
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+uint32_t msm_gemini_platform_v2p(int fd, uint32_t len, struct file **file_p,
+ struct ion_handle **ionhandle)
+{
+ unsigned long paddr;
+ unsigned long size;
+ int rc;
+
+ *ionhandle = ion_import_dma_buf(gemini_client, fd);
+ if (IS_ERR_OR_NULL(*ionhandle))
+ return 0;
+
+ rc = ion_map_iommu(gemini_client, *ionhandle, CAMERA_DOMAIN, GEN_POOL,
+ SZ_4K, 0, &paddr, (unsigned long *)&size, 0, 0);
+ if (rc < 0) {
+ GMN_PR_ERR("%s: get_pmem_file fd %d error %d\n", __func__, fd,
+ rc);
+ goto error1;
+ }
+ /* validate user input */
+ if (len > size) {
+ GMN_PR_ERR("%s: invalid offset + len\n", __func__);
+ goto error1;
+ }
+
+ return paddr;
+error1:
+ ion_free(gemini_client, *ionhandle);
+
+ return 0;
+}
+#else
+uint32_t msm_gemini_platform_v2p(int fd, uint32_t len, struct file **file_p,
+ struct ion_handle **ionhandle)
+{
+ return 0;
+}
+#endif
+
+static struct msm_cam_clk_info gemini_8x_clk_info[] = {
+ {"core_clk", 228571000, 0},
+ {"iface_clk", -1, 0},
+};
+
+static struct msm_cam_clk_info gemini_7x_clk_info[] = {
+ {"core_clk", 153600000, 0},
+ {"iface_clk", -1, 0},
+};
+
+static struct msm_cam_clk_info gemini_imem_clk_info[] = {
+ {"mem_clk", -1, 0},
+};
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+static struct ion_client *msm_gemini_ion_client_create(unsigned int heap_mask,
+ const char *name)
+{
+ return msm_ion_client_create(heap_mask, name);
+}
+#else
+static struct ion_client *msm_gemini_ion_client_create(unsigned int heap_mask,
+ const char *name)
+{
+ return NULL;
+}
+#endif
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+void msm_gemini_ion_client_destroy(struct ion_client *client)
+{
+ ion_client_destroy(client);
+}
+#else
+void msm_gemini_ion_client_destroy(struct ion_client *client)
+{
+
+}
+#endif
+
+int msm_gemini_platform_init(struct platform_device *pdev,
+ struct resource **mem,
+ void **base,
+ int *irq,
+ irqreturn_t (*handler) (int, void *),
+ void *context)
+{
+ int rc = -1;
+ int gemini_irq;
+ struct resource *gemini_mem, *gemini_io, *gemini_irq_res;
+ void *gemini_base;
+ struct msm_gemini_device *pgmn_dev =
+ (struct msm_gemini_device *) context;
+
+ gemini_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!gemini_mem) {
+ GMN_PR_ERR("%s: no mem resource!\n", __func__);
+ return -ENODEV;
+ }
+
+ gemini_irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!gemini_irq_res) {
+ GMN_PR_ERR("no irq resource!\n");
+ return -ENODEV;
+ }
+ gemini_irq = gemini_irq_res->start;
+
+ gemini_io = request_mem_region(gemini_mem->start,
+ resource_size(gemini_mem), pdev->name);
+ if (!gemini_io) {
+ GMN_PR_ERR("%s: region already claimed\n", __func__);
+ return -EBUSY;
+ }
+
+ gemini_base = ioremap(gemini_mem->start, resource_size(gemini_mem));
+ if (!gemini_base) {
+ rc = -ENOMEM;
+ GMN_PR_ERR("%s: ioremap failed\n", __func__);
+ goto fail1;
+ }
+ pgmn_dev->hw_version = GEMINI_8X60;
+ rc = msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_8x_clk_info,
+ pgmn_dev->gemini_clk, ARRAY_SIZE(gemini_8x_clk_info), 1);
+ if (rc < 0) {
+ pgmn_dev->hw_version = GEMINI_7X;
+ rc = msm_cam_clk_enable(&pgmn_dev->pdev->dev,
+ gemini_7x_clk_info, pgmn_dev->gemini_clk,
+ ARRAY_SIZE(gemini_7x_clk_info), 1);
+ if (rc < 0) {
+ GMN_PR_ERR("%s: clk failed rc = %d\n", __func__, rc);
+ goto fail2;
+ }
+ } else {
+ rc = msm_cam_clk_enable(&pgmn_dev->pdev->dev,
+ gemini_imem_clk_info, &pgmn_dev->gemini_clk[2],
+ ARRAY_SIZE(gemini_imem_clk_info), 1);
+ if (!rc)
+ pgmn_dev->hw_version = GEMINI_8960;
+ }
+
+ if (pgmn_dev->hw_version != GEMINI_7X) {
+ if (pgmn_dev->gemini_fs == NULL) {
+ pgmn_dev->gemini_fs =
+ regulator_get(&pgmn_dev->pdev->dev, "vdd");
+ if (IS_ERR(pgmn_dev->gemini_fs)) {
+ GMN_PR_ERR("%s: regulator_get failed %ld\n",
+ __func__, PTR_ERR(pgmn_dev->gemini_fs));
+ pgmn_dev->gemini_fs = NULL;
+ goto gemini_fs_failed;
+ } else if (regulator_enable(pgmn_dev->gemini_fs)) {
+ GMN_PR_ERR("%s: regulator_enable failed\n",
+ __func__);
+ regulator_put(pgmn_dev->gemini_fs);
+ pgmn_dev->gemini_fs = NULL;
+ goto gemini_fs_failed;
+ }
+ }
+ }
+
+ msm_gemini_hw_init(gemini_base, resource_size(gemini_mem));
+ rc = request_irq(gemini_irq, handler, IRQF_TRIGGER_RISING, "gemini",
+ context);
+ if (rc) {
+ GMN_PR_ERR("%s: request_irq failed, %d\n", __func__,
+ gemini_irq);
+ goto fail3;
+ }
+
+ *mem = gemini_mem;
+ *base = gemini_base;
+ *irq = gemini_irq;
+
+ gemini_client = msm_gemini_ion_client_create(-1, "camera/gemini");
+
+ GMN_DBG("%s:%d] success\n", __func__, __LINE__);
+
+ return rc;
+
+fail3:
+ if (pgmn_dev->hw_version != GEMINI_7X) {
+ regulator_disable(pgmn_dev->gemini_fs);
+ regulator_put(pgmn_dev->gemini_fs);
+ pgmn_dev->gemini_fs = NULL;
+ }
+gemini_fs_failed:
+ if (pgmn_dev->hw_version == GEMINI_8960)
+ msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_imem_clk_info,
+ &pgmn_dev->gemini_clk[2], ARRAY_SIZE(gemini_imem_clk_info), 0);
+ if (pgmn_dev->hw_version != GEMINI_7X)
+ msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_8x_clk_info,
+ pgmn_dev->gemini_clk, ARRAY_SIZE(gemini_8x_clk_info), 0);
+ else
+ msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_7x_clk_info,
+ pgmn_dev->gemini_clk, ARRAY_SIZE(gemini_7x_clk_info), 0);
+fail2:
+ iounmap(gemini_base);
+fail1:
+ release_mem_region(gemini_mem->start, resource_size(gemini_mem));
+ GMN_DBG("%s:%d] fail\n", __func__, __LINE__);
+ return rc;
+}
+
+int msm_gemini_platform_release(struct resource *mem, void *base, int irq,
+ void *context)
+{
+ int result = 0;
+ struct msm_gemini_device *pgmn_dev =
+ (struct msm_gemini_device *) context;
+
+ free_irq(irq, context);
+
+ if (pgmn_dev->hw_version != GEMINI_7X) {
+ regulator_disable(pgmn_dev->gemini_fs);
+ regulator_put(pgmn_dev->gemini_fs);
+ pgmn_dev->gemini_fs = NULL;
+ }
+
+ if (pgmn_dev->hw_version == GEMINI_8960)
+ msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_imem_clk_info,
+ &pgmn_dev->gemini_clk[2], ARRAY_SIZE(gemini_imem_clk_info), 0);
+ if (pgmn_dev->hw_version != GEMINI_7X)
+ msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_8x_clk_info,
+ pgmn_dev->gemini_clk, ARRAY_SIZE(gemini_8x_clk_info), 0);
+ else
+ msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_7x_clk_info,
+ pgmn_dev->gemini_clk, ARRAY_SIZE(gemini_7x_clk_info), 0);
+
+ iounmap(base);
+ release_mem_region(mem->start, resource_size(mem));
+
+ msm_gemini_ion_client_destroy(gemini_client);
+
+ GMN_DBG("%s:%d] success\n", __func__, __LINE__);
+ return result;
+}
+
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_platform.h b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_platform.h
new file mode 100644
index 0000000..a071df9
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_platform.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2010-2013, 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.
+ */
+
+#ifndef MSM_GEMINI_PLATFORM_H
+#define MSM_GEMINI_PLATFORM_H
+
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+void msm_gemini_platform_p2v(struct file *file,
+ struct ion_handle **ionhandle);
+uint32_t msm_gemini_platform_v2p(int fd, uint32_t len, struct file **file,
+ struct ion_handle **ionhandle);
+
+int msm_gemini_platform_clk_enable(void);
+int msm_gemini_platform_clk_disable(void);
+
+int msm_gemini_platform_init(struct platform_device *pdev,
+ struct resource **mem,
+ void **base,
+ int *irq,
+ irqreturn_t (*handler) (int, void *),
+ void *context);
+int msm_gemini_platform_release(struct resource *mem, void *base, int irq,
+ void *context);
+
+#endif /* MSM_GEMINI_PLATFORM_H */
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_sync.c b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_sync.c
new file mode 100644
index 0000000..8f84a2c
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_sync.c
@@ -0,0 +1,1081 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <media/msm_gemini.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include "msm_gemini_sync.h"
+#include "msm_gemini_core.h"
+#include "msm_gemini_platform.h"
+#include "msm_gemini_common.h"
+
+static int release_buf;
+
+/* size is based on 4k page size */
+static const int g_max_out_size = 0x7ff000;
+
+/*************** queue helper ****************/
+static inline void msm_gemini_q_init(char const *name, struct msm_gemini_q *q_p)
+{
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, name);
+ q_p->name = name;
+ spin_lock_init(&q_p->lck);
+ INIT_LIST_HEAD(&q_p->q);
+ init_waitqueue_head(&q_p->wait);
+ q_p->unblck = 0;
+}
+
+static inline void *msm_gemini_q_out(struct msm_gemini_q *q_p)
+{
+ unsigned long flags;
+ struct msm_gemini_q_entry *q_entry_p = NULL;
+ void *data = NULL;
+
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ spin_lock_irqsave(&q_p->lck, flags);
+ if (!list_empty(&q_p->q)) {
+ q_entry_p = list_first_entry(&q_p->q, struct msm_gemini_q_entry,
+ list);
+ list_del_init(&q_entry_p->list);
+ }
+ spin_unlock_irqrestore(&q_p->lck, flags);
+
+ if (q_entry_p) {
+ data = q_entry_p->data;
+ kfree(q_entry_p);
+ } else {
+ GMN_DBG("%s:%d] %s no entry\n", __func__, __LINE__,
+ q_p->name);
+ }
+
+ return data;
+}
+
+static inline int msm_gemini_q_in(struct msm_gemini_q *q_p, void *data)
+{
+ unsigned long flags;
+
+ struct msm_gemini_q_entry *q_entry_p;
+
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+
+ q_entry_p = kmalloc(sizeof(struct msm_gemini_q_entry), GFP_ATOMIC);
+ if (!q_entry_p) {
+ GMN_PR_ERR("%s: no mem\n", __func__);
+ return -ENOMEM;
+ }
+ q_entry_p->data = data;
+
+ spin_lock_irqsave(&q_p->lck, flags);
+ list_add_tail(&q_entry_p->list, &q_p->q);
+ spin_unlock_irqrestore(&q_p->lck, flags);
+
+ return 0;
+}
+
+static inline int msm_gemini_q_in_buf(struct msm_gemini_q *q_p,
+ struct msm_gemini_core_buf *buf)
+{
+ struct msm_gemini_core_buf *buf_p;
+
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+ buf_p = kmalloc(sizeof(struct msm_gemini_core_buf), GFP_ATOMIC);
+ if (!buf_p) {
+ GMN_PR_ERR("%s: no mem\n", __func__);
+ return -ENOMEM;
+ }
+
+ memcpy(buf_p, buf, sizeof(struct msm_gemini_core_buf));
+
+ msm_gemini_q_in(q_p, buf_p);
+ return 0;
+}
+
+static inline int msm_gemini_q_wait(struct msm_gemini_q *q_p)
+{
+ int tm = MAX_SCHEDULE_TIMEOUT;
+ int rc;
+
+ GMN_DBG("%s:%d] %s wait\n", __func__, __LINE__, q_p->name);
+ rc = wait_event_interruptible_timeout(q_p->wait,
+ (!list_empty_careful(&q_p->q) || q_p->unblck),
+ msecs_to_jiffies(tm));
+ GMN_DBG("%s:%d] %s wait done\n", __func__, __LINE__, q_p->name);
+ if (list_empty_careful(&q_p->q)) {
+ if (rc == 0) {
+ rc = -ETIMEDOUT;
+ GMN_PR_ERR("%s:%d] %s timeout\n", __func__, __LINE__,
+ q_p->name);
+ } else if (q_p->unblck) {
+ GMN_DBG("%s:%d] %s unblock is true\n", __func__,
+ __LINE__, q_p->name);
+ q_p->unblck = 0;
+ rc = -ECANCELED;
+ } else if (rc < 0) {
+ GMN_PR_ERR("%s:%d] %s rc %d\n", __func__, __LINE__,
+ q_p->name, rc);
+ }
+ }
+ return rc;
+}
+
+static inline int msm_gemini_q_wakeup(struct msm_gemini_q *q_p)
+{
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ wake_up(&q_p->wait);
+ return 0;
+}
+
+static inline int msm_gemini_q_unblock(struct msm_gemini_q *q_p)
+{
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ q_p->unblck = 1;
+ wake_up(&q_p->wait);
+ return 0;
+}
+
+static inline void msm_gemini_outbuf_q_cleanup(struct msm_gemini_q *q_p)
+{
+ struct msm_gemini_core_buf *buf_p;
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ do {
+ buf_p = msm_gemini_q_out(q_p);
+ if (buf_p) {
+ msm_gemini_platform_p2v(buf_p->file,
+ &buf_p->handle);
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ kfree(buf_p);
+ }
+ } while (buf_p);
+ q_p->unblck = 0;
+}
+
+static inline void msm_gemini_q_cleanup(struct msm_gemini_q *q_p)
+{
+ void *data;
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ do {
+ data = msm_gemini_q_out(q_p);
+ if (data) {
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ kfree(data);
+ }
+ } while (data);
+ q_p->unblck = 0;
+}
+
+/*************** event queue ****************/
+
+int msm_gemini_framedone_irq(struct msm_gemini_device *pgmn_dev,
+ struct msm_gemini_core_buf *buf_in)
+{
+ int rc = 0;
+
+ GMN_DBG("%s:%d] buf_in %p", __func__, __LINE__, buf_in);
+
+ if (buf_in) {
+ buf_in->vbuf.framedone_len = buf_in->framedone_len;
+ buf_in->vbuf.type = MSM_GEMINI_EVT_FRAMEDONE;
+ GMN_DBG("%s:%d] 0x%08x %d framedone_len %d\n",
+ __func__, __LINE__,
+ (int) buf_in->y_buffer_addr, buf_in->y_len,
+ buf_in->vbuf.framedone_len);
+ rc = msm_gemini_q_in_buf(&pgmn_dev->evt_q, buf_in);
+ } else {
+ GMN_DBG("%s:%d] no output return buffer\n",
+ __func__, __LINE__);
+ rc = -1;
+ }
+
+ if (buf_in)
+ rc = msm_gemini_q_wakeup(&pgmn_dev->evt_q);
+
+ return rc;
+}
+
+int msm_gemini_evt_get(struct msm_gemini_device *pgmn_dev,
+ void __user *to)
+{
+ struct msm_gemini_core_buf *buf_p;
+ struct msm_gemini_ctrl_cmd ctrl_cmd;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+ msm_gemini_q_wait(&pgmn_dev->evt_q);
+ buf_p = msm_gemini_q_out(&pgmn_dev->evt_q);
+
+ if (!buf_p) {
+ GMN_DBG("%s:%d] no buffer\n", __func__, __LINE__);
+ return -EAGAIN;
+ }
+
+ memset(&ctrl_cmd, 0, sizeof(struct msm_gemini_ctrl_cmd));
+ ctrl_cmd.type = buf_p->vbuf.type;
+ kfree(buf_p);
+
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) ctrl_cmd.value, ctrl_cmd.len);
+
+ if (copy_to_user(to, &ctrl_cmd, sizeof(ctrl_cmd))) {
+ GMN_PR_ERR("%s:%d]\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int msm_gemini_evt_get_unblock(struct msm_gemini_device *pgmn_dev)
+{
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_gemini_q_unblock(&pgmn_dev->evt_q);
+ return 0;
+}
+
+void msm_gemini_reset_ack_irq(struct msm_gemini_device *pgmn_dev)
+{
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+}
+
+void msm_gemini_err_irq(struct msm_gemini_device *pgmn_dev,
+ int event)
+{
+ int rc = 0;
+ struct msm_gemini_core_buf buf;
+
+ GMN_DBG("%s:%d] error: %d\n", __func__, __LINE__, event);
+
+ buf.vbuf.type = MSM_GEMINI_EVT_ERR;
+ rc = msm_gemini_q_in_buf(&pgmn_dev->evt_q, &buf);
+ if (!rc)
+ rc = msm_gemini_q_wakeup(&pgmn_dev->evt_q);
+
+ if (!rc)
+ GMN_PR_ERR("%s:%d] err err\n", __func__, __LINE__);
+
+ return;
+}
+
+/*************** output queue ****************/
+
+int msm_gemini_get_out_buffer(struct msm_gemini_device *pgmn_dev,
+ struct msm_gemini_hw_buf *p_outbuf)
+{
+ int buf_size = 0;
+ int bytes_remaining = 0;
+ if (pgmn_dev->out_offset >= pgmn_dev->out_buf.y_len) {
+ GMN_PR_ERR("%s:%d] no more buffers", __func__, __LINE__);
+ return -EINVAL;
+ }
+ bytes_remaining = pgmn_dev->out_buf.y_len - pgmn_dev->out_offset;
+ buf_size = min(bytes_remaining, pgmn_dev->max_out_size);
+
+ pgmn_dev->out_frag_cnt++;
+ GMN_DBG("%s:%d] buf_size[%d] %d", __func__, __LINE__,
+ pgmn_dev->out_frag_cnt, buf_size);
+ p_outbuf->y_len = buf_size;
+ p_outbuf->y_buffer_addr = pgmn_dev->out_buf.y_buffer_addr +
+ pgmn_dev->out_offset;
+ pgmn_dev->out_offset += buf_size;
+ return 0;
+}
+
+int msm_gemini_outmode_single_we_pingpong_irq(
+ struct msm_gemini_device *pgmn_dev,
+ struct msm_gemini_core_buf *buf_in)
+{
+ int rc = 0;
+ struct msm_gemini_core_buf out_buf;
+ int frame_done = buf_in &&
+ buf_in->vbuf.type == MSM_GEMINI_EVT_FRAMEDONE;
+ GMN_DBG("%s:%d] framedone %d", __func__, __LINE__, frame_done);
+ if (!pgmn_dev->out_buf_set) {
+ GMN_PR_ERR("%s:%d] output buffer not set",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ if (frame_done) {
+ /* send the buffer back */
+ pgmn_dev->out_buf.vbuf.framedone_len = buf_in->framedone_len;
+ pgmn_dev->out_buf.vbuf.type = MSM_GEMINI_EVT_FRAMEDONE;
+ rc = msm_gemini_q_in_buf(&pgmn_dev->output_rtn_q,
+ &pgmn_dev->out_buf);
+ if (rc) {
+ GMN_PR_ERR("%s:%d] cannot queue the output buffer",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ rc = msm_gemini_q_wakeup(&pgmn_dev->output_rtn_q);
+ /*
+ * reset the output buffer since the ownership is
+ * transferred to the rtn queue
+ */
+ if (!rc)
+ pgmn_dev->out_buf_set = 0;
+ } else {
+ /* configure ping/pong */
+ rc = msm_gemini_get_out_buffer(pgmn_dev, &out_buf);
+ if (rc)
+ msm_gemini_core_we_buf_reset(&out_buf);
+ else
+ msm_gemini_core_we_buf_update(&out_buf);
+ }
+ return rc;
+}
+
+int msm_gemini_we_pingpong_irq(struct msm_gemini_device *pgmn_dev,
+ struct msm_gemini_core_buf *buf_in)
+{
+ int rc = 0;
+ struct msm_gemini_core_buf *buf_out;
+
+ GMN_DBG("%s:%d] Enter mode %d", __func__, __LINE__,
+ pgmn_dev->out_mode);
+
+ if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_SINGLE)
+ return msm_gemini_outmode_single_we_pingpong_irq(pgmn_dev,
+ buf_in);
+
+ if (buf_in) {
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_in->y_buffer_addr, buf_in->y_len);
+ rc = msm_gemini_q_in_buf(&pgmn_dev->output_rtn_q, buf_in);
+ } else {
+ GMN_DBG("%s:%d] no output return buffer\n", __func__,
+ __LINE__);
+ rc = -1;
+ return rc;
+ }
+
+ buf_out = msm_gemini_q_out(&pgmn_dev->output_buf_q);
+
+ if (buf_out) {
+ rc = msm_gemini_core_we_buf_update(buf_out);
+ kfree(buf_out);
+ } else {
+ msm_gemini_core_we_buf_reset(buf_in);
+ GMN_DBG("%s:%d] no output buffer\n", __func__, __LINE__);
+ rc = -2;
+ }
+
+ if (buf_in)
+ rc = msm_gemini_q_wakeup(&pgmn_dev->output_rtn_q);
+
+ return rc;
+}
+
+int msm_gemini_output_get(struct msm_gemini_device *pgmn_dev, void __user *to)
+{
+ struct msm_gemini_core_buf *buf_p;
+ struct msm_gemini_buf buf_cmd;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+ msm_gemini_q_wait(&pgmn_dev->output_rtn_q);
+ buf_p = msm_gemini_q_out(&pgmn_dev->output_rtn_q);
+
+ if (!buf_p) {
+ GMN_DBG("%s:%d] no output buffer return\n",
+ __func__, __LINE__);
+ return -EAGAIN;
+ }
+
+ buf_cmd = buf_p->vbuf;
+ msm_gemini_platform_p2v(buf_p->file, &buf_p->handle);
+ kfree(buf_p);
+
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_cmd.vaddr, buf_cmd.y_len);
+
+ if (copy_to_user(to, &buf_cmd, sizeof(buf_cmd))) {
+ GMN_PR_ERR("%s:%d]", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int msm_gemini_output_get_unblock(struct msm_gemini_device *pgmn_dev)
+{
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_gemini_q_unblock(&pgmn_dev->output_rtn_q);
+ return 0;
+}
+
+int msm_gemini_set_output_buf(struct msm_gemini_device *pgmn_dev,
+ void __user *arg)
+{
+ struct msm_gemini_buf buf_cmd;
+
+ if (pgmn_dev->out_buf_set) {
+ GMN_PR_ERR("%s:%d] outbuffer buffer already provided",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_gemini_buf))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ GMN_DBG("%s:%d] output addr 0x%08x len %d", __func__, __LINE__,
+ (int) buf_cmd.vaddr,
+ buf_cmd.y_len);
+
+ pgmn_dev->out_buf.y_buffer_addr = msm_gemini_platform_v2p(
+ buf_cmd.fd,
+ buf_cmd.y_len,
+ &pgmn_dev->out_buf.file,
+ &pgmn_dev->out_buf.handle);
+ if (!pgmn_dev->out_buf.y_buffer_addr) {
+ GMN_PR_ERR("%s:%d] cannot map the output address",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ pgmn_dev->out_buf.y_len = buf_cmd.y_len;
+ pgmn_dev->out_buf.vbuf = buf_cmd;
+ pgmn_dev->out_buf_set = 1;
+
+ return 0;
+}
+
+int msm_gemini_output_buf_enqueue(struct msm_gemini_device *pgmn_dev,
+ void __user *arg)
+{
+ struct msm_gemini_buf buf_cmd;
+ struct msm_gemini_core_buf *buf_p;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_gemini_buf))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ buf_p = kmalloc(sizeof(struct msm_gemini_core_buf), GFP_ATOMIC);
+ if (!buf_p) {
+ GMN_PR_ERR("%s:%d] no mem\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__, (int) buf_cmd.vaddr,
+ buf_cmd.y_len);
+
+ buf_p->y_buffer_addr = msm_gemini_platform_v2p(buf_cmd.fd,
+ buf_cmd.y_len, &buf_p->file, &buf_p->handle);
+ if (!buf_p->y_buffer_addr) {
+ GMN_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
+ kfree(buf_p);
+ return -ENOMEM;
+ }
+ buf_p->y_len = buf_cmd.y_len;
+ buf_p->vbuf = buf_cmd;
+
+ msm_gemini_q_in(&pgmn_dev->output_buf_q, buf_p);
+ return 0;
+}
+
+/*************** input queue ****************/
+
+int msm_gemini_fe_pingpong_irq(struct msm_gemini_device *pgmn_dev,
+ struct msm_gemini_core_buf *buf_in)
+{
+ struct msm_gemini_core_buf *buf_out;
+ int rc = 0;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ if (buf_in) {
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_in->y_buffer_addr, buf_in->y_len);
+ rc = msm_gemini_q_in_buf(&pgmn_dev->input_rtn_q, buf_in);
+ } else {
+ GMN_DBG("%s:%d] no input return buffer\n", __func__,
+ __LINE__);
+ rc = -1;
+ }
+
+ buf_out = msm_gemini_q_out(&pgmn_dev->input_buf_q);
+
+ if (buf_out) {
+ rc = msm_gemini_core_fe_buf_update(buf_out);
+ kfree(buf_out);
+ msm_gemini_core_fe_start();
+ } else {
+ GMN_DBG("%s:%d] no input buffer\n", __func__, __LINE__);
+ rc = -2;
+ }
+
+ if (buf_in)
+ rc = msm_gemini_q_wakeup(&pgmn_dev->input_rtn_q);
+
+ return rc;
+}
+
+int msm_gemini_input_get(struct msm_gemini_device *pgmn_dev, void __user *to)
+{
+ struct msm_gemini_core_buf *buf_p;
+ struct msm_gemini_buf buf_cmd;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_gemini_q_wait(&pgmn_dev->input_rtn_q);
+ buf_p = msm_gemini_q_out(&pgmn_dev->input_rtn_q);
+
+ if (!buf_p) {
+ GMN_DBG("%s:%d] no input buffer return\n",
+ __func__, __LINE__);
+ return -EAGAIN;
+ }
+
+ buf_cmd = buf_p->vbuf;
+ if (pgmn_dev->op_mode == MSM_GEMINI_MODE_OFFLINE_ENCODE ||
+ pgmn_dev->op_mode == MSM_GEMINI_MODE_OFFLINE_ROTATION) {
+ msm_gemini_platform_p2v(buf_p->file, &buf_p->handle);
+ }
+ kfree(buf_p);
+
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_cmd.vaddr, buf_cmd.y_len);
+
+ if (copy_to_user(to, &buf_cmd, sizeof(buf_cmd))) {
+ GMN_PR_ERR("%s:%d]\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int msm_gemini_input_get_unblock(struct msm_gemini_device *pgmn_dev)
+{
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_gemini_q_unblock(&pgmn_dev->input_rtn_q);
+ return 0;
+}
+
+int msm_gemini_input_buf_enqueue(struct msm_gemini_device *pgmn_dev,
+ void __user *arg)
+{
+ struct msm_gemini_core_buf *buf_p;
+ struct msm_gemini_buf buf_cmd;
+ int rc = 0;
+ struct msm_bus_scale_pdata *p_bus_scale_data = NULL;
+
+ if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_gemini_buf))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ buf_p = kmalloc(sizeof(struct msm_gemini_core_buf), GFP_ATOMIC);
+ if (!buf_p) {
+ GMN_PR_ERR("%s:%d] no mem\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_cmd.vaddr, buf_cmd.y_len);
+
+ if (pgmn_dev->op_mode == MSM_GEMINI_MODE_REALTIME_ENCODE) {
+ rc = msm_iommu_map_contig_buffer(
+ (unsigned long)buf_cmd.y_off, CAMERA_DOMAIN, GEN_POOL,
+ ((buf_cmd.y_len + buf_cmd.cbcr_len + 4095) & (~4095)),
+ SZ_4K, IOMMU_WRITE | IOMMU_READ,
+ (unsigned long *)&buf_p->y_buffer_addr);
+ if (rc < 0) {
+ GMN_PR_ERR("%s iommu mapping failed with error %d\n",
+ __func__, rc);
+ kfree(buf_p);
+ return rc;
+ }
+ } else {
+ buf_p->y_buffer_addr = msm_gemini_platform_v2p(buf_cmd.fd,
+ buf_cmd.y_len + buf_cmd.cbcr_len, &buf_p->file,
+ &buf_p->handle) + buf_cmd.offset + buf_cmd.y_off;
+ }
+ buf_p->y_len = buf_cmd.y_len;
+
+ buf_p->cbcr_buffer_addr = buf_p->y_buffer_addr + buf_cmd.y_len +
+ buf_cmd.cbcr_off;
+ buf_p->cbcr_len = buf_cmd.cbcr_len;
+ buf_p->num_of_mcu_rows = buf_cmd.num_of_mcu_rows;
+ GMN_DBG("%s: y_addr=%x,y_len=%x,cbcr_addr=%x,cbcr_len=%x\n", __func__,
+ buf_p->y_buffer_addr, buf_p->y_len, buf_p->cbcr_buffer_addr,
+ buf_p->cbcr_len);
+
+ if (!buf_p->y_buffer_addr || !buf_p->cbcr_buffer_addr) {
+ GMN_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
+ kfree(buf_p);
+ return -EINVAL;
+ }
+ buf_p->vbuf = buf_cmd;
+ buf_p->vbuf.type = MSM_GEMINI_EVT_RESET;
+
+ /* Set bus vectors */
+ p_bus_scale_data = (struct msm_bus_scale_pdata *)
+ pgmn_dev->pdev->dev.platform_data;
+ if (pgmn_dev->bus_perf_client &&
+ (MSM_GMN_OUTMODE_SINGLE == pgmn_dev->out_mode)) {
+ int rc;
+ struct msm_bus_paths *path = &(p_bus_scale_data->usecase[1]);
+ GMN_DBG("%s:%d] Update bus bandwidth", __func__, __LINE__);
+ if (pgmn_dev->op_mode & MSM_GEMINI_MODE_OFFLINE_ENCODE) {
+ path->vectors[0].ab = (buf_p->y_len + buf_p->cbcr_len) *
+ 15 * 2;
+ path->vectors[0].ib = path->vectors[0].ab;
+ path->vectors[1].ab = 0;
+ path->vectors[1].ib = 0;
+ }
+ rc = msm_bus_scale_client_update_request(
+ pgmn_dev->bus_perf_client, 1);
+ if (rc < 0) {
+ GMN_PR_ERR("%s:%d] update_request fails %d",
+ __func__, __LINE__, rc);
+ }
+ }
+
+ msm_gemini_q_in(&pgmn_dev->input_buf_q, buf_p);
+
+ return 0;
+}
+
+int msm_gemini_irq(int event, void *context, void *data)
+{
+ struct msm_gemini_device *pgmn_dev =
+ (struct msm_gemini_device *) context;
+
+ switch (event) {
+ case MSM_GEMINI_HW_MASK_COMP_FRAMEDONE:
+ msm_gemini_framedone_irq(pgmn_dev, data);
+ msm_gemini_we_pingpong_irq(pgmn_dev, data);
+ break;
+
+ case MSM_GEMINI_HW_MASK_COMP_FE:
+ msm_gemini_fe_pingpong_irq(pgmn_dev, data);
+ break;
+
+ case MSM_GEMINI_HW_MASK_COMP_WE:
+ msm_gemini_we_pingpong_irq(pgmn_dev, data);
+ break;
+
+ case MSM_GEMINI_HW_MASK_COMP_RESET_ACK:
+ msm_gemini_reset_ack_irq(pgmn_dev);
+ break;
+
+ case MSM_GEMINI_HW_MASK_COMP_ERR:
+ default:
+ msm_gemini_err_irq(pgmn_dev, event);
+ break;
+ }
+
+ return 0;
+}
+
+int __msm_gemini_open(struct msm_gemini_device *pgmn_dev)
+{
+ int rc;
+ struct msm_bus_scale_pdata *p_bus_scale_data =
+ (struct msm_bus_scale_pdata *)pgmn_dev->pdev->dev.
+ platform_data;
+
+ mutex_lock(&pgmn_dev->lock);
+ if (pgmn_dev->open_count) {
+ /* only open once */
+ GMN_PR_ERR("%s:%d] busy\n", __func__, __LINE__);
+ mutex_unlock(&pgmn_dev->lock);
+ return -EBUSY;
+ }
+ pgmn_dev->open_count++;
+ mutex_unlock(&pgmn_dev->lock);
+
+ msm_gemini_core_irq_install(msm_gemini_irq);
+
+
+ rc = msm_gemini_platform_init(pgmn_dev->pdev,
+ &pgmn_dev->mem, &pgmn_dev->base,
+ &pgmn_dev->irq, msm_gemini_core_irq, pgmn_dev);
+ if (rc) {
+ GMN_PR_ERR("%s:%d] platform_init fail %d\n", __func__,
+ __LINE__, rc);
+ return rc;
+ }
+
+ GMN_DBG("%s:%d] platform resources - mem %p, base %p, irq %d\n",
+ __func__, __LINE__,
+ pgmn_dev->mem, pgmn_dev->base, pgmn_dev->irq);
+
+ msm_gemini_q_cleanup(&pgmn_dev->evt_q);
+ msm_gemini_q_cleanup(&pgmn_dev->output_rtn_q);
+ msm_gemini_outbuf_q_cleanup(&pgmn_dev->output_buf_q);
+ msm_gemini_q_cleanup(&pgmn_dev->input_rtn_q);
+ msm_gemini_q_cleanup(&pgmn_dev->input_buf_q);
+ msm_gemini_core_init();
+ pgmn_dev->out_mode = MSM_GMN_OUTMODE_FRAGMENTED;
+ pgmn_dev->out_buf_set = 0;
+ pgmn_dev->out_offset = 0;
+ pgmn_dev->max_out_size = g_max_out_size;
+ pgmn_dev->out_frag_cnt = 0;
+ pgmn_dev->bus_perf_client = 0;
+
+ if (p_bus_scale_data) {
+ GMN_DBG("%s:%d] register bus client", __func__, __LINE__);
+ pgmn_dev->bus_perf_client =
+ msm_bus_scale_register_client(p_bus_scale_data);
+ if (!pgmn_dev->bus_perf_client) {
+ GMN_PR_ERR("%s:%d] bus client register failed",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ }
+ GMN_DBG("%s:%d] success\n", __func__, __LINE__);
+ return rc;
+}
+
+int __msm_gemini_release(struct msm_gemini_device *pgmn_dev)
+{
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ mutex_lock(&pgmn_dev->lock);
+ if (!pgmn_dev->open_count) {
+ GMN_PR_ERR("%s: not opened\n", __func__);
+ mutex_unlock(&pgmn_dev->lock);
+ return -EINVAL;
+ }
+ pgmn_dev->open_count--;
+ mutex_unlock(&pgmn_dev->lock);
+
+ if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_FRAGMENTED) {
+ msm_gemini_core_release(release_buf);
+ } else if (pgmn_dev->out_buf_set) {
+ msm_gemini_platform_p2v(pgmn_dev->out_buf.file,
+ &pgmn_dev->out_buf.handle);
+ }
+ msm_gemini_q_cleanup(&pgmn_dev->evt_q);
+ msm_gemini_q_cleanup(&pgmn_dev->output_rtn_q);
+ msm_gemini_outbuf_q_cleanup(&pgmn_dev->output_buf_q);
+ msm_gemini_q_cleanup(&pgmn_dev->input_rtn_q);
+ msm_gemini_outbuf_q_cleanup(&pgmn_dev->input_buf_q);
+
+ if (pgmn_dev->bus_perf_client) {
+ msm_bus_scale_unregister_client(pgmn_dev->bus_perf_client);
+ pgmn_dev->bus_perf_client = 0;
+ }
+
+ if (pgmn_dev->open_count)
+ GMN_PR_ERR("%s: multiple opens\n", __func__);
+
+ msm_gemini_platform_release(pgmn_dev->mem, pgmn_dev->base,
+ pgmn_dev->irq, pgmn_dev);
+
+ return 0;
+}
+
+int msm_gemini_ioctl_hw_cmd(struct msm_gemini_device *pgmn_dev,
+ void * __user arg)
+{
+ struct msm_gemini_hw_cmd hw_cmd;
+ int is_copy_to_user;
+
+ if (copy_from_user(&hw_cmd, arg, sizeof(struct msm_gemini_hw_cmd))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ is_copy_to_user = msm_gemini_hw_exec_cmds(&hw_cmd, 1);
+ GMN_DBG("%s:%d] type %d, n %d, offset %d, mask %x, data %x, pdata %x\n",
+ __func__, __LINE__, hw_cmd.type, hw_cmd.n, hw_cmd.offset,
+ hw_cmd.mask, hw_cmd.data, (int) hw_cmd.pdata);
+
+ if (is_copy_to_user >= 0) {
+ if (copy_to_user(arg, &hw_cmd, sizeof(hw_cmd))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+static int msm_gemini_ioctl_hw_cmds(struct msm_gemini_device *pgmn_dev,
+ void * __user arg)
+{
+ int is_copy_to_user;
+ int len;
+ uint32_t m;
+ struct msm_gemini_hw_cmds *hw_cmds_p;
+ struct msm_gemini_hw_cmd *hw_cmd_p;
+
+ if (copy_from_user(&m, arg, sizeof(m))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ len = sizeof(struct msm_gemini_hw_cmds) +
+ sizeof(struct msm_gemini_hw_cmd) * (m - 1);
+ hw_cmds_p = kmalloc(len, GFP_KERNEL);
+ if (!hw_cmds_p) {
+ GMN_PR_ERR("%s:%d] no mem %d\n", __func__, __LINE__, len);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(hw_cmds_p, arg, len)) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ kfree(hw_cmds_p);
+ return -EFAULT;
+ }
+
+ hw_cmd_p = (struct msm_gemini_hw_cmd *) &(hw_cmds_p->hw_cmd);
+
+ is_copy_to_user = msm_gemini_hw_exec_cmds(hw_cmd_p, m);
+
+ if (is_copy_to_user >= 0) {
+ if (copy_to_user(arg, hw_cmds_p, len)) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ kfree(hw_cmds_p);
+ return -EFAULT;
+ }
+ }
+ kfree(hw_cmds_p);
+ return 0;
+}
+
+static int msm_gemini_start(struct msm_gemini_device *pgmn_dev,
+ void * __user arg)
+{
+ struct msm_gemini_core_buf *buf_out;
+ struct msm_gemini_core_buf *buf_out_free[2] = {NULL, NULL};
+ int i, rc;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+ release_buf = 1;
+ for (i = 0; i < 2; i++) {
+ buf_out = msm_gemini_q_out(&pgmn_dev->input_buf_q);
+
+ if (buf_out) {
+ msm_gemini_core_fe_buf_update(buf_out);
+ kfree(buf_out);
+ } else {
+ GMN_DBG("%s:%d] no input buffer\n", __func__, __LINE__);
+ break;
+ }
+ }
+
+ if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_FRAGMENTED) {
+ for (i = 0; i < 2; i++) {
+ buf_out_free[i] =
+ msm_gemini_q_out(&pgmn_dev->output_buf_q);
+
+ if (buf_out_free[i]) {
+ msm_gemini_core_we_buf_update(buf_out_free[i]);
+ } else if (i == 1) {
+ /* set the pong to same address as ping */
+ buf_out_free[0]->y_len >>= 1;
+ buf_out_free[0]->y_buffer_addr +=
+ buf_out_free[0]->y_len;
+ msm_gemini_core_we_buf_update(buf_out_free[0]);
+ /*
+ * since ping and pong are same buf
+ * release only once
+ */
+ release_buf = 0;
+ } else {
+ GMN_DBG("%s:%d] no output buffer\n",
+ __func__, __LINE__);
+ break;
+ }
+ }
+ for (i = 0; i < 2; i++)
+ kfree(buf_out_free[i]);
+ } else {
+ struct msm_gemini_core_buf out_buf;
+ /*
+ * Since the same buffer is fragmented, p2v need not be
+ * called for all the buffers
+ */
+ release_buf = 0;
+ if (!pgmn_dev->out_buf_set) {
+ GMN_PR_ERR("%s:%d] output buffer not set",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ /* configure ping */
+ rc = msm_gemini_get_out_buffer(pgmn_dev, &out_buf);
+ if (rc) {
+ GMN_PR_ERR("%s:%d] no output buffer for ping",
+ __func__, __LINE__);
+ return rc;
+ }
+ msm_gemini_core_we_buf_update(&out_buf);
+ /* configure pong */
+ rc = msm_gemini_get_out_buffer(pgmn_dev, &out_buf);
+ if (rc) {
+ GMN_DBG("%s:%d] no output buffer for pong",
+ __func__, __LINE__);
+ /* fall through to configure same buffer */
+ }
+ msm_gemini_core_we_buf_update(&out_buf);
+ msm_gemini_io_dump(0x150);
+ }
+
+ rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, arg);
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+ return rc;
+}
+
+static int msm_gemini_ioctl_reset(struct msm_gemini_device *pgmn_dev,
+ void * __user arg)
+{
+ int rc;
+ struct msm_gemini_ctrl_cmd ctrl_cmd;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ if (copy_from_user(&ctrl_cmd, arg, sizeof(ctrl_cmd))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ pgmn_dev->op_mode = ctrl_cmd.type;
+
+ rc = msm_gemini_core_reset(pgmn_dev->op_mode, pgmn_dev->base,
+ resource_size(pgmn_dev->mem));
+ return rc;
+}
+
+static int msm_gemini_ioctl_set_outmode(struct msm_gemini_device *pgmn_dev,
+ void * __user arg)
+{
+ int rc = 0;
+ enum msm_gmn_out_mode mode;
+
+ if (copy_from_user(&mode, arg, sizeof(mode))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ GMN_DBG("%s:%d] mode %d", __func__, __LINE__, mode);
+
+ if ((mode == MSM_GMN_OUTMODE_FRAGMENTED)
+ || (mode == MSM_GMN_OUTMODE_SINGLE))
+ pgmn_dev->out_mode = mode;
+ return rc;
+}
+
+long __msm_gemini_ioctl(struct msm_gemini_device *pgmn_dev,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc = 0;
+ switch (cmd) {
+ case MSM_GMN_IOCTL_GET_HW_VERSION:
+ GMN_DBG("%s:%d] VERSION 1\n", __func__, __LINE__);
+ rc = msm_gemini_ioctl_hw_cmd(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_RESET:
+ rc = msm_gemini_ioctl_reset(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_STOP:
+ rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_START:
+ rc = msm_gemini_start(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_INPUT_BUF_ENQUEUE:
+ rc = msm_gemini_input_buf_enqueue(pgmn_dev,
+ (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_INPUT_GET:
+ rc = msm_gemini_input_get(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_INPUT_GET_UNBLOCK:
+ rc = msm_gemini_input_get_unblock(pgmn_dev);
+ break;
+
+ case MSM_GMN_IOCTL_OUTPUT_BUF_ENQUEUE:
+ if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_FRAGMENTED)
+ rc = msm_gemini_output_buf_enqueue(pgmn_dev,
+ (void __user *) arg);
+ else
+ rc = msm_gemini_set_output_buf(pgmn_dev,
+ (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_OUTPUT_GET:
+ rc = msm_gemini_output_get(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_OUTPUT_GET_UNBLOCK:
+ rc = msm_gemini_output_get_unblock(pgmn_dev);
+ break;
+
+ case MSM_GMN_IOCTL_EVT_GET:
+ rc = msm_gemini_evt_get(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_EVT_GET_UNBLOCK:
+ rc = msm_gemini_evt_get_unblock(pgmn_dev);
+ break;
+
+ case MSM_GMN_IOCTL_HW_CMD:
+ rc = msm_gemini_ioctl_hw_cmd(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_HW_CMDS:
+ rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_SET_MODE:
+ rc = msm_gemini_ioctl_set_outmode(pgmn_dev, (void __user *)arg);
+ break;
+
+ default:
+ GMN_PR_ERR("%s:%d] cmd = %d not supported\n",
+ __func__, __LINE__, _IOC_NR(cmd));
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+struct msm_gemini_device *__msm_gemini_init(struct platform_device *pdev)
+{
+ struct msm_gemini_device *pgmn_dev;
+
+ pgmn_dev = kzalloc(sizeof(struct msm_gemini_device), GFP_ATOMIC);
+ if (!pgmn_dev) {
+ GMN_PR_ERR("%s:%d]no mem\n", __func__, __LINE__);
+ return NULL;
+ }
+
+ mutex_init(&pgmn_dev->lock);
+
+ pgmn_dev->pdev = pdev;
+
+ msm_gemini_q_init("evt_q", &pgmn_dev->evt_q);
+ msm_gemini_q_init("output_rtn_q", &pgmn_dev->output_rtn_q);
+ msm_gemini_q_init("output_buf_q", &pgmn_dev->output_buf_q);
+ msm_gemini_q_init("input_rtn_q", &pgmn_dev->input_rtn_q);
+ msm_gemini_q_init("input_buf_q", &pgmn_dev->input_buf_q);
+
+ return pgmn_dev;
+}
+
+int __msm_gemini_exit(struct msm_gemini_device *pgmn_dev)
+{
+ mutex_destroy(&pgmn_dev->lock);
+ kfree(pgmn_dev);
+ return 0;
+}
+
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_sync.h b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_sync.h
new file mode 100644
index 0000000..6982a78
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_sync.h
@@ -0,0 +1,98 @@
+/* Copyright (c) 2010,2013, 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.
+ */
+
+#ifndef MSM_GEMINI_SYNC_H
+#define MSM_GEMINI_SYNC_H
+
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include "msm_gemini_core.h"
+
+#define GEMINI_7X 0x1
+#define GEMINI_8X60 (0x1 << 1)
+#define GEMINI_8960 (0x1 << 2)
+
+struct msm_gemini_q {
+ char const *name;
+ struct list_head q;
+ spinlock_t lck;
+ wait_queue_head_t wait;
+ int unblck;
+};
+
+struct msm_gemini_q_entry {
+ struct list_head list;
+ void *data;
+};
+
+struct msm_gemini_device {
+ struct platform_device *pdev;
+ struct resource *mem;
+ int irq;
+ void *base;
+ struct clk *gemini_clk[3];
+ struct regulator *gemini_fs;
+ uint32_t hw_version;
+
+ struct device *device;
+ struct cdev cdev;
+ struct mutex lock;
+ char open_count;
+ uint8_t op_mode;
+
+ /* event queue including frame done & err indications
+ */
+ struct msm_gemini_q evt_q;
+
+ /* output return queue
+ */
+ struct msm_gemini_q output_rtn_q;
+
+ /* output buf queue
+ */
+ struct msm_gemini_q output_buf_q;
+
+ /* input return queue
+ */
+ struct msm_gemini_q input_rtn_q;
+
+ /* input buf queue
+ */
+ struct msm_gemini_q input_buf_q;
+
+ struct v4l2_subdev subdev;
+ enum msm_gmn_out_mode out_mode;
+
+ /*single out mode parameters*/
+ struct msm_gemini_hw_buf out_buf;
+ int out_offset;
+ int out_buf_set;
+ int max_out_size;
+ int out_frag_cnt;
+
+ uint32_t bus_perf_client;
+};
+
+int __msm_gemini_open(struct msm_gemini_device *pgmn_dev);
+int __msm_gemini_release(struct msm_gemini_device *pgmn_dev);
+
+long __msm_gemini_ioctl(struct msm_gemini_device *pgmn_dev,
+ unsigned int cmd, unsigned long arg);
+
+struct msm_gemini_device *__msm_gemini_init(struct platform_device *pdev);
+int __msm_gemini_exit(struct msm_gemini_device *pgmn_dev);
+
+#endif /* MSM_GEMINI_SYNC_H */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
index 263241b..2a2656f 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
@@ -80,13 +80,20 @@
}
static uint32_t msm_isp_get_buf_handle(
- struct msm_isp_buf_mgr *buf_mgr)
+ struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t session_id, uint32_t stream_id)
{
int i;
if ((buf_mgr->buf_handle_cnt << 8) == 0)
buf_mgr->buf_handle_cnt++;
for (i = 0; i < buf_mgr->num_buf_q; i++) {
+ if (buf_mgr->bufq[i].session_id == session_id &&
+ buf_mgr->bufq[i].stream_id == stream_id)
+ return 0;
+ }
+
+ for (i = 0; i < buf_mgr->num_buf_q; i++) {
if (buf_mgr->bufq[i].bufq_handle == 0) {
memset(&buf_mgr->bufq[i],
0, sizeof(struct msm_isp_bufq));
@@ -256,6 +263,13 @@
buf_info->state ==
MSM_ISP_BUFFER_STATE_INITIALIZED)
continue;
+
+ if (!BUF_SRC(bufq->stream_id)) {
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_DEQUEUED ||
+ buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED)
+ buf_mgr->vb2_ops->put_buf(buf_info->vb2_buf,
+ bufq->session_id, bufq->stream_id);
+ }
msm_isp_unprepare_v4l2_buf(buf_mgr, buf_info);
}
return 0;
@@ -287,13 +301,15 @@
bufq->buf_client_count)
list_del_init(
&temp_buf_info->share_list);
- if (temp_buf_info->buf_reuse_flag)
+ if (temp_buf_info->buf_reuse_flag) {
kfree(temp_buf_info);
- else
+ } else {
*buf_info = temp_buf_info;
+ rc = 0;
+ }
spin_unlock_irqrestore(
&bufq->bufq_lock, flags);
- return 0;
+ return rc;
}
}
}
@@ -342,6 +358,7 @@
(*buf_info)->buf_used[id] = 1;
(*buf_info)->buf_get_count = 1;
(*buf_info)->buf_put_count = 0;
+ (*buf_info)->buf_reuse_flag = 0;
list_add_tail(&(*buf_info)->share_list,
&bufq->share_head);
}
@@ -375,7 +392,6 @@
switch (buf_info->state) {
case MSM_ISP_BUFFER_STATE_PREPARED:
case MSM_ISP_BUFFER_STATE_DEQUEUED:
- case MSM_ISP_BUFFER_STATE_DISPATCHED:
case MSM_ISP_BUFFER_STATE_DIVERTED:
if (BUF_SRC(bufq->stream_id))
list_add_tail(&buf_info->list, &bufq->head);
@@ -385,6 +401,13 @@
buf_info->state = MSM_ISP_BUFFER_STATE_QUEUED;
rc = 0;
break;
+ case MSM_ISP_BUFFER_STATE_DISPATCHED:
+ buf_info->state = MSM_ISP_BUFFER_STATE_QUEUED;
+ rc = 0;
+ break;
+ case MSM_ISP_BUFFER_STATE_QUEUED:
+ rc = 0;
+ break;
default:
pr_err("%s: incorrect state = %d",
__func__, buf_info->state);
@@ -397,7 +420,7 @@
static int msm_isp_buf_done(struct msm_isp_buf_mgr *buf_mgr,
uint32_t bufq_handle, uint32_t buf_index,
- struct timeval *tv, uint32_t frame_id)
+ struct timeval *tv, uint32_t frame_id, uint32_t output_format)
{
int rc = -1;
unsigned long flags;
@@ -444,6 +467,7 @@
} else {
buf_info->vb2_buf->v4l2_buf.timestamp = *tv;
buf_info->vb2_buf->v4l2_buf.sequence = frame_id;
+ buf_info->vb2_buf->v4l2_buf.reserved = output_format;
buf_mgr->vb2_ops->buf_done(buf_info->vb2_buf,
bufq->session_id, bufq->stream_id);
}
@@ -544,11 +568,18 @@
if (buf_state == MSM_ISP_BUFFER_STATE_DIVERTED) {
buf_info = msm_isp_get_buf_ptr(buf_mgr,
info->handle, info->buf_idx);
- if (info->dirty_buf)
- msm_isp_put_buf(buf_mgr, info->handle, info->buf_idx);
- else
- msm_isp_buf_done(buf_mgr, info->handle, info->buf_idx,
- buf_info->tv, buf_info->frame_id);
+ if (info->dirty_buf) {
+ rc = msm_isp_put_buf(buf_mgr,
+ info->handle, info->buf_idx);
+ } else {
+ if (BUF_SRC(bufq->stream_id))
+ pr_err("%s: Invalid native buffer state\n",
+ __func__);
+ else
+ rc = msm_isp_buf_done(buf_mgr,
+ info->handle, info->buf_idx,
+ buf_info->tv, buf_info->frame_id, 0);
+ }
} else {
bufq = msm_isp_get_bufq(buf_mgr, info->handle);
if (BUF_SRC(bufq->stream_id)) {
@@ -588,7 +619,8 @@
return rc;
}
- buf_request->handle = msm_isp_get_buf_handle(buf_mgr);
+ buf_request->handle = msm_isp_get_buf_handle(buf_mgr,
+ buf_request->session_id, buf_request->stream_id);
if (!buf_request->handle) {
pr_err("Invalid buffer handle\n");
return rc;
@@ -660,22 +692,35 @@
}
}
-static int msm_isp_attach_ctx(struct msm_isp_buf_mgr *buf_mgr,
- struct device *iommu_ctx)
+static void msm_isp_register_ctx(struct msm_isp_buf_mgr *buf_mgr,
+ struct device **iommu_ctx, int num_iommu_ctx)
{
- int rc;
- rc = iommu_attach_device(buf_mgr->iommu_domain, iommu_ctx);
- if (rc) {
- pr_err("%s: Iommu attach error\n", __func__);
- return -EINVAL;
+ int i;
+ buf_mgr->num_iommu_ctx = num_iommu_ctx;
+ for (i = 0; i < num_iommu_ctx; i++)
+ buf_mgr->iommu_ctx[i] = iommu_ctx[i];
+}
+
+static int msm_isp_attach_ctx(struct msm_isp_buf_mgr *buf_mgr)
+{
+ int rc, i;
+ for (i = 0; i < buf_mgr->num_iommu_ctx; i++) {
+ rc = iommu_attach_device(buf_mgr->iommu_domain,
+ buf_mgr->iommu_ctx[i]);
+ if (rc) {
+ pr_err("%s: Iommu attach error\n", __func__);
+ return -EINVAL;
+ }
}
return 0;
}
-static void msm_isp_detach_ctx(struct msm_isp_buf_mgr *buf_mgr,
- struct device *iommu_ctx)
+static void msm_isp_detach_ctx(struct msm_isp_buf_mgr *buf_mgr)
{
- iommu_detach_device(buf_mgr->iommu_domain, iommu_ctx);
+ int i;
+ for (i = 0; i < buf_mgr->num_iommu_ctx; i++)
+ iommu_detach_device(buf_mgr->iommu_domain,
+ buf_mgr->iommu_ctx[i]);
}
static int msm_isp_init_isp_buf_mgr(
@@ -692,6 +737,7 @@
}
CDBG("%s: E\n", __func__);
+ msm_isp_attach_ctx(buf_mgr);
buf_mgr->num_buf_q = num_buf_q;
buf_mgr->bufq =
kzalloc(sizeof(struct msm_isp_bufq) * num_buf_q,
@@ -716,6 +762,7 @@
ion_client_destroy(buf_mgr->client);
kfree(buf_mgr->bufq);
buf_mgr->num_buf_q = 0;
+ msm_isp_detach_ctx(buf_mgr);
return 0;
}
@@ -752,8 +799,7 @@
.flush_buf = msm_isp_flush_buf,
.buf_done = msm_isp_buf_done,
.buf_divert = msm_isp_buf_divert,
- .attach_ctx = msm_isp_attach_ctx,
- .detach_ctx = msm_isp_detach_ctx,
+ .register_ctx = msm_isp_register_ctx,
.buf_mgr_init = msm_isp_init_isp_buf_mgr,
.buf_mgr_deinit = msm_isp_deinit_isp_buf_mgr,
};
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.h b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.h
index 066d02a..6d6ff9d 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.h
@@ -108,14 +108,12 @@
int (*buf_done) (struct msm_isp_buf_mgr *buf_mgr,
uint32_t bufq_handle, uint32_t buf_index,
- struct timeval *tv, uint32_t frame_id);
+ struct timeval *tv, uint32_t frame_id, uint32_t output_format);
int (*buf_divert) (struct msm_isp_buf_mgr *buf_mgr,
uint32_t bufq_handle, uint32_t buf_index,
struct timeval *tv, uint32_t frame_id);
- int (*attach_ctx) (struct msm_isp_buf_mgr *buf_mgr,
- struct device *iommu_ctx);
- void (*detach_ctx) (struct msm_isp_buf_mgr *buf_mgr,
- struct device *iommu_ctx);
+ void (*register_ctx) (struct msm_isp_buf_mgr *buf_mgr,
+ struct device **iommu_ctx, int num_iommu_ctx);
int (*buf_mgr_init) (struct msm_isp_buf_mgr *buf_mgr,
const char *ctx_name, uint16_t num_buf_q);
int (*buf_mgr_deinit) (struct msm_isp_buf_mgr *buf_mgr);
@@ -137,6 +135,9 @@
/*IOMMU specific*/
int iommu_domain_num;
struct iommu_domain *iommu_domain;
+
+ int num_iommu_ctx;
+ struct device *iommu_ctx[2];
};
int msm_isp_create_isp_buf_mgr(struct msm_isp_buf_mgr *buf_mgr,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
index 447c752..2c5e136 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
@@ -38,6 +38,10 @@
.compatible = "qcom,vfe40",
.data = &vfe40_hw_info,
},
+ {
+ .compatible = "qcom,vfe32",
+ .data = &vfe32_hw_info,
+ },
{}
};
@@ -121,6 +125,7 @@
vfe_dev->subdev.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
vfe_dev->subdev.sd.entity.group_id = MSM_CAMERA_SUBDEV_VFE;
vfe_dev->subdev.sd.entity.name = pdev->name;
+ vfe_dev->subdev.close_seq = MSM_SD_CLOSE_1ST_CATEGORY | 0x2;
rc = msm_sd_register(&vfe_dev->subdev);
if (rc != 0) {
pr_err("%s: msm_sd_register error = %d\n", __func__, rc);
@@ -138,6 +143,8 @@
kfree(vfe_dev);
return -EINVAL;
}
+ vfe_dev->buf_mgr->ops->register_ctx(vfe_dev->buf_mgr,
+ &vfe_dev->iommu_ctx[0], vfe_dev->hw_info->num_iommu_ctx);
vfe_dev->vfe_open_cnt = 0;
end:
return rc;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
index 1b762ea..f1f4c17 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
@@ -38,6 +38,8 @@
#define VFE_PING_FLAG 0xFFFFFFFF
#define VFE_PONG_FLAG 0x0
+#define VFE_MAX_CFG_TIMEOUT 3000
+
struct vfe_device;
struct msm_vfe_axi_stream;
struct msm_vfe_stats_stream;
@@ -88,7 +90,7 @@
void (*enable_wm) (struct vfe_device *vfe_dev,
uint8_t wm_idx, uint8_t enable);
void (*cfg_io_format) (struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream_request_cmd *stream_req_cmd);
+ struct msm_vfe_axi_stream *stream_info);
void (*cfg_framedrop) (struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info);
void (*clear_framedrop) (struct vfe_device *vfe_dev,
@@ -103,13 +105,13 @@
struct msm_vfe_axi_stream *stream_info);
void (*cfg_wm_reg) (struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ struct msm_vfe_axi_stream *stream_info,
uint8_t plane_idx);
void (*clear_wm_reg) (struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx);
void (*cfg_wm_xbar_reg) (struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ struct msm_vfe_axi_stream *stream_info,
uint8_t plane_idx);
void (*clear_wm_xbar_reg) (struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx);
@@ -148,7 +150,8 @@
struct msm_vfe_stats_stream *stream_info);
void (*clear_framedrop) (struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info);
- void (*cfg_comp_mask) (struct vfe_device *vfe_dev);
+ void (*cfg_comp_mask) (struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable);
void (*cfg_wm_irq_mask) (struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info);
void (*clear_wm_irq_mask) (struct vfe_device *vfe_dev,
@@ -204,12 +207,21 @@
AVALIABLE,
INACTIVE,
ACTIVE,
- PAUSE,
+ PAUSED,
START_PENDING,
STOP_PENDING,
+ PAUSE_PENDING,
+ RESUME_PENDING,
STARTING,
STOPPING,
- PAUSE_PENDING,
+ PAUSING,
+ RESUMING,
+};
+
+enum msm_vfe_axi_cfg_update_state {
+ NO_AXI_CFG_UPDATE,
+ APPLYING_UPDATE_RESUME,
+ UPDATE_REQUESTED,
};
#define VFE_NO_DROP 0xFFFFFFFF
@@ -230,7 +242,8 @@
enum msm_vfe_axi_stream_src stream_src;
uint8_t num_planes;
uint8_t wm[MAX_PLANES_PER_STREAM];
- uint32_t plane_offset[MAX_PLANES_PER_STREAM];
+ uint32_t output_format;/*Planar/RAW/Misc*/
+ struct msm_vfe_axi_plane_cfg plane_cfg[MAX_PLANES_PER_STREAM];
uint8_t comp_mask_index;
struct msm_isp_buffer *buf[2];
uint32_t session_id;
@@ -247,6 +260,7 @@
uint32_t init_frame_drop;
uint32_t burst_frame_count;/*number of sof before burst stop*/
uint8_t framedrop_update;
+ spinlock_t lock;
/*Bandwidth calculation info*/
uint32_t max_width;
@@ -259,6 +273,7 @@
uint32_t runtime_burst_frame_count;/*number of sof before burst stop*/
uint32_t runtime_num_burst_capture;
uint8_t runtime_framedrop_update;
+ uint32_t runtime_output_format;
};
struct msm_vfe_axi_composite_info {
@@ -294,6 +309,8 @@
composite_info[MAX_NUM_COMPOSITE_MASK];
uint8_t num_used_composite_mask;
uint32_t stream_update;
+ atomic_t axi_cfg_update;
+ enum msm_isp_camif_update_state pipeline_update;
struct msm_vfe_src_info src_info[VFE_SRC_MAX];
uint16_t stream_handle_cnt;
unsigned long event_mask;
@@ -312,6 +329,7 @@
STATS_ACTIVE,
STATS_START_PENDING,
STATS_STOP_PENDING,
+ STATS_STARTING,
STATS_STOPPING,
};
@@ -319,9 +337,11 @@
uint32_t session_id;
uint32_t stream_id;
uint32_t stream_handle;
+ uint32_t composite_flag;
enum msm_isp_stats_type stats_type;
enum msm_vfe_stats_state state;
uint32_t framedrop_pattern;
+ uint32_t framedrop_period;
uint32_t irq_subsample_pattern;
uint32_t buffer_offset;
@@ -331,11 +351,10 @@
struct msm_vfe_stats_shared_data {
struct msm_vfe_stats_stream stream_info[MSM_ISP_STATS_MAX];
- enum msm_vfe_stats_pipeline_policy stats_pipeline_policy;
- uint32_t comp_framedrop_pattern;
- uint32_t comp_irq_subsample_pattern;
uint8_t num_active_stream;
+ atomic_t stats_comp_mask;
uint16_t stream_handle_cnt;
+ atomic_t stats_update;
};
struct msm_vfe_tasklet_queue_cmd {
@@ -380,6 +399,7 @@
struct completion reset_complete;
struct completion halt_complete;
struct completion stream_config_complete;
+ struct completion stats_config_complete;
struct mutex realtime_mutex;
struct mutex core_mutex;
@@ -399,6 +419,7 @@
struct msm_vfe_error_info error_info;
struct msm_isp_buf_mgr *buf_mgr;
int dump_reg;
+ int vfe_clk_idx;
uint32_t vfe_open_cnt;
};
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
index d4f6a07..4c5f258 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
@@ -22,7 +22,7 @@
#include "msm.h"
#include "msm_camera_io_util.h"
-#define VFE32_BURST_LEN 3
+#define VFE32_BURST_LEN 1
#define VFE32_UB_SIZE 1024
#define VFE32_EQUAL_SLICE_UB 204
#define VFE32_WM_BASE(idx) (0x4C + 0x18 * idx)
@@ -40,7 +40,17 @@
(~(ping_pong >> (idx + VFE32_STATS_PING_PONG_OFFSET)) & 0x1))
#define VFE32_CLK_IDX 0
-static struct msm_cam_clk_info msm_vfe32_clk_info[] = {
+static struct msm_cam_clk_info msm_vfe32_1_clk_info[] = {
+ /*vfe32 clock info for B-family: 8610 */
+ {"vfe_clk_src", 266670000},
+ {"vfe_clk", -1},
+ {"vfe_ahb_clk", -1},
+ {"csi_vfe_clk", -1},
+ {"bus_clk", -1},
+};
+
+static struct msm_cam_clk_info msm_vfe32_2_clk_info[] = {
+ /*vfe32 clock info for A-family: 8960 */
{"vfe_clk", 266667000},
{"vfe_pclk", -1},
{"csi_vfe_clk", -1},
@@ -49,6 +59,7 @@
static int msm_vfe32_init_hardware(struct vfe_device *vfe_dev)
{
int rc = -1;
+ vfe_dev->vfe_clk_idx = 0;
rc = msm_isp_init_bandwidth_mgr(ISP_VFE0 + vfe_dev->pdev->id);
if (rc < 0) {
pr_err("%s: Bandwidth registration Failed!\n", __func__);
@@ -63,10 +74,18 @@
}
}
- rc = msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_clk_info,
- vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe32_clk_info), 1);
- if (rc < 0)
- goto clk_enable_failed;
+ rc = msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_1_clk_info,
+ vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe32_1_clk_info), 1);
+ if (rc < 0) {
+ rc = msm_cam_clk_enable(&vfe_dev->pdev->dev,
+ msm_vfe32_2_clk_info, vfe_dev->vfe_clk,
+ ARRAY_SIZE(msm_vfe32_2_clk_info), 1);
+ if (rc < 0)
+ goto clk_enable_failed;
+ else
+ vfe_dev->vfe_clk_idx = 2;
+ } else
+ vfe_dev->vfe_clk_idx = 1;
vfe_dev->vfe_base = ioremap(vfe_dev->vfe_mem->start,
resource_size(vfe_dev->vfe_mem));
@@ -87,8 +106,14 @@
irq_req_failed:
iounmap(vfe_dev->vfe_base);
vfe_remap_failed:
- msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_clk_info,
- vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe32_clk_info), 0);
+ if (vfe_dev->vfe_clk_idx == 1)
+ msm_cam_clk_enable(&vfe_dev->pdev->dev,
+ msm_vfe32_1_clk_info, vfe_dev->vfe_clk,
+ ARRAY_SIZE(msm_vfe32_1_clk_info), 0);
+ if (vfe_dev->vfe_clk_idx == 2)
+ msm_cam_clk_enable(&vfe_dev->pdev->dev,
+ msm_vfe32_2_clk_info, vfe_dev->vfe_clk,
+ ARRAY_SIZE(msm_vfe32_2_clk_info), 0);
clk_enable_failed:
regulator_disable(vfe_dev->fs_vfe);
fs_failed:
@@ -102,8 +127,14 @@
free_irq(vfe_dev->vfe_irq->start, vfe_dev);
tasklet_kill(&vfe_dev->vfe_tasklet);
iounmap(vfe_dev->vfe_base);
- msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_clk_info,
- vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe32_clk_info), 0);
+ if (vfe_dev->vfe_clk_idx == 1)
+ msm_cam_clk_enable(&vfe_dev->pdev->dev,
+ msm_vfe32_1_clk_info, vfe_dev->vfe_clk,
+ ARRAY_SIZE(msm_vfe32_1_clk_info), 0);
+ if (vfe_dev->vfe_clk_idx == 2)
+ msm_cam_clk_enable(&vfe_dev->pdev->dev,
+ msm_vfe32_2_clk_info, vfe_dev->vfe_clk,
+ ARRAY_SIZE(msm_vfe32_2_clk_info), 0);
regulator_disable(vfe_dev->fs_vfe);
msm_isp_deinit_bandwidth_mgr(ISP_VFE0 + vfe_dev->pdev->id);
}
@@ -114,7 +145,7 @@
msm_camera_io_w(0x07FFFFFF, vfe_dev->vfe_base + 0xC);
/* BUS_CFG */
msm_camera_io_w(0x00000001, vfe_dev->vfe_base + 0x3C);
- msm_camera_io_w(0x00000025, vfe_dev->vfe_base + 0x1C);
+ msm_camera_io_w(0x01000025, vfe_dev->vfe_base + 0x1C);
msm_camera_io_w_mb(0x1DFFFFFF, vfe_dev->vfe_base + 0x20);
msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x24);
msm_camera_io_w_mb(0x1FFFFFFF, vfe_dev->vfe_base + 0x28);
@@ -142,14 +173,13 @@
return;
if (irq_status0 & BIT(0)) {
- ISP_DBG("%s: PIX0 frame id: %lu\n", __func__,
- vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
- msm_isp_sof_notify(vfe_dev, VFE_PIX_0, ts);
ISP_DBG("%s: SOF IRQ\n", __func__);
if (vfe_dev->axi_data.src_info[VFE_PIX_0].raw_stream_count > 0
&& vfe_dev->axi_data.src_info[VFE_PIX_0].
pix_stream_count == 0) {
msm_isp_sof_notify(vfe_dev, VFE_PIX_0, ts);
+ if (vfe_dev->axi_data.stream_update)
+ msm_isp_axi_stream_update(vfe_dev);
msm_isp_update_framedrop_reg(vfe_dev);
}
}
@@ -304,6 +334,8 @@
if (vfe_dev->axi_data.stream_update)
msm_isp_axi_stream_update(vfe_dev);
+ if (atomic_read(&vfe_dev->stats_data.stats_update))
+ msm_isp_stats_stream_update(vfe_dev);
msm_isp_update_framedrop_reg(vfe_dev);
msm_isp_update_error_frame_count(vfe_dev);
@@ -329,7 +361,18 @@
static void msm_vfe32_axi_reload_wm(
struct vfe_device *vfe_dev, uint32_t reload_mask)
{
- msm_camera_io_w_mb(reload_mask, vfe_dev->vfe_base + 0x38);
+ if (!vfe_dev->pdev->dev.of_node) {
+ /*vfe32 A-family: 8960*/
+ msm_camera_io_w_mb(reload_mask, vfe_dev->vfe_base + 0x38);
+ } else {
+ /*vfe32 B-family: 8610*/
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x24);
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x28);
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x20);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x18);
+ msm_camera_io_w(0x9AAAAAAA , vfe_dev->vfe_base + 0x600);
+ msm_camera_io_w(reload_mask, vfe_dev->vfe_base + 0x38);
+ }
}
static void msm_vfe32_axi_enable_wm(struct vfe_device *vfe_dev,
@@ -440,11 +483,11 @@
}
static void msm_vfe32_cfg_io_format(struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream_request_cmd *stream_req_cmd)
+ struct msm_vfe_axi_stream *stream_info)
{
int bpp, bpp_reg = 0;
uint32_t io_format_reg;
- bpp = msm_isp_get_bit_per_pixel(stream_req_cmd->output_format);
+ bpp = msm_isp_get_bit_per_pixel(stream_info->output_format);
switch (bpp) {
case 8:
@@ -458,7 +501,7 @@
break;
}
io_format_reg = msm_camera_io_r(vfe_dev->vfe_base + 0x6F8);
- switch (stream_req_cmd->stream_src) {
+ switch (stream_info->stream_src) {
case CAMIF_RAW:
io_format_reg &= 0xFFFFCFFF;
io_format_reg |= bpp_reg << 12;
@@ -564,44 +607,40 @@
static void msm_vfe32_axi_cfg_wm_reg(
struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ struct msm_vfe_axi_stream *stream_info,
uint8_t plane_idx)
{
uint32_t val;
- struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- struct msm_vfe_axi_stream *stream_info =
- &axi_data->stream_info[
- (stream_cfg_cmd->axi_stream_handle & 0xFF)];
uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[plane_idx]);
if (!stream_info->frame_based) {
/*WR_IMAGE_SIZE*/
val =
((msm_isp_cal_word_per_line(
- stream_cfg_cmd->output_format,
- stream_cfg_cmd->plane_cfg[plane_idx].
+ stream_info->output_format,
+ stream_info->plane_cfg[plane_idx].
output_width)+1)/2 - 1) << 16 |
- (stream_cfg_cmd->plane_cfg[plane_idx].
+ (stream_info->plane_cfg[plane_idx].
output_height - 1);
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x10);
/*WR_BUFFER_CFG*/
val =
msm_isp_cal_word_per_line(
- stream_cfg_cmd->output_format,
- stream_cfg_cmd->plane_cfg[plane_idx].
+ stream_info->output_format,
+ stream_info->plane_cfg[plane_idx].
output_stride) << 16 |
- (stream_cfg_cmd->plane_cfg[plane_idx].
+ (stream_info->plane_cfg[plane_idx].
output_height - 1) << 4 | VFE32_BURST_LEN;
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
} else {
msm_camera_io_w(0x2, vfe_dev->vfe_base + wm_base);
val =
msm_isp_cal_word_per_line(
- stream_cfg_cmd->output_format,
- stream_cfg_cmd->plane_cfg[plane_idx].
+ stream_info->output_format,
+ stream_info->plane_cfg[plane_idx].
output_width) << 16 |
- (stream_cfg_cmd->plane_cfg[plane_idx].
+ (stream_info->plane_cfg[plane_idx].
output_height - 1) << 4 | VFE32_BURST_LEN;
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
}
@@ -623,19 +662,15 @@
static void msm_vfe32_axi_cfg_wm_xbar_reg(
struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
- uint8_t plane_idx)
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
{
- struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- struct msm_vfe_axi_stream *stream_info =
- &axi_data->stream_info[(stream_cfg_cmd->axi_stream_handle & 0xFF)];
struct msm_vfe_axi_plane_cfg *plane_cfg =
- &stream_cfg_cmd->plane_cfg[plane_idx];
+ &stream_info->plane_cfg[plane_idx];
uint8_t wm = stream_info->wm[plane_idx];
uint32_t xbar_cfg = 0;
uint32_t xbar_reg_cfg = 0;
- switch (stream_cfg_cmd->stream_src) {
+ switch (stream_info->stream_src) {
case PIX_ENCODER:
case PIX_VIEWFINDER: {
if (plane_cfg->output_plane_format != CRCB_PLANE &&
@@ -643,15 +678,16 @@
/*SINGLE_STREAM_SEL*/
xbar_cfg |= plane_cfg->output_plane_format << 5;
} else {
- switch (stream_cfg_cmd->output_format) {
+ switch (stream_info->output_format) {
case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV14:
case V4L2_PIX_FMT_NV16:
xbar_cfg |= 0x3 << 3; /*PAIR_STREAM_SWAP_CTRL*/
break;
}
xbar_cfg |= BIT(1); /*PAIR_STREAM_EN*/
}
- if (stream_cfg_cmd->stream_src == PIX_VIEWFINDER)
+ if (stream_info->stream_src == PIX_VIEWFINDER)
xbar_cfg |= 0x1; /*VIEW_STREAM_EN*/
break;
}
@@ -767,7 +803,8 @@
}
}
-static void msm_vfe32_stats_cfg_comp_mask(struct vfe_device *vfe_dev)
+static void msm_vfe32_stats_cfg_comp_mask(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable)
{
return;
}
@@ -919,14 +956,22 @@
goto vfe_no_resource;
}
- vfe_dev->iommu_ctx[0] = msm_iommu_get_ctx("vfe_imgwr");
+ if (!vfe_dev->pdev->dev.of_node)
+ vfe_dev->iommu_ctx[0] = msm_iommu_get_ctx("vfe_imgwr");
+ else
+ vfe_dev->iommu_ctx[0] = msm_iommu_get_ctx("vfe0");
+
if (!vfe_dev->iommu_ctx[0]) {
pr_err("%s: no iommux ctx resource?\n", __func__);
rc = -ENODEV;
goto vfe_no_resource;
}
- vfe_dev->iommu_ctx[1] = msm_iommu_get_ctx("vfe_misc");
+ if (!vfe_dev->pdev->dev.of_node)
+ vfe_dev->iommu_ctx[1] = msm_iommu_get_ctx("vfe_misc");
+ else
+ vfe_dev->iommu_ctx[1] = msm_iommu_get_ctx("vfe0");
+
if (!vfe_dev->iommu_ctx[1]) {
pr_err("%s: no iommux ctx resource?\n", __func__);
rc = -ENODEV;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
index 256d136..84ba4d1 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <mach/iommu.h>
+#include <linux/ratelimit.h>
#include "msm_isp40.h"
#include "msm_isp_util.h"
@@ -29,8 +30,9 @@
#define CDBG(fmt, args...) do { } while (0)
#endif
-#define VFE40_V1_VERSION 0x10000018
-#define VFE40_V2_VERSION 0x1001001A
+#define VFE40_8974V1_VERSION 0x10000018
+#define VFE40_8974V2_VERSION 0x1001001A
+#define VFE40_8x26_VERSION 0x20000013
#define VFE40_BURST_LEN 3
#define VFE40_STATS_BURST_LEN 2
@@ -90,7 +92,8 @@
static void msm_vfe40_init_qos_parms(struct vfe_device *vfe_dev)
{
void __iomem *vfebase = vfe_dev->vfe_base;
- if (vfe_dev->vfe_hw_version == VFE40_V1_VERSION) {
+ if (vfe_dev->vfe_hw_version == VFE40_8974V1_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8x26_VERSION) {
msm_camera_io_w(0xAAAAAAAA, vfebase + VFE40_BUS_BDG_QOS_CFG_0);
msm_camera_io_w(0xAAAAAAAA, vfebase + VFE40_BUS_BDG_QOS_CFG_1);
msm_camera_io_w(0xAAAAAAAA, vfebase + VFE40_BUS_BDG_QOS_CFG_2);
@@ -99,7 +102,7 @@
msm_camera_io_w(0xAAAAAAAA, vfebase + VFE40_BUS_BDG_QOS_CFG_5);
msm_camera_io_w(0xAAAAAAAA, vfebase + VFE40_BUS_BDG_QOS_CFG_6);
msm_camera_io_w(0x0002AAAA, vfebase + VFE40_BUS_BDG_QOS_CFG_7);
- } else if (vfe_dev->vfe_hw_version == VFE40_V2_VERSION) {
+ } else if (vfe_dev->vfe_hw_version == VFE40_8974V2_VERSION) {
msm_camera_io_w(0xAAA9AAA9, vfebase + VFE40_BUS_BDG_QOS_CFG_0);
msm_camera_io_w(0xAAA9AAA9, vfebase + VFE40_BUS_BDG_QOS_CFG_1);
msm_camera_io_w(0xAAA9AAA9, vfebase + VFE40_BUS_BDG_QOS_CFG_2);
@@ -108,81 +111,138 @@
msm_camera_io_w(0xAAA9AAA9, vfebase + VFE40_BUS_BDG_QOS_CFG_5);
msm_camera_io_w(0xAAA9AAA9, vfebase + VFE40_BUS_BDG_QOS_CFG_6);
msm_camera_io_w(0x0001AAA9, vfebase + VFE40_BUS_BDG_QOS_CFG_7);
+ } else {
+ BUG();
+ pr_err("%s: QOS is NOT configured for HW Version %x\n",
+ __func__, vfe_dev->vfe_hw_version);
}
}
+static void msm_vfe40_init_vbif_parms_8974_v1(struct vfe_device *vfe_dev)
+{
+ void __iomem *vfe_vbif_base = vfe_dev->vfe_vbif_base;
+ msm_camera_io_w(0x1,
+ vfe_vbif_base + VFE40_VBIF_CLKON);
+ msm_camera_io_w(0x01010101,
+ vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF0);
+ msm_camera_io_w(0x01010101,
+ vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF1);
+ msm_camera_io_w(0x10010110,
+ vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF2);
+ msm_camera_io_w(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF0);
+ msm_camera_io_w(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF1);
+ msm_camera_io_w(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF2);
+ msm_camera_io_w(0x00001010,
+ vfe_vbif_base + VFE40_VBIF_OUT_RD_LIM_CONF0);
+ msm_camera_io_w(0x00001010,
+ vfe_vbif_base + VFE40_VBIF_OUT_WR_LIM_CONF0);
+ msm_camera_io_w(0x00000707,
+ vfe_vbif_base + VFE40_VBIF_DDR_OUT_MAX_BURST);
+ msm_camera_io_w(0x00000707,
+ vfe_vbif_base + VFE40_VBIF_OCMEM_OUT_MAX_BURST);
+ msm_camera_io_w(0x00000030,
+ vfe_vbif_base + VFE40_VBIF_ARB_CTL);
+ msm_camera_io_w(0x00000FFF,
+ vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO_EN);
+ msm_camera_io_w(0x0FFF0FFF,
+ vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO);
+ msm_camera_io_w(0x00000001,
+ vfe_vbif_base + VFE40_VBIF_ROUND_ROBIN_QOS_ARB);
+ msm_camera_io_w(0x22222222,
+ vfe_vbif_base + VFE40_VBIF_OUT_AXI_AMEMTYPE_CONF0);
+ msm_camera_io_w(0x00002222,
+ vfe_vbif_base + VFE40_VBIF_OUT_AXI_AMEMTYPE_CONF1);
+ return;
+}
+
+static void msm_vfe40_init_vbif_parms_8974_v2(struct vfe_device *vfe_dev)
+{
+ void __iomem *vfe_vbif_base = vfe_dev->vfe_vbif_base;
+ msm_camera_io_w(0x1,
+ vfe_vbif_base + VFE40_VBIF_CLKON);
+ msm_camera_io_w(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF0);
+ msm_camera_io_w(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF1);
+ msm_camera_io_w(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF2);
+ msm_camera_io_w(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF0);
+ msm_camera_io_w(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF1);
+ msm_camera_io_w(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF2);
+ msm_camera_io_w(0x00000010,
+ vfe_vbif_base + VFE40_VBIF_OUT_RD_LIM_CONF0);
+ msm_camera_io_w(0x00000010,
+ vfe_vbif_base + VFE40_VBIF_OUT_WR_LIM_CONF0);
+ msm_camera_io_w(0x00000707,
+ vfe_vbif_base + VFE40_VBIF_DDR_OUT_MAX_BURST);
+ msm_camera_io_w(0x00000010,
+ vfe_vbif_base + VFE40_VBIF_ARB_CTL);
+ msm_camera_io_w(0x00000FFF,
+ vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO_EN);
+ msm_camera_io_w(0x0FFF0FFF,
+ vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO);
+ msm_camera_io_w(0x00000003,
+ vfe_vbif_base + VFE40_VBIF_ROUND_ROBIN_QOS_ARB);
+ msm_camera_io_w(0x22222222,
+ vfe_vbif_base + VFE40_VBIF_OUT_AXI_AMEMTYPE_CONF0);
+ msm_camera_io_w(0x00002222,
+ vfe_vbif_base + VFE40_VBIF_OUT_AXI_AMEMTYPE_CONF1);
+ return;
+}
+
+static void msm_vfe40_init_vbif_parms_8x26(struct vfe_device *vfe_dev)
+{
+ void __iomem *vfe_vbif_base = vfe_dev->vfe_vbif_base;
+ msm_camera_io_w(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF0);
+ msm_camera_io_w(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF1);
+ msm_camera_io_w(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF0);
+ msm_camera_io_w(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF1);
+ msm_camera_io_w(0x00000010,
+ vfe_vbif_base + VFE40_VBIF_OUT_RD_LIM_CONF0);
+ msm_camera_io_w(0x00000010,
+ vfe_vbif_base + VFE40_VBIF_OUT_WR_LIM_CONF0);
+ msm_camera_io_w(0x00000707,
+ vfe_vbif_base + VFE40_VBIF_DDR_OUT_MAX_BURST);
+ msm_camera_io_w(0x000000FF,
+ vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO_EN);
+ msm_camera_io_w(0x00FF00FF,
+ vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO);
+ msm_camera_io_w(0x00000003,
+ vfe_vbif_base + VFE40_VBIF_ROUND_ROBIN_QOS_ARB);
+ msm_camera_io_w(0x22222222,
+ vfe_vbif_base + VFE40_VBIF_OUT_AXI_AMEMTYPE_CONF0);
+ return;
+}
+
static void msm_vfe40_init_vbif_parms(struct vfe_device *vfe_dev)
{
- void __iomem *vfe_vbif_base = vfe_dev->vfe_vbif_base;
- if (vfe_dev->vfe_hw_version == VFE40_V1_VERSION) {
- msm_camera_io_w(0x1,
- vfe_vbif_base + VFE40_VBIF_CLKON);
- msm_camera_io_w(0x01010101,
- vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF0);
- msm_camera_io_w(0x01010101,
- vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF1);
- msm_camera_io_w(0x10010110,
- vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF2);
- msm_camera_io_w(0x10101010,
- vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF0);
- msm_camera_io_w(0x10101010,
- vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF1);
- msm_camera_io_w(0x10101010,
- vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF2);
- msm_camera_io_w(0x00001010,
- vfe_vbif_base + VFE40_VBIF_OUT_RD_LIM_CONF0);
- msm_camera_io_w(0x00001010,
- vfe_vbif_base + VFE40_VBIF_OUT_WR_LIM_CONF0);
- msm_camera_io_w(0x00000707,
- vfe_vbif_base + VFE40_VBIF_DDR_OUT_MAX_BURST);
- msm_camera_io_w(0x00000707,
- vfe_vbif_base + VFE40_VBIF_OCMEM_OUT_MAX_BURST);
- msm_camera_io_w(0x00000030,
- vfe_vbif_base + VFE40_VBIF_ARB_CTL);
- msm_camera_io_w(0x00000FFF,
- vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO_EN);
- msm_camera_io_w(0x0FFF0FFF,
- vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO);
- msm_camera_io_w(0x00000001,
- vfe_vbif_base + VFE40_VBIF_ROUND_ROBIN_QOS_ARB);
- msm_camera_io_w(0x22222222,
- vfe_vbif_base + VFE40_VBIF_OUT_AXI_AMEMTYPE_CONF0);
- msm_camera_io_w(0x00002222,
- vfe_vbif_base + VFE40_VBIF_OUT_AXI_AMEMTYPE_CONF1);
- } else if (vfe_dev->vfe_hw_version == VFE40_V2_VERSION) {
- msm_camera_io_w(0x1,
- vfe_vbif_base + VFE40_VBIF_CLKON);
- msm_camera_io_w(0x10101010,
- vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF0);
- msm_camera_io_w(0x10101010,
- vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF1);
- msm_camera_io_w(0x10101010,
- vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF2);
- msm_camera_io_w(0x10101010,
- vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF0);
- msm_camera_io_w(0x10101010,
- vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF1);
- msm_camera_io_w(0x10101010,
- vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF2);
- msm_camera_io_w(0x00000010,
- vfe_vbif_base + VFE40_VBIF_OUT_RD_LIM_CONF0);
- msm_camera_io_w(0x00000010,
- vfe_vbif_base + VFE40_VBIF_OUT_WR_LIM_CONF0);
- msm_camera_io_w(0x00000707,
- vfe_vbif_base + VFE40_VBIF_DDR_OUT_MAX_BURST);
- msm_camera_io_w(0x00000010,
- vfe_vbif_base + VFE40_VBIF_ARB_CTL);
- msm_camera_io_w(0x00000FFF,
- vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO_EN);
- msm_camera_io_w(0x0FFF0FFF,
- vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO);
- msm_camera_io_w(0x00000003,
- vfe_vbif_base + VFE40_VBIF_ROUND_ROBIN_QOS_ARB);
- msm_camera_io_w(0x22222222,
- vfe_vbif_base + VFE40_VBIF_OUT_AXI_AMEMTYPE_CONF0);
- msm_camera_io_w(0x00002222,
- vfe_vbif_base + VFE40_VBIF_OUT_AXI_AMEMTYPE_CONF1);
+ switch (vfe_dev->vfe_hw_version) {
+ case VFE40_8974V1_VERSION:
+ msm_vfe40_init_vbif_parms_8974_v1(vfe_dev);
+ break;
+ case VFE40_8974V2_VERSION:
+ msm_vfe40_init_vbif_parms_8974_v2(vfe_dev);
+ break;
+ case VFE40_8x26_VERSION:
+ msm_vfe40_init_vbif_parms_8x26(vfe_dev);
+ break;
+ default:
+ BUG();
+ pr_err("%s: VBIF is NOT configured for HW Version %x\n",
+ __func__, vfe_dev->vfe_hw_version);
+ break;
}
+
}
static int msm_vfe40_init_hardware(struct vfe_device *vfe_dev)
@@ -266,7 +326,7 @@
msm_camera_io_w(0xC001FF7F, vfe_dev->vfe_base + 0x974);
/* BUS_CFG */
msm_camera_io_w(0x10000001, vfe_dev->vfe_base + 0x50);
- msm_camera_io_w(0x800000F3, vfe_dev->vfe_base + 0x28);
+ msm_camera_io_w(0xE00000F3, vfe_dev->vfe_base + 0x28);
msm_camera_io_w_mb(0xFEFFFFFF, vfe_dev->vfe_base + 0x2C);
msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30);
msm_camera_io_w_mb(0xFEFFFFFF, vfe_dev->vfe_base + 0x34);
@@ -299,6 +359,8 @@
&& vfe_dev->axi_data.src_info[VFE_PIX_0].
pix_stream_count == 0) {
msm_isp_sof_notify(vfe_dev, VFE_PIX_0, ts);
+ if (vfe_dev->axi_data.stream_update)
+ msm_isp_axi_stream_update(vfe_dev);
msm_isp_update_framedrop_reg(vfe_dev);
}
}
@@ -434,9 +496,16 @@
{
*irq_status0 = msm_camera_io_r(vfe_dev->vfe_base + 0x38);
*irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x3C);
+ /*Ignore composite 3 irq which is used for dual VFE only*/
+ if (*irq_status0 & 0x6000000)
+ *irq_status0 &= ~(0x10000000);
msm_camera_io_w(*irq_status0, vfe_dev->vfe_base + 0x30);
msm_camera_io_w(*irq_status1, vfe_dev->vfe_base + 0x34);
msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x24);
+ if (*irq_status0 & 0x10000000) {
+ pr_err_ratelimited("%s: Protection triggered\n", __func__);
+ *irq_status0 &= ~(0x10000000);
+ }
if (*irq_status1 & (1 << 0))
vfe_dev->error_info.camif_status =
@@ -466,6 +535,10 @@
if (vfe_dev->axi_data.stream_update)
msm_isp_axi_stream_update(vfe_dev);
+ if (atomic_read(&vfe_dev->stats_data.stats_update))
+ msm_isp_stats_stream_update(vfe_dev);
+ if (atomic_read(&vfe_dev->axi_data.axi_cfg_update))
+ msm_isp_axi_cfg_update(vfe_dev);
msm_isp_update_framedrop_reg(vfe_dev);
msm_isp_update_error_frame_count(vfe_dev);
@@ -516,26 +589,37 @@
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x40);
comp_mask &= ~(0x7F << (comp_mask_index * 8));
comp_mask |= (axi_data->composite_info[comp_mask_index].
- stream_composite_mask << (comp_mask_index * 8));
+ stream_composite_mask << (comp_mask_index * 8));
+ if (stream_info->plane_cfg[0].plane_addr_offset)
+ comp_mask |= (axi_data->composite_info[comp_mask_index].
+ stream_composite_mask << 24);
msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40);
irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28);
irq_mask |= 1 << (comp_mask_index + 25);
+ if (stream_info->plane_cfg[0].plane_addr_offset && (comp_mask >> 24))
+ irq_mask |= BIT(28);
msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28);
}
static void msm_vfe40_axi_clear_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
uint32_t comp_mask, comp_mask_index = stream_info->comp_mask_index;
uint32_t irq_mask;
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x40);
comp_mask &= ~(0x7F << (comp_mask_index * 8));
+ if (stream_info->plane_cfg[0].plane_addr_offset)
+ comp_mask &= ~(axi_data->composite_info[comp_mask_index].
+ stream_composite_mask << 24);
msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40);
irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28);
irq_mask &= ~(1 << (comp_mask_index + 25));
+ if (stream_info->plane_cfg[0].plane_addr_offset && (comp_mask >> 24))
+ irq_mask &= ~BIT(28);
msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28);
}
@@ -597,11 +681,11 @@
}
static void msm_vfe40_cfg_io_format(struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream_request_cmd *stream_req_cmd)
+ struct msm_vfe_axi_stream *stream_info)
{
int bpp, bpp_reg = 0;
uint32_t io_format_reg;
- bpp = msm_isp_get_bit_per_pixel(stream_req_cmd->output_format);
+ bpp = msm_isp_get_bit_per_pixel(stream_info->output_format);
switch (bpp) {
case 8:
@@ -615,7 +699,7 @@
break;
}
io_format_reg = msm_camera_io_r(vfe_dev->vfe_base + 0x54);
- switch (stream_req_cmd->stream_src) {
+ switch (stream_info->stream_src) {
case CAMIF_RAW:
io_format_reg &= 0xFFFFCFFF;
io_format_reg |= bpp_reg << 12;
@@ -736,43 +820,40 @@
static void msm_vfe40_axi_cfg_wm_reg(
struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ struct msm_vfe_axi_stream *stream_info,
uint8_t plane_idx)
{
uint32_t val;
- struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- struct msm_vfe_axi_stream *stream_info =
- &axi_data->stream_info[
- (stream_cfg_cmd->axi_stream_handle & 0xFF)];
uint32_t wm_base = VFE40_WM_BASE(stream_info->wm[plane_idx]);
if (!stream_info->frame_based) {
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + wm_base);
/*WR_IMAGE_SIZE*/
val =
((msm_isp_cal_word_per_line(
- stream_cfg_cmd->output_format,
- stream_cfg_cmd->plane_cfg[plane_idx].
+ stream_info->output_format,
+ stream_info->plane_cfg[plane_idx].
output_width)+1)/2 - 1) << 16 |
- (stream_cfg_cmd->plane_cfg[plane_idx].
+ (stream_info->plane_cfg[plane_idx].
output_height - 1);
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
/*WR_BUFFER_CFG*/
val =
- msm_isp_cal_word_per_line(stream_cfg_cmd->output_format,
- stream_cfg_cmd->plane_cfg[
+ msm_isp_cal_word_per_line(stream_info->output_format,
+ stream_info->plane_cfg[
plane_idx].output_stride) << 16 |
- (stream_cfg_cmd->plane_cfg[
+ (stream_info->plane_cfg[
plane_idx].output_height - 1) << 4 |
VFE40_BURST_LEN;
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x18);
} else {
msm_camera_io_w(0x2, vfe_dev->vfe_base + wm_base);
val =
- msm_isp_cal_word_per_line(stream_cfg_cmd->output_format,
- stream_cfg_cmd->plane_cfg[
+ msm_isp_cal_word_per_line(stream_info->output_format,
+ stream_info->plane_cfg[
plane_idx].output_width) << 16 |
- (stream_cfg_cmd->plane_cfg[
+ (stream_info->plane_cfg[
plane_idx].output_height - 1) << 4 |
VFE40_BURST_LEN;
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x18);
@@ -804,20 +885,16 @@
static void msm_vfe40_axi_cfg_wm_xbar_reg(
struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ struct msm_vfe_axi_stream *stream_info,
uint8_t plane_idx)
{
- struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- struct msm_vfe_axi_stream *stream_info =
- &axi_data->stream_info[
- (stream_cfg_cmd->axi_stream_handle & 0xFF)];
struct msm_vfe_axi_plane_cfg *plane_cfg =
- &stream_cfg_cmd->plane_cfg[plane_idx];
+ &stream_info->plane_cfg[plane_idx];
uint8_t wm = stream_info->wm[plane_idx];
uint32_t xbar_cfg = 0;
uint32_t xbar_reg_cfg = 0;
- switch (stream_cfg_cmd->stream_src) {
+ switch (stream_info->stream_src) {
case PIX_ENCODER:
case PIX_VIEWFINDER: {
if (plane_cfg->output_plane_format != CRCB_PLANE &&
@@ -825,15 +902,16 @@
/*SINGLE_STREAM_SEL*/
xbar_cfg |= plane_cfg->output_plane_format << 8;
} else {
- switch (stream_cfg_cmd->output_format) {
+ switch (stream_info->output_format) {
case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV14:
case V4L2_PIX_FMT_NV16:
xbar_cfg |= 0x3 << 4; /*PAIR_STREAM_SWAP_CTRL*/
break;
}
xbar_cfg |= 0x1 << 1; /*PAIR_STREAM_EN*/
}
- if (stream_cfg_cmd->stream_src == PIX_VIEWFINDER)
+ if (stream_info->stream_src == PIX_VIEWFINDER)
xbar_cfg |= 0x1; /*VIEW_STREAM_EN*/
break;
}
@@ -1003,12 +1081,16 @@
}
}
-static void msm_vfe40_stats_cfg_comp_mask(struct vfe_device *vfe_dev)
+static void msm_vfe40_stats_cfg_comp_mask(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable)
{
- if (vfe_dev->stats_data.stats_pipeline_policy == STATS_COMP_ALL)
- msm_camera_io_w(0x00FF0000, vfe_dev->vfe_base + 0x44);
+ uint32_t comp_mask;
+ comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x44) >> 16;
+ if (enable)
+ comp_mask |= stats_mask;
else
- msm_camera_io_w(0x00000000, vfe_dev->vfe_base + 0x44);
+ comp_mask &= ~stats_mask;
+ msm_camera_io_w(comp_mask << 16, vfe_dev->vfe_base + 0x44);
}
static void msm_vfe40_stats_cfg_wm_irq_mask(
@@ -1039,9 +1121,10 @@
uint32_t stats_base = VFE40_STATS_BASE(stats_idx);
/*WR_ADDR_CFG*/
- msm_camera_io_w(0x7C, vfe_dev->vfe_base + stats_base + 0x8);
+ msm_camera_io_w(stream_info->framedrop_period << 2,
+ vfe_dev->vfe_base + stats_base + 0x8);
/*WR_IRQ_FRAMEDROP_PATTERN*/
- msm_camera_io_w(0xFFFFFFFF,
+ msm_camera_io_w(stream_info->framedrop_pattern,
vfe_dev->vfe_base + stats_base + 0x10);
/*WR_IRQ_SUBSAMPLE_PATTERN*/
msm_camera_io_w(0xFFFFFFFF,
@@ -1189,11 +1272,9 @@
goto vfe_no_resource;
}
- if (vfe_dev->pdev->id == 0)
- vfe_dev->iommu_ctx[0] = msm_iommu_get_ctx("vfe0");
- else if (vfe_dev->pdev->id == 1)
- vfe_dev->iommu_ctx[0] = msm_iommu_get_ctx("vfe1");
- if (!vfe_dev->iommu_ctx[0]) {
+ vfe_dev->iommu_ctx[0] = msm_iommu_get_ctx("vfe0");
+ vfe_dev->iommu_ctx[1] = msm_iommu_get_ctx("vfe1");
+ if (!vfe_dev->iommu_ctx[0] || !vfe_dev->iommu_ctx[1]) {
pr_err("%s: cannot get iommu_ctx\n", __func__);
rc = -ENODEV;
goto vfe_no_resource;
@@ -1212,7 +1293,7 @@
static struct msm_vfe_axi_hardware_info msm_vfe40_axi_hw_info = {
.num_wm = 4,
- .num_comp_mask = 4,
+ .num_comp_mask = 3,
.num_rdi = 3,
.num_rdi_master = 3,
.min_wm_ub = 64,
@@ -1245,7 +1326,7 @@
};
struct msm_vfe_hardware_info vfe40_hw_info = {
- .num_iommu_ctx = 1,
+ .num_iommu_ctx = 2,
.vfe_clk_idx = VFE40_CLK_IDX,
.vfe_ops = {
.irq_ops = {
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
index 477985d..d3138ed 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
@@ -43,6 +43,7 @@
memset(&axi_data->stream_info[i], 0,
sizeof(struct msm_vfe_axi_stream));
+ spin_lock_init(&axi_data->stream_info[i].lock);
axi_data->stream_info[i].session_id = stream_cfg_cmd->session_id;
axi_data->stream_info[i].stream_id = stream_cfg_cmd->stream_id;
axi_data->stream_info[i].buf_divert = stream_cfg_cmd->buf_divert;
@@ -96,11 +97,15 @@
case V4L2_PIX_FMT_QGBRG12:
case V4L2_PIX_FMT_QGRBG12:
case V4L2_PIX_FMT_QRGGB12:
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_META:
stream_info->num_planes = 1;
stream_info->format_factor = ISP_Q2;
break;
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV41:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
stream_info->num_planes = 2;
@@ -136,23 +141,24 @@
}
for (i = 0; i < stream_info->num_planes; i++) {
- stream_info->plane_offset[i] =
- stream_cfg_cmd->plane_cfg[i].plane_addr_offset;
+ stream_info->plane_cfg[i] = stream_cfg_cmd->plane_cfg[i];
stream_info->max_width = max(stream_info->max_width,
stream_cfg_cmd->plane_cfg[i].output_width);
}
+ stream_info->output_format = stream_cfg_cmd->output_format;
+ stream_info->runtime_output_format = stream_info->output_format;
stream_info->stream_src = stream_cfg_cmd->stream_src;
stream_info->frame_based = stream_cfg_cmd->frame_base;
return 0;
}
static uint32_t msm_isp_axi_get_plane_size(
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd, int plane_idx)
+ struct msm_vfe_axi_stream *stream_info, int plane_idx)
{
uint32_t size = 0;
- struct msm_vfe_axi_plane_cfg *plane_cfg = stream_cfg_cmd->plane_cfg;
- switch (stream_cfg_cmd->output_format) {
+ struct msm_vfe_axi_plane_cfg *plane_cfg = stream_info->plane_cfg;
+ switch (stream_info->output_format) {
case V4L2_PIX_FMT_SBGGR8:
case V4L2_PIX_FMT_SGBRG8:
case V4L2_PIX_FMT_SGRBG8:
@@ -161,6 +167,8 @@
case V4L2_PIX_FMT_QGBRG8:
case V4L2_PIX_FMT_QGRBG8:
case V4L2_PIX_FMT_QRGGB8:
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_META:
size = plane_cfg[plane_idx].output_height *
plane_cfg[plane_idx].output_width;
break;
@@ -197,6 +205,15 @@
size = plane_cfg[plane_idx].output_height *
plane_cfg[plane_idx].output_width / 2;
break;
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV41:
+ if (plane_cfg[plane_idx].output_plane_format == Y_PLANE)
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ else
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width / 8;
+ break;
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
size = plane_cfg[plane_idx].output_height *
@@ -211,21 +228,17 @@
}
void msm_isp_axi_reserve_wm(struct msm_vfe_axi_shared_data *axi_data,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+ struct msm_vfe_axi_stream *stream_info)
{
int i, j;
- struct msm_vfe_axi_stream *stream_info =
- &axi_data->stream_info[
- HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)];
-
for (i = 0; i < stream_info->num_planes; i++) {
for (j = 0; j < axi_data->hw_info->num_wm; j++) {
if (!axi_data->free_wm[j]) {
axi_data->free_wm[j] =
- stream_cfg_cmd->axi_stream_handle;
+ stream_info->stream_handle;
axi_data->wm_image_size[j] =
msm_isp_axi_get_plane_size(
- stream_cfg_cmd, i);
+ stream_info, i);
axi_data->num_used_wm++;
break;
}
@@ -246,20 +259,17 @@
void msm_isp_axi_reserve_comp_mask(
struct msm_vfe_axi_shared_data *axi_data,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+ struct msm_vfe_axi_stream *stream_info)
{
int i;
uint8_t comp_mask = 0;
- struct msm_vfe_axi_stream *stream_info =
- &axi_data->stream_info[
- HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)];
for (i = 0; i < stream_info->num_planes; i++)
comp_mask |= 1 << stream_info->wm[i];
for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) {
if (!axi_data->composite_info[i].stream_handle) {
axi_data->composite_info[i].stream_handle =
- stream_cfg_cmd->axi_stream_handle;
+ stream_info->stream_handle;
axi_data->composite_info[i].
stream_composite_mask = comp_mask;
axi_data->num_used_composite_mask++;
@@ -285,19 +295,32 @@
struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
{
int rc = 0, i;
+ unsigned long flags;
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
struct msm_vfe_axi_stream *stream_info;
enum msm_vfe_axi_state valid_state =
(stream_cfg_cmd->cmd == START_STREAM) ? INACTIVE : ACTIVE;
-
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
stream_info = &axi_data->stream_info[
HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+ spin_lock_irqsave(&stream_info->lock, flags);
if (stream_info->state != valid_state) {
- pr_err("%s: Invalid stream state\n", __func__);
- rc = -EINVAL;
- break;
+ if ((stream_info->state == PAUSING ||
+ stream_info->state == PAUSED ||
+ stream_info->state == RESUME_PENDING ||
+ stream_info->state == RESUMING) &&
+ stream_cfg_cmd->cmd == STOP_STREAM) {
+ stream_info->state = ACTIVE;
+ } else {
+ pr_err("%s: Invalid stream state: %d\n",
+ __func__, stream_info->state);
+ spin_unlock_irqrestore(
+ &stream_info->lock, flags);
+ rc = -EINVAL;
+ break;
+ }
}
+ spin_unlock_irqrestore(&stream_info->lock, flags);
if (stream_cfg_cmd->cmd == START_STREAM) {
stream_info->bufq_handle =
@@ -384,36 +407,12 @@
break;
}
- sof_event.frame_id = vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ sof_event.frame_id = vfe_dev->axi_data.src_info[frame_src].frame_id;
sof_event.timestamp = ts->event_time;
+ sof_event.mono_timestamp = ts->buf_time;
msm_isp_send_event(vfe_dev, ISP_EVENT_SOF, &sof_event);
}
-uint32_t msm_isp_get_framedrop_period(
- enum msm_vfe_frame_skip_pattern frame_skip_pattern)
-{
- switch (frame_skip_pattern) {
- case NO_SKIP:
- case EVERY_2FRAME:
- case EVERY_3FRAME:
- case EVERY_4FRAME:
- case EVERY_5FRAME:
- case EVERY_6FRAME:
- case EVERY_7FRAME:
- case EVERY_8FRAME:
- return frame_skip_pattern + 1;
- case EVERY_16FRAME:
- return 16;
- break;
- case EVERY_32FRAME:
- return 32;
- break;
- default:
- return 1;
- }
- return 1;
-}
-
void msm_isp_calculate_framedrop(
struct msm_vfe_axi_shared_data *axi_data,
struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
@@ -424,7 +423,10 @@
uint32_t framedrop_period = msm_isp_get_framedrop_period(
stream_cfg_cmd->frame_skip_pattern);
- stream_info->framedrop_pattern = 0x1;
+ if (stream_cfg_cmd->frame_skip_pattern == SKIP_ALL)
+ stream_info->framedrop_pattern = 0x0;
+ else
+ stream_info->framedrop_pattern = 0x1;
stream_info->framedrop_period = framedrop_period - 1;
if (stream_cfg_cmd->init_frame_drop < framedrop_period) {
@@ -493,18 +495,18 @@
stream_info = &vfe_dev->axi_data.
stream_info[HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)];
- msm_isp_axi_reserve_wm(&vfe_dev->axi_data, stream_cfg_cmd);
+ msm_isp_axi_reserve_wm(&vfe_dev->axi_data, stream_info);
if (stream_cfg_cmd->stream_src == CAMIF_RAW ||
stream_cfg_cmd->stream_src == IDEAL_RAW)
vfe_dev->hw_info->vfe_ops.axi_ops.
- cfg_io_format(vfe_dev, stream_cfg_cmd);
+ cfg_io_format(vfe_dev, stream_info);
msm_isp_calculate_framedrop(&vfe_dev->axi_data, stream_cfg_cmd);
if (stream_info->num_planes > 1) {
msm_isp_axi_reserve_comp_mask(
- &vfe_dev->axi_data, stream_cfg_cmd);
+ &vfe_dev->axi_data, stream_info);
vfe_dev->hw_info->vfe_ops.axi_ops.
cfg_comp_mask(vfe_dev, stream_info);
} else {
@@ -514,10 +516,10 @@
for (i = 0; i < stream_info->num_planes; i++) {
vfe_dev->hw_info->vfe_ops.axi_ops.
- cfg_wm_reg(vfe_dev, stream_cfg_cmd, i);
+ cfg_wm_reg(vfe_dev, stream_info, i);
vfe_dev->hw_info->vfe_ops.axi_ops.
- cfg_wm_xbar_reg(vfe_dev, stream_cfg_cmd, i);
+ cfg_wm_xbar_reg(vfe_dev, stream_info, i);
}
return rc;
}
@@ -551,9 +553,9 @@
}
if (stream_info->num_planes > 1) {
- msm_isp_axi_free_comp_mask(&vfe_dev->axi_data, stream_info);
vfe_dev->hw_info->vfe_ops.axi_ops.
- clear_comp_mask(vfe_dev, stream_info);
+ clear_comp_mask(vfe_dev, stream_info);
+ msm_isp_axi_free_comp_mask(&vfe_dev->axi_data, stream_info);
} else {
vfe_dev->hw_info->vfe_ops.axi_ops.
clear_wm_irq_mask(vfe_dev, stream_info);
@@ -577,7 +579,8 @@
if (stream_info->state == INACTIVE)
return;
for (i = 0; i < stream_info->num_planes; i++) {
- if (stream_info->state == START_PENDING)
+ if (stream_info->state == START_PENDING ||
+ stream_info->state == RESUME_PENDING)
vfe_dev->hw_info->vfe_ops.axi_ops.
enable_wm(vfe_dev, stream_info->wm[i], 1);
else
@@ -587,7 +590,7 @@
if (stream_info->state == START_PENDING)
axi_data->num_active_stream++;
- else
+ else if (stream_info->state == STOP_PENDING)
axi_data->num_active_stream--;
}
@@ -611,11 +614,72 @@
ACTIVE : INACTIVE;
}
}
+
+ if (vfe_dev->axi_data.pipeline_update == DISABLE_CAMIF) {
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ enable_module(vfe_dev, 0xFF, 0);
+ vfe_dev->axi_data.pipeline_update = NO_UPDATE;
+ }
+
vfe_dev->axi_data.stream_update--;
if (vfe_dev->axi_data.stream_update == 0)
complete(&vfe_dev->stream_config_complete);
}
+static void msm_isp_reload_ping_pong_offset(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int i, j;
+ uint32_t flag;
+ struct msm_isp_buffer *buf;
+ for (i = 0; i < 2; i++) {
+ buf = stream_info->buf[i];
+ flag = i ? VFE_PONG_FLAG : VFE_PING_FLAG;
+ for (j = 0; j < stream_info->num_planes; j++) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr(
+ vfe_dev, stream_info->wm[j], flag,
+ buf->mapped_info[j].paddr +
+ stream_info->plane_cfg[j].plane_addr_offset);
+ }
+ }
+}
+
+void msm_isp_axi_cfg_update(struct vfe_device *vfe_dev)
+{
+ int i, j;
+ uint32_t update_state;
+ unsigned long flags;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info;
+ for (i = 0; i < MAX_NUM_STREAM; i++) {
+ stream_info = &axi_data->stream_info[i];
+ if (stream_info->stream_type == BURST_STREAM ||
+ stream_info->state == AVALIABLE)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ if (stream_info->state == PAUSING) {
+ /*AXI Stopped, apply update*/
+ stream_info->state = PAUSED;
+ msm_isp_reload_ping_pong_offset(vfe_dev, stream_info);
+ for (j = 0; j < stream_info->num_planes; j++)
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_reg(vfe_dev, stream_info, j);
+ /*Resume AXI*/
+ stream_info->state = RESUME_PENDING;
+ msm_isp_axi_stream_enable_cfg(
+ vfe_dev, &axi_data->stream_info[i]);
+ stream_info->state = RESUMING;
+ } else if (stream_info->state == RESUMING) {
+ stream_info->runtime_output_format =
+ stream_info->output_format;
+ stream_info->state = ACTIVE;
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+
+ update_state = atomic_dec_return(&axi_data->axi_cfg_update);
+}
+
static void msm_isp_cfg_pong_address(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
@@ -625,7 +689,7 @@
vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr(
vfe_dev, stream_info->wm[i],
VFE_PONG_FLAG, buf->mapped_info[i].paddr +
- stream_info->plane_offset[i]);
+ stream_info->plane_cfg[i].plane_addr_offset);
stream_info->buf[1] = buf;
}
@@ -672,7 +736,7 @@
vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr(
vfe_dev, stream_info->wm[i],
pingpong_status, buf->mapped_info[i].paddr +
- stream_info->plane_offset[i]);
+ stream_info->plane_cfg[i].plane_addr_offset);
pingpong_bit = (~(pingpong_status >> stream_info->wm[0]) & 0x1);
stream_info->buf[pingpong_bit] = buf;
@@ -712,6 +776,8 @@
buf_event.u.buf_done.handle =
stream_info->bufq_handle;
buf_event.u.buf_done.buf_idx = buf->buf_idx;
+ buf_event.u.buf_done.output_format =
+ stream_info->runtime_output_format;
msm_isp_send_event(vfe_dev,
ISP_EVENT_BUF_DIVERT + stream_idx,
&buf_event);
@@ -719,40 +785,66 @@
} else {
vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr,
buf->bufq_handle, buf->buf_idx,
- &ts->buf_time, frame_id);
+ &ts->buf_time, frame_id,
+ stream_info->runtime_output_format);
}
}
}
-enum msm_isp_camif_update_state
- msm_isp_update_camif_output_count(
- struct vfe_device *vfe_dev,
+static enum msm_isp_camif_update_state
+ msm_isp_get_camif_update_state(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
{
int i;
struct msm_vfe_axi_stream *stream_info;
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- uint8_t cur_pix_count = axi_data->src_info[VFE_PIX_0].
- pix_stream_count;
- uint8_t cur_raw_count = axi_data->src_info[VFE_PIX_0].
- raw_stream_count;
- uint8_t pix_stream_cnt = 0;
+ uint8_t pix_stream_cnt = 0, cur_pix_stream_cnt;
+ cur_pix_stream_cnt =
+ axi_data->src_info[VFE_PIX_0].pix_stream_count +
+ axi_data->src_info[VFE_PIX_0].raw_stream_count;
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
stream_info =
&axi_data->stream_info[
HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
if (stream_info->stream_src < RDI_INTF_0)
pix_stream_cnt++;
+ }
+
+ if (pix_stream_cnt) {
+ if (cur_pix_stream_cnt == 0 && pix_stream_cnt &&
+ stream_cfg_cmd->cmd == START_STREAM)
+ return ENABLE_CAMIF;
+ else if (cur_pix_stream_cnt &&
+ (cur_pix_stream_cnt - pix_stream_cnt) == 0 &&
+ stream_cfg_cmd->cmd == STOP_STREAM)
+ return DISABLE_CAMIF;
+ }
+ return NO_UPDATE;
+}
+
+static void msm_isp_update_camif_output_count(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ stream_info =
+ &axi_data->stream_info[
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+ if (stream_info->stream_src >= RDI_INTF_0)
+ continue;
if (stream_info->stream_src == PIX_ENCODER ||
- stream_info->stream_src == PIX_VIEWFINDER) {
+ stream_info->stream_src == PIX_VIEWFINDER ||
+ stream_info->stream_src == IDEAL_RAW) {
if (stream_cfg_cmd->cmd == START_STREAM)
vfe_dev->axi_data.src_info[VFE_PIX_0].
pix_stream_count++;
else
vfe_dev->axi_data.src_info[VFE_PIX_0].
pix_stream_count--;
- } else if (stream_info->stream_src == CAMIF_RAW ||
- stream_info->stream_src == IDEAL_RAW) {
+ } else if (stream_info->stream_src == CAMIF_RAW) {
if (stream_cfg_cmd->cmd == START_STREAM)
vfe_dev->axi_data.src_info[VFE_PIX_0].
raw_stream_count++;
@@ -761,25 +853,6 @@
raw_stream_count--;
}
}
- if (pix_stream_cnt) {
- if ((cur_pix_count + cur_raw_count == 0) &&
- (axi_data->src_info[VFE_PIX_0].
- pix_stream_count +
- axi_data->src_info[VFE_PIX_0].
- raw_stream_count != 0)) {
- return ENABLE_CAMIF;
- }
-
- if ((cur_pix_count + cur_raw_count != 0) &&
- (axi_data->src_info[VFE_PIX_0].
- pix_stream_count +
- axi_data->src_info[VFE_PIX_0].
- raw_stream_count == 0)) {
- return DISABLE_CAMIF;
- }
- }
-
- return NO_UPDATE;
}
void msm_camera_io_dump_2(void __iomem *addr, int size)
@@ -849,17 +922,19 @@
return rc;
}
-static int msm_isp_axi_wait_for_cfg_done(struct vfe_device *vfe_dev)
+static int msm_isp_axi_wait_for_cfg_done(struct vfe_device *vfe_dev,
+ enum msm_isp_camif_update_state camif_update)
{
int rc;
unsigned long flags;
spin_lock_irqsave(&vfe_dev->shared_data_lock, flags);
init_completion(&vfe_dev->stream_config_complete);
+ vfe_dev->axi_data.pipeline_update = camif_update;
vfe_dev->axi_data.stream_update = 2;
spin_unlock_irqrestore(&vfe_dev->shared_data_lock, flags);
rc = wait_for_completion_interruptible_timeout(
&vfe_dev->stream_config_complete,
- msecs_to_jiffies(500));
+ msecs_to_jiffies(VFE_MAX_CFG_TIMEOUT));
if (rc == 0) {
pr_err("%s: wait timeout\n", __func__);
rc = -1;
@@ -902,6 +977,20 @@
return rc;
}
+static void msm_isp_deinit_stream_ping_pong_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int i;
+ for (i = 0; i < 2; i++) {
+ struct msm_isp_buffer *buf;
+ buf = stream_info->buf[i];
+ if (buf)
+ vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ }
+}
+
static void msm_isp_get_stream_wm_mask(
struct msm_vfe_axi_stream *stream_info,
uint32_t *wm_reload_mask)
@@ -953,12 +1042,14 @@
vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev, wm_reload_mask);
vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev);
- if (camif_update == ENABLE_CAMIF)
+ msm_isp_update_camif_output_count(vfe_dev, stream_cfg_cmd);
+ if (camif_update == ENABLE_CAMIF) {
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id = 0;
vfe_dev->hw_info->vfe_ops.core_ops.
update_camif_state(vfe_dev, camif_update);
-
+ }
if (wait_for_complete)
- rc = msm_isp_axi_wait_for_cfg_done(vfe_dev);
+ rc = msm_isp_axi_wait_for_cfg_done(vfe_dev, camif_update);
return rc;
}
@@ -968,23 +1059,45 @@
enum msm_isp_camif_update_state camif_update)
{
int i, rc = 0;
+ uint8_t wait_for_complete = 0;
struct msm_vfe_axi_stream *stream_info;
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
stream_info = &axi_data->stream_info[
HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+
stream_info->state = STOP_PENDING;
+ if (stream_info->stream_type == BURST_STREAM &&
+ stream_info->runtime_num_burst_capture == 0) {
+ /*Configure AXI writemasters to stop immediately
+ *since for burst case, write masters already skip
+ *all frames.
+ */
+ msm_isp_axi_stream_enable_cfg(vfe_dev, stream_info);
+ stream_info->state = INACTIVE;
+ } else {
+ wait_for_complete = 1;
+ }
}
- rc = msm_isp_axi_wait_for_cfg_done(vfe_dev);
- if (rc < 0) {
- pr_err("%s: wait for config done failed\n", __func__);
- return rc;
+ if (wait_for_complete) {
+ rc = msm_isp_axi_wait_for_cfg_done(vfe_dev, camif_update);
+ if (rc < 0) {
+ pr_err("%s: wait for config done failed\n", __func__);
+ return rc;
+ }
}
msm_isp_update_stream_bandwidth(vfe_dev);
if (camif_update == DISABLE_CAMIF)
vfe_dev->hw_info->vfe_ops.core_ops.
update_camif_state(vfe_dev, DISABLE_CAMIF);
+ msm_isp_update_camif_output_count(vfe_dev, stream_cfg_cmd);
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+ msm_isp_deinit_stream_ping_pong_reg(vfe_dev, stream_info);
+ }
return rc;
}
@@ -1006,8 +1119,7 @@
/*Configure UB*/
vfe_dev->hw_info->vfe_ops.axi_ops.cfg_ub(vfe_dev);
}
- camif_update =
- msm_isp_update_camif_output_count(vfe_dev, stream_cfg_cmd);
+ camif_update = msm_isp_get_camif_update_state(vfe_dev, stream_cfg_cmd);
if (stream_cfg_cmd->cmd == START_STREAM)
rc = msm_isp_start_axi_stream(
@@ -1023,40 +1135,87 @@
int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
{
- int rc = 0;
+ int rc = 0, i, j;
struct msm_vfe_axi_stream *stream_info;
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
struct msm_vfe_axi_stream_update_cmd *update_cmd = arg;
- stream_info = &axi_data->stream_info[
- HANDLE_TO_IDX(update_cmd->stream_handle)];
- if (stream_info->state != ACTIVE && stream_info->state != INACTIVE) {
- pr_err("%s: Invalid stream state\n", __func__);
- return -EINVAL;
+ struct msm_vfe_axi_stream_cfg_update_info *update_info;
+
+ if (update_cmd->update_type == UPDATE_STREAM_AXI_CONFIG &&
+ atomic_read(&axi_data->axi_cfg_update)) {
+ pr_err("%s: AXI stream config updating\n", __func__);
+ return -EBUSY;
}
- switch (update_cmd->update_type) {
- case ENABLE_STREAM_BUF_DIVERT:
- stream_info->buf_divert = 1;
- break;
- case DISABLE_STREAM_BUF_DIVERT:
- stream_info->buf_divert = 0;
- vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr,
- stream_info->bufq_handle,
- MSM_ISP_BUFFER_FLUSH_DIVERTED);
- break;
- case UPDATE_STREAM_FRAMEDROP_PATTERN: {
- uint32_t framedrop_period =
- msm_isp_get_framedrop_period(update_cmd->skip_pattern);
- stream_info->runtime_init_frame_drop = 0;
- stream_info->framedrop_pattern = 0x1;
- stream_info->framedrop_period = framedrop_period - 1;
- vfe_dev->hw_info->vfe_ops.axi_ops.
- cfg_framedrop(vfe_dev, stream_info);
- break;
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info = &update_cmd->update_info[i];
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(update_info->stream_handle)];
+ if (stream_info->state != ACTIVE &&
+ stream_info->state != INACTIVE) {
+ pr_err("%s: Invalid stream state\n", __func__);
+ return -EINVAL;
+ }
+ if (stream_info->state == ACTIVE &&
+ stream_info->stream_type == BURST_STREAM) {
+ pr_err("%s: Cannot update active burst stream\n",
+ __func__);
+ return -EINVAL;
+ }
}
- default:
- pr_err("%s: Invalid update type\n", __func__);
- return -EINVAL;
+
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info = &update_cmd->update_info[i];
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(update_info->stream_handle)];
+
+ switch (update_cmd->update_type) {
+ case ENABLE_STREAM_BUF_DIVERT:
+ stream_info->buf_divert = 1;
+ break;
+ case DISABLE_STREAM_BUF_DIVERT:
+ stream_info->buf_divert = 0;
+ vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr,
+ stream_info->bufq_handle,
+ MSM_ISP_BUFFER_FLUSH_DIVERTED);
+ break;
+ case UPDATE_STREAM_FRAMEDROP_PATTERN: {
+ uint32_t framedrop_period =
+ msm_isp_get_framedrop_period(
+ update_info->skip_pattern);
+ stream_info->runtime_init_frame_drop = 0;
+ stream_info->framedrop_pattern = 0x1;
+ stream_info->framedrop_period = framedrop_period - 1;
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_framedrop(vfe_dev, stream_info);
+ break;
+ }
+ case UPDATE_STREAM_AXI_CONFIG: {
+ for (j = 0; j < stream_info->num_planes; j++) {
+ stream_info->plane_cfg[j] =
+ update_info->plane_cfg[j];
+ }
+ stream_info->output_format = update_info->output_format;
+ if (stream_info->state == ACTIVE) {
+ stream_info->state = PAUSE_PENDING;
+ msm_isp_axi_stream_enable_cfg(
+ vfe_dev, stream_info);
+ stream_info->state = PAUSING;
+ atomic_set(&axi_data->axi_cfg_update,
+ UPDATE_REQUESTED);
+ } else {
+ for (j = 0; j < stream_info->num_planes; j++)
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_reg(vfe_dev, stream_info, j);
+ stream_info->runtime_output_format =
+ stream_info->output_format;
+ }
+ break;
+ }
+ default:
+ pr_err("%s: Invalid update type\n", __func__);
+ return -EINVAL;
+ }
}
return rc;
}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
index f592a60..7d282bd 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
@@ -27,15 +27,11 @@
void msm_isp_axi_reserve_wm(
struct msm_vfe_axi_shared_data *axi_data,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
-
-void msm_isp_axi_reserve_rdi(
- struct msm_vfe_axi_shared_data *axi_data,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
+ struct msm_vfe_axi_stream *stream_info);
void msm_isp_axi_reserve_comp_mask(
struct msm_vfe_axi_shared_data *axi_data,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
+ struct msm_vfe_axi_stream *stream_info);
int msm_isp_axi_check_stream_state(
struct vfe_device *vfe_dev,
@@ -45,6 +41,7 @@
int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg);
int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg);
int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg);
+void msm_isp_axi_cfg_update(struct vfe_device *vfe_dev);
void msm_isp_axi_stream_update(struct vfe_device *vfe_dev);
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
index c47209f..d857a14 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*/
#include <linux/io.h>
+#include <linux/atomic.h>
#include <media/v4l2-subdev.h>
#include "msm_isp_util.h"
#include "msm_isp_stats_util.h"
@@ -67,6 +68,7 @@
struct msm_isp_buffer *done_buf;
struct msm_vfe_stats_stream *stream_info = NULL;
uint32_t pingpong_status;
+ uint32_t comp_stats_type_mask = 0;
uint32_t stats_comp_mask = 0, stats_irq_mask = 0;
stats_comp_mask = vfe_dev->hw_info->vfe_ops.stats_ops.
get_comp_mask(irq_status0, irq_status1);
@@ -76,13 +78,17 @@
return;
ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0);
- if (vfe_dev->stats_data.stats_pipeline_policy == STATS_COMP_ALL) {
- if (!stats_comp_mask)
- return;
- stats_irq_mask = 0xFFFFFFFF;
- }
+ if (!stats_comp_mask)
+ stats_irq_mask &=
+ ~atomic_read(&vfe_dev->stats_data.stats_comp_mask);
+ else
+ stats_irq_mask |=
+ atomic_read(&vfe_dev->stats_data.stats_comp_mask);
memset(&buf_event, 0, sizeof(struct msm_isp_event_data));
+ buf_event.timestamp = ts->event_time;
+ buf_event.frame_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
pingpong_status = vfe_dev->hw_info->
vfe_ops.stats_ops.get_pingpong_status(vfe_dev);
@@ -98,22 +104,32 @@
done_buf->bufq_handle, done_buf->buf_idx,
&ts->buf_time, vfe_dev->axi_data.
src_info[VFE_PIX_0].frame_id);
- if (rc == 0) {
- stats_event->stats_mask |=
+ if (rc != 0)
+ continue;
+
+ stats_event->stats_buf_idxs[stream_info->stats_type] =
+ done_buf->buf_idx;
+ if (!stream_info->composite_flag) {
+ stats_event->stats_mask =
1 << stream_info->stats_type;
- stats_event->stats_buf_idxs[
- stream_info->stats_type] =
- done_buf->buf_idx;
+ ISP_DBG("%s: stats event frame id: 0x%x\n",
+ __func__, buf_event.frame_id);
+ msm_isp_send_event(vfe_dev,
+ ISP_EVENT_STATS_NOTIFY +
+ stream_info->stats_type, &buf_event);
+ } else {
+ comp_stats_type_mask |=
+ 1 << stream_info->stats_type;
}
}
}
- if (stats_event->stats_mask) {
- buf_event.timestamp = ts->event_time;
- buf_event.frame_id =
- vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
- msm_isp_send_event(vfe_dev, ISP_EVENT_STATS_NOTIFY +
- stream_info->stats_type, &buf_event);
+ if (comp_stats_type_mask) {
+ ISP_DBG("%s: composite stats event frame id: 0x%x mask: 0x%x\n",
+ __func__, buf_event.frame_id, comp_stats_type_mask);
+ stats_event->stats_mask = comp_stats_type_mask;
+ msm_isp_send_event(vfe_dev,
+ ISP_EVENT_COMP_STATS_NOTIFY, &buf_event);
}
}
@@ -140,36 +156,19 @@
return rc;
}
- if (stats_data->stats_pipeline_policy != STATS_COMP_ALL) {
- if (stream_req_cmd->framedrop_pattern >= MAX_SKIP) {
- pr_err("%s: Invalid framedrop pattern\n", __func__);
- return rc;
- }
+ if (stream_req_cmd->framedrop_pattern >= MAX_SKIP) {
+ pr_err("%s: Invalid framedrop pattern\n", __func__);
+ return rc;
+ }
- if (stream_req_cmd->irq_subsample_pattern >= MAX_SKIP) {
- pr_err("%s: Invalid irq subsample pattern\n", __func__);
- return rc;
- }
- } else {
- if (stats_data->comp_framedrop_pattern >= MAX_SKIP) {
- pr_err("%s: Invalid comp framedrop pattern\n",
- __func__);
- return rc;
- }
-
- if (stats_data->comp_irq_subsample_pattern >= MAX_SKIP) {
- pr_err("%s: Invalid comp irq subsample pattern\n",
- __func__);
- return rc;
- }
- stream_req_cmd->framedrop_pattern =
- vfe_dev->stats_data.comp_framedrop_pattern;
- stream_req_cmd->irq_subsample_pattern =
- vfe_dev->stats_data.comp_irq_subsample_pattern;
+ if (stream_req_cmd->irq_subsample_pattern >= MAX_SKIP) {
+ pr_err("%s: Invalid irq subsample pattern\n", __func__);
+ return rc;
}
stream_info->session_id = stream_req_cmd->session_id;
stream_info->stream_id = stream_req_cmd->stream_id;
+ stream_info->composite_flag = stream_req_cmd->composite_flag;
stream_info->stats_type = stream_req_cmd->stats_type;
stream_info->buffer_offset = stream_req_cmd->buffer_offset;
stream_info->framedrop_pattern = stream_req_cmd->framedrop_pattern;
@@ -193,6 +192,7 @@
struct msm_vfe_stats_stream_request_cmd *stream_req_cmd = arg;
struct msm_vfe_stats_stream *stream_info = NULL;
struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ uint32_t framedrop_period;
uint32_t stats_idx;
rc = msm_isp_stats_create_stream(vfe_dev, stream_req_cmd);
@@ -204,31 +204,16 @@
stats_idx = STATS_IDX(stream_req_cmd->stream_handle);
stream_info = &stats_data->stream_info[stats_idx];
- switch (stream_info->framedrop_pattern) {
- case NO_SKIP:
- stream_info->framedrop_pattern = VFE_NO_DROP;
- break;
- case EVERY_2FRAME:
- stream_info->framedrop_pattern = VFE_DROP_EVERY_2FRAME;
- break;
- case EVERY_4FRAME:
- stream_info->framedrop_pattern = VFE_DROP_EVERY_4FRAME;
- break;
- case EVERY_8FRAME:
- stream_info->framedrop_pattern = VFE_DROP_EVERY_8FRAME;
- break;
- case EVERY_16FRAME:
- stream_info->framedrop_pattern = VFE_DROP_EVERY_16FRAME;
- break;
- case EVERY_32FRAME:
- stream_info->framedrop_pattern = VFE_DROP_EVERY_32FRAME;
- break;
- default:
- stream_info->framedrop_pattern = VFE_NO_DROP;
- break;
- }
+ framedrop_period = msm_isp_get_framedrop_period(
+ stream_req_cmd->framedrop_pattern);
- if (stats_data->stats_pipeline_policy == STATS_COMP_NONE)
+ if (stream_req_cmd->framedrop_pattern == SKIP_ALL)
+ stream_info->framedrop_pattern = 0x0;
+ else
+ stream_info->framedrop_pattern = 0x1;
+ stream_info->framedrop_period = framedrop_period - 1;
+
+ if (!stream_info->composite_flag)
vfe_dev->hw_info->vfe_ops.stats_ops.
cfg_wm_irq_mask(vfe_dev, stream_info);
@@ -257,7 +242,7 @@
rc = msm_isp_cfg_stats_stream(vfe_dev, &stream_cfg_cmd);
}
- if (stats_data->stats_pipeline_policy == STATS_COMP_NONE)
+ if (!stream_info->composite_flag)
vfe_dev->hw_info->vfe_ops.stats_ops.
clear_wm_irq_mask(vfe_dev, stream_info);
@@ -266,18 +251,159 @@
return 0;
}
-int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg)
+static int msm_isp_init_stats_ping_pong_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int rc = 0;
+ stream_info->bufq_handle =
+ vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr, stream_info->session_id,
+ stream_info->stream_id);
+ if (stream_info->bufq_handle == 0) {
+ pr_err("%s: no buf configured for stream: 0x%x\n",
+ __func__, stream_info->stream_handle);
+ return -EINVAL;
+ }
+
+ rc = msm_isp_stats_cfg_ping_pong_address(vfe_dev,
+ stream_info, VFE_PING_FLAG, NULL);
+ if (rc < 0) {
+ pr_err("%s: No free buffer for ping\n", __func__);
+ return rc;
+ }
+ rc = msm_isp_stats_cfg_ping_pong_address(vfe_dev,
+ stream_info, VFE_PONG_FLAG, NULL);
+ if (rc < 0) {
+ pr_err("%s: No free buffer for pong\n", __func__);
+ return rc;
+ }
+ return rc;
+}
+
+static void msm_isp_deinit_stats_ping_pong_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int i;
+ struct msm_isp_buffer *buf;
+ for (i = 0; i < 2; i++) {
+ buf = stream_info->buf[i];
+ if (buf)
+ vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ }
+}
+
+void msm_isp_stats_stream_update(struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t stats_mask = 0, comp_stats_mask = 0;
+ uint32_t enable = 0;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) {
+ if (stats_data->stream_info[i].state == STATS_START_PENDING ||
+ stats_data->stream_info[i].state ==
+ STATS_STOP_PENDING) {
+ stats_mask |= i;
+ enable = stats_data->stream_info[i].state ==
+ STATS_START_PENDING ? 1 : 0;
+ stats_data->stream_info[i].state =
+ stats_data->stream_info[i].state ==
+ STATS_START_PENDING ?
+ STATS_STARTING : STATS_STOPPING;
+ vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
+ vfe_dev, BIT(i), enable);
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask(
+ vfe_dev, BIT(i), enable);
+ } else if (stats_data->stream_info[i].state == STATS_STARTING ||
+ stats_data->stream_info[i].state == STATS_STOPPING) {
+ if (stats_data->stream_info[i].composite_flag)
+ comp_stats_mask |= i;
+ if (stats_data->stream_info[i].state == STATS_STARTING)
+ atomic_add(BIT(i),
+ &stats_data->stats_comp_mask);
+ else
+ atomic_sub(BIT(i),
+ &stats_data->stats_comp_mask);
+ stats_data->stream_info[i].state =
+ stats_data->stream_info[i].state ==
+ STATS_STARTING ? STATS_ACTIVE : STATS_INACTIVE;
+ }
+ }
+ atomic_sub(1, &stats_data->stats_update);
+ if (!atomic_read(&stats_data->stats_update))
+ complete(&vfe_dev->stats_config_complete);
+}
+
+static int msm_isp_stats_wait_for_cfg_done(struct vfe_device *vfe_dev)
+{
+ int rc;
+ init_completion(&vfe_dev->stats_config_complete);
+ atomic_set(&vfe_dev->stats_data.stats_update, 2);
+ rc = wait_for_completion_interruptible_timeout(
+ &vfe_dev->stats_config_complete,
+ msecs_to_jiffies(VFE_MAX_CFG_TIMEOUT));
+ if (rc == 0) {
+ pr_err("%s: wait timeout\n", __func__);
+ rc = -1;
+ } else {
+ rc = 0;
+ }
+ return rc;
+}
+
+static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
{
int i, rc = 0;
- struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd = arg;
+ uint32_t stats_mask = 0, comp_stats_mask = 0, idx;
struct msm_vfe_stats_stream *stream_info;
struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
- int idx;
- uint32_t stats_mask = 0;
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+ stream_info = &stats_data->stream_info[idx];
+ if (stream_info->stream_handle !=
+ stream_cfg_cmd->stream_handle[i]) {
+ pr_err("%s: Invalid stream handle: 0x%x received\n",
+ __func__, stream_cfg_cmd->stream_handle[i]);
+ continue;
+ }
+ rc = msm_isp_init_stats_ping_pong_reg(vfe_dev, stream_info);
+ if (rc < 0) {
+ pr_err("%s: No buffer for stream%d\n", __func__, idx);
+ return rc;
+ }
- if (stats_data->num_active_stream == 0)
- vfe_dev->hw_info->vfe_ops.stats_ops.cfg_ub(vfe_dev);
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active)
+ stream_info->state = STATS_START_PENDING;
+ else
+ stream_info->state = STATS_ACTIVE;
+ stats_data->num_active_stream++;
+ stats_mask |= 1 << idx;
+ if (stream_info->composite_flag)
+ comp_stats_mask |= 1 << idx;
+ }
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) {
+ rc = msm_isp_stats_wait_for_cfg_done(vfe_dev);
+ } else {
+ vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
+ vfe_dev, stats_mask, stream_cfg_cmd->enable);
+ atomic_add(comp_stats_mask, &stats_data->stats_comp_mask);
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask(
+ vfe_dev, comp_stats_mask, 1);
+ }
+ return rc;
+}
+
+static int msm_isp_stop_stats_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i, rc = 0;
+ uint32_t stats_mask = 0, comp_stats_mask = 0, idx;
+ struct msm_vfe_stats_stream *stream_info;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
stream_info = &stats_data->stream_info[idx];
@@ -288,71 +414,45 @@
continue;
}
- if (stream_cfg_cmd->enable) {
- stream_info->bufq_handle =
- vfe_dev->buf_mgr->ops->get_bufq_handle(
- vfe_dev->buf_mgr, stream_info->session_id,
- stream_info->stream_id);
- if (stream_info->bufq_handle == 0) {
- pr_err("%s: no buf configured for stream: 0x%x\n",
- __func__,
- stream_info->stream_handle);
- return -EINVAL;
- }
-
- msm_isp_stats_cfg_ping_pong_address(vfe_dev,
- stream_info, VFE_PING_FLAG, NULL);
- msm_isp_stats_cfg_ping_pong_address(vfe_dev,
- stream_info, VFE_PONG_FLAG, NULL);
- stream_info->state = STATS_START_PENDING;
- stats_data->num_active_stream++;
- } else {
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active)
stream_info->state = STATS_STOP_PENDING;
- stats_data->num_active_stream--;
- }
+ else
+ stream_info->state = STATS_INACTIVE;
+
+ stats_data->num_active_stream--;
stats_mask |= 1 << idx;
+ if (stream_info->composite_flag)
+ comp_stats_mask |= 1 << idx;
}
- vfe_dev->hw_info->vfe_ops.stats_ops.
- enable_module(vfe_dev, stats_mask, stream_cfg_cmd->enable);
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) {
+ rc = msm_isp_stats_wait_for_cfg_done(vfe_dev);
+ } else {
+ vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
+ vfe_dev, stats_mask, stream_cfg_cmd->enable);
+ atomic_sub(comp_stats_mask, &stats_data->stats_comp_mask);
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask(
+ vfe_dev, comp_stats_mask, 0);
+ }
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+ stream_info = &stats_data->stream_info[idx];
+ msm_isp_deinit_stats_ping_pong_reg(vfe_dev, stream_info);
+ }
return rc;
}
-int msm_isp_cfg_stats_comp_policy(struct vfe_device *vfe_dev, void *arg)
+int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg)
{
- int rc = -1;
- struct msm_vfe_stats_comp_policy_cfg *policy_cfg_cmd = arg;
- struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ int rc = 0;
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd = arg;
+ if (vfe_dev->stats_data.num_active_stream == 0)
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_ub(vfe_dev);
- if (stats_data->num_active_stream != 0) {
- pr_err("%s: Cannot update policy when there are active streams\n",
- __func__);
- return rc;
- }
+ if (stream_cfg_cmd->enable)
+ rc = msm_isp_start_stats_stream(vfe_dev, stream_cfg_cmd);
+ else
+ rc = msm_isp_stop_stats_stream(vfe_dev, stream_cfg_cmd);
- if (policy_cfg_cmd->stats_pipeline_policy >= MAX_STATS_POLICY) {
- pr_err("%s: Invalid stats composite policy\n", __func__);
- return rc;
- }
-
- if (policy_cfg_cmd->comp_framedrop_pattern >= MAX_SKIP) {
- pr_err("%s: Invalid comp framedrop pattern\n", __func__);
- return rc;
- }
-
- if (policy_cfg_cmd->comp_irq_subsample_pattern >= MAX_SKIP) {
- pr_err("%s: Invalid comp irq subsample pattern\n", __func__);
- return rc;
- }
-
- stats_data->stats_pipeline_policy =
- policy_cfg_cmd->stats_pipeline_policy;
- stats_data->comp_framedrop_pattern =
- policy_cfg_cmd->comp_framedrop_pattern;
- stats_data->comp_irq_subsample_pattern =
- policy_cfg_cmd->comp_irq_subsample_pattern;
-
- vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask(vfe_dev);
-
- return 0;
+ return rc;
}
-
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h
index 13e1fd6..7b4c4b4 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h
@@ -18,8 +18,8 @@
void msm_isp_process_stats_irq(struct vfe_device *vfe_dev,
uint32_t irq_status0, uint32_t irq_status1,
struct msm_isp_timestamp *ts);
+void msm_isp_stats_stream_update(struct vfe_device *vfe_dev);
int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg);
int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg);
int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg);
-int msm_isp_cfg_stats_comp_policy(struct vfe_device *vfe_dev, void *arg);
#endif /* __MSM_ISP_STATS_UTIL_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
index 2b047ad..908d3c6 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
@@ -12,6 +12,7 @@
#include <linux/mutex.h>
#include <linux/io.h>
#include <media/v4l2-subdev.h>
+#include <linux/ratelimit.h>
#include "msm.h"
#include "msm_isp_util.h"
@@ -154,6 +155,33 @@
mutex_unlock(&bandwidth_mgr_mutex);
}
+uint32_t msm_isp_get_framedrop_period(
+ enum msm_vfe_frame_skip_pattern frame_skip_pattern)
+{
+ switch (frame_skip_pattern) {
+ case NO_SKIP:
+ case EVERY_2FRAME:
+ case EVERY_3FRAME:
+ case EVERY_4FRAME:
+ case EVERY_5FRAME:
+ case EVERY_6FRAME:
+ case EVERY_7FRAME:
+ case EVERY_8FRAME:
+ return frame_skip_pattern + 1;
+ case EVERY_16FRAME:
+ return 16;
+ break;
+ case EVERY_32FRAME:
+ return 32;
+ break;
+ case SKIP_ALL:
+ return 1;
+ default:
+ return 1;
+ }
+ return 1;
+}
+
static inline void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp)
{
struct timespec ts;
@@ -202,12 +230,12 @@
return rc;
}
-static int msm_isp_set_clk_rate(struct vfe_device *vfe_dev, uint32_t rate)
+static int msm_isp_set_clk_rate(struct vfe_device *vfe_dev, long *rate)
{
int rc = 0;
int clk_idx = vfe_dev->hw_info->vfe_clk_idx;
long round_rate =
- clk_round_rate(vfe_dev->vfe_clk[clk_idx], rate);
+ clk_round_rate(vfe_dev->vfe_clk[clk_idx], *rate);
if (round_rate < 0) {
pr_err("%s: Invalid vfe clock rate\n", __func__);
return round_rate;
@@ -218,6 +246,7 @@
pr_err("%s: Vfe set rate error\n", __func__);
return rc;
}
+ *rate = round_rate;
return 0;
}
@@ -238,7 +267,7 @@
input_cfg->d.pix_cfg.camif_cfg.pixels_per_line;
rc = msm_isp_set_clk_rate(vfe_dev,
- vfe_dev->axi_data.src_info[VFE_PIX_0].pixel_clock);
+ &vfe_dev->axi_data.src_info[VFE_PIX_0].pixel_clock);
if (rc < 0) {
pr_err("%s: clock set rate failed\n", __func__);
return rc;
@@ -355,16 +384,16 @@
rc = msm_isp_cfg_stats_stream(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
- case VIDIOC_MSM_ISP_CFG_STATS_COMP_POLICY:
- mutex_lock(&vfe_dev->core_mutex);
- rc = msm_isp_cfg_stats_comp_policy(vfe_dev, arg);
- mutex_unlock(&vfe_dev->core_mutex);
- break;
case VIDIOC_MSM_ISP_UPDATE_STREAM:
mutex_lock(&vfe_dev->core_mutex);
rc = msm_isp_update_axi_stream(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
+ case MSM_SD_SHUTDOWN:
+ while (vfe_dev->vfe_open_cnt != 0)
+ msm_isp_close_node(sd, NULL);
+ break;
+
default:
pr_err("%s: Invalid ISP command\n", __func__);
rc = -EINVAL;
@@ -590,6 +619,8 @@
case V4L2_PIX_FMT_QGBRG8:
case V4L2_PIX_FMT_QGRBG8:
case V4L2_PIX_FMT_QRGGB8:
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_META:
val = CAL_WORD(pixel_per_line, 1, 8);
break;
case V4L2_PIX_FMT_SBGGR10:
@@ -618,6 +649,8 @@
break;
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV41:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
val = CAL_WORD(pixel_per_line, 1, 8);
@@ -641,6 +674,8 @@
case V4L2_PIX_FMT_QGBRG8:
case V4L2_PIX_FMT_QGRBG8:
case V4L2_PIX_FMT_QRGGB8:
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_META:
return 8;
case V4L2_PIX_FMT_SBGGR10:
case V4L2_PIX_FMT_SGBRG10:
@@ -662,6 +697,8 @@
return 12;
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV41:
return 8;
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
@@ -686,6 +723,11 @@
{
int i;
struct msm_vfe_error_info *error_info = &vfe_dev->error_info;
+ static DEFINE_RATELIMIT_STATE(rs,
+ DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
+ static DEFINE_RATELIMIT_STATE(rs_stats,
+ DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
+
if (error_info->error_count == 1 ||
!(error_info->info_dump_frame_count % 100)) {
vfe_dev->hw_info->vfe_ops.core_ops.
@@ -695,7 +737,8 @@
error_info->camif_status = 0;
error_info->violation_status = 0;
for (i = 0; i < MAX_NUM_STREAM; i++) {
- if (error_info->stream_framedrop_count[i] != 0) {
+ if (error_info->stream_framedrop_count[i] != 0 &&
+ __ratelimit(&rs)) {
pr_err("%s: Stream[%d]: dropped %d frames\n",
__func__, i,
error_info->stream_framedrop_count[i]);
@@ -703,7 +746,8 @@
}
}
for (i = 0; i < MSM_ISP_STATS_MAX; i++) {
- if (error_info->stats_framedrop_count[i] != 0) {
+ if (error_info->stats_framedrop_count[i] != 0 &&
+ __ratelimit(&rs_stats)) {
pr_err("%s: Stats stream[%d]: dropped %d frames\n",
__func__, i,
error_info->stats_framedrop_count[i]);
@@ -750,7 +794,8 @@
spin_lock_irqsave(&vfe_dev->tasklet_lock, flags);
queue_cmd = &vfe_dev->tasklet_queue_cmd[vfe_dev->taskletq_idx];
if (queue_cmd->cmd_used) {
- pr_err("%s: Tasklet queue overflow\n", __func__);
+ pr_err_ratelimited("%s: Tasklet queue overflow: %d\n",
+ __func__, vfe_dev->pdev->id);
list_del(&queue_cmd->list);
} else {
atomic_add(1, &vfe_dev->irq_cnt);
@@ -818,7 +863,6 @@
int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
- uint32_t i;
struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
long rc;
ISP_DBG("%s\n", __func__);
@@ -851,9 +895,6 @@
vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev);
- for (i = 0; i < vfe_dev->hw_info->num_iommu_ctx; i++)
- vfe_dev->buf_mgr->ops->attach_ctx(vfe_dev->buf_mgr,
- vfe_dev->iommu_ctx[i]);
vfe_dev->buf_mgr->ops->buf_mgr_init(vfe_dev->buf_mgr, "msm_isp", 28);
memset(&vfe_dev->axi_data, 0, sizeof(struct msm_vfe_axi_shared_data));
@@ -870,7 +911,6 @@
int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
- int i;
long rc;
struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
ISP_DBG("%s\n", __func__);
@@ -885,16 +925,10 @@
rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev);
if (rc <= 0)
- pr_err("%s: halt timeout\n", __func__);
+ pr_err("%s: halt timeout rc=%ld\n", __func__, rc);
vfe_dev->buf_mgr->ops->buf_mgr_deinit(vfe_dev->buf_mgr);
-
- for (i = vfe_dev->hw_info->num_iommu_ctx - 1; i >= 0; i--)
- vfe_dev->buf_mgr->ops->detach_ctx(vfe_dev->buf_mgr,
- vfe_dev->iommu_ctx[i]);
-
vfe_dev->hw_info->vfe_ops.core_ops.release_hw(vfe_dev);
-
vfe_dev->vfe_open_cnt--;
mutex_unlock(&vfe_dev->core_mutex);
mutex_unlock(&vfe_dev->realtime_mutex);
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h
index 7934f26..34b9859 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h
@@ -43,6 +43,9 @@
struct msm_isp_bandwidth_info client_info[MAX_ISP_CLIENT];
};
+uint32_t msm_isp_get_framedrop_period(
+ enum msm_vfe_frame_skip_pattern frame_skip_pattern);
+
int msm_isp_init_bandwidth_mgr(enum msm_isp_hw_client client);
int msm_isp_update_bandwidth(enum msm_isp_hw_client client,
uint64_t ab, uint64_t ib);
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
index 90cc61c..3082e99 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -55,7 +55,7 @@
static inline int msm_ispif_is_intf_valid(uint32_t csid_version,
uint8_t intf_type)
{
- return (csid_version <= CSID_VERSION_V2 && intf_type != VFE0) ?
+ return (csid_version <= CSID_VERSION_V22 && intf_type != VFE0) ?
false : true;
}
@@ -67,7 +67,7 @@
{
int rc = 0;
- if (ispif->csid_version < CSID_VERSION_V3) {
+ if (ispif->csid_version < CSID_VERSION_V30) {
/* Older ISPIF versiond don't need ahb clokc */
return 0;
}
@@ -91,10 +91,10 @@
BUG_ON(!ispif);
memset(ispif->sof_count, 0, sizeof(ispif->sof_count));
-
for (i = 0; i < ispif->vfe_info.num_vfe; i++) {
- msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_CTRL_0(i));
+ msm_camera_io_w(1 << PIX0_LINE_BUF_EN_BIT,
+ ispif->base + ISPIF_VFE_m_CTRL_0(i));
msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_IRQ_MASK_0(i));
msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_IRQ_MASK_1(i));
msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_IRQ_MASK_2(i));
@@ -104,9 +104,13 @@
ISPIF_VFE_m_IRQ_CLEAR_1(i));
msm_camera_io_w(0xFFFFFFFF, ispif->base +
ISPIF_VFE_m_IRQ_CLEAR_2(i));
+
msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_INPUT_SEL(i));
- msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_INTF_CMD_0(i));
- msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_INTF_CMD_1(i));
+
+ msm_camera_io_w(ISPIF_STOP_INTF_IMMEDIATELY,
+ ispif->base + ISPIF_VFE_m_INTF_CMD_0(i));
+ msm_camera_io_w(ISPIF_STOP_INTF_IMMEDIATELY,
+ ispif->base + ISPIF_VFE_m_INTF_CMD_1(i));
msm_camera_io_w(0, ispif->base +
ISPIF_VFE_m_PIX_INTF_n_CID_MASK(i, 0));
@@ -175,9 +179,9 @@
data |= (csid << 20);
break;
}
- if (data)
- msm_camera_io_w_mb(data, ispif->base +
- ISPIF_VFE_m_INPUT_SEL(vfe_intf));
+
+ msm_camera_io_w_mb(data, ispif->base +
+ ISPIF_VFE_m_INPUT_SEL(vfe_intf));
}
static void msm_ispif_enable_crop(struct ispif_device *ispif,
@@ -294,6 +298,58 @@
return rc;
}
+static void msm_ispif_select_clk_mux(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t csid, uint8_t vfe_intf)
+{
+ uint32_t data = 0;
+
+ switch (intftype) {
+ case PIX0:
+ data = msm_camera_io_r(ispif->clk_mux_base);
+ data &= ~(0xf << (vfe_intf * 8));
+ data |= (csid << (vfe_intf * 8));
+ msm_camera_io_w(data, ispif->clk_mux_base);
+ break;
+
+ case RDI0:
+ data = msm_camera_io_r(ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ data &= ~(0xf << (vfe_intf * 12));
+ data |= (csid << (vfe_intf * 12));
+ msm_camera_io_w(data, ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ break;
+
+ case PIX1:
+ data = msm_camera_io_r(ispif->clk_mux_base);
+ data &= ~(0xf0 << (vfe_intf * 8));
+ data |= (csid << (4 + (vfe_intf * 8)));
+ msm_camera_io_w(data, ispif->clk_mux_base);
+ break;
+
+ case RDI1:
+ data = msm_camera_io_r(ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ data &= ~(0xf << (4 + (vfe_intf * 12)));
+ data |= (csid << (4 + (vfe_intf * 12)));
+ msm_camera_io_w(data, ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ break;
+
+ case RDI2:
+ data = msm_camera_io_r(ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ data &= ~(0xf << (8 + (vfe_intf * 12)));
+ data |= (csid << (8 + (vfe_intf * 12)));
+ msm_camera_io_w(data, ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ break;
+ }
+ CDBG("%s intftype %d data %x\n", __func__, intftype, data);
+ mb();
+ return;
+}
+
static uint16_t msm_ispif_get_cids_mask_from_cfg(
struct msm_ispif_params_entry *entry)
{
@@ -351,13 +407,17 @@
if ((intftype >= INTF_MAX) ||
(vfe_intf >= ispif->vfe_info.num_vfe) ||
- (ispif->csid_version <= CSID_VERSION_V2 &&
+ (ispif->csid_version <= CSID_VERSION_V22 &&
(vfe_intf > VFE0))) {
pr_err("%s: VFEID %d and CSID version %d mismatch\n",
__func__, vfe_intf, ispif->csid_version);
return -EINVAL;
}
+ if (ispif->csid_version >= CSID_VERSION_V30)
+ msm_ispif_select_clk_mux(ispif, intftype,
+ params->entries[i].csid, vfe_intf);
+
rc = msm_ispif_validate_intf_status(ispif, intftype, vfe_intf);
if (rc) {
pr_err("%s:validate_intf_status failed, rc = %d\n",
@@ -428,7 +488,7 @@
vfe_intf = params->entries[i].vfe_intf;
for (k = 0; k < params->entries[i].num_cids; k++) {
cid = params->entries[i].cids[k];
- vc = cid % 4;
+ vc = cid / 4;
if (intf_type == RDI2) {
/* zero out two bits */
ispif->applied_intf_cmd[vfe_intf].intf_cmd1 &=
@@ -716,6 +776,22 @@
ispif->csid_version = csid_version;
+ if (ispif->csid_version >= CSID_VERSION_V30) {
+ if (!ispif->clk_mux_mem || !ispif->clk_mux_io) {
+ pr_err("%s csi clk mux mem %p io %p\n", __func__,
+ ispif->clk_mux_mem, ispif->clk_mux_io);
+ rc = -ENOMEM;
+ return rc;
+ }
+ ispif->clk_mux_base = ioremap(ispif->clk_mux_mem->start,
+ resource_size(ispif->clk_mux_mem));
+ if (!ispif->clk_mux_base) {
+ pr_err("%s: clk_mux_mem ioremap failed\n", __func__);
+ rc = -ENOMEM;
+ return rc;
+ }
+ }
+
ispif->base = ioremap(ispif->mem->start,
resource_size(ispif->mem));
if (!ispif->base) {
@@ -771,6 +847,8 @@
iounmap(ispif->base);
+ iounmap(ispif->clk_mux_base);
+
ispif->ispif_state = ISPIF_POWER_DOWN;
}
@@ -830,6 +908,12 @@
switch (cmd) {
case VIDIOC_MSM_ISPIF_CFG:
return msm_ispif_cmd(sd, arg);
+ case MSM_SD_SHUTDOWN: {
+ struct ispif_device *ispif =
+ (struct ispif_device *)v4l2_get_subdevdata(sd);
+ msm_ispif_release(ispif);
+ return 0;
+ }
default:
pr_err("%s: invalid cmd 0x%x received\n", __func__, cmd);
return -ENOIOCTLCMD;
@@ -911,6 +995,7 @@
ispif->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
ispif->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_ISPIF;
ispif->msm_sd.sd.entity.name = pdev->name;
+ ispif->msm_sd.close_seq = MSM_SD_CLOSE_1ST_CATEGORY | 0x1;
rc = msm_sd_register(&ispif->msm_sd);
if (rc) {
pr_err("%s: msm_sd_register error = %d\n", __func__, rc);
@@ -942,6 +1027,16 @@
rc = -EBUSY;
goto error;
}
+ ispif->clk_mux_mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "csi_clk_mux");
+ if (ispif->clk_mux_mem) {
+ ispif->clk_mux_io = request_mem_region(
+ ispif->clk_mux_mem->start,
+ resource_size(ispif->clk_mux_mem),
+ ispif->clk_mux_mem->name);
+ if (!ispif->clk_mux_io)
+ pr_err("%s: no valid csi_mux region\n", __func__);
+ }
ispif->pdev = pdev;
ispif->ispif_state = ISPIF_POWER_DOWN;
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h
index fae7a38..faa32aa 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h
@@ -42,9 +42,12 @@
struct platform_device *pdev;
struct msm_sd_subdev msm_sd;
struct resource *mem;
+ struct resource *clk_mux_mem;
struct resource *irq;
struct resource *io;
+ struct resource *clk_mux_io;
void __iomem *base;
+ void __iomem *clk_mux_base;
struct mutex mutex;
uint8_t start_ack_pending;
uint32_t csid_version;
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h
index 9f8b2fa..c8a4366 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h
@@ -50,6 +50,8 @@
+/* CSID CLK MUX SEL REGISTERS */
+#define ISPIF_RDI_CLK_MUX_SEL_ADDR 0x8
/*ISPIF RESET BITS*/
#define VFE_CLK_DOMAIN_RST BIT(31)
@@ -101,4 +103,5 @@
#define ISPIF_IRQ_GLOBAL_CLEAR_CMD 0x000001
+#define ISPIF_STOP_INTF_IMMEDIATELY 0xAAAAAAAA
#endif /* __MSM_ISPIF_HWREG_V1_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h
index 5e61a4d..3cc21a7 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h
@@ -46,6 +46,9 @@
#define ISPIF_VFE_m_RDI_INTF_n_STATUS(m, n) (0x2D0 + ISPIF_VFE(m) + 4*(n))
#define ISPIF_VFE_m_3D_DESKEW_SIZE(m) (0x2E4 + ISPIF_VFE(m))
+/* CSID CLK MUX SEL REGISTERS */
+#define ISPIF_RDI_CLK_MUX_SEL_ADDR 0x8
+
/*ISPIF RESET BITS*/
#define VFE_CLK_DOMAIN_RST BIT(31)
#define PIX_1_CLK_DOMAIN_RST BIT(30)
@@ -88,4 +91,6 @@
#define ISPIF_IRQ_GLOBAL_CLEAR_CMD 0x1
+#define ISPIF_STOP_INTF_IMMEDIATELY 0xAAAAAAAA
+
#endif /* __MSM_ISPIF_HWREG_V2_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c
index 8b96227..7a06c00 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c
@@ -228,6 +228,7 @@
fail_1:
__msm_jpeg_exit(msm_jpeg_device_p);
+ return rc;
fail:
kfree(msm_jpeg_device_p);
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c
index 59b9746..509567c 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c
@@ -70,7 +70,7 @@
}
static struct msm_cam_clk_info jpeg_8x_clk_info[] = {
- {"core_clk", 228570000},
+ {"core_clk", JPEG_CLK_RATE},
{"iface_clk", -1},
{"bus_clk0", -1},
{"camss_top_ahb_clk", -1},
@@ -135,8 +135,8 @@
{
.src = MSM_BUS_MASTER_JPEG,
.dst = MSM_BUS_SLAVE_EBI_CH0,
- .ab = 1027648000,
- .ib = 1105920000,
+ .ab = JPEG_CLK_RATE * 2.5,
+ .ib = JPEG_CLK_RATE * 2.5,
},
};
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.h b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.h
index cd80d2e..a14b8ee 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.h
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -19,6 +19,7 @@
#include <linux/iommu.h>
#include <mach/iommu.h>
#include "msm_jpeg_sync.h"
+#define JPEG_CLK_RATE 266670000
void msm_jpeg_platform_p2v(struct msm_jpeg_device *pgmn_dev, struct file *file,
struct ion_handle **ionhandle, int domain_num);
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index 9f1c81a..27aba5c 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -31,6 +31,7 @@
#include "msm_sd.h"
static struct v4l2_device *msm_v4l2_dev;
+static struct list_head ordered_sd_list;
static struct msm_queue_head *msm_session_q;
@@ -301,6 +302,20 @@
return rc;
}
+static void msm_add_sd_in_position(struct msm_sd_subdev *msm_subdev,
+ struct list_head *sd_list)
+{
+ struct msm_sd_subdev *temp_sd;
+
+ list_for_each_entry(temp_sd, sd_list, list) {
+ if (msm_subdev->close_seq < temp_sd->close_seq) {
+ list_add_tail(&msm_subdev->list, &temp_sd->list);
+ return;
+ }
+ }
+ list_add_tail(&msm_subdev->list, sd_list);
+}
+
int msm_sd_register(struct msm_sd_subdev *msm_subdev)
{
if (WARN_ON(!msm_subdev))
@@ -309,6 +324,7 @@
if (WARN_ON(!msm_v4l2_dev) || WARN_ON(!msm_v4l2_dev->dev))
return -EIO;
+ msm_add_sd_in_position(msm_subdev, &ordered_sd_list);
return __msm_sd_register_subdev(&msm_subdev->sd);
}
@@ -397,66 +413,40 @@
spin_lock_irqsave(&(session->command_ack_q.lock), flags);
list_del_init(&cmd_ack->list);
+ kzfree(cmd_ack);
session->command_ack_q.len--;
spin_unlock_irqrestore(&(session->command_ack_q.lock), flags);
}
-static inline int __msm_v4l2_subdev_shutdown(struct v4l2_subdev *sd)
-{
- return 0;
-}
-
-static void msm_sd_try_shutdown(void)
-{
- unsigned long flags;
- struct v4l2_subdev *sd;
-
- /* release all subdev's resource */
- spin_lock_irqsave(&msm_v4l2_dev->lock, flags);
- if (!list_empty(&msm_v4l2_dev->subdevs)) {
- list_for_each_entry(sd, &msm_v4l2_dev->subdevs, list)
- __msm_v4l2_subdev_shutdown(sd);
- }
- spin_unlock_irqrestore(&msm_v4l2_dev->lock, flags);
-}
-
-static inline int __msm_sd_close_session_streams(struct v4l2_subdev *sd,
+static inline int __msm_sd_close_subdevs(struct msm_sd_subdev *msm_sd,
struct msm_sd_close_ioctl *sd_close)
{
+ struct v4l2_subdev *sd;
+ sd = &msm_sd->sd;
+ pr_debug("%s: Shutting down subdev %s", __func__, sd->name);
+
+ v4l2_subdev_call(sd, core, ioctl, MSM_SD_SHUTDOWN, sd_close);
+ v4l2_subdev_call(sd, core, s_power, 0);
+
return 0;
}
static inline int __msm_destroy_session_streams(void *d1, void *d2)
{
struct msm_stream *stream = d1;
- struct msm_sd_close_ioctl *sd_close = d2;
- struct v4l2_subdev *sd;
- unsigned long flags;
- sd_close->stream = stream->stream_id;
-
- spin_lock_irqsave(&msm_v4l2_dev->lock, flags);
- if (!list_empty(&msm_v4l2_dev->subdevs))
- list_for_each_entry(sd, &msm_v4l2_dev->subdevs, list)
- __msm_sd_close_session_streams(sd, sd_close);
- spin_unlock_irqrestore(&msm_v4l2_dev->lock, flags);
INIT_LIST_HEAD(&stream->queued_list);
return 0;
}
static void msm_destroy_session_streams(struct msm_session *session)
{
- struct msm_sd_close_ioctl sd_close;
- /* to ensure error handling purpose, it needs to detach all subdevs
- * which are being connected to streams */
if (!session)
return;
- sd_close.session = session->session_id;
-
msm_queue_traverse_action(&session->stream_q, struct msm_stream, list,
- __msm_destroy_session_streams, &sd_close);
+ __msm_destroy_session_streams, NULL);
msm_queue_drain(&session->stream_q, struct msm_stream, list);
}
@@ -465,6 +455,9 @@
{
struct msm_command_ack *cmd_ack = d1;
+ if (!(&cmd_ack->command_q))
+ return 0;
+
msm_queue_drain(&cmd_ack->command_q, struct msm_command, list);
return 0;
@@ -472,7 +465,7 @@
static void msm_remove_session_cmd_ack_q(struct msm_session *session)
{
- if (!session)
+ if ((!session) || !(&session->command_ack_q))
return;
/* to ensure error handling purpose, it needs to detach all subdevs
@@ -502,6 +495,22 @@
return 0;
}
+static int __msm_close_destry_session_notify_apps(void *d1, void *d2)
+{
+ struct v4l2_event event;
+ struct msm_v4l2_event_data *event_data =
+ (struct msm_v4l2_event_data *)&event.u.data[0];
+ struct msm_session *session = d1;
+
+ event.type = MSM_CAMERA_V4L2_EVENT_TYPE;
+ event.id = MSM_CAMERA_MSM_NOTIFY;
+ event_data->command = MSM_CAMERA_PRIV_SHUTDOWN;
+
+ v4l2_event_queue(session->event_q.vdev, &event);
+
+ return 0;
+}
+
static long msm_private_ioctl(struct file *file, void *fh,
bool valid_prio, int cmd, void *arg)
{
@@ -561,6 +570,13 @@
}
break;
+ case MSM_CAM_V4L2_IOCTL_NOTIFY_ERROR:
+ /* send v4l2_event to HAL next*/
+ msm_queue_traverse_action(msm_session_q,
+ struct msm_session, list,
+ __msm_close_destry_session_notify_apps, NULL);
+ break;
+
default:
rc = -ENOTTY;
break;
@@ -661,6 +677,7 @@
rc = -ETIMEDOUT;
}
if (rc < 0) {
+ pr_err("%s: rc = %d\n", __func__, rc);
mutex_unlock(&session->lock);
return rc;
}
@@ -687,46 +704,28 @@
return rc;
}
-static int __msm_close_destry_session_notify_apps(void *d1, void *d2)
-{
- struct v4l2_event event;
- struct msm_v4l2_event_data *event_data =
- (struct msm_v4l2_event_data *)&event.u.data[0];
- struct msm_session *session = d1;
- mutex_lock(&session->lock);
- event.type = MSM_CAMERA_V4L2_EVENT_TYPE;
- event.id = MSM_CAMERA_MSM_NOTIFY;
- event_data->command = MSM_CAMERA_PRIV_SHUTDOWN;
-
- v4l2_event_queue(session->event_q.vdev, &event);
-
- msm_destroy_session_streams(session);
- msm_remove_session_cmd_ack_q(session);
- mutex_unlock(&session->lock);
- return 0;
-}
-
static int msm_close(struct file *filep)
{
int rc = 0;
unsigned long flags;
struct msm_video_device *pvdev = video_drvdata(filep);
+ struct msm_sd_close_ioctl sd_close;
+ struct msm_sd_subdev *msm_sd;
- /* 1st thing 1st, send v4l2_event to HAL immediately,
- * to ensure error handling purpose, it needs to detach all subdevs
- * which are being connected to streams */
+ /*stop all hardware blocks immediately*/
+ if (!list_empty(&msm_v4l2_dev->subdevs))
+ list_for_each_entry(msm_sd, &ordered_sd_list, list)
+ __msm_sd_close_subdevs(msm_sd, &sd_close);
+
+ /* send v4l2_event to HAL next*/
msm_queue_traverse_action(msm_session_q, struct msm_session, list,
__msm_close_destry_session_notify_apps, NULL);
- msm_queue_drain(msm_session_q, struct msm_session, list);
-
spin_lock_irqsave(&msm_eventq_lock, flags);
msm_eventq = NULL;
spin_unlock_irqrestore(&msm_eventq_lock, flags);
v4l2_fh_release(filep);
- msm_sd_try_shutdown();
-
spin_lock_irqsave(&msm_pid_lock, flags);
put_pid(msm_pid);
msm_pid = NULL;
@@ -753,7 +752,6 @@
int rc;
unsigned long flags;
struct msm_video_device *pvdev = video_drvdata(filep);
-
BUG_ON(!pvdev);
/* !!! only ONE open is allowed !!! */
@@ -989,7 +987,7 @@
msm_init_queue(msm_session_q);
spin_lock_init(&msm_eventq_lock);
spin_lock_init(&msm_pid_lock);
-
+ INIT_LIST_HEAD(&ordered_sd_list);
goto probe_end;
v4l2_fail:
diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
index 35210a0..154ee87 100644
--- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
@@ -35,7 +35,7 @@
new_entry->vb2_buf = buf_mngr_dev->vb2_ops.get_buf(buf_info->session_id,
buf_info->stream_id);
if (!new_entry->vb2_buf) {
- pr_err("%s:Get buf is null\n", __func__);
+ pr_debug("%s:Get buf is null\n", __func__);
kfree(new_entry);
return -EINVAL;
}
@@ -62,6 +62,7 @@
(bufs->vb2_buf->v4l2_buf.index == buf_info->index)) {
bufs->vb2_buf->v4l2_buf.sequence = buf_info->frame_id;
bufs->vb2_buf->v4l2_buf.timestamp = buf_info->timestamp;
+ bufs->vb2_buf->v4l2_buf.reserved = 0;
ret = buf_mngr_dev->vb2_ops.buf_done
(bufs->vb2_buf,
buf_info->session_id,
@@ -161,6 +162,7 @@
msm_buf_mngr_dev->subdev.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
msm_buf_mngr_dev->subdev.sd.entity.group_id =
MSM_CAMERA_SUBDEV_BUF_MNGR;
+ msm_buf_mngr_dev->subdev.close_seq = MSM_SD_CLOSE_4TH_CATEGORY;
rc = msm_sd_register(&msm_buf_mngr_dev->subdev);
if (rc != 0) {
pr_err("%s: msm_sd_register error = %d\n", __func__, rc);
diff --git a/drivers/media/platform/msm/camera_v2/msm_sd.h b/drivers/media/platform/msm/camera_v2/msm_sd.h
index 958e030..7c1519d 100644
--- a/drivers/media/platform/msm/camera_v2/msm_sd.h
+++ b/drivers/media/platform/msm/camera_v2/msm_sd.h
@@ -50,10 +50,12 @@
#define MSM_SD_CLOSE_1ST_CATEGORY 0x00010000
#define MSM_SD_CLOSE_2ND_CATEGORY 0x00020000
#define MSM_SD_CLOSE_3RD_CATEGORY 0x00030000
+#define MSM_SD_CLOSE_4TH_CATEGORY 0x00040000
struct msm_sd_subdev {
struct v4l2_subdev sd;
int close_seq;
+ struct list_head list;
};
struct msm_sd_req_sd {
diff --git a/drivers/media/platform/msm/camera_v2/pproc/Makefile b/drivers/media/platform/msm/camera_v2/pproc/Makefile
index 854e4e7..4193adc 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/Makefile
+++ b/drivers/media/platform/msm/camera_v2/pproc/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_MSMB_CAMERA) += cpp/
+obj-$(CONFIG_MSMB_CAMERA) += vpe/
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
index 8cdaa4b..e74f1082 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -43,6 +43,9 @@
#define CONFIG_MSM_CPP_DBG 0
+/* dump the frame command before writing to the hardware */
+#define MSM_CPP_DUMP_FRM_CMD 0
+
#if CONFIG_MSM_CPP_DBG
#define CPP_DBG(fmt, args...) pr_err(fmt, ##args)
#else
@@ -208,7 +211,6 @@
pr_err("ION import failed\n");
goto QUEUE_BUFF_ERROR1;
}
-
rc = ion_map_iommu(cpp_dev->client, buff->map_info.ion_handle,
cpp_dev->domain_num, 0, SZ_4K, 0,
(unsigned long *)&buff->map_info.phy_addr,
@@ -237,6 +239,7 @@
static void msm_cpp_dequeue_buffer_info(struct cpp_device *cpp_dev,
struct msm_cpp_buffer_map_list_t *buff)
{
+
ion_unmap_iommu(cpp_dev->client, buff->map_info.ion_handle,
cpp_dev->domain_num, 0);
ion_free(cpp_dev->client, buff->map_info.ion_handle);
@@ -390,6 +393,8 @@
pr_err("Queue not free sessionid: %d, streamid: %d\n",
cpp_dev->buff_queue[i].session_id,
cpp_dev->buff_queue[i].stream_id);
+ msm_cpp_dequeue_buff_info_list
+ (cpp_dev, &cpp_dev->buff_queue[i]);
msm_cpp_free_buff_queue_entry(cpp_dev,
cpp_dev->buff_queue[i].session_id,
cpp_dev->buff_queue[i].stream_id);
@@ -736,10 +741,14 @@
if (NULL != fw)
ptr_bin = (uint32_t *)fw->data;
- for (i = 0; i < fw->size/4; i++) {
- if (ptr_bin) {
- msm_cpp_write(*ptr_bin, cpp_dev->base);
- ptr_bin++;
+ if (ptr_bin == NULL) {
+ pr_err("ptr_bin is NULL\n");
+ } else {
+ for (i = 0; i < fw->size/4; i++) {
+ if (ptr_bin) {
+ msm_cpp_write(*ptr_bin, cpp_dev->base);
+ ptr_bin++;
+ }
}
}
if (fw)
@@ -821,9 +830,16 @@
{
uint32_t i;
struct cpp_device *cpp_dev = v4l2_get_subdevdata(sd);
+
mutex_lock(&cpp_dev->mutex);
+
+ if (cpp_dev->cpp_open_cnt == 0) {
+ mutex_unlock(&cpp_dev->mutex);
+ return 0;
+ }
+
for (i = 0; i < MAX_ACTIVE_CPP_INSTANCE; i++) {
- if (cpp_dev->cpp_subscribe_list[i].vfh == &fh->vfh) {
+ if (cpp_dev->cpp_subscribe_list[i].active == 1) {
cpp_dev->cpp_subscribe_list[i].active = 0;
cpp_dev->cpp_subscribe_list[i].vfh = NULL;
break;
@@ -835,7 +851,6 @@
return -ENODEV;
}
- CPP_DBG("close %d %p\n", i, &fh->vfh);
cpp_dev->cpp_open_cnt--;
if (cpp_dev->cpp_open_cnt == 0) {
msm_camera_io_w(0x0, cpp_dev->base + MSM_CPP_MICRO_CLKEN_CTL);
@@ -843,6 +858,7 @@
cpp_release_hardware(cpp_dev);
cpp_dev->state = CPP_STATE_OFF;
}
+
mutex_unlock(&cpp_dev->mutex);
return 0;
}
@@ -860,7 +876,7 @@
rc = v4l2_subdev_call(cpp_dev->buf_mgr_subdev, core, ioctl,
buff_mgr_ops, buff_mgr_info);
if (rc < 0)
- pr_err("%s: line %d rc = %d\n", __func__, __LINE__, rc);
+ pr_debug("%s: line %d rc = %d\n", __func__, __LINE__, rc);
return rc;
}
@@ -888,8 +904,7 @@
event_qcmd->command = processed_frame;
CPP_DBG("fid %d\n", processed_frame->frame_id);
msm_enqueue(&cpp_dev->eventData_q, &event_qcmd->list_eventdata);
-
- if (!processed_frame->output_buffer_info.processed_divert) {
+ if (!processed_frame->output_buffer_info[0].processed_divert) {
memset(&buff_mgr_info, 0 ,
sizeof(struct msm_buf_mngr_info));
buff_mgr_info.session_id =
@@ -899,7 +914,7 @@
buff_mgr_info.frame_id = processed_frame->frame_id;
buff_mgr_info.timestamp = processed_frame->timestamp;
buff_mgr_info.index =
- processed_frame->output_buffer_info.index;
+ processed_frame->output_buffer_info[0].index;
rc = msm_cpp_buffer_ops(cpp_dev,
VIDIOC_MSM_BUF_MNGR_BUF_DONE,
&buff_mgr_info);
@@ -908,6 +923,28 @@
rc = -EINVAL;
}
}
+
+ if (processed_frame->duplicate_output &&
+ !processed_frame->
+ output_buffer_info[1].processed_divert) {
+ memset(&buff_mgr_info, 0 ,
+ sizeof(struct msm_buf_mngr_info));
+ buff_mgr_info.session_id =
+ ((processed_frame->duplicate_identity >> 16) & 0xFFFF);
+ buff_mgr_info.stream_id =
+ (processed_frame->duplicate_identity & 0xFFFF);
+ buff_mgr_info.frame_id = processed_frame->frame_id;
+ buff_mgr_info.timestamp = processed_frame->timestamp;
+ buff_mgr_info.index =
+ processed_frame->output_buffer_info[1].index;
+ rc = msm_cpp_buffer_ops(cpp_dev,
+ VIDIOC_MSM_BUF_MNGR_BUF_DONE,
+ &buff_mgr_info);
+ if (rc < 0) {
+ pr_err("error putting buffer\n");
+ rc = -EINVAL;
+ }
+ }
v4l2_evt.id = processed_frame->inst_id;
v4l2_evt.type = V4L2_EVENT_CPP_FRAME_DONE;
v4l2_event_queue(cpp_dev->msm_sd.sd.devnode, &v4l2_evt);
@@ -915,6 +952,23 @@
return rc;
}
+#if MSM_CPP_DUMP_FRM_CMD
+static int msm_cpp_dump_frame_cmd(uint32_t *cmd, int32_t len)
+{
+ int i;
+ pr_err("%s: -------- cpp frame cmd msg start --------", __func__);
+ for (i = 0; i < len; i++)
+ pr_err("%s: msg[%03d] = 0x%08x", __func__, i, cmd[i]);
+ pr_err("%s: --------- cpp frame cmd msg end ---------", __func__);
+ return 0;
+}
+#else
+static int msm_cpp_dump_frame_cmd(uint32_t *cmd, int32_t len)
+{
+ return 0;
+}
+#endif
+
static int msm_cpp_send_frame_to_hardware(struct cpp_device *cpp_dev,
struct msm_queue_cmd *frame_qcmd)
{
@@ -927,6 +981,8 @@
msm_enqueue(&cpp_dev->processing_q,
&frame_qcmd->list_frame);
msm_cpp_write(0x6, cpp_dev->base);
+ msm_cpp_dump_frame_cmd(process_frame->cpp_cmd_msg,
+ process_frame->msg_len);
for (i = 0; i < process_frame->msg_len; i++)
msm_cpp_write(process_frame->cpp_cmd_msg[i],
cpp_dev->base);
@@ -951,12 +1007,13 @@
struct msm_cpp_frame_info_t *new_frame =
kzalloc(sizeof(struct msm_cpp_frame_info_t), GFP_KERNEL);
uint32_t *cpp_frame_msg;
- unsigned long in_phyaddr, out_phyaddr;
+ unsigned long in_phyaddr, out_phyaddr0, out_phyaddr1;
uint16_t num_stripes = 0;
struct msm_buf_mngr_info buff_mgr_info;
struct msm_cpp_frame_info_t *u_frame_info =
(struct msm_cpp_frame_info_t *)ioctl_ptr->ioctl_ptr;
int32_t status = 0;
+ uint8_t fw_version_1_2_x = 0;
int i = 0;
if (!new_frame) {
@@ -1001,7 +1058,7 @@
goto ERROR2;
}
- memset(&new_frame->output_buffer_info, 0,
+ memset(&new_frame->output_buffer_info[0], 0,
sizeof(struct msm_cpp_buffer_info_t));
memset(&buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info));
buff_mgr_info.session_id = ((new_frame->identity >> 16) & 0xFFFF);
@@ -1010,31 +1067,72 @@
&buff_mgr_info);
if (rc < 0) {
rc = -EAGAIN;
- pr_err("error getting buffer rc:%d\n", rc);
+ pr_debug("error getting buffer rc:%d\n", rc);
goto ERROR2;
}
-
- new_frame->output_buffer_info.index = buff_mgr_info.index;
- out_phyaddr = msm_cpp_fetch_buffer_info(cpp_dev,
- &new_frame->output_buffer_info,
+ new_frame->output_buffer_info[0].index = buff_mgr_info.index;
+ out_phyaddr0 = msm_cpp_fetch_buffer_info(cpp_dev,
+ &new_frame->output_buffer_info[0],
((new_frame->identity >> 16) & 0xFFFF),
(new_frame->identity & 0xFFFF));
- if (!out_phyaddr) {
+ if (!out_phyaddr0) {
pr_err("error gettting output physical address\n");
rc = -EINVAL;
goto ERROR3;
}
+ out_phyaddr1 = out_phyaddr0;
+
+ /* get buffer for duplicate output */
+ if (new_frame->duplicate_output) {
+ CPP_DBG("duplication enabled, dup_id=0x%x",
+ new_frame->duplicate_identity);
+ memset(&new_frame->output_buffer_info[1], 0,
+ sizeof(struct msm_cpp_buffer_info_t));
+ memset(&buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info));
+ buff_mgr_info.session_id =
+ ((new_frame->duplicate_identity >> 16) & 0xFFFF);
+ buff_mgr_info.stream_id =
+ (new_frame->duplicate_identity & 0xFFFF);
+ rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_GET_BUF,
+ &buff_mgr_info);
+ if (rc < 0) {
+ rc = -EAGAIN;
+ pr_err("error getting buffer rc:%d\n", rc);
+ goto ERROR2;
+ }
+ new_frame->output_buffer_info[1].index = buff_mgr_info.index;
+ out_phyaddr1 = msm_cpp_fetch_buffer_info(cpp_dev,
+ &new_frame->output_buffer_info[1],
+ ((new_frame->duplicate_identity >> 16) & 0xFFFF),
+ (new_frame->duplicate_identity & 0xFFFF));
+ if (!out_phyaddr1) {
+ pr_err("error gettting output physical address\n");
+ rc = -EINVAL;
+ goto ERROR3;
+ }
+ /* set duplicate enable bit */
+ cpp_frame_msg[5] |= 0x1;
+ }
num_stripes = ((cpp_frame_msg[12] >> 20) & 0x3FF) +
((cpp_frame_msg[12] >> 10) & 0x3FF) +
(cpp_frame_msg[12] & 0x3FF);
+ fw_version_1_2_x = 0;
+ if (cpp_dev->hw_info.cpp_hw_version == 0x10010000)
+ fw_version_1_2_x = 2;
+
for (i = 0; i < num_stripes; i++) {
- cpp_frame_msg[133 + i * 27] += (uint32_t) in_phyaddr;
- cpp_frame_msg[139 + i * 27] += (uint32_t) out_phyaddr;
- cpp_frame_msg[140 + i * 27] += (uint32_t) out_phyaddr;
- cpp_frame_msg[141 + i * 27] += (uint32_t) out_phyaddr;
- cpp_frame_msg[142 + i * 27] += (uint32_t) out_phyaddr;
+ cpp_frame_msg[(133 + fw_version_1_2_x) + i * 27] +=
+ (uint32_t) in_phyaddr;
+ cpp_frame_msg[(139 + fw_version_1_2_x) + i * 27] +=
+ (uint32_t) out_phyaddr0;
+ cpp_frame_msg[(140 + fw_version_1_2_x) + i * 27] +=
+ (uint32_t) out_phyaddr1;
+ cpp_frame_msg[(141 + fw_version_1_2_x) + i * 27] +=
+ (uint32_t) out_phyaddr0;
+ cpp_frame_msg[(142 + fw_version_1_2_x) + i * 27] +=
+ (uint32_t) out_phyaddr1;
}
frame_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
@@ -1088,6 +1186,11 @@
int rc = 0;
char *fw_name_bin;
+ if (ioctl_ptr == NULL) {
+ pr_err("ioctl_ptr is null\n");
+ return -EINVAL;
+ }
+
mutex_lock(&cpp_dev->mutex);
CPP_DBG("E cmd: %d\n", cmd);
switch (cmd) {
@@ -1103,7 +1206,7 @@
case VIDIOC_MSM_CPP_LOAD_FIRMWARE: {
if (cpp_dev->is_firmware_loaded == 0) {
- fw_name_bin = kzalloc(ioctl_ptr->len, GFP_KERNEL);
+ fw_name_bin = kzalloc(ioctl_ptr->len+1, GFP_KERNEL);
if (!fw_name_bin) {
pr_err("%s:%d: malloc error\n", __func__,
__LINE__);
@@ -1111,6 +1214,14 @@
return -EINVAL;
}
+ if (ioctl_ptr->ioctl_ptr == NULL) {
+ pr_err("ioctl_ptr->ioctl_ptr=NULL\n");
+ return -EINVAL;
+ }
+ if (ioctl_ptr->len == 0) {
+ pr_err("ioctl_ptr->len is 0\n");
+ return -EINVAL;
+ }
rc = (copy_from_user(fw_name_bin,
(void __user *)ioctl_ptr->ioctl_ptr,
ioctl_ptr->len) ? -EFAULT : 0);
@@ -1120,6 +1231,11 @@
mutex_unlock(&cpp_dev->mutex);
return -EINVAL;
}
+ *(fw_name_bin+ioctl_ptr->len) = '\0';
+ if (cpp_dev == NULL) {
+ pr_err("cpp_dev is null\n");
+ return -EINVAL;
+ }
disable_irq(cpp_dev->irq->start);
cpp_load_fw(cpp_dev, fw_name_bin);
@@ -1245,6 +1361,13 @@
kfree(event_qcmd);
break;
}
+ case MSM_SD_SHUTDOWN: {
+ mutex_unlock(&cpp_dev->mutex);
+ while (cpp_dev->cpp_open_cnt != 0)
+ cpp_close_node(sd, NULL);
+ rc = 0;
+ break;
+ }
}
mutex_unlock(&cpp_dev->mutex);
CPP_DBG("X\n");
@@ -1436,9 +1559,9 @@
}
cpp_dev->iommu_ctx = msm_iommu_get_ctx("cpp");
- if (!cpp_dev->iommu_ctx) {
+ if (IS_ERR(cpp_dev->iommu_ctx)) {
pr_err("%s: cannot get iommu_ctx\n", __func__);
- rc = -ENODEV;
+ rc = -EPROBE_DEFER;
goto ERROR3;
}
@@ -1446,6 +1569,7 @@
cpp_dev->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
cpp_dev->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_CPP;
cpp_dev->msm_sd.sd.entity.name = pdev->name;
+ cpp_dev->msm_sd.close_seq = MSM_SD_CLOSE_3RD_CATEGORY;
msm_sd_register(&cpp_dev->msm_sd);
msm_cpp_v4l2_subdev_fops.owner = v4l2_subdev_fops.owner;
msm_cpp_v4l2_subdev_fops.open = v4l2_subdev_fops.open;
diff --git a/drivers/media/platform/msm/camera_v2/pproc/vpe/Makefile b/drivers/media/platform/msm/camera_v2/pproc/vpe/Makefile
new file mode 100644
index 0000000..65a7e34
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/pproc/vpe/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+obj-$(CONFIG_MSMB_CAMERA) += msm_vpe.o
diff --git a/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.c b/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.c
new file mode 100644
index 0000000..d53d766
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.c
@@ -0,0 +1,1643 @@
+/* Copyright (c) 2012-2013, 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.
+ */
+
+#define pr_fmt(fmt) "MSM-VPE %s:%d " fmt, __func__, __LINE__
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+#include <mach/iommu_domains.h>
+#include <mach/iommu.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <media/media-entity.h>
+#include <media/msmb_pproc.h>
+#include <media/msmb_generic_buf_mgr.h>
+#include "msm_vpe.h"
+#include "msm_camera_io_util.h"
+
+#define MSM_VPE_IDENT_TO_SESSION_ID(identity) ((identity >> 16) & 0xFFFF)
+#define MSM_VPE_IDENT_TO_STREAM_ID(identity) (identity & 0xFFFF)
+
+#define MSM_VPE_DRV_NAME "msm_vpe"
+
+#define MSM_VPE_MAX_BUFF_QUEUE 16
+
+#define CONFIG_MSM_VPE_DBG 0
+
+#if CONFIG_MSM_VPE_DBG
+#define VPE_DBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define VPE_DBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+static void vpe_mem_dump(const char * const name, const void * const addr,
+ int size)
+{
+ char line_str[128], *p_str;
+ int i;
+ u32 *p = (u32 *) addr;
+ u32 data;
+ VPE_DBG("%s: (%s) %p %d\n", __func__, name, addr, size);
+ line_str[0] = '\0';
+ p_str = line_str;
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+ snprintf(p_str, 12, "%08x: ", (u32) p);
+ p_str += 10;
+ }
+ data = *p++;
+ snprintf(p_str, 12, "%08x ", data);
+ p_str += 9;
+ if ((i + 1) % 4 == 0) {
+ VPE_DBG("%s\n", line_str);
+ line_str[0] = '\0';
+ p_str = line_str;
+ }
+ }
+ if (line_str[0] != '\0')
+ VPE_DBG("%s\n", line_str);
+}
+
+static inline long long vpe_do_div(long long num, long long den)
+{
+ do_div(num, den);
+ return num;
+}
+
+#define msm_dequeue(queue, member) ({ \
+ unsigned long flags; \
+ struct msm_device_queue *__q = (queue); \
+ struct msm_queue_cmd *qcmd = 0; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ __q->len--; \
+ qcmd = list_first_entry(&__q->list, \
+ struct msm_queue_cmd, \
+ member); \
+ list_del_init(&qcmd->member); \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+ qcmd; \
+ })
+
+static void msm_queue_init(struct msm_device_queue *queue, const char *name)
+{
+ spin_lock_init(&queue->lock);
+ queue->len = 0;
+ queue->max = 0;
+ queue->name = name;
+ INIT_LIST_HEAD(&queue->list);
+ init_waitqueue_head(&queue->wait);
+}
+
+static struct msm_cam_clk_info vpe_clk_info[] = {
+ {"vpe_clk", 160000000},
+ {"vpe_pclk", -1},
+};
+
+static int msm_vpe_notify_frame_done(struct vpe_device *vpe_dev);
+
+static void msm_enqueue(struct msm_device_queue *queue,
+ struct list_head *entry)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&queue->lock, flags);
+ queue->len++;
+ if (queue->len > queue->max) {
+ queue->max = queue->len;
+ pr_debug("queue %s new max is %d\n", queue->name, queue->max);
+ }
+ list_add_tail(entry, &queue->list);
+ wake_up(&queue->wait);
+ VPE_DBG("woke up %s\n", queue->name);
+ spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+static struct msm_vpe_buff_queue_info_t *msm_vpe_get_buff_queue_entry(
+ struct vpe_device *vpe_dev, uint32_t session_id, uint32_t stream_id)
+{
+ uint32_t i = 0;
+ struct msm_vpe_buff_queue_info_t *buff_queue_info = NULL;
+
+ for (i = 0; i < vpe_dev->num_buffq; i++) {
+ if ((vpe_dev->buff_queue[i].used == 1) &&
+ (vpe_dev->buff_queue[i].session_id == session_id) &&
+ (vpe_dev->buff_queue[i].stream_id == stream_id)) {
+ buff_queue_info = &vpe_dev->buff_queue[i];
+ break;
+ }
+ }
+
+ if (buff_queue_info == NULL) {
+ pr_err("error buffer queue entry for sess:%d strm:%d not found\n",
+ session_id, stream_id);
+ }
+ return buff_queue_info;
+}
+
+static unsigned long msm_vpe_get_phy_addr(struct vpe_device *vpe_dev,
+ struct msm_vpe_buff_queue_info_t *buff_queue_info, uint32_t buff_index,
+ uint8_t native_buff)
+{
+ unsigned long phy_add = 0;
+ struct list_head *buff_head;
+ struct msm_vpe_buffer_map_list_t *buff, *save;
+
+ if (native_buff)
+ buff_head = &buff_queue_info->native_buff_head;
+ else
+ buff_head = &buff_queue_info->vb2_buff_head;
+
+ list_for_each_entry_safe(buff, save, buff_head, entry) {
+ if (buff->map_info.buff_info.index == buff_index) {
+ phy_add = buff->map_info.phy_addr;
+ break;
+ }
+ }
+
+ return phy_add;
+}
+
+static unsigned long msm_vpe_queue_buffer_info(struct vpe_device *vpe_dev,
+ struct msm_vpe_buff_queue_info_t *buff_queue,
+ struct msm_vpe_buffer_info_t *buffer_info)
+{
+ struct list_head *buff_head;
+ struct msm_vpe_buffer_map_list_t *buff, *save;
+ int rc = 0;
+
+ if (buffer_info->native_buff)
+ buff_head = &buff_queue->native_buff_head;
+ else
+ buff_head = &buff_queue->vb2_buff_head;
+
+ list_for_each_entry_safe(buff, save, buff_head, entry) {
+ if (buff->map_info.buff_info.index == buffer_info->index) {
+ pr_err("error buffer index already queued\n");
+ return -EINVAL;
+ }
+ }
+
+ buff = kzalloc(
+ sizeof(struct msm_vpe_buffer_map_list_t), GFP_KERNEL);
+ if (!buff) {
+ pr_err("error allocating memory\n");
+ return -EINVAL;
+ }
+
+ buff->map_info.buff_info = *buffer_info;
+ buff->map_info.ion_handle = ion_import_dma_buf(vpe_dev->client,
+ buffer_info->fd);
+ if (IS_ERR_OR_NULL(buff->map_info.ion_handle)) {
+ pr_err("ION import failed\n");
+ goto queue_buff_error1;
+ }
+
+ rc = ion_map_iommu(vpe_dev->client, buff->map_info.ion_handle,
+ vpe_dev->domain_num, 0, SZ_4K, 0,
+ (unsigned long *)&buff->map_info.phy_addr,
+ &buff->map_info.len, 0, 0);
+ if (rc < 0) {
+ pr_err("ION mmap failed\n");
+ goto queue_buff_error2;
+ }
+
+ INIT_LIST_HEAD(&buff->entry);
+ list_add_tail(&buff->entry, buff_head);
+
+ return buff->map_info.phy_addr;
+
+queue_buff_error2:
+ ion_unmap_iommu(vpe_dev->client, buff->map_info.ion_handle,
+ vpe_dev->domain_num, 0);
+queue_buff_error1:
+ ion_free(vpe_dev->client, buff->map_info.ion_handle);
+ buff->map_info.ion_handle = NULL;
+ kzfree(buff);
+
+ return 0;
+}
+
+static void msm_vpe_dequeue_buffer_info(struct vpe_device *vpe_dev,
+ struct msm_vpe_buffer_map_list_t *buff)
+{
+ ion_unmap_iommu(vpe_dev->client, buff->map_info.ion_handle,
+ vpe_dev->domain_num, 0);
+ ion_free(vpe_dev->client, buff->map_info.ion_handle);
+ buff->map_info.ion_handle = NULL;
+
+ list_del_init(&buff->entry);
+ kzfree(buff);
+
+ return;
+}
+
+static unsigned long msm_vpe_fetch_buffer_info(struct vpe_device *vpe_dev,
+ struct msm_vpe_buffer_info_t *buffer_info, uint32_t session_id,
+ uint32_t stream_id)
+{
+ unsigned long phy_addr = 0;
+ struct msm_vpe_buff_queue_info_t *buff_queue_info;
+ uint8_t native_buff = buffer_info->native_buff;
+
+ buff_queue_info = msm_vpe_get_buff_queue_entry(vpe_dev, session_id,
+ stream_id);
+ if (buff_queue_info == NULL) {
+ pr_err("error finding buffer queue entry for sessid:%d strmid:%d\n",
+ session_id, stream_id);
+ return phy_addr;
+ }
+
+ phy_addr = msm_vpe_get_phy_addr(vpe_dev, buff_queue_info,
+ buffer_info->index, native_buff);
+ if ((phy_addr == 0) && (native_buff)) {
+ phy_addr = msm_vpe_queue_buffer_info(vpe_dev, buff_queue_info,
+ buffer_info);
+ }
+ return phy_addr;
+}
+
+static int32_t msm_vpe_enqueue_buff_info_list(struct vpe_device *vpe_dev,
+ struct msm_vpe_stream_buff_info_t *stream_buff_info)
+{
+ uint32_t j;
+ struct msm_vpe_buff_queue_info_t *buff_queue_info;
+
+ buff_queue_info = msm_vpe_get_buff_queue_entry(vpe_dev,
+ (stream_buff_info->identity >> 16) & 0xFFFF,
+ stream_buff_info->identity & 0xFFFF);
+ if (buff_queue_info == NULL) {
+ pr_err("error finding buffer queue entry for sessid:%d strmid:%d\n",
+ (stream_buff_info->identity >> 16) & 0xFFFF,
+ stream_buff_info->identity & 0xFFFF);
+ return -EINVAL;
+ }
+
+ for (j = 0; j < stream_buff_info->num_buffs; j++) {
+ msm_vpe_queue_buffer_info(vpe_dev, buff_queue_info,
+ &stream_buff_info->buffer_info[j]);
+ }
+ return 0;
+}
+
+static int32_t msm_vpe_dequeue_buff_info_list(struct vpe_device *vpe_dev,
+ struct msm_vpe_buff_queue_info_t *buff_queue_info)
+{
+ struct msm_vpe_buffer_map_list_t *buff, *save;
+ struct list_head *buff_head;
+
+ buff_head = &buff_queue_info->native_buff_head;
+ list_for_each_entry_safe(buff, save, buff_head, entry) {
+ msm_vpe_dequeue_buffer_info(vpe_dev, buff);
+ }
+
+ buff_head = &buff_queue_info->vb2_buff_head;
+ list_for_each_entry_safe(buff, save, buff_head, entry) {
+ msm_vpe_dequeue_buffer_info(vpe_dev, buff);
+ }
+
+ return 0;
+}
+
+static int32_t msm_vpe_add_buff_queue_entry(struct vpe_device *vpe_dev,
+ uint16_t session_id, uint16_t stream_id)
+{
+ uint32_t i;
+ struct msm_vpe_buff_queue_info_t *buff_queue_info;
+
+ for (i = 0; i < vpe_dev->num_buffq; i++) {
+ if (vpe_dev->buff_queue[i].used == 0) {
+ buff_queue_info = &vpe_dev->buff_queue[i];
+ buff_queue_info->used = 1;
+ buff_queue_info->session_id = session_id;
+ buff_queue_info->stream_id = stream_id;
+ INIT_LIST_HEAD(&buff_queue_info->vb2_buff_head);
+ INIT_LIST_HEAD(&buff_queue_info->native_buff_head);
+ return 0;
+ }
+ }
+ pr_err("buffer queue full. error for sessionid: %d streamid: %d\n",
+ session_id, stream_id);
+ return -EINVAL;
+}
+
+static int32_t msm_vpe_free_buff_queue_entry(struct vpe_device *vpe_dev,
+ uint32_t session_id, uint32_t stream_id)
+{
+ struct msm_vpe_buff_queue_info_t *buff_queue_info;
+
+ buff_queue_info = msm_vpe_get_buff_queue_entry(vpe_dev, session_id,
+ stream_id);
+ if (buff_queue_info == NULL) {
+ pr_err("error finding buffer queue entry for sessid:%d strmid:%d\n",
+ session_id, stream_id);
+ return -EINVAL;
+ }
+
+ buff_queue_info->used = 0;
+ buff_queue_info->session_id = 0;
+ buff_queue_info->stream_id = 0;
+ INIT_LIST_HEAD(&buff_queue_info->vb2_buff_head);
+ INIT_LIST_HEAD(&buff_queue_info->native_buff_head);
+ return 0;
+}
+
+static int32_t msm_vpe_create_buff_queue(struct vpe_device *vpe_dev,
+ uint32_t num_buffq)
+{
+ struct msm_vpe_buff_queue_info_t *buff_queue;
+ buff_queue = kzalloc(
+ sizeof(struct msm_vpe_buff_queue_info_t) * num_buffq,
+ GFP_KERNEL);
+ if (!buff_queue) {
+ pr_err("Buff queue allocation failure\n");
+ return -ENOMEM;
+ }
+
+ if (vpe_dev->buff_queue) {
+ pr_err("Buff queue not empty\n");
+ kzfree(buff_queue);
+ return -EINVAL;
+ } else {
+ vpe_dev->buff_queue = buff_queue;
+ vpe_dev->num_buffq = num_buffq;
+ }
+ return 0;
+}
+
+static void msm_vpe_delete_buff_queue(struct vpe_device *vpe_dev)
+{
+ uint32_t i;
+
+ for (i = 0; i < vpe_dev->num_buffq; i++) {
+ if (vpe_dev->buff_queue[i].used == 1) {
+ pr_err("Queue not free sessionid: %d, streamid: %d\n",
+ vpe_dev->buff_queue[i].session_id,
+ vpe_dev->buff_queue[i].stream_id);
+ msm_vpe_free_buff_queue_entry(vpe_dev,
+ vpe_dev->buff_queue[i].session_id,
+ vpe_dev->buff_queue[i].stream_id);
+ }
+ }
+ kzfree(vpe_dev->buff_queue);
+ vpe_dev->buff_queue = NULL;
+ vpe_dev->num_buffq = 0;
+ return;
+}
+
+void vpe_release_ion_client(struct kref *ref)
+{
+ struct vpe_device *vpe_dev = container_of(ref,
+ struct vpe_device, refcount);
+ ion_client_destroy(vpe_dev->client);
+}
+
+static int vpe_init_mem(struct vpe_device *vpe_dev)
+{
+ kref_init(&vpe_dev->refcount);
+ kref_get(&vpe_dev->refcount);
+ vpe_dev->client = msm_ion_client_create(-1, "vpe");
+
+ if (!vpe_dev->client) {
+ pr_err("couldn't create ion client\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void vpe_deinit_mem(struct vpe_device *vpe_dev)
+{
+ kref_put(&vpe_dev->refcount, vpe_release_ion_client);
+}
+
+static irqreturn_t msm_vpe_irq(int irq_num, void *data)
+{
+ unsigned long flags;
+ uint32_t irq_status;
+ struct msm_vpe_tasklet_queue_cmd *queue_cmd;
+ struct vpe_device *vpe_dev = (struct vpe_device *) data;
+
+ irq_status = msm_camera_io_r_mb(vpe_dev->base +
+ VPE_INTR_STATUS_OFFSET);
+
+ spin_lock_irqsave(&vpe_dev->tasklet_lock, flags);
+ queue_cmd = &vpe_dev->tasklet_queue_cmd[vpe_dev->taskletq_idx];
+ if (queue_cmd->cmd_used) {
+ VPE_DBG("%s: vpe tasklet queue overflow\n", __func__);
+ list_del(&queue_cmd->list);
+ } else {
+ atomic_add(1, &vpe_dev->irq_cnt);
+ }
+ queue_cmd->irq_status = irq_status;
+
+ queue_cmd->cmd_used = 1;
+ vpe_dev->taskletq_idx =
+ (vpe_dev->taskletq_idx + 1) % MSM_VPE_TASKLETQ_SIZE;
+ list_add_tail(&queue_cmd->list, &vpe_dev->tasklet_q);
+ spin_unlock_irqrestore(&vpe_dev->tasklet_lock, flags);
+
+ tasklet_schedule(&vpe_dev->vpe_tasklet);
+
+ msm_camera_io_w_mb(irq_status, vpe_dev->base + VPE_INTR_CLEAR_OFFSET);
+ msm_camera_io_w(0, vpe_dev->base + VPE_INTR_ENABLE_OFFSET);
+ VPE_DBG("%s: irq_status=0x%x.\n", __func__, irq_status);
+
+ return IRQ_HANDLED;
+}
+
+static void msm_vpe_do_tasklet(unsigned long data)
+{
+ unsigned long flags;
+ struct vpe_device *vpe_dev = (struct vpe_device *)data;
+ struct msm_vpe_tasklet_queue_cmd *queue_cmd;
+
+ while (atomic_read(&vpe_dev->irq_cnt)) {
+ spin_lock_irqsave(&vpe_dev->tasklet_lock, flags);
+ queue_cmd = list_first_entry(&vpe_dev->tasklet_q,
+ struct msm_vpe_tasklet_queue_cmd, list);
+ if (!queue_cmd) {
+ atomic_set(&vpe_dev->irq_cnt, 0);
+ spin_unlock_irqrestore(&vpe_dev->tasklet_lock, flags);
+ return;
+ }
+ atomic_sub(1, &vpe_dev->irq_cnt);
+ list_del(&queue_cmd->list);
+ queue_cmd->cmd_used = 0;
+
+ spin_unlock_irqrestore(&vpe_dev->tasklet_lock, flags);
+
+ VPE_DBG("Frame done!!\n");
+ msm_vpe_notify_frame_done(vpe_dev);
+ }
+}
+
+static int vpe_init_hardware(struct vpe_device *vpe_dev)
+{
+ int rc = 0;
+
+ if (vpe_dev->fs_vpe == NULL) {
+ vpe_dev->fs_vpe =
+ regulator_get(&vpe_dev->pdev->dev, "vdd");
+ if (IS_ERR(vpe_dev->fs_vpe)) {
+ pr_err("Regulator vpe vdd get failed %ld\n",
+ PTR_ERR(vpe_dev->fs_vpe));
+ vpe_dev->fs_vpe = NULL;
+ rc = -ENODEV;
+ goto fail;
+ } else if (regulator_enable(vpe_dev->fs_vpe)) {
+ pr_err("Regulator vpe vdd enable failed\n");
+ regulator_put(vpe_dev->fs_vpe);
+ vpe_dev->fs_vpe = NULL;
+ rc = -ENODEV;
+ goto fail;
+ }
+ }
+
+ rc = msm_cam_clk_enable(&vpe_dev->pdev->dev, vpe_clk_info,
+ vpe_dev->vpe_clk, ARRAY_SIZE(vpe_clk_info), 1);
+ if (rc < 0) {
+ rc = -ENODEV;
+ pr_err("clk enable failed\n");
+ goto disable_and_put_regulator;
+ }
+
+ vpe_dev->base = ioremap(vpe_dev->mem->start,
+ resource_size(vpe_dev->mem));
+ if (!vpe_dev->base) {
+ rc = -ENOMEM;
+ pr_err("ioremap failed\n");
+ goto disable_and_put_regulator;
+ }
+
+ if (vpe_dev->state != VPE_STATE_BOOT) {
+ rc = request_irq(vpe_dev->irq->start, msm_vpe_irq,
+ IRQF_TRIGGER_RISING,
+ "vpe", vpe_dev);
+ if (rc < 0) {
+ pr_err("irq request fail! start=%u\n",
+ vpe_dev->irq->start);
+ rc = -EBUSY;
+ goto unmap_base;
+ } else {
+ VPE_DBG("Got irq! %d\n", vpe_dev->irq->start);
+ }
+ } else {
+ VPE_DBG("Skip requesting the irq since device is booting\n");
+ }
+ vpe_dev->buf_mgr_subdev = msm_buf_mngr_get_subdev();
+
+ msm_vpe_create_buff_queue(vpe_dev, MSM_VPE_MAX_BUFF_QUEUE);
+ return rc;
+
+unmap_base:
+ iounmap(vpe_dev->base);
+disable_and_put_regulator:
+ regulator_disable(vpe_dev->fs_vpe);
+ regulator_put(vpe_dev->fs_vpe);
+fail:
+ return rc;
+}
+
+static int vpe_release_hardware(struct vpe_device *vpe_dev)
+{
+ if (vpe_dev->state != VPE_STATE_BOOT) {
+ free_irq(vpe_dev->irq->start, vpe_dev);
+ tasklet_kill(&vpe_dev->vpe_tasklet);
+ atomic_set(&vpe_dev->irq_cnt, 0);
+ }
+
+ msm_vpe_delete_buff_queue(vpe_dev);
+ iounmap(vpe_dev->base);
+ msm_cam_clk_enable(&vpe_dev->pdev->dev, vpe_clk_info,
+ vpe_dev->vpe_clk, ARRAY_SIZE(vpe_clk_info), 0);
+ return 0;
+}
+
+static int vpe_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ int rc = 0;
+ uint32_t i;
+ struct vpe_device *vpe_dev = v4l2_get_subdevdata(sd);
+
+ mutex_lock(&vpe_dev->mutex);
+ if (vpe_dev->vpe_open_cnt == MAX_ACTIVE_VPE_INSTANCE) {
+ pr_err("No free VPE instance\n");
+ rc = -ENODEV;
+ goto err_mutex_unlock;
+ }
+
+ for (i = 0; i < MAX_ACTIVE_VPE_INSTANCE; i++) {
+ if (vpe_dev->vpe_subscribe_list[i].active == 0) {
+ vpe_dev->vpe_subscribe_list[i].active = 1;
+ vpe_dev->vpe_subscribe_list[i].vfh = &fh->vfh;
+ break;
+ }
+ }
+ if (i == MAX_ACTIVE_VPE_INSTANCE) {
+ pr_err("No free instance\n");
+ rc = -ENODEV;
+ goto err_mutex_unlock;
+ }
+
+ VPE_DBG("open %d %p\n", i, &fh->vfh);
+ vpe_dev->vpe_open_cnt++;
+ if (vpe_dev->vpe_open_cnt == 1) {
+ rc = vpe_init_hardware(vpe_dev);
+ if (rc < 0) {
+ pr_err("%s: Couldn't init vpe hardware\n", __func__);
+ vpe_dev->vpe_open_cnt--;
+ rc = -ENODEV;
+ goto err_fixup_sub_list;
+ }
+ rc = vpe_init_mem(vpe_dev);
+ if (rc < 0) {
+ pr_err("%s: Couldn't init mem\n", __func__);
+ vpe_dev->vpe_open_cnt--;
+ rc = -ENODEV;
+ goto err_release_hardware;
+ }
+ vpe_dev->state = VPE_STATE_IDLE;
+ }
+ mutex_unlock(&vpe_dev->mutex);
+
+ return rc;
+
+err_release_hardware:
+ vpe_release_hardware(vpe_dev);
+err_fixup_sub_list:
+ for (i = 0; i < MAX_ACTIVE_VPE_INSTANCE; i++) {
+ if (vpe_dev->vpe_subscribe_list[i].vfh == &fh->vfh) {
+ vpe_dev->vpe_subscribe_list[i].active = 0;
+ vpe_dev->vpe_subscribe_list[i].vfh = NULL;
+ break;
+ }
+ }
+err_mutex_unlock:
+ mutex_unlock(&vpe_dev->mutex);
+ return rc;
+}
+
+static int vpe_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ uint32_t i;
+ struct vpe_device *vpe_dev = v4l2_get_subdevdata(sd);
+ mutex_lock(&vpe_dev->mutex);
+ for (i = 0; i < MAX_ACTIVE_VPE_INSTANCE; i++) {
+ if (vpe_dev->vpe_subscribe_list[i].vfh == &fh->vfh) {
+ vpe_dev->vpe_subscribe_list[i].active = 0;
+ vpe_dev->vpe_subscribe_list[i].vfh = NULL;
+ break;
+ }
+ }
+ if (i == MAX_ACTIVE_VPE_INSTANCE) {
+ pr_err("Invalid close\n");
+ mutex_unlock(&vpe_dev->mutex);
+ return -ENODEV;
+ }
+
+ VPE_DBG("close %d %p\n", i, &fh->vfh);
+ vpe_dev->vpe_open_cnt--;
+ if (vpe_dev->vpe_open_cnt == 0) {
+ vpe_deinit_mem(vpe_dev);
+ vpe_release_hardware(vpe_dev);
+ vpe_dev->state = VPE_STATE_OFF;
+ }
+ mutex_unlock(&vpe_dev->mutex);
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops msm_vpe_internal_ops = {
+ .open = vpe_open_node,
+ .close = vpe_close_node,
+};
+
+static int msm_vpe_buffer_ops(struct vpe_device *vpe_dev,
+ uint32_t buff_mgr_ops, struct msm_buf_mngr_info *buff_mgr_info)
+{
+ int rc = -EINVAL;
+
+ rc = v4l2_subdev_call(vpe_dev->buf_mgr_subdev, core, ioctl,
+ buff_mgr_ops, buff_mgr_info);
+ if (rc < 0)
+ pr_err("%s: line %d rc = %d\n", __func__, __LINE__, rc);
+ return rc;
+}
+
+static int msm_vpe_notify_frame_done(struct vpe_device *vpe_dev)
+{
+ struct v4l2_event v4l2_evt;
+ struct msm_queue_cmd *frame_qcmd;
+ struct msm_queue_cmd *event_qcmd;
+ struct msm_vpe_frame_info_t *processed_frame;
+ struct msm_device_queue *queue = &vpe_dev->processing_q;
+ struct msm_buf_mngr_info buff_mgr_info;
+ int rc = 0;
+
+ if (queue->len > 0) {
+ frame_qcmd = msm_dequeue(queue, list_frame);
+ processed_frame = frame_qcmd->command;
+ do_gettimeofday(&(processed_frame->out_time));
+ kfree(frame_qcmd);
+ event_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_ATOMIC);
+ if (!event_qcmd) {
+ pr_err("%s: Insufficient memory\n", __func__);
+ return -ENOMEM;
+ }
+ atomic_set(&event_qcmd->on_heap, 1);
+ event_qcmd->command = processed_frame;
+ VPE_DBG("fid %d\n", processed_frame->frame_id);
+ msm_enqueue(&vpe_dev->eventData_q, &event_qcmd->list_eventdata);
+
+ if (!processed_frame->output_buffer_info.processed_divert) {
+ memset(&buff_mgr_info, 0 ,
+ sizeof(buff_mgr_info));
+ buff_mgr_info.session_id =
+ ((processed_frame->identity >> 16) & 0xFFFF);
+ buff_mgr_info.stream_id =
+ (processed_frame->identity & 0xFFFF);
+ buff_mgr_info.frame_id = processed_frame->frame_id;
+ buff_mgr_info.timestamp = processed_frame->timestamp;
+ buff_mgr_info.index =
+ processed_frame->output_buffer_info.index;
+ rc = msm_vpe_buffer_ops(vpe_dev,
+ VIDIOC_MSM_BUF_MNGR_BUF_DONE,
+ &buff_mgr_info);
+ if (rc < 0) {
+ pr_err("%s: error doing VIDIOC_MSM_BUF_MNGR_BUF_DONE\n",
+ __func__);
+ rc = -EINVAL;
+ }
+ }
+
+ v4l2_evt.id = processed_frame->inst_id;
+ v4l2_evt.type = V4L2_EVENT_VPE_FRAME_DONE;
+ v4l2_event_queue(vpe_dev->msm_sd.sd.devnode, &v4l2_evt);
+ }
+ return rc;
+}
+
+static void vpe_update_scaler_params(struct vpe_device *vpe_dev,
+ struct msm_vpe_frame_strip_info strip_info)
+{
+ uint32_t out_ROI_width, out_ROI_height;
+ uint32_t src_ROI_width, src_ROI_height;
+
+ /*
+ * phase_step_x, phase_step_y, phase_init_x and phase_init_y
+ * are represented in fixed-point, unsigned 3.29 format
+ */
+ uint32_t phase_step_x = 0;
+ uint32_t phase_step_y = 0;
+ uint32_t phase_init_x = 0;
+ uint32_t phase_init_y = 0;
+
+ uint32_t src_roi, src_x, src_y, src_xy, temp;
+ uint32_t yscale_filter_sel, xscale_filter_sel;
+ uint32_t scale_unit_sel_x, scale_unit_sel_y;
+ uint64_t numerator, denominator;
+
+ /*
+ * assumption is both direction need zoom. this can be
+ * improved.
+ */
+ temp = msm_camera_io_r(vpe_dev->base + VPE_OP_MODE_OFFSET) | 0x3;
+ msm_camera_io_w(temp, vpe_dev->base + VPE_OP_MODE_OFFSET);
+
+ src_ROI_width = strip_info.src_w;
+ src_ROI_height = strip_info.src_h;
+ out_ROI_width = strip_info.dst_w;
+ out_ROI_height = strip_info.dst_h;
+
+ VPE_DBG("src w = %u, h=%u, dst w = %u, h =%u.\n",
+ src_ROI_width, src_ROI_height, out_ROI_width,
+ out_ROI_height);
+ src_roi = (src_ROI_height << 16) + src_ROI_width;
+
+ msm_camera_io_w(src_roi, vpe_dev->base + VPE_SRC_SIZE_OFFSET);
+
+ src_x = strip_info.src_x;
+ src_y = strip_info.src_y;
+
+ VPE_DBG("src_x = %d, src_y=%d.\n", src_x, src_y);
+
+ src_xy = src_y*(1<<16) + src_x;
+ msm_camera_io_w(src_xy, vpe_dev->base +
+ VPE_SRC_XY_OFFSET);
+ VPE_DBG("src_xy = 0x%x, src_roi=0x%x.\n", src_xy, src_roi);
+
+ /* decide whether to use FIR or M/N for scaling */
+ if ((out_ROI_width == 1 && src_ROI_width < 4) ||
+ (src_ROI_width < 4 * out_ROI_width - 3))
+ scale_unit_sel_x = 0;/* use FIR scalar */
+ else
+ scale_unit_sel_x = 1;/* use M/N scalar */
+
+ if ((out_ROI_height == 1 && src_ROI_height < 4) ||
+ (src_ROI_height < 4 * out_ROI_height - 3))
+ scale_unit_sel_y = 0;/* use FIR scalar */
+ else
+ scale_unit_sel_y = 1;/* use M/N scalar */
+
+ /* calculate phase step for the x direction */
+
+ /*
+ * if destination is only 1 pixel wide, the value of
+ * phase_step_x is unimportant. Assigning phase_step_x to src
+ * ROI width as an arbitrary value.
+ */
+ if (out_ROI_width == 1)
+ phase_step_x = (uint32_t) ((src_ROI_width) <<
+ SCALER_PHASE_BITS);
+
+ /* if using FIR scalar */
+ else if (scale_unit_sel_x == 0) {
+
+ /*
+ * Calculate the quotient ( src_ROI_width - 1 ) (
+ * out_ROI_width - 1) with u3.29 precision. Quotient
+ * is rounded up to the larger 29th decimal point
+ */
+ numerator = (uint64_t)(src_ROI_width - 1) <<
+ SCALER_PHASE_BITS;
+ /*
+ * never equals to 0 because of the "(out_ROI_width ==
+ * 1 )"
+ */
+ denominator = (uint64_t)(out_ROI_width - 1);
+ /*
+ * divide and round up to the larger 29th decimal
+ * point.
+ */
+ phase_step_x = (uint32_t) vpe_do_div((numerator +
+ denominator - 1), denominator);
+ } else if (scale_unit_sel_x == 1) { /* if M/N scalar */
+ /*
+ * Calculate the quotient ( src_ROI_width ) / (
+ * out_ROI_width) with u3.29 precision. Quotient is
+ * rounded down to the smaller 29th decimal point.
+ */
+ numerator = (uint64_t)(src_ROI_width) <<
+ SCALER_PHASE_BITS;
+ denominator = (uint64_t)(out_ROI_width);
+ phase_step_x =
+ (uint32_t) vpe_do_div(numerator, denominator);
+ }
+ /* calculate phase step for the y direction */
+
+ /*
+ * if destination is only 1 pixel wide, the value of
+ * phase_step_x is unimportant. Assigning phase_step_x to src
+ * ROI width as an arbitrary value.
+ */
+ if (out_ROI_height == 1)
+ phase_step_y =
+ (uint32_t) ((src_ROI_height) << SCALER_PHASE_BITS);
+
+ /* if FIR scalar */
+ else if (scale_unit_sel_y == 0) {
+ /*
+ * Calculate the quotient ( src_ROI_height - 1 ) / (
+ * out_ROI_height - 1) with u3.29 precision. Quotient
+ * is rounded up to the larger 29th decimal point.
+ */
+ numerator = (uint64_t)(src_ROI_height - 1) <<
+ SCALER_PHASE_BITS;
+ /*
+ * never equals to 0 because of the " ( out_ROI_height
+ * == 1 )" case
+ */
+ denominator = (uint64_t)(out_ROI_height - 1);
+ /*
+ * Quotient is rounded up to the larger 29th decimal
+ * point.
+ */
+ phase_step_y =
+ (uint32_t) vpe_do_div(
+ (numerator + denominator - 1), denominator);
+ } else if (scale_unit_sel_y == 1) { /* if M/N scalar */
+ /*
+ * Calculate the quotient ( src_ROI_height ) (
+ * out_ROI_height) with u3.29 precision. Quotient is
+ * rounded down to the smaller 29th decimal point.
+ */
+ numerator = (uint64_t)(src_ROI_height) <<
+ SCALER_PHASE_BITS;
+ denominator = (uint64_t)(out_ROI_height);
+ phase_step_y = (uint32_t) vpe_do_div(
+ numerator, denominator);
+ }
+
+ /* decide which set of FIR coefficients to use */
+ if (phase_step_x > HAL_MDP_PHASE_STEP_2P50)
+ xscale_filter_sel = 0;
+ else if (phase_step_x > HAL_MDP_PHASE_STEP_1P66)
+ xscale_filter_sel = 1;
+ else if (phase_step_x > HAL_MDP_PHASE_STEP_1P25)
+ xscale_filter_sel = 2;
+ else
+ xscale_filter_sel = 3;
+
+ if (phase_step_y > HAL_MDP_PHASE_STEP_2P50)
+ yscale_filter_sel = 0;
+ else if (phase_step_y > HAL_MDP_PHASE_STEP_1P66)
+ yscale_filter_sel = 1;
+ else if (phase_step_y > HAL_MDP_PHASE_STEP_1P25)
+ yscale_filter_sel = 2;
+ else
+ yscale_filter_sel = 3;
+
+ /* calculate phase init for the x direction */
+
+ /* if using FIR scalar */
+ if (scale_unit_sel_x == 0) {
+ if (out_ROI_width == 1)
+ phase_init_x =
+ (uint32_t) ((src_ROI_width - 1) <<
+ SCALER_PHASE_BITS);
+ else
+ phase_init_x = 0;
+ } else if (scale_unit_sel_x == 1) /* M over N scalar */
+ phase_init_x = 0;
+
+ /*
+ * calculate phase init for the y direction if using FIR
+ * scalar
+ */
+ if (scale_unit_sel_y == 0) {
+ if (out_ROI_height == 1)
+ phase_init_y =
+ (uint32_t) ((src_ROI_height -
+ 1) << SCALER_PHASE_BITS);
+ else
+ phase_init_y = 0;
+ } else if (scale_unit_sel_y == 1) /* M over N scalar */
+ phase_init_y = 0;
+
+ strip_info.phase_step_x = phase_step_x;
+ strip_info.phase_step_y = phase_step_y;
+ strip_info.phase_init_x = phase_init_x;
+ strip_info.phase_init_y = phase_init_y;
+ VPE_DBG("phase step x = %d, step y = %d.\n",
+ strip_info.phase_step_x, strip_info.phase_step_y);
+ VPE_DBG("phase init x = %d, init y = %d.\n",
+ strip_info.phase_init_x, strip_info.phase_init_y);
+
+ msm_camera_io_w(strip_info.phase_step_x, vpe_dev->base +
+ VPE_SCALE_PHASEX_STEP_OFFSET);
+ msm_camera_io_w(strip_info.phase_step_y, vpe_dev->base +
+ VPE_SCALE_PHASEY_STEP_OFFSET);
+
+ msm_camera_io_w(strip_info.phase_init_x, vpe_dev->base +
+ VPE_SCALE_PHASEX_INIT_OFFSET);
+ msm_camera_io_w(strip_info.phase_init_y, vpe_dev->base +
+ VPE_SCALE_PHASEY_INIT_OFFSET);
+}
+
+static void vpe_program_buffer_addresses(
+ struct vpe_device *vpe_dev,
+ unsigned long srcP0,
+ unsigned long srcP1,
+ unsigned long outP0,
+ unsigned long outP1)
+{
+ VPE_DBG("%s VPE Configured with:\n"
+ "Src %x, %x Dest %x, %x",
+ __func__, (uint32_t)srcP0, (uint32_t)srcP1,
+ (uint32_t)outP0, (uint32_t)outP1);
+
+ msm_camera_io_w(srcP0, vpe_dev->base + VPE_SRCP0_ADDR_OFFSET);
+ msm_camera_io_w(srcP1, vpe_dev->base + VPE_SRCP1_ADDR_OFFSET);
+ msm_camera_io_w(outP0, vpe_dev->base + VPE_OUTP0_ADDR_OFFSET);
+ msm_camera_io_w(outP1, vpe_dev->base + VPE_OUTP1_ADDR_OFFSET);
+}
+
+static int vpe_start(struct vpe_device *vpe_dev)
+{
+ /* enable the frame irq, bit 0 = Display list 0 ROI done */
+ msm_camera_io_w_mb(1, vpe_dev->base + VPE_INTR_ENABLE_OFFSET);
+ msm_camera_io_dump(vpe_dev->base, 0x120);
+ msm_camera_io_dump(vpe_dev->base + 0x00400, 0x18);
+ msm_camera_io_dump(vpe_dev->base + 0x10000, 0x250);
+ msm_camera_io_dump(vpe_dev->base + 0x30000, 0x20);
+ msm_camera_io_dump(vpe_dev->base + 0x50000, 0x30);
+ msm_camera_io_dump(vpe_dev->base + 0x50400, 0x10);
+
+ /*
+ * This triggers the operation. When the VPE is done,
+ * msm_vpe_irq will fire.
+ */
+ msm_camera_io_w_mb(1, vpe_dev->base + VPE_DL0_START_OFFSET);
+ return 0;
+}
+
+static void vpe_config_axi_default(struct vpe_device *vpe_dev)
+{
+ msm_camera_io_w(0x25, vpe_dev->base + VPE_AXI_ARB_2_OFFSET);
+}
+
+static int vpe_reset(struct vpe_device *vpe_dev)
+{
+ uint32_t vpe_version;
+ uint32_t rc = 0;
+
+ vpe_version = msm_camera_io_r(
+ vpe_dev->base + VPE_HW_VERSION_OFFSET);
+ VPE_DBG("vpe_version = 0x%x\n", vpe_version);
+ /* disable all interrupts.*/
+ msm_camera_io_w(0, vpe_dev->base + VPE_INTR_ENABLE_OFFSET);
+ /* clear all pending interrupts*/
+ msm_camera_io_w(0x1fffff, vpe_dev->base + VPE_INTR_CLEAR_OFFSET);
+ /* write sw_reset to reset the core. */
+ msm_camera_io_w(0x10, vpe_dev->base + VPE_SW_RESET_OFFSET);
+ /* then poll the reset bit, it should be self-cleared. */
+ while (1) {
+ rc = msm_camera_io_r(vpe_dev->base + VPE_SW_RESET_OFFSET) \
+ & 0x10;
+ if (rc == 0)
+ break;
+ cpu_relax();
+ }
+ /*
+ * at this point, hardware is reset. Then pogram to default
+ * values.
+ */
+ msm_camera_io_w(VPE_AXI_RD_ARB_CONFIG_VALUE,
+ vpe_dev->base + VPE_AXI_RD_ARB_CONFIG_OFFSET);
+
+ msm_camera_io_w(VPE_CGC_ENABLE_VALUE,
+ vpe_dev->base + VPE_CGC_EN_OFFSET);
+ msm_camera_io_w(1, vpe_dev->base + VPE_CMD_MODE_OFFSET);
+ msm_camera_io_w(VPE_DEFAULT_OP_MODE_VALUE,
+ vpe_dev->base + VPE_OP_MODE_OFFSET);
+ msm_camera_io_w(VPE_DEFAULT_SCALE_CONFIG,
+ vpe_dev->base + VPE_SCALE_CONFIG_OFFSET);
+ vpe_config_axi_default(vpe_dev);
+ return rc;
+}
+
+static void vpe_update_scale_coef(struct vpe_device *vpe_dev, uint32_t *p)
+{
+ uint32_t i, offset;
+ offset = *p;
+ for (i = offset; i < (VPE_SCALE_COEFF_NUM + offset); i++) {
+ VPE_DBG("Setting scale table %d\n", i);
+ msm_camera_io_w(*(++p),
+ vpe_dev->base + VPE_SCALE_COEFF_LSBn(i));
+ msm_camera_io_w(*(++p),
+ vpe_dev->base + VPE_SCALE_COEFF_MSBn(i));
+ }
+}
+
+static void vpe_input_plane_config(struct vpe_device *vpe_dev, uint32_t *p)
+{
+ msm_camera_io_w(*p, vpe_dev->base + VPE_SRC_FORMAT_OFFSET);
+ msm_camera_io_w(*(++p),
+ vpe_dev->base + VPE_SRC_UNPACK_PATTERN1_OFFSET);
+ msm_camera_io_w(*(++p), vpe_dev->base + VPE_SRC_IMAGE_SIZE_OFFSET);
+ msm_camera_io_w(*(++p), vpe_dev->base + VPE_SRC_YSTRIDE1_OFFSET);
+ msm_camera_io_w(*(++p), vpe_dev->base + VPE_SRC_SIZE_OFFSET);
+ msm_camera_io_w(*(++p), vpe_dev->base + VPE_SRC_XY_OFFSET);
+}
+
+static void vpe_output_plane_config(struct vpe_device *vpe_dev, uint32_t *p)
+{
+ msm_camera_io_w(*p, vpe_dev->base + VPE_OUT_FORMAT_OFFSET);
+ msm_camera_io_w(*(++p),
+ vpe_dev->base + VPE_OUT_PACK_PATTERN1_OFFSET);
+ msm_camera_io_w(*(++p), vpe_dev->base + VPE_OUT_YSTRIDE1_OFFSET);
+ msm_camera_io_w(*(++p), vpe_dev->base + VPE_OUT_SIZE_OFFSET);
+ msm_camera_io_w(*(++p), vpe_dev->base + VPE_OUT_XY_OFFSET);
+}
+
+static void vpe_operation_config(struct vpe_device *vpe_dev, uint32_t *p)
+{
+ msm_camera_io_w(*p, vpe_dev->base + VPE_OP_MODE_OFFSET);
+}
+
+/**
+ * msm_vpe_transaction_setup() - send setup for one frame to VPE
+ * @vpe_dev: vpe device
+ * @data: packed setup commands
+ *
+ * See msm_vpe.h for the expected format of `data'
+ */
+static void msm_vpe_transaction_setup(struct vpe_device *vpe_dev, void *data)
+{
+ int i;
+ void *iter = data;
+
+ vpe_mem_dump("vpe_transaction", data, VPE_TRANSACTION_SETUP_CONFIG_LEN);
+
+ for (i = 0; i < VPE_NUM_SCALER_TABLES; ++i) {
+ vpe_update_scale_coef(vpe_dev, (uint32_t *)iter);
+ iter += VPE_SCALER_CONFIG_LEN;
+ }
+ vpe_input_plane_config(vpe_dev, (uint32_t *)iter);
+ iter += VPE_INPUT_PLANE_CFG_LEN;
+ vpe_output_plane_config(vpe_dev, (uint32_t *)iter);
+ iter += VPE_OUTPUT_PLANE_CFG_LEN;
+ vpe_operation_config(vpe_dev, (uint32_t *)iter);
+}
+
+static int msm_vpe_send_frame_to_hardware(struct vpe_device *vpe_dev,
+ struct msm_queue_cmd *frame_qcmd)
+{
+ struct msm_vpe_frame_info_t *process_frame;
+
+ if (vpe_dev->processing_q.len < MAX_VPE_PROCESSING_FRAME) {
+ process_frame = frame_qcmd->command;
+ msm_enqueue(&vpe_dev->processing_q,
+ &frame_qcmd->list_frame);
+
+ vpe_update_scaler_params(vpe_dev, process_frame->strip_info);
+ vpe_program_buffer_addresses(
+ vpe_dev,
+ process_frame->src_phyaddr,
+ process_frame->src_phyaddr
+ + process_frame->src_chroma_plane_offset,
+ process_frame->dest_phyaddr,
+ process_frame->dest_phyaddr
+ + process_frame->dest_chroma_plane_offset);
+ vpe_start(vpe_dev);
+ do_gettimeofday(&(process_frame->in_time));
+ }
+ return 0;
+}
+
+static int msm_vpe_cfg(struct vpe_device *vpe_dev,
+ struct msm_camera_v4l2_ioctl_t *ioctl_ptr)
+{
+ int rc = 0;
+ struct msm_queue_cmd *frame_qcmd = NULL;
+ struct msm_vpe_frame_info_t *new_frame =
+ kzalloc(sizeof(struct msm_vpe_frame_info_t), GFP_KERNEL);
+ unsigned long in_phyaddr, out_phyaddr;
+ struct msm_buf_mngr_info buff_mgr_info;
+
+ if (!new_frame) {
+ pr_err("Insufficient memory. return\n");
+ return -ENOMEM;
+ }
+
+ rc = copy_from_user(new_frame, (void __user *)ioctl_ptr->ioctl_ptr,
+ sizeof(struct msm_vpe_frame_info_t));
+ if (rc) {
+ pr_err("%s:%d copy from user\n", __func__, __LINE__);
+ rc = -EINVAL;
+ goto err_free_new_frame;
+ }
+
+ in_phyaddr = msm_vpe_fetch_buffer_info(vpe_dev,
+ &new_frame->input_buffer_info,
+ ((new_frame->identity >> 16) & 0xFFFF),
+ (new_frame->identity & 0xFFFF));
+ if (!in_phyaddr) {
+ pr_err("error gettting input physical address\n");
+ rc = -EINVAL;
+ goto err_free_new_frame;
+ }
+
+ memset(&new_frame->output_buffer_info, 0,
+ sizeof(struct msm_vpe_buffer_info_t));
+ memset(&buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info));
+ buff_mgr_info.session_id = ((new_frame->identity >> 16) & 0xFFFF);
+ buff_mgr_info.stream_id = (new_frame->identity & 0xFFFF);
+ rc = msm_vpe_buffer_ops(vpe_dev, VIDIOC_MSM_BUF_MNGR_GET_BUF,
+ &buff_mgr_info);
+ if (rc < 0) {
+ pr_err("error getting buffer\n");
+ rc = -EINVAL;
+ goto err_free_new_frame;
+ }
+
+ new_frame->output_buffer_info.index = buff_mgr_info.index;
+ out_phyaddr = msm_vpe_fetch_buffer_info(vpe_dev,
+ &new_frame->output_buffer_info,
+ ((new_frame->identity >> 16) & 0xFFFF),
+ (new_frame->identity & 0xFFFF));
+ if (!out_phyaddr) {
+ pr_err("error gettting output physical address\n");
+ rc = -EINVAL;
+ goto err_put_buf;
+ }
+
+ new_frame->src_phyaddr = in_phyaddr;
+ new_frame->dest_phyaddr = out_phyaddr;
+
+ frame_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+ if (!frame_qcmd) {
+ pr_err("Insufficient memory. return\n");
+ rc = -ENOMEM;
+ goto err_put_buf;
+ }
+
+ atomic_set(&frame_qcmd->on_heap, 1);
+ frame_qcmd->command = new_frame;
+ rc = msm_vpe_send_frame_to_hardware(vpe_dev, frame_qcmd);
+ if (rc < 0) {
+ pr_err("error cannot send frame to hardware\n");
+ rc = -EINVAL;
+ goto err_free_frame_qcmd;
+ }
+
+ return rc;
+
+err_free_frame_qcmd:
+ kfree(frame_qcmd);
+err_put_buf:
+ msm_vpe_buffer_ops(vpe_dev, VIDIOC_MSM_BUF_MNGR_PUT_BUF,
+ &buff_mgr_info);
+err_free_new_frame:
+ kfree(new_frame);
+ return rc;
+}
+
+static long msm_vpe_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct vpe_device *vpe_dev = v4l2_get_subdevdata(sd);
+ struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
+ int rc = 0;
+
+ mutex_lock(&vpe_dev->mutex);
+ switch (cmd) {
+ case VIDIOC_MSM_VPE_TRANSACTION_SETUP: {
+ struct msm_vpe_transaction_setup_cfg *cfg;
+ VPE_DBG("VIDIOC_MSM_VPE_TRANSACTION_SETUP\n");
+ if (sizeof(*cfg) != ioctl_ptr->len) {
+ pr_err("%s: size mismatch cmd=%d, len=%d, expected=%d",
+ __func__, cmd, ioctl_ptr->len,
+ sizeof(*cfg));
+ rc = -EINVAL;
+ break;
+ }
+
+ cfg = kzalloc(ioctl_ptr->len, GFP_KERNEL);
+ if (!cfg) {
+ pr_err("%s:%d: malloc error\n", __func__, __LINE__);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ rc = copy_from_user(cfg, (void __user *)ioctl_ptr->ioctl_ptr,
+ ioctl_ptr->len);
+ if (rc) {
+ pr_err("%s:%d copy from user\n", __func__, __LINE__);
+ kfree(cfg);
+ break;
+ }
+
+ msm_vpe_transaction_setup(vpe_dev, (void *)cfg);
+ kfree(cfg);
+ break;
+ }
+ case VIDIOC_MSM_VPE_CFG: {
+ VPE_DBG("VIDIOC_MSM_VPE_CFG\n");
+ rc = msm_vpe_cfg(vpe_dev, ioctl_ptr);
+ break;
+ }
+ case VIDIOC_MSM_VPE_ENQUEUE_STREAM_BUFF_INFO: {
+ struct msm_vpe_stream_buff_info_t *u_stream_buff_info;
+ struct msm_vpe_stream_buff_info_t k_stream_buff_info;
+
+ VPE_DBG("VIDIOC_MSM_VPE_ENQUEUE_STREAM_BUFF_INFO\n");
+
+ if (sizeof(struct msm_vpe_stream_buff_info_t) !=
+ ioctl_ptr->len) {
+ pr_err("%s:%d: invalid length\n", __func__, __LINE__);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ u_stream_buff_info = kzalloc(ioctl_ptr->len, GFP_KERNEL);
+ if (!u_stream_buff_info) {
+ pr_err("%s:%d: malloc error\n", __func__, __LINE__);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ rc = (copy_from_user(u_stream_buff_info,
+ (void __user *)ioctl_ptr->ioctl_ptr,
+ ioctl_ptr->len) ? -EFAULT : 0);
+ if (rc) {
+ pr_err("%s:%d copy from user\n", __func__, __LINE__);
+ kfree(u_stream_buff_info);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ k_stream_buff_info.num_buffs = u_stream_buff_info->num_buffs;
+ k_stream_buff_info.identity = u_stream_buff_info->identity;
+ k_stream_buff_info.buffer_info =
+ kzalloc(k_stream_buff_info.num_buffs *
+ sizeof(struct msm_vpe_buffer_info_t), GFP_KERNEL);
+ if (!k_stream_buff_info.buffer_info) {
+ pr_err("%s:%d: malloc error\n", __func__, __LINE__);
+ kfree(u_stream_buff_info);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ rc = (copy_from_user(k_stream_buff_info.buffer_info,
+ (void __user *)u_stream_buff_info->buffer_info,
+ k_stream_buff_info.num_buffs *
+ sizeof(struct msm_vpe_buffer_info_t)) ?
+ -EFAULT : 0);
+ if (rc) {
+ pr_err("%s:%d copy from user\n", __func__, __LINE__);
+ kfree(k_stream_buff_info.buffer_info);
+ kfree(u_stream_buff_info);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ rc = msm_vpe_add_buff_queue_entry(vpe_dev,
+ ((k_stream_buff_info.identity >> 16) & 0xFFFF),
+ (k_stream_buff_info.identity & 0xFFFF));
+ if (!rc)
+ rc = msm_vpe_enqueue_buff_info_list(vpe_dev,
+ &k_stream_buff_info);
+
+ kfree(k_stream_buff_info.buffer_info);
+ kfree(u_stream_buff_info);
+ break;
+ }
+ case VIDIOC_MSM_VPE_DEQUEUE_STREAM_BUFF_INFO: {
+ uint32_t identity;
+ struct msm_vpe_buff_queue_info_t *buff_queue_info;
+
+ VPE_DBG("VIDIOC_MSM_VPE_DEQUEUE_STREAM_BUFF_INFO\n");
+
+ rc = (copy_from_user(&identity,
+ (void __user *)ioctl_ptr->ioctl_ptr,
+ ioctl_ptr->len) ? -EFAULT : 0);
+ if (rc) {
+ pr_err("%s:%d copy from user\n", __func__, __LINE__);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ buff_queue_info = msm_vpe_get_buff_queue_entry(vpe_dev,
+ ((identity >> 16) & 0xFFFF), (identity & 0xFFFF));
+ if (buff_queue_info == NULL) {
+ pr_err("error finding buffer queue entry for identity:%d\n",
+ identity);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ msm_vpe_dequeue_buff_info_list(vpe_dev, buff_queue_info);
+ rc = msm_vpe_free_buff_queue_entry(vpe_dev,
+ buff_queue_info->session_id,
+ buff_queue_info->stream_id);
+ break;
+ }
+ case VIDIOC_MSM_VPE_GET_EVENTPAYLOAD: {
+ struct msm_device_queue *queue = &vpe_dev->eventData_q;
+ struct msm_queue_cmd *event_qcmd;
+ struct msm_vpe_frame_info_t *process_frame;
+ VPE_DBG("VIDIOC_MSM_VPE_GET_EVENTPAYLOAD\n");
+ event_qcmd = msm_dequeue(queue, list_eventdata);
+ process_frame = event_qcmd->command;
+ VPE_DBG("fid %d\n", process_frame->frame_id);
+ if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
+ process_frame,
+ sizeof(struct msm_vpe_frame_info_t))) {
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ kfree(process_frame);
+ kfree(event_qcmd);
+ break;
+ }
+ }
+ mutex_unlock(&vpe_dev->mutex);
+ return rc;
+}
+
+static int msm_vpe_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ return v4l2_event_subscribe(fh, sub, MAX_VPE_V4l2_EVENTS);
+}
+
+static int msm_vpe_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ return v4l2_event_unsubscribe(fh, sub);
+}
+
+static struct v4l2_subdev_core_ops msm_vpe_subdev_core_ops = {
+ .ioctl = msm_vpe_subdev_ioctl,
+ .subscribe_event = msm_vpe_subscribe_event,
+ .unsubscribe_event = msm_vpe_unsubscribe_event,
+};
+
+static const struct v4l2_subdev_ops msm_vpe_subdev_ops = {
+ .core = &msm_vpe_subdev_core_ops,
+};
+
+static struct v4l2_file_operations msm_vpe_v4l2_subdev_fops;
+
+static long msm_vpe_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct v4l2_fh *vfh = file->private_data;
+
+ switch (cmd) {
+ case VIDIOC_DQEVENT:
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
+ return -ENOIOCTLCMD;
+
+ return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);
+
+ case VIDIOC_SUBSCRIBE_EVENT:
+ return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);
+
+ case VIDIOC_UNSUBSCRIBE_EVENT:
+ return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);
+ case VIDIOC_MSM_VPE_GET_INST_INFO: {
+ uint32_t i;
+ struct vpe_device *vpe_dev = v4l2_get_subdevdata(sd);
+ struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
+ struct msm_vpe_frame_info_t inst_info;
+ for (i = 0; i < MAX_ACTIVE_VPE_INSTANCE; i++) {
+ if (vpe_dev->vpe_subscribe_list[i].vfh == vfh) {
+ inst_info.inst_id = i;
+ break;
+ }
+ }
+ if (copy_to_user(
+ (void __user *)ioctl_ptr->ioctl_ptr, &inst_info,
+ sizeof(struct msm_vpe_frame_info_t))) {
+ return -EINVAL;
+ }
+ }
+ default:
+ return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+ }
+
+ return 0;
+}
+
+static long msm_vpe_subdev_fops_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_vpe_subdev_do_ioctl);
+}
+
+static int vpe_register_domain(void)
+{
+ struct msm_iova_partition vpe_iommu_partition = {
+ /* TODO: verify that these are correct? */
+ .start = SZ_128K,
+ .size = SZ_2G - SZ_128K,
+ };
+ struct msm_iova_layout vpe_iommu_layout = {
+ .partitions = &vpe_iommu_partition,
+ .npartitions = 1,
+ .client_name = "camera_vpe",
+ .domain_flags = 0,
+ };
+
+ return msm_register_domain(&vpe_iommu_layout);
+}
+
+static int __devinit vpe_probe(struct platform_device *pdev)
+{
+ struct vpe_device *vpe_dev;
+ int rc = 0;
+
+ vpe_dev = kzalloc(sizeof(struct vpe_device), GFP_KERNEL);
+ if (!vpe_dev) {
+ pr_err("not enough memory\n");
+ return -ENOMEM;
+ }
+
+ vpe_dev->vpe_clk = kzalloc(sizeof(struct clk *) *
+ ARRAY_SIZE(vpe_clk_info), GFP_KERNEL);
+ if (!vpe_dev->vpe_clk) {
+ pr_err("not enough memory\n");
+ rc = -ENOMEM;
+ goto err_free_vpe_dev;
+ }
+
+ v4l2_subdev_init(&vpe_dev->msm_sd.sd, &msm_vpe_subdev_ops);
+ vpe_dev->msm_sd.sd.internal_ops = &msm_vpe_internal_ops;
+ snprintf(vpe_dev->msm_sd.sd.name, ARRAY_SIZE(vpe_dev->msm_sd.sd.name),
+ "vpe");
+ vpe_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ vpe_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
+ v4l2_set_subdevdata(&vpe_dev->msm_sd.sd, vpe_dev);
+ platform_set_drvdata(pdev, &vpe_dev->msm_sd.sd);
+ mutex_init(&vpe_dev->mutex);
+ spin_lock_init(&vpe_dev->tasklet_lock);
+
+ vpe_dev->pdev = pdev;
+
+ vpe_dev->mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "vpe");
+ if (!vpe_dev->mem) {
+ pr_err("no mem resource?\n");
+ rc = -ENODEV;
+ goto err_free_vpe_clk;
+ }
+
+ vpe_dev->irq = platform_get_resource_byname(pdev,
+ IORESOURCE_IRQ, "vpe");
+ if (!vpe_dev->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto err_release_mem;
+ }
+
+ vpe_dev->domain_num = vpe_register_domain();
+ if (vpe_dev->domain_num < 0) {
+ pr_err("%s: could not register domain\n", __func__);
+ rc = -ENODEV;
+ goto err_release_mem;
+ }
+
+ vpe_dev->domain =
+ msm_get_iommu_domain(vpe_dev->domain_num);
+ if (!vpe_dev->domain) {
+ pr_err("%s: cannot find domain\n", __func__);
+ rc = -ENODEV;
+ goto err_release_mem;
+ }
+
+ vpe_dev->iommu_ctx_src = msm_iommu_get_ctx("vpe_src");
+ vpe_dev->iommu_ctx_dst = msm_iommu_get_ctx("vpe_dst");
+ if (!vpe_dev->iommu_ctx_src || !vpe_dev->iommu_ctx_dst) {
+ pr_err("%s: cannot get iommu_ctx\n", __func__);
+ rc = -ENODEV;
+ goto err_release_mem;
+ }
+
+ media_entity_init(&vpe_dev->msm_sd.sd.entity, 0, NULL, 0);
+ vpe_dev->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ vpe_dev->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_VPE;
+ vpe_dev->msm_sd.sd.entity.name = pdev->name;
+ msm_sd_register(&vpe_dev->msm_sd);
+ msm_vpe_v4l2_subdev_fops.owner = v4l2_subdev_fops.owner;
+ msm_vpe_v4l2_subdev_fops.open = v4l2_subdev_fops.open;
+ msm_vpe_v4l2_subdev_fops.unlocked_ioctl = msm_vpe_subdev_fops_ioctl;
+ msm_vpe_v4l2_subdev_fops.release = v4l2_subdev_fops.release;
+ msm_vpe_v4l2_subdev_fops.poll = v4l2_subdev_fops.poll;
+
+ vpe_dev->msm_sd.sd.devnode->fops = &msm_vpe_v4l2_subdev_fops;
+ vpe_dev->msm_sd.sd.entity.revision = vpe_dev->msm_sd.sd.devnode->num;
+ vpe_dev->state = VPE_STATE_BOOT;
+ rc = vpe_init_hardware(vpe_dev);
+ if (rc < 0) {
+ pr_err("%s: Couldn't init vpe hardware\n", __func__);
+ rc = -ENODEV;
+ goto err_unregister_sd;
+ }
+ vpe_reset(vpe_dev);
+ vpe_release_hardware(vpe_dev);
+ vpe_dev->state = VPE_STATE_OFF;
+
+ rc = iommu_attach_device(vpe_dev->domain, vpe_dev->iommu_ctx_src);
+ if (rc < 0) {
+ pr_err("Couldn't attach to vpe_src context bank\n");
+ rc = -ENODEV;
+ goto err_unregister_sd;
+ }
+ rc = iommu_attach_device(vpe_dev->domain, vpe_dev->iommu_ctx_dst);
+ if (rc < 0) {
+ pr_err("Couldn't attach to vpe_dst context bank\n");
+ rc = -ENODEV;
+ goto err_detach_src;
+ }
+
+ vpe_dev->state = VPE_STATE_OFF;
+
+ msm_queue_init(&vpe_dev->eventData_q, "vpe-eventdata");
+ msm_queue_init(&vpe_dev->processing_q, "vpe-frame");
+ INIT_LIST_HEAD(&vpe_dev->tasklet_q);
+ tasklet_init(&vpe_dev->vpe_tasklet, msm_vpe_do_tasklet,
+ (unsigned long)vpe_dev);
+ vpe_dev->vpe_open_cnt = 0;
+
+ return rc;
+
+err_detach_src:
+ iommu_detach_device(vpe_dev->domain, vpe_dev->iommu_ctx_src);
+err_unregister_sd:
+ msm_sd_unregister(&vpe_dev->msm_sd);
+err_release_mem:
+ release_mem_region(vpe_dev->mem->start, resource_size(vpe_dev->mem));
+err_free_vpe_clk:
+ kfree(vpe_dev->vpe_clk);
+err_free_vpe_dev:
+ kfree(vpe_dev);
+ return rc;
+}
+
+static int vpe_device_remove(struct platform_device *dev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(dev);
+ struct vpe_device *vpe_dev;
+ if (!sd) {
+ pr_err("%s: Subdevice is NULL\n", __func__);
+ return 0;
+ }
+
+ vpe_dev = (struct vpe_device *)v4l2_get_subdevdata(sd);
+ if (!vpe_dev) {
+ pr_err("%s: vpe device is NULL\n", __func__);
+ return 0;
+ }
+
+ iommu_detach_device(vpe_dev->domain, vpe_dev->iommu_ctx_dst);
+ iommu_detach_device(vpe_dev->domain, vpe_dev->iommu_ctx_src);
+ msm_sd_unregister(&vpe_dev->msm_sd);
+ release_mem_region(vpe_dev->mem->start, resource_size(vpe_dev->mem));
+ mutex_destroy(&vpe_dev->mutex);
+ kfree(vpe_dev);
+ return 0;
+}
+
+static struct platform_driver vpe_driver = {
+ .probe = vpe_probe,
+ .remove = __devexit_p(vpe_device_remove),
+ .driver = {
+ .name = MSM_VPE_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_vpe_init_module(void)
+{
+ return platform_driver_register(&vpe_driver);
+}
+
+static void __exit msm_vpe_exit_module(void)
+{
+ platform_driver_unregister(&vpe_driver);
+}
+
+module_init(msm_vpe_init_module);
+module_exit(msm_vpe_exit_module);
+MODULE_DESCRIPTION("MSM VPE driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.h b/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.h
new file mode 100644
index 0000000..c02432e
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.h
@@ -0,0 +1,255 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#ifndef __MSM_VPE_H__
+#define __MSM_VPE_H__
+
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-subdev.h>
+#include "msm_sd.h"
+
+/*********** start of register offset *********************/
+#define VPE_INTR_ENABLE_OFFSET 0x0020
+#define VPE_INTR_STATUS_OFFSET 0x0024
+#define VPE_INTR_CLEAR_OFFSET 0x0028
+#define VPE_DL0_START_OFFSET 0x0030
+#define VPE_HW_VERSION_OFFSET 0x0070
+#define VPE_SW_RESET_OFFSET 0x0074
+#define VPE_AXI_RD_ARB_CONFIG_OFFSET 0x0078
+#define VPE_SEL_CLK_OR_HCLK_TEST_BUS_OFFSET 0x007C
+#define VPE_CGC_EN_OFFSET 0x0100
+#define VPE_CMD_STATUS_OFFSET 0x10008
+#define VPE_PROFILE_EN_OFFSET 0x10010
+#define VPE_PROFILE_COUNT_OFFSET 0x10014
+#define VPE_CMD_MODE_OFFSET 0x10060
+#define VPE_SRC_SIZE_OFFSET 0x10108
+#define VPE_SRCP0_ADDR_OFFSET 0x1010C
+#define VPE_SRCP1_ADDR_OFFSET 0x10110
+#define VPE_SRC_YSTRIDE1_OFFSET 0x1011C
+#define VPE_SRC_FORMAT_OFFSET 0x10124
+#define VPE_SRC_UNPACK_PATTERN1_OFFSET 0x10128
+#define VPE_OP_MODE_OFFSET 0x10138
+#define VPE_SCALE_PHASEX_INIT_OFFSET 0x1013C
+#define VPE_SCALE_PHASEY_INIT_OFFSET 0x10140
+#define VPE_SCALE_PHASEX_STEP_OFFSET 0x10144
+#define VPE_SCALE_PHASEY_STEP_OFFSET 0x10148
+#define VPE_OUT_FORMAT_OFFSET 0x10150
+#define VPE_OUT_PACK_PATTERN1_OFFSET 0x10154
+#define VPE_OUT_SIZE_OFFSET 0x10164
+#define VPE_OUTP0_ADDR_OFFSET 0x10168
+#define VPE_OUTP1_ADDR_OFFSET 0x1016C
+#define VPE_OUT_YSTRIDE1_OFFSET 0x10178
+#define VPE_OUT_XY_OFFSET 0x1019C
+#define VPE_SRC_XY_OFFSET 0x10200
+#define VPE_SRC_IMAGE_SIZE_OFFSET 0x10208
+#define VPE_SCALE_CONFIG_OFFSET 0x10230
+#define VPE_DEINT_STATUS_OFFSET 0x30000
+#define VPE_DEINT_DECISION_OFFSET 0x30004
+#define VPE_DEINT_COEFF0_OFFSET 0x30010
+#define VPE_SCALE_STATUS_OFFSET 0x50000
+#define VPE_SCALE_SVI_PARAM_OFFSET 0x50010
+#define VPE_SCALE_SHARPEN_CFG_OFFSET 0x50020
+#define VPE_SCALE_COEFF_LSP_0_OFFSET 0x50400
+#define VPE_SCALE_COEFF_MSP_0_OFFSET 0x50404
+
+#define VPE_AXI_ARB_1_OFFSET 0x00408
+#define VPE_AXI_ARB_2_OFFSET 0x0040C
+
+#define VPE_SCALE_COEFF_LSBn(n) (0x50400 + 8 * (n))
+#define VPE_SCALE_COEFF_MSBn(n) (0x50404 + 8 * (n))
+#define VPE_SCALE_COEFF_NUM 32
+
+/*********** end of register offset ********************/
+
+
+#define VPE_HARDWARE_VERSION 0x00080308
+#define VPE_SW_RESET_VALUE 0x00000010 /* bit 4 for PPP*/
+#define VPE_AXI_RD_ARB_CONFIG_VALUE 0x124924
+#define VPE_CMD_MODE_VALUE 0x1
+#define VPE_DEFAULT_OP_MODE_VALUE 0x40FC0004
+#define VPE_CGC_ENABLE_VALUE 0xffff
+#define VPE_DEFAULT_SCALE_CONFIG 0x3c
+
+#define VPE_NORMAL_MODE_CLOCK_RATE 150000000
+#define VPE_TURBO_MODE_CLOCK_RATE 200000000
+#define VPE_SUBDEV_MAX_EVENTS 30
+
+/**************************************************/
+/*********** End of command id ********************/
+/**************************************************/
+
+#define SCALER_PHASE_BITS 29
+#define HAL_MDP_PHASE_STEP_2P50 0x50000000
+#define HAL_MDP_PHASE_STEP_1P66 0x35555555
+#define HAL_MDP_PHASE_STEP_1P25 0x28000000
+
+
+#define MAX_ACTIVE_VPE_INSTANCE 8
+#define MAX_VPE_PROCESSING_FRAME 2
+#define MAX_VPE_V4l2_EVENTS 30
+
+#define MSM_VPE_TASKLETQ_SIZE 16
+
+/**
+ * The format of the msm_vpe_transaction_setup_cfg is as follows:
+ *
+ * - vpe_update_scale_coef (65*4 uint32_t's)
+ * - Each table is 65 uint32_t's long
+ * - 1st uint32_t in each table indicates offset
+ * - Following 64 uint32_t's are the data
+ *
+ * - vpe_input_plane_config (6 uint32_t's)
+ * - VPE_SRC_FORMAT_OFFSET
+ * - VPE_SRC_UNPACK_PATTERN1_OFFSET
+ * - VPE_SRC_IMAGE_SIZE_OFFSET
+ * - VPE_SRC_YSTRIDE1_OFFSET
+ * - VPE_SRC_SIZE_OFFSET
+ * - VPE_SRC_XY_OFFSET
+ *
+ * - vpe_output_plane_config (5 uint32_t's)
+ * - VPE_OUT_FORMAT_OFFSET
+ * - VPE_OUT_PACK_PATTERN1_OFFSET
+ * - VPE_OUT_YSTRIDE1_OFFSET
+ * - VPE_OUT_SIZE_OFFSET
+ * - VPE_OUT_XY_OFFSET
+ *
+ * - vpe_operation_config (1 uint32_t)
+ * - VPE_OP_MODE_OFFSET
+ *
+ */
+
+#define VPE_SCALER_CONFIG_LEN 260
+#define VPE_INPUT_PLANE_CFG_LEN 24
+#define VPE_OUTPUT_PLANE_CFG_LEN 20
+#define VPE_OPERATION_MODE_CFG_LEN 4
+#define VPE_NUM_SCALER_TABLES 4
+
+#define VPE_TRANSACTION_SETUP_CONFIG_LEN ( \
+ (VPE_SCALER_CONFIG_LEN * VPE_NUM_SCALER_TABLES) \
+ + VPE_INPUT_PLANE_CFG_LEN \
+ + VPE_OUTPUT_PLANE_CFG_LEN \
+ + VPE_OPERATION_MODE_CFG_LEN)
+/* VPE_TRANSACTION_SETUP_CONFIG_LEN = 1088 */
+
+struct msm_vpe_transaction_setup_cfg {
+ uint8_t scaler_cfg[VPE_TRANSACTION_SETUP_CONFIG_LEN];
+};
+
+struct vpe_subscribe_info {
+ struct v4l2_fh *vfh;
+ uint32_t active;
+};
+
+enum vpe_state {
+ VPE_STATE_BOOT,
+ VPE_STATE_IDLE,
+ VPE_STATE_ACTIVE,
+ VPE_STATE_OFF,
+};
+
+struct msm_queue_cmd {
+ struct list_head list_config;
+ struct list_head list_control;
+ struct list_head list_frame;
+ struct list_head list_pict;
+ struct list_head list_vpe_frame;
+ struct list_head list_eventdata;
+ void *command;
+ atomic_t on_heap;
+ struct timespec ts;
+ uint32_t error_code;
+ uint32_t trans_code;
+};
+
+struct msm_device_queue {
+ struct list_head list;
+ spinlock_t lock;
+ wait_queue_head_t wait;
+ int max;
+ int len;
+ const char *name;
+};
+
+struct msm_vpe_tasklet_queue_cmd {
+ struct list_head list;
+ uint32_t irq_status;
+ uint8_t cmd_used;
+};
+
+struct msm_vpe_buffer_map_info_t {
+ unsigned long len;
+ unsigned long phy_addr;
+ struct ion_handle *ion_handle;
+ struct msm_vpe_buffer_info_t buff_info;
+};
+
+struct msm_vpe_buffer_map_list_t {
+ struct msm_vpe_buffer_map_info_t map_info;
+ struct list_head entry;
+};
+
+struct msm_vpe_buff_queue_info_t {
+ uint32_t used;
+ uint16_t session_id;
+ uint16_t stream_id;
+ struct list_head vb2_buff_head;
+ struct list_head native_buff_head;
+};
+
+struct vpe_device {
+ struct platform_device *pdev;
+ struct msm_sd_subdev msm_sd;
+ struct v4l2_subdev subdev;
+ struct resource *mem;
+ struct resource *irq;
+ void __iomem *base;
+ struct clk **vpe_clk;
+ struct regulator *fs_vpe;
+ struct mutex mutex;
+ enum vpe_state state;
+
+ int domain_num;
+ struct iommu_domain *domain;
+ struct device *iommu_ctx_src;
+ struct device *iommu_ctx_dst;
+ struct ion_client *client;
+ struct kref refcount;
+
+ /* Reusing proven tasklet from msm isp */
+ atomic_t irq_cnt;
+ uint8_t taskletq_idx;
+ spinlock_t tasklet_lock;
+ struct list_head tasklet_q;
+ struct tasklet_struct vpe_tasklet;
+ struct msm_vpe_tasklet_queue_cmd
+ tasklet_queue_cmd[MSM_VPE_TASKLETQ_SIZE];
+
+ struct vpe_subscribe_info vpe_subscribe_list[MAX_ACTIVE_VPE_INSTANCE];
+ uint32_t vpe_open_cnt;
+
+ struct msm_device_queue eventData_q; /* V4L2 Event Payload Queue */
+
+ /*
+ * Processing Queue: store frame info for frames sent to
+ * microcontroller
+ */
+ struct msm_device_queue processing_q;
+
+ struct msm_vpe_buff_queue_info_t *buff_queue;
+ uint32_t num_buffq;
+ struct v4l2_subdev *buf_mgr_subdev;
+};
+
+#endif /* __MSM_VPE_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
index e1b978f..300daca 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
@@ -435,6 +435,8 @@
return -EFAULT;
}
+ kfree(a_ctrl->i2c_reg_tbl);
+
a_ctrl->i2c_reg_tbl =
kmalloc(sizeof(struct msm_camera_i2c_reg_tbl) *
(set_info->af_tuning_params.total_steps + 1), GFP_KERNEL);
@@ -608,6 +610,9 @@
if (rc < 0)
pr_err("cci_init failed\n");
}
+ kfree(a_ctrl->i2c_reg_tbl);
+ a_ctrl->i2c_reg_tbl = NULL;
+
CDBG("Exit\n");
return rc;
}
@@ -629,6 +634,9 @@
return msm_actuator_get_subdev_id(a_ctrl, argp);
case VIDIOC_MSM_ACTUATOR_CFG:
return msm_actuator_config(a_ctrl, argp);
+ case MSM_SD_SHUTDOWN:
+ msm_actuator_close(sd, NULL);
+ return 0;
default:
return -ENOIOCTLCMD;
}
@@ -710,6 +718,7 @@
media_entity_init(&act_ctrl_t->msm_sd.sd.entity, 0, NULL, 0);
act_ctrl_t->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
act_ctrl_t->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_ACTUATOR;
+ act_ctrl_t->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2;
msm_sd_register(&act_ctrl_t->msm_sd);
CDBG("succeeded\n");
CDBG("Exit\n");
@@ -780,6 +789,7 @@
media_entity_init(&msm_actuator_t->msm_sd.sd.entity, 0, NULL, 0);
msm_actuator_t->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
msm_actuator_t->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_ACTUATOR;
+ msm_actuator_t->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2;
msm_sd_register(&msm_actuator_t->msm_sd);
CDBG("Exit\n");
return rc;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cam_cci_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cam_cci_hwreg.h
index 642df76..591c464 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cam_cci_hwreg.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cam_cci_hwreg.h
@@ -43,10 +43,6 @@
#define CCI_IRQ_MASK_0_RMSK 0x7fff7ff7
#define CCI_IRQ_CLEAR_0_ADDR 0x00000c08
#define CCI_IRQ_STATUS_0_ADDR 0x00000c0c
-#define CCI_IRQ_STATUS_0_I2C_M1_Q1_NACK_ERR_BMSK 0x40000000
-#define CCI_IRQ_STATUS_0_I2C_M1_Q0_NACK_ERR_BMSK 0x20000000
-#define CCI_IRQ_STATUS_0_I2C_M0_Q1_NACK_ERR_BMSK 0x10000000
-#define CCI_IRQ_STATUS_0_I2C_M0_Q0_NACK_ERR_BMSK 0x8000000
#define CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK 0x4000000
#define CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK 0x2000000
#define CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK 0x1000000
@@ -55,6 +51,8 @@
#define CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK 0x1000
#define CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK 0x100
#define CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK 0x10
+#define CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK 0x18000EE6
+#define CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK 0x60EE6000
#define CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK 0x1
#define CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x00000c00
#endif /* __MSM_CAM_CCI_HWREG__ */
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
index 9300ce0..676862f 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
@@ -271,6 +271,21 @@
master = c_ctrl->cci_info->cci_i2c_master;
read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg;
mutex_lock(&cci_dev->cci_master_info[master].mutex);
+
+ /*
+ * Call validate queue to make sure queue is empty before starting.
+ * If this call fails, don't proceed with i2c_read call. This is to
+ * avoid overflow / underflow of queue
+ */
+ rc = msm_cci_validate_queue(cci_dev,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size - 1,
+ master, queue);
+ if (rc < 0) {
+ pr_err("%s:%d Initial validataion failed rc %d\n", __func__,
+ __LINE__, rc);
+ goto ERROR;
+ }
+
CDBG("%s master %d, queue %d\n", __func__, master, queue);
CDBG("%s set param sid 0x%x retries %d id_map %d\n", __func__,
c_ctrl->cci_info->sid, c_ctrl->cci_info->retries,
@@ -292,10 +307,10 @@
}
if (read_cfg->addr_type == MSM_CAMERA_I2C_BYTE_ADDR)
- val = CCI_I2C_WRITE_CMD | (read_cfg->addr_type << 4) |
+ val = CCI_I2C_WRITE_DISABLE_P_CMD | (read_cfg->addr_type << 4) |
((read_cfg->addr & 0xFF) << 8);
if (read_cfg->addr_type == MSM_CAMERA_I2C_WORD_ADDR)
- val = CCI_I2C_WRITE_CMD | (read_cfg->addr_type << 4) |
+ val = CCI_I2C_WRITE_DISABLE_P_CMD | (read_cfg->addr_type << 4) |
(((read_cfg->addr & 0xFF00) >> 8) << 8) |
((read_cfg->addr & 0xFF) << 16);
rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
@@ -450,6 +465,21 @@
c_ctrl->cci_info->sid, c_ctrl->cci_info->retries,
c_ctrl->cci_info->id_map);
mutex_lock(&cci_dev->cci_master_info[master].mutex);
+
+ /*
+ * Call validate queue to make sure queue is empty before starting.
+ * If this call fails, don't proceed with i2c_write call. This is to
+ * avoid overflow / underflow of queue
+ */
+ rc = msm_cci_validate_queue(cci_dev,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size - 1,
+ master, queue);
+ if (rc < 0) {
+ pr_err("%s:%d Initial validataion failed rc %d\n", __func__,
+ __LINE__, rc);
+ goto ERROR;
+ }
+
val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 |
c_ctrl->cci_info->retries << 16 |
c_ctrl->cci_info->id_map << 18;
@@ -684,40 +714,40 @@
complete(&cci_dev->cci_master_info[MASTER_1].
reset_complete);
}
- } else if ((irq & CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK) ||
+ }
+ if ((irq & CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK) ||
(irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK) ||
(irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK)) {
cci_dev->cci_master_info[MASTER_0].status = 0;
complete(&cci_dev->cci_master_info[MASTER_0].reset_complete);
- } else if ((irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) ||
+ }
+ if ((irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) ||
(irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK) ||
(irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK)) {
cci_dev->cci_master_info[MASTER_1].status = 0;
complete(&cci_dev->cci_master_info[MASTER_1].reset_complete);
- } else if ((irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_NACK_ERR_BMSK) ||
- (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_NACK_ERR_BMSK)) {
- cci_dev->cci_master_info[MASTER_0].status = -EINVAL;
- msm_camera_io_w(CCI_M0_HALT_REQ_RMSK,
- cci_dev->base + CCI_HALT_REQ_ADDR);
- } else if ((irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_NACK_ERR_BMSK) ||
- (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_NACK_ERR_BMSK)) {
- cci_dev->cci_master_info[MASTER_1].status = -EINVAL;
- msm_camera_io_w(CCI_M1_HALT_REQ_RMSK,
- cci_dev->base + CCI_HALT_REQ_ADDR);
- } else if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) {
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) {
cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
msm_camera_io_w(CCI_M0_RESET_RMSK,
cci_dev->base + CCI_RESET_CMD_ADDR);
- } else if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK) {
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK) {
cci_dev->cci_master_info[MASTER_1].reset_pending = TRUE;
msm_camera_io_w(CCI_M1_RESET_RMSK,
cci_dev->base + CCI_RESET_CMD_ADDR);
- } else {
- pr_err("%s unhandled irq 0x%x\n", __func__, irq);
- cci_dev->cci_master_info[MASTER_0].status = 0;
- complete(&cci_dev->cci_master_info[MASTER_0].reset_complete);
- cci_dev->cci_master_info[MASTER_1].status = 0;
- complete(&cci_dev->cci_master_info[MASTER_1].reset_complete);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK) {
+ pr_err("%s:%d MASTER_0 error %x\n", __func__, __LINE__, irq);
+ cci_dev->cci_master_info[MASTER_0].status = -EINVAL;
+ msm_camera_io_w(CCI_M0_HALT_REQ_RMSK,
+ cci_dev->base + CCI_HALT_REQ_ADDR);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK) {
+ pr_err("%s:%d MASTER_1 error %x\n", __func__, __LINE__, irq);
+ cci_dev->cci_master_info[MASTER_1].status = -EINVAL;
+ msm_camera_io_w(CCI_M1_HALT_REQ_RMSK,
+ cci_dev->base + CCI_HALT_REQ_ADDR);
}
return IRQ_HANDLED;
}
@@ -743,6 +773,12 @@
case VIDIOC_MSM_CCI_CFG:
rc = msm_cci_config(sd, arg);
break;
+ case MSM_SD_SHUTDOWN: {
+ struct msm_camera_cci_ctrl ctrl_cmd;
+ ctrl_cmd.cmd = MSM_CCI_RELEASE;
+ rc = msm_cci_config(sd, &ctrl_cmd);
+ break;
+ }
default:
rc = -ENOIOCTLCMD;
}
@@ -977,6 +1013,7 @@
goto cci_release_mem;
}
disable_irq(new_cci_dev->irq->start);
+ new_cci_dev->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x6;
msm_sd_register(&new_cci_dev->msm_sd);
new_cci_dev->pdev = pdev;
msm_cci_init_cci_params(new_cci_dev);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/Makefile b/drivers/media/platform/msm/camera_v2/sensor/csid/Makefile
index 572e722..771167d 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csid/Makefile
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/Makefile
@@ -2,6 +2,8 @@
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
ifeq ($(CONFIG_MSM_CSI20_HEADER),y)
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/csid/include/csi2.0
+else ifeq ($(CONFIG_MSM_CSI22_HEADER),y)
+ ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/csid/include/csi2.2
else ifeq ($(CONFIG_MSM_CSI30_HEADER),y)
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/csid/include/csi3.0
endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/include/csi2.2/msm_csid_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csid/include/csi2.2/msm_csid_hwreg.h
new file mode 100644
index 0000000..5b30472
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/include/csi2.2/msm_csid_hwreg.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#ifndef MSM_CSID_HWREG_H
+#define MSM_CSID_HWREG_H
+
+/* MIPI CSID registers */
+#define CSID_HW_VERSION_ADDR 0x0
+#define CSID_CORE_CTRL_0_ADDR 0x4
+#define CSID_CORE_CTRL_1_ADDR 0x4
+#define CSID_RST_CMD_ADDR 0x8
+#define CSID_CID_LUT_VC_0_ADDR 0xc
+#define CSID_CID_LUT_VC_1_ADDR 0x10
+#define CSID_CID_LUT_VC_2_ADDR 0x14
+#define CSID_CID_LUT_VC_3_ADDR 0x18
+#define CSID_CID_n_CFG_ADDR 0x1C
+#define CSID_IRQ_CLEAR_CMD_ADDR 0x5c
+#define CSID_IRQ_MASK_ADDR 0x60
+#define CSID_IRQ_STATUS_ADDR 0x64
+#define CSID_CAPTURED_UNMAPPED_LONG_PKT_HDR_ADDR 0x68
+#define CSID_CAPTURED_MMAPPED_LONG_PKT_HDR_ADDR 0x6c
+#define CSID_CAPTURED_SHORT_PKT_ADDR 0x70
+#define CSID_CAPTURED_LONG_PKT_HDR_ADDR 0x74
+#define CSID_CAPTURED_LONG_PKT_FTR_ADDR 0x78
+#define CSID_PIF_MISR_DL0_ADDR 0x7C
+#define CSID_PIF_MISR_DL1_ADDR 0x80
+#define CSID_PIF_MISR_DL2_ADDR 0x84
+#define CSID_PIF_MISR_DL3_ADDR 0x88
+#define CSID_STATS_TOTAL_PKTS_RCVD_ADDR 0x8C
+#define CSID_STATS_ECC_ADDR 0x90
+#define CSID_STATS_CRC_ADDR 0x94
+#define CSID_TG_CTRL_ADDR 0x9C
+#define CSID_TG_VC_CFG_ADDR 0xA0
+#define CSID_TG_DT_n_CFG_0_ADDR 0xA8
+#define CSID_TG_DT_n_CFG_1_ADDR 0xAC
+#define CSID_TG_DT_n_CFG_2_ADDR 0xB0
+#define CSID_RST_DONE_IRQ_BITSHIFT 11
+#define CSID_RST_STB_ALL 0x7FFF
+#define CSID_DL_INPUT_SEL_SHIFT 0x2
+#define CSID_PHY_SEL_SHIFT 17
+#define CSID_VERSION 0x02001000
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
index fbd4a2e..7af0c14 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
@@ -20,8 +20,9 @@
#include "msm_camera_io_util.h"
#define V4L2_IDENT_CSID 50002
-#define CSID_VERSION_V2 0x02000011
-#define CSID_VERSION_V3 0x30000000
+#define CSID_VERSION_V20 0x02000011
+#define CSID_VERSION_V22 0x02001000
+#define CSID_VERSION_V30 0x30000000
#define MSM_CSID_DRV_NAME "msm_csid"
#define DBG_CSID 0
@@ -185,49 +186,24 @@
{"csi_pclk", -1},
};
-static struct msm_cam_clk_info csid0_8974_clk_info[] = {
+static struct msm_cam_clk_info csid_8974_clk_info[] = {
{"camss_top_ahb_clk", -1},
{"ispif_ahb_clk", -1},
- {"csi0_ahb_clk", -1},
- {"csi0_src_clk", 200000000},
- {"csi0_clk", -1},
- {"csi0_phy_clk", -1},
- {"csi0_pix_clk", -1},
- {"csi0_rdi_clk", -1},
+ {"csi_ahb_clk", -1},
+ {"csi_src_clk", 200000000},
+ {"csi_clk", -1},
+ {"csi_phy_clk", -1},
+ {"csi_pix_clk", -1},
+ {"csi_rdi_clk", -1},
};
-static struct msm_cam_clk_info csid1_8974_clk_info[] = {
- {"csi1_ahb_clk", -1},
- {"csi1_src_clk", 200000000},
- {"csi1_clk", -1},
- {"csi1_phy_clk", -1},
- {"csi1_pix_clk", -1},
- {"csi1_rdi_clk", -1},
-};
-
-static struct msm_cam_clk_info csid2_8974_clk_info[] = {
- {"csi2_ahb_clk", -1},
- {"csi2_src_clk", 200000000},
- {"csi2_clk", -1},
- {"csi2_phy_clk", -1},
- {"csi2_pix_clk", -1},
- {"csi2_rdi_clk", -1},
-};
-
-static struct msm_cam_clk_info csid3_8974_clk_info[] = {
- {"csi3_ahb_clk", -1},
- {"csi3_src_clk", 200000000},
- {"csi3_clk", -1},
- {"csi3_phy_clk", -1},
- {"csi3_pix_clk", -1},
- {"csi3_rdi_clk", -1},
-};
-
-static struct msm_cam_clk_setting csid_8974_clk_info[] = {
- {&csid0_8974_clk_info[0], ARRAY_SIZE(csid0_8974_clk_info)},
- {&csid1_8974_clk_info[0], ARRAY_SIZE(csid1_8974_clk_info)},
- {&csid2_8974_clk_info[0], ARRAY_SIZE(csid2_8974_clk_info)},
- {&csid3_8974_clk_info[0], ARRAY_SIZE(csid3_8974_clk_info)},
+static struct msm_cam_clk_info csid_8610_clk_info[] = {
+ {"csi_ahb_clk", -1},
+ {"csi_src_clk", 200000000},
+ {"csi_clk", -1},
+ {"csi_phy_clk", -1},
+ {"csi_pix_clk", -1},
+ {"csi_rdi_clk", -1},
};
static struct camera_vreg_t csid_8960_vreg_info[] = {
@@ -241,7 +217,7 @@
static int msm_csid_init(struct csid_device *csid_dev, uint32_t *csid_version)
{
int rc = 0;
- uint8_t core_id = 0;
+ struct camera_vreg_t *cam_vreg;
if (!csid_version) {
pr_err("%s:%d csid_version NULL\n", __func__, __LINE__);
@@ -264,9 +240,49 @@
return rc;
}
- if (CSID_VERSION <= CSID_VERSION_V2) {
+ if (CSID_VERSION == CSID_VERSION_V20)
+ cam_vreg = csid_8960_vreg_info;
+ else
+ cam_vreg = csid_vreg_info;
+
+ if (CSID_VERSION < CSID_VERSION_V30) {
rc = msm_camera_config_vreg(&csid_dev->pdev->dev,
- csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 1);
+ if (rc < 0) {
+ pr_err("%s: regulator on failed\n", __func__);
+ goto vreg_config_failed;
+ }
+ rc = msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 1);
+ if (rc < 0) {
+ pr_err("%s: regulator enable failed\n", __func__);
+ goto vreg_enable_failed;
+ }
+
+ if (CSID_VERSION == CSID_VERSION_V20) {
+ rc = msm_cam_clk_enable(&csid_dev->pdev->dev,
+ csid_8960_clk_info, csid_dev->csid_clk,
+ ARRAY_SIZE(csid_8960_clk_info), 1);
+ if (rc < 0) {
+ pr_err("%s: 8960: clock enable failed\n",
+ __func__);
+ goto clk_enable_failed;
+ }
+ } else {
+ rc = msm_cam_clk_enable(&csid_dev->pdev->dev,
+ csid_8610_clk_info, csid_dev->csid_clk,
+ ARRAY_SIZE(csid_8610_clk_info), 1);
+ if (rc < 0) {
+ pr_err("%s: 8610: clock enable failed\n",
+ __func__);
+ goto clk_enable_failed;
+ }
+ }
+ } else if (CSID_VERSION >= CSID_VERSION_V30) {
+ rc = msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 1);
if (rc < 0) {
pr_err("%s: regulator on failed\n", __func__);
@@ -274,7 +290,7 @@
}
rc = msm_camera_enable_vreg(&csid_dev->pdev->dev,
- csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 1);
if (rc < 0) {
pr_err("%s: regulator enable failed\n", __func__);
@@ -282,50 +298,14 @@
}
rc = msm_cam_clk_enable(&csid_dev->pdev->dev,
- csid_8960_clk_info, csid_dev->csid_clk,
- ARRAY_SIZE(csid_8960_clk_info), 1);
+ csid_8974_clk_info, csid_dev->csid_clk,
+ ARRAY_SIZE(csid_8974_clk_info), 1);
if (rc < 0) {
pr_err("%s: clock enable failed\n", __func__);
goto clk_enable_failed;
}
- } else if (CSID_VERSION >= CSID_VERSION_V3) {
- rc = msm_camera_config_vreg(&csid_dev->pdev->dev,
- csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
- NULL, 0, &csid_dev->csi_vdd, 1);
- if (rc < 0) {
- pr_err("%s: regulator on failed\n", __func__);
- goto vreg_config_failed;
- }
-
- rc = msm_camera_enable_vreg(&csid_dev->pdev->dev,
- csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
- NULL, 0, &csid_dev->csi_vdd, 1);
- if (rc < 0) {
- pr_err("%s: regulator enable failed\n", __func__);
- goto vreg_enable_failed;
- }
-
- rc = msm_cam_clk_enable(&csid_dev->pdev->dev,
- csid_8974_clk_info[0].clk_info, csid_dev->csid0_clk,
- csid_8974_clk_info[0].num_clk_info, 1);
- if (rc < 0) {
- pr_err("%s: clock enable failed\n", __func__);
- goto csid0_clk_enable_failed;
- }
- core_id = csid_dev->pdev->id;
- if (core_id) {
- rc = msm_cam_clk_enable(&csid_dev->pdev->dev,
- csid_8974_clk_info[core_id].clk_info,
- csid_dev->csid_clk,
- csid_8974_clk_info[core_id].num_clk_info, 1);
- if (rc < 0) {
- pr_err("%s: clock enable failed\n",
- __func__);
- goto clk_enable_failed;
- }
- }
}
-
+ CDBG("%s:%d called\n", __func__, __LINE__);
csid_dev->hw_version =
msm_camera_io_r(csid_dev->base + CSID_HW_VERSION_ADDR);
CDBG("%s:%d called csid_dev->hw_version %x\n", __func__, __LINE__,
@@ -341,27 +321,21 @@
return rc;
clk_enable_failed:
- if (CSID_VERSION >= CSID_VERSION_V3) {
- msm_cam_clk_enable(&csid_dev->pdev->dev,
- csid_8974_clk_info[0].clk_info, csid_dev->csid0_clk,
- csid_8974_clk_info[0].num_clk_info, 0);
- }
-csid0_clk_enable_failed:
- if (CSID_VERSION <= CSID_VERSION_V2) {
+ if (CSID_VERSION < CSID_VERSION_V30) {
msm_camera_enable_vreg(&csid_dev->pdev->dev,
- csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 0);
- } else if (CSID_VERSION >= CSID_VERSION_V3) {
+ } else if (CSID_VERSION >= CSID_VERSION_V30) {
msm_camera_enable_vreg(&csid_dev->pdev->dev,
csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 0);
}
vreg_enable_failed:
- if (CSID_VERSION <= CSID_VERSION_V2) {
+ if (CSID_VERSION < CSID_VERSION_V30) {
msm_camera_config_vreg(&csid_dev->pdev->dev,
- csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 0);
- } else if (CSID_VERSION >= CSID_VERSION_V3) {
+ } else if (CSID_VERSION >= CSID_VERSION_V30) {
msm_camera_config_vreg(&csid_dev->pdev->dev,
csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 0);
@@ -375,7 +349,6 @@
static int msm_csid_release(struct csid_device *csid_dev)
{
uint32_t irq;
- uint8_t core_id = 0;
if (csid_dev->csid_state != CSID_POWER_UP) {
pr_err("%s: csid invalid state %d\n", __func__,
@@ -389,7 +362,7 @@
disable_irq(csid_dev->irq->start);
- if (csid_dev->hw_version <= CSID_VERSION_V2) {
+ if (csid_dev->hw_version == CSID_VERSION_V20) {
msm_cam_clk_enable(&csid_dev->pdev->dev, csid_8960_clk_info,
csid_dev->csid_clk, ARRAY_SIZE(csid_8960_clk_info), 0);
@@ -400,17 +373,22 @@
msm_camera_config_vreg(&csid_dev->pdev->dev,
csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 0);
- } else if (csid_dev->hw_version >= CSID_VERSION_V3) {
- core_id = csid_dev->pdev->id;
- if (core_id)
- msm_cam_clk_enable(&csid_dev->pdev->dev,
- csid_8974_clk_info[core_id].clk_info,
- csid_dev->csid_clk,
- csid_8974_clk_info[core_id].num_clk_info, 0);
-
+ } else if (csid_dev->hw_version == CSID_VERSION_V22) {
msm_cam_clk_enable(&csid_dev->pdev->dev,
- csid_8974_clk_info[0].clk_info, csid_dev->csid0_clk,
- csid_8974_clk_info[0].num_clk_info, 0);
+ csid_8610_clk_info,
+ csid_dev->csid_clk,
+ ARRAY_SIZE(csid_8610_clk_info), 0);
+
+ msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+
+ msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+ } else if (csid_dev->hw_version >= CSID_VERSION_V30) {
+ msm_cam_clk_enable(&csid_dev->pdev->dev, csid_8974_clk_info,
+ csid_dev->csid_clk, ARRAY_SIZE(csid_8974_clk_info), 0);
msm_camera_enable_vreg(&csid_dev->pdev->dev,
csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
@@ -522,6 +500,7 @@
rc = msm_csid_cmd(csid_dev, arg);
break;
case VIDIOC_MSM_CSID_RELEASE:
+ case MSM_SD_SHUTDOWN:
rc = msm_csid_release(csid_dev);
break;
default:
@@ -614,6 +593,7 @@
media_entity_init(&new_csid_dev->msm_sd.sd.entity, 0, NULL, 0);
new_csid_dev->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
new_csid_dev->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_CSID;
+ new_csid_dev->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x5;
msm_sd_register(&new_csid_dev->msm_sd);
rc = request_irq(new_csid_dev->irq->start, msm_csid_irq,
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.h b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.h
index 7ae1392..fd4db79 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.h
@@ -38,7 +38,6 @@
uint32_t hw_version;
enum msm_csid_state_t csid_state;
- struct clk *csid0_clk[11];
struct clk *csid_clk[11];
};
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/Makefile b/drivers/media/platform/msm/camera_v2/sensor/csiphy/Makefile
index eab1f6f..61671ea 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/Makefile
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/Makefile
@@ -2,6 +2,8 @@
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
ifeq ($(CONFIG_MSM_CSI20_HEADER),y)
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/csiphy/include/csi2.0
+else ifeq ($(CONFIG_MSM_CSI22_HEADER),y)
+ ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/csiphy/include/csi2.2
else ifeq ($(CONFIG_MSM_CSI30_HEADER),y)
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/csiphy/include/csi3.0
endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/csi2.0/msm_csiphy_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/csi2.0/msm_csiphy_hwreg.h
index e5093f8..ba964a2 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/csi2.0/msm_csiphy_hwreg.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/csi2.0/msm_csiphy_hwreg.h
@@ -14,7 +14,7 @@
#define MSM_CSIPHY_HWREG_H
/*MIPI CSI PHY registers*/
-#define MIPI_CSIPHY_HW_VERSION_ADDR 0x180
+#define MIPI_CSIPHY_HW_VERSION_ADDR 0x17C
#define MIPI_CSIPHY_LNn_CFG1_ADDR 0x0
#define MIPI_CSIPHY_LNn_CFG2_ADDR 0x4
#define MIPI_CSIPHY_LNn_CFG3_ADDR 0x8
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/csi2.2/msm_csiphy_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/csi2.2/msm_csiphy_hwreg.h
new file mode 100644
index 0000000..c4ff05d
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/csi2.2/msm_csiphy_hwreg.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#ifndef MSM_CSIPHY_HWREG_H
+#define MSM_CSIPHY_HWREG_H
+
+/*MIPI CSI PHY registers*/
+#define MIPI_CSIPHY_HW_VERSION_ADDR 0x17C
+#define MIPI_CSIPHY_LNn_CFG1_ADDR 0x0
+#define MIPI_CSIPHY_LNn_CFG2_ADDR 0x4
+#define MIPI_CSIPHY_LNn_CFG3_ADDR 0x8
+#define MIPI_CSIPHY_LNn_CFG4_ADDR 0xC
+#define MIPI_CSIPHY_LNn_CFG5_ADDR 0x10
+#define MIPI_CSIPHY_LNCK_CFG1_ADDR 0x100
+#define MIPI_CSIPHY_LNCK_CFG2_ADDR 0x104
+#define MIPI_CSIPHY_LNCK_CFG3_ADDR 0x108
+#define MIPI_CSIPHY_LNCK_CFG4_ADDR 0x10C
+#define MIPI_CSIPHY_LNCK_CFG5_ADDR 0x110
+#define MIPI_CSIPHY_LNCK_MISC1_ADDR 0x128
+#define MIPI_CSIPHY_GLBL_RESET_ADDR 0x140
+#define MIPI_CSIPHY_GLBL_PWR_CFG_ADDR 0x144
+#define MIPI_CSIPHY_GLBL_IRQ_CMD_ADDR 0x164
+#define MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR 0x180
+#define MIPI_CSIPHY_INTERRUPT_MASK0_ADDR 0x1A0
+#define MIPI_CSIPHY_INTERRUPT_MASK_VAL 0x6F
+#define MIPI_CSIPHY_INTERRUPT_MASK_ADDR 0x1A4
+#define MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR 0x1C0
+#define MIPI_CSIPHY_INTERRUPT_CLEAR_ADDR 0x1C4
+#define MIPI_CSIPHY_MODE_CONFIG_SHIFT 0x4
+#define MIPI_CSIPHY_GLBL_T_INIT_CFG0_ADDR 0x1E0
+#define MIPI_CSIPHY_T_WAKEUP_CFG0_ADDR 0x1E8
+#define CSIPHY_VERSION 0x1
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
index df3ee60..0fbe238 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
@@ -24,7 +24,9 @@
#define DBG_CSIPHY 0
#define V4L2_IDENT_CSIPHY 50003
-#define CSIPHY_VERSION_V3 0x10
+#define CSIPHY_VERSION_V22 0x01
+#define CSIPHY_VERSION_V20 0x00
+#define CSIPHY_VERSION_V30 0x10
#define MSM_CSIPHY_DRV_NAME "msm_csiphy"
#undef CDBG
@@ -43,14 +45,15 @@
uint8_t lane_cnt = 0;
uint16_t lane_mask = 0;
void __iomem *csiphybase;
+ uint8_t csiphy_id = csiphy_dev->pdev->id;
csiphybase = csiphy_dev->base;
if (!csiphybase) {
pr_err("%s: csiphybase NULL\n", __func__);
return -EINVAL;
}
- csiphy_dev->lane_mask[csiphy_dev->pdev->id] |= csiphy_params->lane_mask;
- lane_mask = csiphy_dev->lane_mask[csiphy_dev->pdev->id];
+ csiphy_dev->lane_mask[csiphy_id] |= csiphy_params->lane_mask;
+ lane_mask = csiphy_dev->lane_mask[csiphy_id];
lane_cnt = csiphy_params->lane_cnt;
if (csiphy_params->lane_cnt < 1 || csiphy_params->lane_cnt > 4) {
pr_err("%s: unsupported lane cnt %d\n",
@@ -58,15 +61,32 @@
return rc;
}
- CDBG("%s csiphy_params, mask = %x, cnt = %d, settle cnt = %x\n",
+ CDBG("%s csiphy_params, mask = %x cnt = %d settle cnt = %x csid %d\n",
__func__,
csiphy_params->lane_mask,
csiphy_params->lane_cnt,
- csiphy_params->settle_cnt);
+ csiphy_params->settle_cnt,
+ csiphy_params->csid_core);
+
+ if (csiphy_dev->hw_version >= CSIPHY_VERSION_V30) {
+ val = msm_camera_io_r(csiphy_dev->clk_mux_base);
+ if (csiphy_params->combo_mode &&
+ (csiphy_params->lane_mask & 0x18)) {
+ val &= ~0xf0;
+ val |= csiphy_params->csid_core << 4;
+ } else {
+ val &= ~0xf;
+ val |= csiphy_params->csid_core;
+ }
+ msm_camera_io_w(val, csiphy_dev->clk_mux_base);
+ CDBG("%s clk mux addr %p val 0x%x\n", __func__,
+ csiphy_dev->clk_mux_base, val);
+ mb();
+ }
msm_camera_io_w(0x1, csiphybase + MIPI_CSIPHY_GLBL_T_INIT_CFG0_ADDR);
msm_camera_io_w(0x1, csiphybase + MIPI_CSIPHY_T_WAKEUP_CFG0_ADDR);
- if (csiphy_dev->hw_version < CSIPHY_VERSION_V3) {
+ if (csiphy_dev->hw_version < CSIPHY_VERSION_V30) {
val = 0x3;
msm_camera_io_w((lane_mask << 2) | val,
csiphybase + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
@@ -155,6 +175,11 @@
{"csiphy_timer_clk", -1},
};
+static struct msm_cam_clk_info csiphy_8610_clk_info[] = {
+ {"csiphy_timer_src_clk", 200000000},
+ {"csiphy_timer_clk", -1},
+};
+
static struct msm_cam_clk_info csiphy_8974_clk_info[] = {
{"camss_top_ahb_clk", -1},
{"ispif_ahb_clk", -1},
@@ -198,16 +223,42 @@
}
CDBG("%s:%d called\n", __func__, __LINE__);
- if (CSIPHY_VERSION < CSIPHY_VERSION_V3) {
+ if (CSIPHY_VERSION == CSIPHY_VERSION_V20) {
CDBG("%s:%d called\n", __func__, __LINE__);
rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8960_clk_info), 1);
- } else {
+ } else if (CSIPHY_VERSION == CSIPHY_VERSION_V22) {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8610_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8610_clk_info), 1);
+ } else if (CSIPHY_VERSION == CSIPHY_VERSION_V30) {
+ if (!csiphy_dev->clk_mux_mem || !csiphy_dev->clk_mux_io) {
+ pr_err("%s clk mux mem %p io %p\n", __func__,
+ csiphy_dev->clk_mux_mem,
+ csiphy_dev->clk_mux_io);
+ rc = -ENOMEM;
+ return rc;
+ }
+ csiphy_dev->clk_mux_base = ioremap(
+ csiphy_dev->clk_mux_mem->start,
+ resource_size(csiphy_dev->clk_mux_mem));
+ if (!csiphy_dev->clk_mux_base) {
+ pr_err("%s: ERROR %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ return rc;
+ }
+
CDBG("%s:%d called\n", __func__, __LINE__);
rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8974_clk_info), 1);
+ } else {
+ pr_err("%s: ERROR Invalid CSIPHY Version %d",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ return rc;
}
CDBG("%s:%d called\n", __func__, __LINE__);
@@ -225,8 +276,13 @@
msm_csiphy_reset(csiphy_dev);
CDBG("%s:%d called\n", __func__, __LINE__);
- csiphy_dev->hw_version =
- msm_camera_io_r(csiphy_dev->base + MIPI_CSIPHY_HW_VERSION_ADDR);
+
+ if (CSIPHY_VERSION == CSIPHY_VERSION_V30)
+ csiphy_dev->hw_version =
+ msm_camera_io_r(csiphy_dev->base +
+ MIPI_CSIPHY_HW_VERSION_ADDR);
+ else
+ csiphy_dev->hw_version = CSIPHY_VERSION;
CDBG("%s:%d called csiphy_dev->hw_version %x\n", __func__, __LINE__,
csiphy_dev->hw_version);
@@ -269,16 +325,44 @@
}
CDBG("%s:%d called\n", __func__, __LINE__);
- if (CSIPHY_VERSION < CSIPHY_VERSION_V3) {
+
+ if (CSIPHY_VERSION == CSIPHY_VERSION_V20) {
CDBG("%s:%d called\n", __func__, __LINE__);
rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8960_clk_info), 1);
- } else {
+ } else if (CSIPHY_VERSION == CSIPHY_VERSION_V22) {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8610_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8610_clk_info), 1);
+ } else if (CSIPHY_VERSION == CSIPHY_VERSION_V30) {
+ if (!csiphy_dev->clk_mux_mem || !csiphy_dev->clk_mux_io) {
+ pr_err("%s clk mux mem %p io %p\n", __func__,
+ csiphy_dev->clk_mux_mem,
+ csiphy_dev->clk_mux_io);
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ csiphy_dev->clk_mux_base = ioremap(
+ csiphy_dev->clk_mux_mem->start,
+ resource_size(csiphy_dev->clk_mux_mem));
+ if (!csiphy_dev->clk_mux_base) {
+ pr_err("%s: ERROR %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ return rc;
+ }
+
CDBG("%s:%d called\n", __func__, __LINE__);
rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8974_clk_info), 1);
+ } else {
+ pr_err("%s: ERROR Invalid CSIPHY Version %d",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ return rc;
}
CDBG("%s:%d called\n", __func__, __LINE__);
@@ -294,8 +378,13 @@
msm_csiphy_reset(csiphy_dev);
CDBG("%s:%d called\n", __func__, __LINE__);
- csiphy_dev->hw_version =
- msm_camera_io_r(csiphy_dev->base + MIPI_CSIPHY_HW_VERSION_ADDR);
+
+ if (CSIPHY_VERSION == CSIPHY_VERSION_V30)
+ csiphy_dev->hw_version =
+ msm_camera_io_r(csiphy_dev->base +
+ MIPI_CSIPHY_HW_VERSION_ADDR);
+ else
+ csiphy_dev->hw_version = CSIPHY_VERSION;
CDBG("%s:%d called csiphy_dev->hw_version %x\n", __func__, __LINE__,
csiphy_dev->hw_version);
@@ -329,7 +418,7 @@
csi_lane_params->csi_lane_assign,
csi_lane_params->csi_lane_mask);
- if (csiphy_dev->hw_version < CSIPHY_VERSION_V3) {
+ if (csiphy_dev->hw_version < CSIPHY_VERSION_V30) {
csiphy_dev->lane_mask[csiphy_dev->pdev->id] = 0;
for (i = 0; i < 4; i++)
msm_camera_io_w(0x0, csiphy_dev->base +
@@ -359,14 +448,20 @@
disable_irq(csiphy_dev->irq->start);
- if (CSIPHY_VERSION < CSIPHY_VERSION_V3)
+ if (CSIPHY_VERSION == CSIPHY_VERSION_V20) {
msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8960_clk_info), 0);
- else
+ } else if (CSIPHY_VERSION == CSIPHY_VERSION_V22) {
+ msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8610_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8610_clk_info), 0);
+ } else if (CSIPHY_VERSION == CSIPHY_VERSION_V30) {
msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8974_clk_info), 0);
+ iounmap(csiphy_dev->clk_mux_base);
+ }
iounmap(csiphy_dev->base);
csiphy_dev->base = NULL;
@@ -398,7 +493,7 @@
csi_lane_params->csi_lane_assign,
csi_lane_params->csi_lane_mask);
- if (csiphy_dev->hw_version < CSIPHY_VERSION_V3) {
+ if (csiphy_dev->hw_version < CSIPHY_VERSION_V30) {
csiphy_dev->lane_mask[csiphy_dev->pdev->id] = 0;
for (i = 0; i < 4; i++)
msm_camera_io_w(0x0, csiphy_dev->base +
@@ -426,20 +521,29 @@
msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_LNCK_CFG2_ADDR);
msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
- if (CSIPHY_VERSION < CSIPHY_VERSION_V3)
+ if (CSIPHY_VERSION == CSIPHY_VERSION_V20) {
msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8960_clk_info), 0);
- else
+
+ } else if (CSIPHY_VERSION == CSIPHY_VERSION_V22) {
+ msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8610_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8610_clk_info), 0);
+
+ } else if (CSIPHY_VERSION == CSIPHY_VERSION_V30) {
msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8974_clk_info), 0);
+ iounmap(csiphy_dev->clk_mux_base);
+ }
iounmap(csiphy_dev->base);
csiphy_dev->base = NULL;
csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
return 0;
}
+
#endif
static long msm_csiphy_cmd(struct csiphy_device *csiphy_dev, void *arg)
@@ -512,6 +616,7 @@
rc = msm_csiphy_cmd(csiphy_dev, arg);
break;
case VIDIOC_MSM_CSIPHY_RELEASE:
+ case MSM_SD_SHUTDOWN:
rc = msm_csiphy_release(csiphy_dev, arg);
break;
default:
@@ -588,6 +693,17 @@
}
disable_irq(new_csiphy_dev->irq->start);
+ new_csiphy_dev->clk_mux_mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "csiphy_clk_mux");
+ if (new_csiphy_dev->clk_mux_mem) {
+ new_csiphy_dev->clk_mux_io = request_mem_region(
+ new_csiphy_dev->clk_mux_mem->start,
+ resource_size(new_csiphy_dev->clk_mux_mem),
+ new_csiphy_dev->clk_mux_mem->name);
+ if (!new_csiphy_dev->clk_mux_io)
+ pr_err("%s: ERROR %d\n", __func__, __LINE__);
+ }
+
new_csiphy_dev->pdev = pdev;
new_csiphy_dev->msm_sd.sd.internal_ops = &msm_csiphy_internal_ops;
new_csiphy_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
@@ -596,7 +712,7 @@
media_entity_init(&new_csiphy_dev->msm_sd.sd.entity, 0, NULL, 0);
new_csiphy_dev->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
new_csiphy_dev->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_CSIPHY;
-
+ new_csiphy_dev->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x4;
msm_sd_register(&new_csiphy_dev->msm_sd);
new_csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
return 0;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
index e19be34..a11b958 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
@@ -32,9 +32,12 @@
struct msm_sd_subdev msm_sd;
struct v4l2_subdev subdev;
struct resource *mem;
+ struct resource *clk_mux_mem;
struct resource *irq;
struct resource *io;
+ struct resource *clk_mux_io;
void __iomem *base;
+ void __iomem *clk_mux_base;
struct mutex mutex;
uint32_t hw_version;
enum msm_csiphy_state_t csiphy_state;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
index 47e672d..3dd3a4e 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
@@ -42,13 +42,13 @@
e_ctrl->eboard_info->eeprom_name,
sizeof(cdata->cfg.eeprom_name));
break;
- case CFG_EEPROM_GET_DATA:
- CDBG("%s E CFG_EEPROM_GET_DATA\n", __func__);
+ case CFG_EEPROM_GET_CAL_DATA:
+ CDBG("%s E CFG_EEPROM_GET_CAL_DATA\n", __func__);
cdata->cfg.get_data.num_bytes =
e_ctrl->num_bytes;
break;
- case CFG_EEPROM_READ_DATA:
- CDBG("%s E CFG_EEPROM_READ_DATA\n", __func__);
+ case CFG_EEPROM_READ_CAL_DATA:
+ CDBG("%s E CFG_EEPROM_READ_CAL_DATA\n", __func__);
rc = copy_to_user(cdata->cfg.read_data.dbuffer,
e_ctrl->memory_data,
cdata->cfg.read_data.num_bytes);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c
index 9119a13..044fd31 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c
@@ -81,6 +81,7 @@
media_entity_init(&fctrl->msm_sd.sd.entity, 0, NULL, 0);
fctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
fctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_LED_FLASH;
+ fctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1;
msm_sd_register(&fctrl->msm_sd);
CDBG("probe success\n");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.h b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.h
index 76aa695..ac697fb 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.h
@@ -40,7 +40,7 @@
struct msm_flash_fn_t *func_tbl;
const char *led_trigger_name[MAX_LED_TRIGGERS];
struct led_trigger *led_trigger[MAX_LED_TRIGGERS];
- uint32_t max_current[MAX_LED_TRIGGERS];
+ uint32_t op_current[MAX_LED_TRIGGERS];
void *data;
};
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_trigger.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_trigger.c
index 1a75a5a..c6f1f72 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_trigger.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_trigger.c
@@ -59,11 +59,11 @@
case MSM_CAMERA_LED_LOW:
led_trigger_event(fctrl->led_trigger[0],
- fctrl->max_current[0] / 2);
+ fctrl->op_current[0] / 2);
break;
case MSM_CAMERA_LED_HIGH:
- led_trigger_event(fctrl->led_trigger[0], fctrl->max_current[0]);
+ led_trigger_event(fctrl->led_trigger[0], fctrl->op_current[0]);
break;
case MSM_CAMERA_LED_INIT:
@@ -144,7 +144,7 @@
CDBG("default trigger %s\n", fctrl.led_trigger_name[i]);
rc = of_property_read_u32(flash_src_node,
- "qcom,max-current", &fctrl.max_current[i]);
+ "qcom,current", &fctrl.op_current[i]);
if (rc < 0) {
pr_err("failed rc %d\n", rc);
of_node_put(flash_src_node);
@@ -153,7 +153,7 @@
of_node_put(flash_src_node);
- CDBG("max_current[%d] %d\n", i, fctrl.max_current[i]);
+ CDBG("max_current[%d] %d\n", i, fctrl.op_current[i]);
led_trigger_register_simple(fctrl.led_trigger_name[i],
&fctrl.led_trigger[i]);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/imx135.c b/drivers/media/platform/msm/camera_v2/sensor/imx135.c
index c9476ee..8368a4d 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/imx135.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/imx135.c
@@ -36,6 +36,12 @@
.delay = 0,
},
{
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VAF,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
.seq_type = SENSOR_GPIO,
.seq_val = SENSOR_GPIO_RESET,
.config_val = GPIO_OUT_LOW,
@@ -48,6 +54,18 @@
.delay = 30,
},
{
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 1,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 30,
+ },
+ {
.seq_type = SENSOR_CLK,
.seq_val = SENSOR_CAM_MCLK,
.config_val = 0,
@@ -75,9 +93,15 @@
{ }
};
+static int32_t msm_imx135_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return msm_sensor_i2c_probe(client, id, &imx135_s_ctrl);
+}
+
static struct i2c_driver imx135_i2c_driver = {
.id_table = imx135_i2c_id,
- .probe = msm_sensor_i2c_probe,
+ .probe = msm_imx135_i2c_probe,
.driver = {
.name = IMX135_SENSOR_NAME,
},
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_io_util.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_io_util.c
index 7dbbc03..e30e798 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_io_util.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_io_util.c
@@ -122,7 +122,7 @@
rc = PTR_ERR(clk_ptr[i]);
goto cam_clk_get_err;
}
- if (clk_info[i].clk_rate >= 0) {
+ if (clk_info[i].clk_rate > 0) {
rc = clk_set_rate(clk_ptr[i],
clk_info[i].clk_rate);
if (rc < 0) {
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
index fa63e2b..c834925 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
@@ -16,6 +16,9 @@
#include "msm_cci.h"
#include "msm_camera_io_util.h"
#include "msm_camera_i2c_mux.h"
+#include <mach/rpm-regulator.h>
+#include <mach/rpm-regulator-smd.h>
+#include <linux/regulator/consumer.h>
#undef CDBG
#ifdef CONFIG_MSMB_CAMERA_DEBUG
@@ -610,11 +613,10 @@
return rc;
}
-static int32_t msm_sensor_get_dt_data(struct platform_device *pdev,
+static int32_t msm_sensor_get_dt_data(struct device_node *of_node,
struct msm_sensor_ctrl_t *s_ctrl)
{
- int32_t rc = 0, i = 0;
- struct device_node *of_node = pdev->dev.of_node;
+ int32_t rc = 0, i = 0, ret = 0;
struct msm_camera_gpio_conf *gconf = NULL;
struct msm_camera_sensor_board_info *sensordata = NULL;
uint16_t *gpio_array = NULL;
@@ -774,7 +776,14 @@
sensordata->slave_info->sensor_id_reg_addr = id_info[1];
sensordata->slave_info->sensor_id = id_info[2];
+ /*Optional property, don't return error if absent */
+ ret = of_property_read_string(of_node, "qcom,vdd-cx-name",
+ &sensordata->misc_regulator);
+ CDBG("%s qcom,misc_regulator %s, rc %d\n", __func__,
+ sensordata->misc_regulator, ret);
+
kfree(gpio_array);
+
return rc;
ERROR9:
@@ -799,6 +808,41 @@
return rc;
}
+static void msm_sensor_misc_regulator(
+ struct msm_sensor_ctrl_t *sctrl, uint32_t enable)
+{
+ int32_t rc = 0;
+ if (enable) {
+ sctrl->misc_regulator = (void *)rpm_regulator_get(
+ &sctrl->pdev->dev, sctrl->sensordata->misc_regulator);
+ if (sctrl->misc_regulator) {
+ rc = rpm_regulator_set_mode(sctrl->misc_regulator,
+ RPM_REGULATOR_MODE_HPM);
+ if (rc < 0) {
+ pr_err("%s: Failed to set for rpm regulator on %s: %d\n",
+ __func__,
+ sctrl->sensordata->misc_regulator, rc);
+ rpm_regulator_put(sctrl->misc_regulator);
+ }
+ } else {
+ pr_err("%s: Failed to vote for rpm regulator on %s: %d\n",
+ __func__,
+ sctrl->sensordata->misc_regulator, rc);
+ }
+ } else {
+ if (sctrl->misc_regulator) {
+ rc = rpm_regulator_set_mode(
+ (struct rpm_regulator *)sctrl->misc_regulator,
+ RPM_REGULATOR_MODE_AUTO);
+ if (rc < 0)
+ pr_err("%s: Failed to set for rpm regulator on %s: %d\n",
+ __func__,
+ sctrl->sensordata->misc_regulator, rc);
+ rpm_regulator_put(sctrl->misc_regulator);
+ }
+ }
+}
+
int32_t msm_sensor_free_sensor_data(struct msm_sensor_ctrl_t *s_ctrl)
{
if (!s_ctrl->pdev)
@@ -814,6 +858,7 @@
kfree(s_ctrl->sensordata->sensor_info);
kfree(s_ctrl->sensordata->sensor_init_params);
kfree(s_ctrl->sensordata);
+ kfree(s_ctrl->clk_info);
return 0;
}
@@ -821,6 +866,11 @@
[SENSOR_CAM_MCLK] = {"cam_clk", 24000000},
};
+static struct msm_cam_clk_info cam_8610_clk_info[] = {
+ [SENSOR_CAM_MCLK] = {"cam_src_clk", 24000000},
+ [SENSOR_CAM_CLK] = {"cam_clk", 0},
+};
+
static struct msm_cam_clk_info cam_8974_clk_info[] = {
[SENSOR_CAM_MCLK] = {"cam_src_clk", 19200000},
[SENSOR_CAM_CLK] = {"cam_clk", 0},
@@ -1001,6 +1051,7 @@
struct msm_sensor_power_setting_array *power_setting_array = NULL;
struct msm_sensor_power_setting *power_setting = NULL;
struct msm_camera_sensor_board_info *data = s_ctrl->sensordata;
+ s_ctrl->stop_setting_valid = 0;
CDBG("%s:%d\n", __func__, __LINE__);
power_setting_array = &s_ctrl->power_setting_array;
@@ -1100,9 +1151,14 @@
static void msm_sensor_stop_stream(struct msm_sensor_ctrl_t *s_ctrl)
{
- s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
- s_ctrl->sensor_i2c_client, &s_ctrl->stop_setting);
- kfree(s_ctrl->stop_setting.reg_setting);
+ mutex_lock(s_ctrl->msm_sensor_mutex);
+ if (s_ctrl->sensor_state == MSM_SENSOR_POWER_UP) {
+ s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
+ s_ctrl->sensor_i2c_client, &s_ctrl->stop_setting);
+ kfree(s_ctrl->stop_setting.reg_setting);
+ s_ctrl->stop_setting.reg_setting = NULL;
+ }
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
return;
}
@@ -1119,6 +1175,7 @@
case VIDIOC_MSM_SENSOR_CFG:
return s_ctrl->func_tbl->sensor_config(s_ctrl, argp);
case VIDIOC_MSM_SENSOR_RELEASE:
+ case MSM_SD_SHUTDOWN:
msm_sensor_stop_stream(s_ctrl);
return 0;
default:
@@ -1202,6 +1259,7 @@
power_setting_array->size *
sizeof(struct msm_sensor_power_setting))) {
pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(power_setting_array->power_setting);
rc = -EFAULT;
break;
}
@@ -1233,6 +1291,13 @@
struct msm_camera_i2c_reg_setting conf_array;
struct msm_camera_i2c_reg_array *reg_setting = NULL;
+ if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) {
+ pr_err("%s:%d failed: invalid state %d\n", __func__,
+ __LINE__, s_ctrl->sensor_state);
+ rc = -EFAULT;
+ break;
+ }
+
if (copy_from_user(&conf_array,
(void *)cdata->cfg.setting,
sizeof(struct msm_camera_i2c_reg_setting))) {
@@ -1263,10 +1328,138 @@
kfree(reg_setting);
break;
}
+ case CFG_SLAVE_READ_I2C: {
+ struct msm_camera_i2c_read_config read_config;
+ uint16_t local_data = 0;
+ uint16_t orig_slave_addr = 0, read_slave_addr = 0;
+ if (copy_from_user(&read_config,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_read_config))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ read_slave_addr = read_config.slave_addr;
+ CDBG("%s:CFG_SLAVE_READ_I2C:", __func__);
+ CDBG("%s:slave_addr=0x%x reg_addr=0x%x, data_type=%d\n",
+ __func__, read_config.slave_addr,
+ read_config.reg_addr, read_config.data_type);
+ if (s_ctrl->sensor_i2c_client->cci_client) {
+ orig_slave_addr =
+ s_ctrl->sensor_i2c_client->cci_client->sid;
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ read_slave_addr >> 1;
+ } else if (s_ctrl->sensor_i2c_client->client) {
+ orig_slave_addr =
+ s_ctrl->sensor_i2c_client->client->addr;
+ s_ctrl->sensor_i2c_client->client->addr =
+ read_slave_addr >> 1;
+ } else {
+ pr_err("%s: error: no i2c/cci client found.", __func__);
+ rc = -EFAULT;
+ break;
+ }
+ CDBG("%s:orig_slave_addr=0x%x, new_slave_addr=0x%x",
+ __func__, orig_slave_addr,
+ read_slave_addr >> 1);
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read(
+ s_ctrl->sensor_i2c_client,
+ read_config.reg_addr,
+ &local_data, read_config.data_type);
+ if (rc < 0) {
+ pr_err("%s:%d: i2c_read failed\n", __func__, __LINE__);
+ break;
+ }
+ if (copy_to_user((void __user *)read_config.data,
+ (void *)&local_data, sizeof(uint16_t))) {
+ pr_err("%s:%d copy failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case CFG_SLAVE_WRITE_I2C_ARRAY: {
+ struct msm_camera_i2c_array_write_config write_config;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+ uint16_t write_slave_addr = 0;
+ uint16_t orig_slave_addr = 0;
+
+ if (copy_from_user(&write_config,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_array_write_config))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ CDBG("%s:CFG_SLAVE_WRITE_I2C_ARRAY:", __func__);
+ CDBG("%s:slave_addr=0x%x, array_size=%d\n", __func__,
+ write_config.slave_addr,
+ write_config.conf_array.size);
+ reg_setting = kzalloc(write_config.conf_array.size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting,
+ (void *)(write_config.conf_array.reg_setting),
+ write_config.conf_array.size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+ write_config.conf_array.reg_setting = reg_setting;
+ write_slave_addr = write_config.slave_addr;
+ if (s_ctrl->sensor_i2c_client->cci_client) {
+ orig_slave_addr =
+ s_ctrl->sensor_i2c_client->cci_client->sid;
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ write_slave_addr >> 1;
+ } else if (s_ctrl->sensor_i2c_client->client) {
+ orig_slave_addr =
+ s_ctrl->sensor_i2c_client->client->addr;
+ s_ctrl->sensor_i2c_client->client->addr =
+ write_slave_addr >> 1;
+ } else {
+ pr_err("%s: error: no i2c/cci client found.", __func__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+ CDBG("%s:orig_slave_addr=0x%x, new_slave_addr=0x%x",
+ __func__, orig_slave_addr,
+ write_slave_addr >> 1);
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
+ s_ctrl->sensor_i2c_client, &(write_config.conf_array));
+ if (s_ctrl->sensor_i2c_client->cci_client) {
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ orig_slave_addr;
+ } else if (s_ctrl->sensor_i2c_client->client) {
+ s_ctrl->sensor_i2c_client->client->addr =
+ orig_slave_addr;
+ } else {
+ pr_err("%s: error: no i2c/cci client found.", __func__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+ kfree(reg_setting);
+ break;
+ }
case CFG_WRITE_I2C_SEQ_ARRAY: {
struct msm_camera_i2c_seq_reg_setting conf_array;
struct msm_camera_i2c_seq_reg_array *reg_setting = NULL;
+ if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) {
+ pr_err("%s:%d failed: invalid state %d\n", __func__,
+ __LINE__, s_ctrl->sensor_state);
+ rc = -EFAULT;
+ break;
+ }
+
if (copy_from_user(&conf_array,
(void *)cdata->cfg.setting,
sizeof(struct msm_camera_i2c_seq_reg_setting))) {
@@ -1301,18 +1494,56 @@
}
case CFG_POWER_UP:
- if (s_ctrl->func_tbl->sensor_power_up)
- rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
- else
+ if (s_ctrl->sensor_state != MSM_SENSOR_POWER_DOWN) {
+ pr_err("%s:%d failed: invalid state %d\n", __func__,
+ __LINE__, s_ctrl->sensor_state);
rc = -EFAULT;
+ break;
+ }
+ if (s_ctrl->func_tbl->sensor_power_up) {
+ if (s_ctrl->sensordata->misc_regulator)
+ msm_sensor_misc_regulator(s_ctrl, 1);
+
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %ld\n", __func__,
+ __LINE__, rc);
+ break;
+ }
+ s_ctrl->sensor_state = MSM_SENSOR_POWER_UP;
+ pr_err("%s:%d sensor state %d\n", __func__, __LINE__,
+ s_ctrl->sensor_state);
+ } else {
+ rc = -EFAULT;
+ }
break;
case CFG_POWER_DOWN:
- if (s_ctrl->func_tbl->sensor_power_down)
+ kfree(s_ctrl->stop_setting.reg_setting);
+ s_ctrl->stop_setting.reg_setting = NULL;
+ if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) {
+ pr_err("%s:%d failed: invalid state %d\n", __func__,
+ __LINE__, s_ctrl->sensor_state);
+ rc = -EFAULT;
+ break;
+ }
+ if (s_ctrl->func_tbl->sensor_power_down) {
+ if (s_ctrl->sensordata->misc_regulator)
+ msm_sensor_misc_regulator(s_ctrl, 0);
+
rc = s_ctrl->func_tbl->sensor_power_down(
s_ctrl);
- else
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %ld\n", __func__,
+ __LINE__, rc);
+ break;
+ }
+ s_ctrl->sensor_state = MSM_SENSOR_POWER_DOWN;
+ pr_err("%s:%d sensor state %d\n", __func__, __LINE__,
+ s_ctrl->sensor_state);
+ } else {
rc = -EFAULT;
+ }
break;
case CFG_SET_STOP_STREAM_SETTING: {
@@ -1326,6 +1557,7 @@
rc = -EFAULT;
break;
}
+ s_ctrl->stop_setting_valid = 1;
reg_setting = stop_setting->reg_setting;
stop_setting->reg_setting = kzalloc(stop_setting->size *
@@ -1363,8 +1595,10 @@
int rc = 0;
struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd);
mutex_lock(s_ctrl->msm_sensor_mutex);
- if (!on)
- rc = s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ if (!on && s_ctrl->sensor_state == MSM_SENSOR_POWER_UP) {
+ s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ s_ctrl->sensor_state = MSM_SENSOR_POWER_DOWN;
+ }
if (s_ctrl->free_power_setting == true) {
kfree(s_ctrl->power_setting_array.power_setting);
s_ctrl->free_power_setting = false;
@@ -1440,7 +1674,7 @@
CDBG("%s called data %p\n", __func__, data);
CDBG("%s pdev name %s\n", __func__, pdev->id_entry->name);
if (pdev->dev.of_node) {
- rc = msm_sensor_get_dt_data(pdev, s_ctrl);
+ rc = msm_sensor_get_dt_data(pdev->dev.of_node, s_ctrl);
if (rc < 0) {
pr_err("%s failed line %d\n", __func__, __LINE__);
return rc;
@@ -1468,12 +1702,20 @@
&msm_sensor_cci_func_tbl;
if (!s_ctrl->sensor_v4l2_subdev_ops)
s_ctrl->sensor_v4l2_subdev_ops = &msm_sensor_subdev_ops;
- s_ctrl->clk_info = cam_8974_clk_info;
+ s_ctrl->clk_info = kzalloc(sizeof(cam_8974_clk_info),
+ GFP_KERNEL);
+ if (!s_ctrl->clk_info) {
+ pr_err("%s:%d failed nomem\n", __func__, __LINE__);
+ kfree(cci_client);
+ return -ENOMEM;
+ }
+ memcpy(s_ctrl->clk_info, cam_8974_clk_info, sizeof(cam_8974_clk_info));
s_ctrl->clk_info_size = ARRAY_SIZE(cam_8974_clk_info);
rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
if (rc < 0) {
pr_err("%s %s power up failed\n", __func__,
s_ctrl->sensordata->sensor_name);
+ kfree(s_ctrl->clk_info);
kfree(cci_client);
return rc;
}
@@ -1496,6 +1738,7 @@
rc = camera_init_v4l2(&s_ctrl->pdev->dev, &session_id);
CDBG("%s rc %d session_id %d\n", __func__, rc, session_id);
s_ctrl->sensordata->sensor_info->session_id = session_id;
+ s_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x3;
msm_sd_register(&s_ctrl->msm_sd);
CDBG("%s:%d\n", __func__, __LINE__);
@@ -1505,10 +1748,9 @@
}
int32_t msm_sensor_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id, struct msm_sensor_ctrl_t *s_ctrl)
{
int rc = 0;
- struct msm_sensor_ctrl_t *s_ctrl;
uint32_t session_id;
CDBG("%s %s_i2c_probe called\n", __func__, client->name);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
@@ -1518,15 +1760,25 @@
return rc;
}
- s_ctrl = (struct msm_sensor_ctrl_t *)(id->driver_data);
- if (!s_ctrl) {
- pr_err("%s:%d sensor ctrl structure NULL\n", __func__,
- __LINE__);
- return -EINVAL;
+ if (!client->dev.of_node) {
+ CDBG("msm_sensor_i2c_probe: of_node is NULL");
+ s_ctrl = (struct msm_sensor_ctrl_t *)(id->driver_data);
+ if (!s_ctrl) {
+ pr_err("%s:%d sensor ctrl structure NULL\n", __func__,
+ __LINE__);
+ return -EINVAL;
+ }
+ s_ctrl->sensordata = client->dev.platform_data;
+ } else {
+ CDBG("msm_sensor_i2c_probe: of_node exisists");
+ rc = msm_sensor_get_dt_data(client->dev.of_node, s_ctrl);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
}
s_ctrl->sensor_device_type = MSM_CAMERA_I2C_DEVICE;
- s_ctrl->sensordata = client->dev.platform_data;
if (s_ctrl->sensordata == NULL) {
pr_err("%s %s NULL sensor data\n", __func__, client->name);
return -EFAULT;
@@ -1554,12 +1806,32 @@
if (!s_ctrl->sensor_v4l2_subdev_ops)
s_ctrl->sensor_v4l2_subdev_ops = &msm_sensor_subdev_ops;
- s_ctrl->clk_info = cam_8960_clk_info;
- s_ctrl->clk_info_size = ARRAY_SIZE(cam_8960_clk_info);
+ if (!client->dev.of_node) {
+ s_ctrl->clk_info = kzalloc(sizeof(cam_8960_clk_info),
+ GFP_KERNEL);
+ if (!s_ctrl->clk_info) {
+ pr_err("%s:%d failed nomem\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+ memcpy(s_ctrl->clk_info, cam_8960_clk_info,
+ sizeof(cam_8960_clk_info));
+ s_ctrl->clk_info_size = ARRAY_SIZE(cam_8960_clk_info);
+ } else {
+ s_ctrl->clk_info = kzalloc(sizeof(cam_8610_clk_info),
+ GFP_KERNEL);
+ if (!s_ctrl->clk_info) {
+ pr_err("%s:%d failed nomem\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+ memcpy(s_ctrl->clk_info, cam_8610_clk_info,
+ sizeof(cam_8610_clk_info));
+ s_ctrl->clk_info_size = ARRAY_SIZE(cam_8610_clk_info);
+ }
rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
if (rc < 0) {
pr_err("%s %s power up failed\n", __func__, client->name);
+ kfree(s_ctrl->clk_info);
return rc;
}
@@ -1580,6 +1852,7 @@
&session_id);
CDBG("%s rc %d session_id %d\n", __func__, rc, session_id);
s_ctrl->sensordata->sensor_info->session_id = session_id;
+ s_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x3;
msm_sd_register(&s_ctrl->msm_sd);
CDBG("%s:%d\n", __func__, __LINE__);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h
index 6c36e47d..80a2bd4 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h
@@ -37,6 +37,11 @@
struct msm_sensor_ctrl_t;
+enum msm_sensor_state_t {
+ MSM_SENSOR_POWER_DOWN,
+ MSM_SENSOR_POWER_UP,
+};
+
struct msm_sensor_fn_t {
int (*sensor_config) (struct msm_sensor_ctrl_t *, void __user *);
int (*sensor_power_down)
@@ -62,9 +67,12 @@
struct v4l2_subdev_ops *sensor_v4l2_subdev_ops;
struct msm_sensor_fn_t *func_tbl;
struct msm_camera_i2c_reg_setting stop_setting;
+ bool stop_setting_valid;
bool free_power_setting;
struct msm_cam_clk_info *clk_info;
uint16_t clk_info_size;
+ void *misc_regulator;
+ enum msm_sensor_state_t sensor_state;
};
int32_t msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl,
@@ -80,7 +88,7 @@
void *data);
int32_t msm_sensor_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id);
+ const struct i2c_device_id *id, struct msm_sensor_ctrl_t *s_ctrl);
int32_t msm_sensor_free_sensor_data(struct msm_sensor_ctrl_t *s_ctrl);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/mt9m114.c b/drivers/media/platform/msm/camera_v2/sensor/mt9m114.c
index 9911a59..c1cf862 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/mt9m114.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/mt9m114.c
@@ -1107,9 +1107,15 @@
{ }
};
+static int32_t msm_mt9m114_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return msm_sensor_i2c_probe(client, id, &mt9m114_s_ctrl);
+}
+
static struct i2c_driver mt9m114_i2c_driver = {
.id_table = mt9m114_i2c_id,
- .probe = msm_sensor_i2c_probe,
+ .probe = msm_mt9m114_i2c_probe,
.driver = {
.name = MT9M114_SENSOR_NAME,
},
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ov2720.c b/drivers/media/platform/msm/camera_v2/sensor/ov2720.c
index d790c65..397564b 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/ov2720.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/ov2720.c
@@ -75,9 +75,15 @@
{ }
};
+static int32_t msm_ov2720_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return msm_sensor_i2c_probe(client, id, &ov2720_s_ctrl);
+}
+
static struct i2c_driver ov2720_i2c_driver = {
.id_table = ov2720_i2c_id,
- .probe = msm_sensor_i2c_probe,
+ .probe = msm_ov2720_i2c_probe,
.driver = {
.name = OV2720_SENSOR_NAME,
},
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ov8825.c b/drivers/media/platform/msm/camera_v2/sensor/ov8825.c
index b56eb10..3256c5c 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/ov8825.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/ov8825.c
@@ -93,9 +93,15 @@
{ }
};
+static int32_t msm_ov8825_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return msm_sensor_i2c_probe(client, id, &ov8825_s_ctrl);
+}
+
static struct i2c_driver ov8825_i2c_driver = {
.id_table = ov8825_i2c_id,
- .probe = msm_sensor_i2c_probe,
+ .probe = msm_ov8825_i2c_probe,
.driver = {
.name = OV8825_SENSOR_NAME,
},
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ov9724.c b/drivers/media/platform/msm/camera_v2/sensor/ov9724.c
index 981cc10..56af02b 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/ov9724.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/ov9724.c
@@ -83,6 +83,12 @@
},
};
+static int32_t msm_ov9724_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return msm_sensor_i2c_probe(client, id, &ov9724_s_ctrl);
+}
+
static const struct i2c_device_id ov9724_i2c_id[] = {
{OV9724_SENSOR_NAME, (kernel_ulong_t)&ov9724_s_ctrl},
{ }
@@ -90,7 +96,7 @@
static struct i2c_driver ov9724_i2c_driver = {
.id_table = ov9724_i2c_id,
- .probe = msm_sensor_i2c_probe,
+ .probe = msm_ov9724_i2c_probe,
.driver = {
.name = OV9724_SENSOR_NAME,
},
diff --git a/drivers/media/platform/msm/camera_v2/sensor/s5k3l1yx.c b/drivers/media/platform/msm/camera_v2/sensor/s5k3l1yx.c
index 21a7369..225e81d 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/s5k3l1yx.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/s5k3l1yx.c
@@ -93,9 +93,15 @@
{ }
};
+static int32_t msm_s5k3l1yx_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return msm_sensor_i2c_probe(client, id, &s5k3l1yx_s_ctrl);
+}
+
static struct i2c_driver s5k3l1yx_i2c_driver = {
.id_table = s5k3l1yx_i2c_id,
- .probe = msm_sensor_i2c_probe,
+ .probe = msm_s5k3l1yx_i2c_probe,
.driver = {
.name = S5K3L1YX_SENSOR_NAME,
},
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
index bb3a797..85fdff6 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
@@ -14,11 +14,12 @@
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/file.h>
+#include <linux/scatterlist.h>
#include "mpq_dvb_debug.h"
#include "mpq_dmx_plugin_common.h"
#include "mpq_sdmx.h"
-#define SDMX_MAJOR_VERSION_MATCH (3)
+#define SDMX_MAJOR_VERSION_MATCH (4)
#define TS_PACKET_HEADER_LENGTH (4)
@@ -3357,7 +3358,6 @@
if (status != SDMX_SUCCESS) {
MPQ_DVB_ERR_PRINT("%s: sdmx_close_session failed %d\n",
__func__, status);
- return -EINVAL;
}
mpq_demux->sdmx_eos = 0;
mpq_demux->sdmx_session_handle = SDMX_INVALID_SESSION_HANDLE;
@@ -3369,14 +3369,64 @@
}
EXPORT_SYMBOL(mpq_sdmx_close_session);
+static int mpq_sdmx_get_buffer_chunks(struct mpq_demux *mpq_demux,
+ struct ion_handle *buff_handle,
+ u32 actual_buff_size,
+ struct sdmx_buff_descr buff_chunks[SDMX_MAX_PHYSICAL_CHUNKS])
+{
+ int i;
+ struct sg_table *sg_ptr;
+ struct scatterlist *sg;
+ u32 chunk_size;
+
+ memset(buff_chunks, 0,
+ sizeof(struct sdmx_buff_descr) * SDMX_MAX_PHYSICAL_CHUNKS);
+
+ sg_ptr = ion_sg_table(mpq_demux->ion_client, buff_handle);
+ if (sg_ptr == NULL) {
+ MPQ_DVB_ERR_PRINT("%s: ion_sg_table failed\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (sg_ptr->nents == 0) {
+ MPQ_DVB_ERR_PRINT("%s: num of scattered entries is 0\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (sg_ptr->nents > SDMX_MAX_PHYSICAL_CHUNKS) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: num of scattered entries %d greater than max supported %d\n",
+ __func__, sg_ptr->nents, SDMX_MAX_PHYSICAL_CHUNKS);
+ return -EINVAL;
+ }
+
+ sg = sg_ptr->sgl;
+ for (i = 0; i < sg_ptr->nents; i++) {
+ buff_chunks[i].base_addr =
+ (void *)sg_dma_address(sg);
+ if (sg->length > actual_buff_size)
+ chunk_size = actual_buff_size;
+ else
+ chunk_size = sg->length;
+
+ buff_chunks[i].size = chunk_size;
+ sg = sg_next(sg);
+ actual_buff_size -= chunk_size;
+ }
+
+ return 0;
+}
+
static int mpq_sdmx_init_data_buffer(struct mpq_demux *mpq_demux,
struct mpq_feed *feed, u32 *num_buffers,
- struct sdmx_buff_descr *buf_desc, enum sdmx_buf_mode *buf_mode)
+ struct sdmx_data_buff_descr buf_desc[DMX_MAX_DECODER_BUFFER_NUM],
+ enum sdmx_buf_mode *buf_mode)
{
struct dvb_demux_feed *dvbdmx_feed = feed->dvb_demux_feed;
struct dvb_ringbuffer *buffer;
struct mpq_video_feed_info *feed_data = &feed->video_info;
- ion_phys_addr_t addr;
struct ion_handle *sdmx_buff;
int ret;
int i;
@@ -3389,57 +3439,56 @@
*num_buffers = feed_data->buffer_desc.decoder_buffers_num;
for (i = 0; i < *num_buffers; i++) {
- ret = ion_phys(mpq_demux->ion_client,
- feed_data->buffer_desc.ion_handle[i],
- &addr, &buf_desc[i].size);
+ buf_desc[i].length =
+ feed_data->buffer_desc.desc[i].size;
+
+ ret = mpq_sdmx_get_buffer_chunks(mpq_demux,
+ feed_data->buffer_desc.ion_handle[i],
+ buf_desc[i].length,
+ buf_desc[i].buff_chunks);
if (ret) {
MPQ_DVB_ERR_PRINT(
- "%s: FAILED to get physical buffer address, ret=%d\n",
- __func__, ret);
- goto end;
+ "%s: mpq_sdmx_get_buffer_chunks failed\n",
+ __func__);
+ return ret;
}
- buf_desc[i].base_addr = (void *)addr;
- buf_desc[i].size = feed_data->buffer_desc.desc[i].size;
- }
- } else {
- *num_buffers = 1;
- if (dvb_dmx_is_sec_feed(dvbdmx_feed) ||
- dvb_dmx_is_pcr_feed(dvbdmx_feed)) {
- buffer = &feed->sdmx_buf;
- sdmx_buff = feed->sdmx_buf_handle;
- } else {
- buffer = (struct dvb_ringbuffer *)
- dvbdmx_feed->feed.ts.buffer.ringbuff;
- sdmx_buff = dvbdmx_feed->feed.ts.buffer.priv_handle;
}
- if (sdmx_buff == NULL) {
- MPQ_DVB_ERR_PRINT(
- "%s: Invalid buffer allocation\n",
- __func__);
- return -ENOMEM;
- }
-
- ret = ion_phys(mpq_demux->ion_client, sdmx_buff, &addr,
- &buf_desc[0].size);
- if (ret) {
- MPQ_DVB_ERR_PRINT(
- "%s: FAILED to get physical buffer address, ret=%d\n",
- __func__, ret);
- goto end;
- } else {
- buf_desc[0].size = buffer->size;
- buf_desc[0].base_addr = (void *)addr;
- }
-
+ return 0;
}
+
+ *num_buffers = 1;
+ if (dvb_dmx_is_sec_feed(dvbdmx_feed) ||
+ dvb_dmx_is_pcr_feed(dvbdmx_feed)) {
+ buffer = &feed->sdmx_buf;
+ sdmx_buff = feed->sdmx_buf_handle;
+ } else {
+ buffer = (struct dvb_ringbuffer *)
+ dvbdmx_feed->feed.ts.buffer.ringbuff;
+ sdmx_buff = dvbdmx_feed->feed.ts.buffer.priv_handle;
+ }
+
+ if (sdmx_buff == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Invalid buffer allocation\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ buf_desc[0].length = buffer->size;
+ ret = mpq_sdmx_get_buffer_chunks(mpq_demux, sdmx_buff,
+ buf_desc[0].length,
+ buf_desc[0].buff_chunks);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_sdmx_get_buffer_chunks failed\n",
+ __func__);
+ return ret;
+ }
+
return 0;
-
-end:
- return ret;
}
-
static int mpq_sdmx_filter_setup(struct mpq_demux *mpq_demux,
struct dvb_demux_feed *dvbdmx_feed)
{
@@ -3447,7 +3496,7 @@
struct mpq_feed *feed;
struct mpq_feed *main_rec_feed;
struct sdmx_buff_descr metadata_buff_desc;
- struct sdmx_buff_descr data_buff_desc[DMX_MAX_DECODER_BUFFER_NUM];
+ struct sdmx_data_buff_descr *data_buff_desc = NULL;
u32 data_buf_num = DMX_MAX_DECODER_BUFFER_NUM;
enum sdmx_buf_mode buf_mode;
enum sdmx_raw_out_format ts_out_format = SDMX_188_OUTPUT;
@@ -3490,6 +3539,15 @@
MPQ_DVB_DBG_PRINT("%s: SDMX_PES_FILTER\n", __func__);
}
+ data_buff_desc = vmalloc(
+ sizeof(*data_buff_desc)*DMX_MAX_DECODER_BUFFER_NUM);
+ if (!data_buff_desc) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: failed to allocate memory for data buffer\n",
+ __func__);
+ return -ENOMEM;
+ }
+
/*
* Recording feed sdmx filter handle lookup:
* In case this is a recording filter with multiple feeds,
@@ -3530,7 +3588,7 @@
MPQ_DVB_ERR_PRINT(
"%s: Failed to initialize metadata buffer. ret=%d\n",
__func__, ret);
- goto end;
+ goto sdmx_filter_setup_failed;
}
}
@@ -3541,7 +3599,7 @@
"%s: Failed to initialize data buffer. ret=%d\n",
__func__, ret);
mpq_sdmx_terminate_metadata_buffer(feed);
- goto end;
+ goto sdmx_filter_setup_failed;
}
ret = sdmx_add_filter(mpq_demux->sdmx_session_handle,
dvbdmx_feed->pid,
@@ -3559,14 +3617,14 @@
__func__, ret);
ret = -ENODEV;
mpq_sdmx_terminate_metadata_buffer(feed);
- goto end;
+ goto sdmx_filter_setup_failed;
}
MPQ_DVB_DBG_PRINT(
"%s: feed=0x%p, filter pid=%d, handle=%d, data buffer(s)=%d, size=%d\n",
__func__, feed, dvbdmx_feed->pid,
feed->sdmx_filter_handle,
- data_buf_num, data_buff_desc[0].size);
+ data_buf_num, data_buff_desc[0].length);
mpq_demux->sdmx_filter_count++;
} else {
@@ -3583,7 +3641,7 @@
"%s: FAILED to add raw pid, ret=%d\n",
__func__, ret);
ret = -ENODEV;
- goto end;
+ goto sdmx_filter_setup_failed;
}
}
@@ -3605,12 +3663,14 @@
MPQ_DVB_ERR_PRINT(
"%s: FAILED to set key ladder, ret=%d\n",
__func__, ret);
- ret = -ENODEV;
- goto end;
}
}
-end:
+ vfree(data_buff_desc);
+ return 0;
+
+sdmx_filter_setup_failed:
+ vfree(data_buff_desc);
return ret;
}
@@ -4055,11 +4115,13 @@
pes_event.pes_end.start_gap = 0;
pes_event.data_length = 0;
- /* Parse error indicators - TODO: these should be per filter */
+ /* Parse error indicators */
if (sts->error_indicators & SDMX_FILTER_ERR_INVALID_PES_LEN)
pes_event.pes_end.pes_length_mismatch = 1;
- if (sts->error_indicators & SDMX_FILTER_ERR_CONT_CNT_INVALID)
- pes_event.pes_end.disc_indicator_set = 0;
+ else
+ pes_event.pes_end.pes_length_mismatch = 0;
+
+ pes_event.pes_end.disc_indicator_set = 0;
pes_event.pes_end.stc = 0;
pes_event.pes_end.tei_counter = counters.transport_err_count;
@@ -4109,7 +4171,7 @@
MPQ_DVB_DBG_PRINT("%s: Notify CRC err event\n", __func__);
event.status = DMX_CRC_ERROR;
event.data_length = 0;
- feed->data_ready_cb.sec(&feed->filter->filter, &event);
+ dvb_dmx_notify_section_event(feed, &event, 1);
}
if (sts->error_indicators & SDMX_FILTER_ERR_D_BUF_FULL)
@@ -4143,12 +4205,7 @@
if (sts->status_indicators & SDMX_FILTER_STATUS_EOS) {
event.data_length = 0;
event.status = DMX_OK_EOS;
- f = feed->filter;
-
- while (f && sec->is_filtering) {
- feed->data_ready_cb.sec(&f->filter, &event);
- f = f->next;
- }
+ dvb_dmx_notify_section_event(feed, &event, 1);
}
}
@@ -4469,10 +4526,12 @@
{
int i;
int j;
+ int sdmx_filters;
struct sdmx_filter_status *sts;
struct mpq_feed *mpq_feed;
- for (i = 0; i < mpq_demux->sdmx_filter_count; i++) {
+ sdmx_filters = mpq_demux->sdmx_filter_count;
+ for (i = 0; i < sdmx_filters; i++) {
/*
* MPQ_TODO: review lookup optimization
* Can have the related mpq_feed index already associated with
@@ -4594,7 +4653,7 @@
}
MPQ_DVB_DBG_PRINT(
- "\n\n%s: Before SDMX_process: input read_offset=%u, fill count=%u\n",
+ "%s: Before SDMX_process: input read_offset=%u, fill count=%u\n",
__func__, read_offset, fill_count);
process_start_time = current_kernel_time();
@@ -4606,11 +4665,11 @@
mpq_demux->sdmx_filter_count, mpq_demux->filters_status);
process_end_time = current_kernel_time();
- mpq_dmx_update_sdmx_stat(mpq_demux, prev_fill_count,
- &process_start_time, &process_end_time);
-
bytes_read = prev_fill_count - fill_count;
+ mpq_dmx_update_sdmx_stat(mpq_demux, bytes_read,
+ &process_start_time, &process_end_time);
+
MPQ_DVB_DBG_PRINT(
"%s: SDMX result=%d, input_fill_count=%u, read_offset=%u, read %d bytes from input, status=0x%X, errors=0x%X\n",
__func__, sdmx_res, fill_count, read_offset, bytes_read,
@@ -4637,14 +4696,19 @@
int mpq_sdmx_process(struct mpq_demux *mpq_demux,
struct sdmx_buff_descr *input,
u32 fill_count,
- u32 read_offset)
+ u32 read_offset,
+ size_t tsp_size)
{
int ret;
int todo;
int total_bytes_read = 0;
- int limit = mpq_sdmx_proc_limit * mpq_demux->demux.ts_packet_size;
+ int limit = mpq_sdmx_proc_limit * tsp_size;
- while (fill_count >= mpq_demux->demux.ts_packet_size) {
+ MPQ_DVB_DBG_PRINT(
+ "\n\n%s: read_offset=%u, fill_count=%u, tsp_size=%u\n",
+ __func__, read_offset, fill_count, tsp_size);
+
+ while (fill_count >= tsp_size) {
todo = fill_count > limit ? limit : fill_count;
ret = mpq_sdmx_process_buffer(mpq_demux, input, todo,
read_offset);
@@ -4693,7 +4757,8 @@
}
read_offset = mpq_demux->demux.dmx.dvr_input.ringbuff->pread;
- return mpq_sdmx_process(mpq_demux, &buf_desc, count, read_offset);
+ return mpq_sdmx_process(mpq_demux, &buf_desc, count,
+ read_offset, mpq_demux->demux.ts_packet_size);
}
int mpq_dmx_write(struct dmx_demux *demux, const char *buf, size_t count)
@@ -4766,6 +4831,12 @@
mutex_lock(&mpq_demux->mutex);
mpq_feed = feed->priv;
+ if (!dvb_dmx_is_video_feed(feed) && !dvb_dmx_is_pcr_feed(feed) &&
+ !feed->secure_mode.is_secured) {
+ mutex_unlock(&mpq_demux->mutex);
+ return 0;
+ }
+
event.data_length = 0;
switch (cmd->type) {
@@ -4803,21 +4874,11 @@
event.status = DMX_OK_MARKER;
event.marker.id = cmd->params.marker.id;
- if (feed->type == DMX_TYPE_SEC) {
- struct dvb_demux_filter *f = feed->filter;
- struct dmx_section_feed *sec = &feed->feed.sec;
-
- while (f && sec->is_filtering) {
- ret = feed->data_ready_cb.sec(&f->filter,
- &event);
- if (ret)
- break;
- f = f->next;
- }
- } else {
+ if (feed->type == DMX_TYPE_SEC)
+ ret = dvb_dmx_notify_section_event(feed, &event, 1);
+ else
/* MPQ_TODO: Notify decoder via the stream buffer */
ret = feed->data_ready_cb.ts(&feed->feed.ts, &event);
- }
break;
default:
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
index 899ef4c..ca7c15a 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
@@ -721,13 +721,15 @@
* @input: input buffer descriptor
* @fill_count: number of data bytes in input buffer that can be read
* @read_offset: offset in buffer for reading
+ * @tsp_size: size of single TS packet
*
* Return number of bytes read or error code
*/
int mpq_sdmx_process(struct mpq_demux *mpq_demux,
struct sdmx_buff_descr *input,
u32 fill_count,
- u32 read_offset);
+ u32 read_offset,
+ size_t tsp_size);
/**
* mpq_sdmx_loaded - Returns 1 if secure demux application is loaded,
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tsif.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tsif.c
index 8855e85..29369de 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tsif.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tsif.c
@@ -703,7 +703,8 @@
mpq_demux->dmxdev.capabilities =
DMXDEV_CAP_DUPLEX |
DMXDEV_CAP_PULL_MODE |
- DMXDEV_CAP_INDEXING;
+ DMXDEV_CAP_INDEXING |
+ DMXDEV_CAP_TS_INSERTION;
mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
mpq_demux->dmxdev.demux->get_stc = mpq_tsif_dmx_get_stc;
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c
index 193141a..a2ce428 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c
@@ -54,14 +54,16 @@
#define TSPP_RAW_TTS_SIZE 192
#define TSPP_RAW_SIZE 188
-#define MAX_BAM_DESCRIPTOR_SIZE (32*1024 - 1)
+#define MAX_BAM_DESCRIPTOR_SIZE (32 * 1024 - 1)
+
+#define MAX_BAM_DESCRIPTOR_COUNT (8 * 1024 - 2)
#define TSPP_BUFFER_SIZE (500 * 1024) /* 500KB */
#define TSPP_DESCRIPTOR_SIZE (TSPP_RAW_TTS_SIZE)
#define TSPP_BUFFER_COUNT(buffer_size) \
- ((buffer_size) / TSPP_RAW_TTS_SIZE)
+ ((buffer_size) / TSPP_DESCRIPTOR_SIZE)
/* When TSPP notifies demux that new packets are received.
* Using max descriptor size (170 packets).
@@ -382,7 +384,8 @@
buff_current_addr_phys - buff_start_addr_phys);
mpq_sdmx_process(mpq_demux, &input, aggregate_len,
- buff_current_addr_phys - buff_start_addr_phys);
+ buff_current_addr_phys - buff_start_addr_phys,
+ TSPP_RAW_TTS_SIZE);
}
for (i = 0; i < aggregate_count; i++)
@@ -1758,7 +1761,8 @@
mpq_demux->dmxdev.capabilities =
DMXDEV_CAP_DUPLEX |
DMXDEV_CAP_PULL_MODE |
- DMXDEV_CAP_INDEXING;
+ DMXDEV_CAP_INDEXING |
+ DMXDEV_CAP_TS_INSERTION;
mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
mpq_demux->dmxdev.demux->get_stc = mpq_tspp_dmx_get_stc;
@@ -1797,6 +1801,11 @@
mpq_dmx_tspp_info.tsif[i].buffer_count =
TSPP_BUFFER_COUNT(tspp_out_buffer_size);
+ if (mpq_dmx_tspp_info.tsif[i].buffer_count >
+ MAX_BAM_DESCRIPTOR_COUNT)
+ mpq_dmx_tspp_info.tsif[i].buffer_count =
+ MAX_BAM_DESCRIPTOR_COUNT;
+
mpq_dmx_tspp_info.tsif[i].aggregate_ids =
vzalloc(mpq_dmx_tspp_info.tsif[i].buffer_count *
sizeof(int));
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c
index c306488..60e3cb4 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c
@@ -143,7 +143,8 @@
mpq_demux->dmxdev.capabilities =
DMXDEV_CAP_DUPLEX |
DMXDEV_CAP_PULL_MODE |
- DMXDEV_CAP_INDEXING;
+ DMXDEV_CAP_INDEXING |
+ DMXDEV_CAP_TS_INSERTION;
mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
mpq_demux->dmxdev.demux->get_caps = mpq_tspp_dmx_get_caps;
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_sdmx.c b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.c
index 14d3a39..888bbbf 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_sdmx.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.c
@@ -115,7 +115,7 @@
enum sdmx_raw_out_format ts_out_format;
u32 flags;
u32 num_data_bufs;
- struct sdmx_buff_descr data_bufs[];
+ struct sdmx_data_buff_descr data_bufs[];
};
struct sdmx_add_filt_rsp {
@@ -478,7 +478,7 @@
struct sdmx_buff_descr *meta_data_buf,
enum sdmx_buf_mode d_buf_mode,
u32 num_data_bufs,
- struct sdmx_buff_descr *data_bufs,
+ struct sdmx_data_buff_descr *data_bufs,
int *filter_handle,
enum sdmx_raw_out_format ts_out_format,
u32 flags)
@@ -493,7 +493,7 @@
return SDMX_STATUS_INVALID_INPUT_PARAMS;
cmd_len = sizeof(struct sdmx_add_filt_req)
- + num_data_bufs * sizeof(struct sdmx_buff_descr);
+ + num_data_bufs * sizeof(struct sdmx_data_buff_descr);
rsp_len = sizeof(struct sdmx_add_filt_rsp);
/* Will be later overridden by SDMX response */
@@ -523,7 +523,7 @@
cmd->buffer_mode = d_buf_mode;
cmd->num_data_bufs = num_data_bufs;
memcpy(cmd->data_bufs, data_bufs,
- num_data_bufs * sizeof(struct sdmx_buff_descr));
+ num_data_bufs * sizeof(struct sdmx_data_buff_descr));
/* Issue QSEECom command */
res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h
index 6b669e4..fadb6b2 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h
+++ b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h
@@ -19,6 +19,8 @@
#define SDMX_MAX_SESSIONS (4)
#define SDMX_LOOPBACK_PID (0x2000)
+#define SDMX_MAX_PHYSICAL_CHUNKS (10)
+
/* Filter-level error indicators */
#define SDMX_FILTER_SUCCESS (0)
#define SDMX_FILTER_ERR_MD_BUF_FULL BIT(0)
@@ -164,10 +166,18 @@
/* Physical address where buffer starts */
void *base_addr;
- /* Total size of buffer */
+ /* Size of buffer */
u32 size;
};
+struct sdmx_data_buff_descr {
+ /* Physical chunks of the buffer */
+ struct sdmx_buff_descr buff_chunks[SDMX_MAX_PHYSICAL_CHUNKS];
+
+ /* Length of buffer */
+ u32 length;
+};
+
/*
* Data payload residing in the data buffers is described using this meta-data
* header. The meta data header specifies where the payload is located in the
@@ -231,7 +241,7 @@
int sdmx_add_filter(int session_handle, u16 pid, enum sdmx_filter filter_type,
struct sdmx_buff_descr *meta_data_buf, enum sdmx_buf_mode data_buf_mode,
- u32 num_data_bufs, struct sdmx_buff_descr *data_bufs,
+ u32 num_data_bufs, struct sdmx_data_buff_descr *data_bufs,
int *filter_handle, enum sdmx_raw_out_format ts_out_format, u32 flags);
int sdmx_remove_filter(int session_handle, int filter_handle);
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index ddf271e..6e8c809 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -45,6 +45,34 @@
[ilog2(HAL_H264_CABAC_MODEL_2)] = HFI_H264_CABAC_MODEL_2,
};
+static int color_format[] = {
+ [ilog2(HAL_COLOR_FORMAT_MONOCHROME)] = HFI_COLOR_FORMAT_MONOCHROME,
+ [ilog2(HAL_COLOR_FORMAT_NV12)] = HFI_COLOR_FORMAT_NV12,
+ [ilog2(HAL_COLOR_FORMAT_NV21)] = HFI_COLOR_FORMAT_NV21,
+ [ilog2(HAL_COLOR_FORMAT_NV12_4x4TILE)] = HFI_COLOR_FORMAT_NV12_4x4TILE,
+ [ilog2(HAL_COLOR_FORMAT_NV21_4x4TILE)] = HFI_COLOR_FORMAT_NV21_4x4TILE,
+ [ilog2(HAL_COLOR_FORMAT_YUYV)] = HFI_COLOR_FORMAT_YUYV,
+ [ilog2(HAL_COLOR_FORMAT_YVYU)] = HFI_COLOR_FORMAT_YVYU,
+ [ilog2(HAL_COLOR_FORMAT_UYVY)] = HFI_COLOR_FORMAT_UYVY,
+ [ilog2(HAL_COLOR_FORMAT_VYUY)] = HFI_COLOR_FORMAT_VYUY,
+ [ilog2(HAL_COLOR_FORMAT_RGB565)] = HFI_COLOR_FORMAT_RGB565,
+ [ilog2(HAL_COLOR_FORMAT_BGR565)] = HFI_COLOR_FORMAT_BGR565,
+ [ilog2(HAL_COLOR_FORMAT_RGB888)] = HFI_COLOR_FORMAT_RGB888,
+ [ilog2(HAL_COLOR_FORMAT_BGR888)] = HFI_COLOR_FORMAT_BGR888,
+};
+
+static int nal_type[] = {
+ [ilog2(HAL_NAL_FORMAT_STARTCODES)] = HFI_NAL_FORMAT_STARTCODES,
+ [ilog2(HAL_NAL_FORMAT_ONE_NAL_PER_BUFFER)] =
+ HFI_NAL_FORMAT_ONE_NAL_PER_BUFFER,
+ [ilog2(HAL_NAL_FORMAT_ONE_BYTE_LENGTH)] =
+ HFI_NAL_FORMAT_ONE_BYTE_LENGTH,
+ [ilog2(HAL_NAL_FORMAT_TWO_BYTE_LENGTH)] =
+ HFI_NAL_FORMAT_TWO_BYTE_LENGTH,
+ [ilog2(HAL_NAL_FORMAT_FOUR_BYTE_LENGTH)] =
+ HFI_NAL_FORMAT_FOUR_BYTE_LENGTH,
+};
+
static inline int hal_to_hfi_type(int property, int hal_type)
{
if (hal_type && (roundup_pow_of_two(hal_type) != hal_type)) {
@@ -66,6 +94,12 @@
case HAL_PARAM_VENC_H264_ENTROPY_CABAC_MODEL:
return (hal_type >= ARRAY_SIZE(cabac_model)) ?
-ENOTSUPP : cabac_model[hal_type];
+ case HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT:
+ return (hal_type >= ARRAY_SIZE(color_format)) ?
+ -ENOTSUPP : color_format[hal_type];
+ case HAL_PARAM_NAL_STREAM_FORMAT_SELECT:
+ return (hal_type >= ARRAY_SIZE(nal_type)) ?
+ -ENOTSUPP : nal_type[hal_type];
default:
return -ENOTSUPP;
}
@@ -316,6 +350,9 @@
case HAL_EXTRADATA_ASPECT_RATIO:
ret = HFI_PROPERTY_PARAM_INDEX_EXTRADATA;
break;
+ case HAL_EXTRADATA_MPEG2_SEQDISP:
+ ret = HFI_PROPERTY_PARAM_VDEC_MPEG2_SEQDISP_EXTRADATA;
+ break;
default:
dprintk(VIDC_WARN, "Extradata index not found: %d\n", index);
break;
@@ -629,11 +666,13 @@
hfi->buffer_type = buffer_type;
else
return -EINVAL;
- hfi->format = prop->format;
+ hfi->format = hal_to_hfi_type(
+ HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT,
+ prop->format);
pkt->size += sizeof(u32) +
sizeof(struct hfi_uncompressed_format_select);
break;
- }
+ }
case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO:
break;
case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO:
@@ -693,40 +732,20 @@
}
case HAL_PARAM_NAL_STREAM_FORMAT_SELECT:
{
- struct hal_nal_stream_format_supported *prop =
- (struct hal_nal_stream_format_supported *)pdata;
+ struct hfi_nal_stream_format_select *hfi;
+ struct hal_nal_stream_format_select *prop =
+ (struct hal_nal_stream_format_select *)pdata;
pkt->rg_property_data[0] =
HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT;
+ hfi = (struct hfi_nal_stream_format_select *)
+ &pkt->rg_property_data[1];
dprintk(VIDC_DBG, "data is :%d",
- prop->nal_stream_format_supported);
-
- switch (prop->nal_stream_format_supported) {
- case HAL_NAL_FORMAT_STARTCODES:
- pkt->rg_property_data[1] =
- HFI_NAL_FORMAT_STARTCODES;
- break;
- case HAL_NAL_FORMAT_ONE_NAL_PER_BUFFER:
- pkt->rg_property_data[1] =
- HFI_NAL_FORMAT_ONE_NAL_PER_BUFFER;
- break;
- case HAL_NAL_FORMAT_ONE_BYTE_LENGTH:
- pkt->rg_property_data[1] =
- HFI_NAL_FORMAT_ONE_BYTE_LENGTH;
- break;
- case HAL_NAL_FORMAT_TWO_BYTE_LENGTH:
- pkt->rg_property_data[1] =
- HFI_NAL_FORMAT_TWO_BYTE_LENGTH;
- break;
- case HAL_NAL_FORMAT_FOUR_BYTE_LENGTH:
- pkt->rg_property_data[1] =
- HFI_NAL_FORMAT_FOUR_BYTE_LENGTH;
- break;
- default:
- dprintk(VIDC_ERR, "Invalid nal format: 0x%x",
- prop->nal_stream_format_supported);
- break;
- }
- pkt->size += sizeof(u32) * 2;
+ prop->nal_stream_format_select);
+ hfi->nal_stream_format_select = hal_to_hfi_type(
+ HAL_PARAM_NAL_STREAM_FORMAT_SELECT,
+ prop->nal_stream_format_select);
+ pkt->size += sizeof(u32) +
+ sizeof(struct hfi_nal_stream_format_select);
break;
}
case HAL_PARAM_VDEC_OUTPUT_ORDER:
@@ -1091,6 +1110,17 @@
sizeof(struct hfi_quantization_range);
break;
}
+ case HAL_PARAM_VENC_MAX_NUM_B_FRAMES:
+ {
+ struct hfi_max_num_b_frames *hfi;
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES;
+ hfi = (struct hfi_max_num_b_frames *) &pkt->rg_property_data[1];
+ memcpy(hfi, (struct hfi_max_num_b_frames *) pdata,
+ sizeof(struct hfi_max_num_b_frames));
+ pkt->size += sizeof(u32) + sizeof(struct hfi_max_num_b_frames);
+ break;
+ }
case HAL_CONFIG_VENC_INTRA_PERIOD:
{
struct hfi_intra_period *hfi;
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index 91fb514..859345f 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -174,8 +174,8 @@
switch (pkt->event_id) {
case HFI_EVENT_SYS_ERROR:
- dprintk(VIDC_ERR, "HFI_EVENT_SYS_ERROR: %d\n",
- pkt->event_data1);
+ dprintk(VIDC_ERR, "HFI_EVENT_SYS_ERROR: %d, 0x%x\n",
+ pkt->event_data1, pkt->event_data2);
hfi_process_sys_error(callback, device_id);
break;
case HFI_EVENT_SESSION_ERROR:
@@ -676,7 +676,7 @@
{
struct msm_vidc_cb_cmd_done cmd_done;
struct vidc_hal_session_init_done session_init_done;
-
+ struct hal_session *sess_close = NULL;
dprintk(VIDC_DBG, "RECEIVED:SESSION_INIT_DONE");
if (sizeof(struct hfi_msg_sys_session_init_done_packet)
> pkt->size) {
@@ -696,6 +696,16 @@
if (!cmd_done.status) {
cmd_done.status = hfi_process_sess_init_done_prop_read(
pkt, &session_init_done);
+ } else {
+ sess_close = (struct hal_session *)pkt->session_id;
+ if (sess_close) {
+ dprintk(VIDC_INFO,
+ "Sess init failed: Deleting session: 0x%x 0x%p",
+ sess_close->session_id, sess_close);
+ list_del(&sess_close->list);
+ kfree(sess_close);
+ sess_close = NULL;
+ }
}
cmd_done.size = sizeof(struct vidc_hal_session_init_done);
callback(SESSION_INIT_DONE, &cmd_done);
@@ -1015,9 +1025,46 @@
sess_close->session_id);
list_del(&sess_close->list);
kfree(sess_close);
+ sess_close = NULL;
callback(SESSION_END_DONE, &cmd_done);
}
+static void hfi_process_session_abort_done(
+ msm_vidc_callback callback, u32 device_id,
+ struct hfi_msg_sys_session_abort_done_packet *pkt)
+{
+ struct msm_vidc_cb_cmd_done cmd_done;
+ struct hal_session *sess_close;
+
+ dprintk(VIDC_DBG, "RECEIVED:SESSION_ABORT_DONE");
+
+ if (!pkt || pkt->size !=
+ sizeof(struct hfi_msg_sys_session_abort_done_packet)) {
+ dprintk(VIDC_ERR, "%s: bad packet/packet size: %d",
+ __func__, pkt ? pkt->size : 0);
+ return;
+ }
+ memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+ cmd_done.device_id = device_id;
+ cmd_done.session_id =
+ ((struct hal_session *) pkt->session_id)->session_id;
+ cmd_done.status = hfi_map_err_status((u32)pkt->error_type);
+ cmd_done.data = NULL;
+ cmd_done.size = 0;
+
+ sess_close = (struct hal_session *)pkt->session_id;
+ if (!sess_close) {
+ dprintk(VIDC_ERR, "%s: invalid session pointer\n", __func__);
+ return;
+ }
+ dprintk(VIDC_ERR, "deleted the session: 0x%x",
+ sess_close->session_id);
+ list_del(&sess_close->list);
+ kfree(sess_close);
+ sess_close = NULL;
+ callback(SESSION_ABORT_DONE, &cmd_done);
+}
+
static void hfi_process_session_get_seq_hdr_done(
msm_vidc_callback callback, u32 device_id,
struct hfi_msg_session_get_sequence_header_done_packet *pkt)
@@ -1134,6 +1181,10 @@
hfi_msg_session_release_buffers_done_packet*)
msg_hdr);
break;
+ case HFI_MSG_SYS_SESSION_ABORT_DONE:
+ hfi_process_session_abort_done(callback, device_id, (struct
+ hfi_msg_sys_session_abort_done_packet*) msg_hdr);
+ break;
default:
dprintk(VIDC_ERR, "UNKNOWN_MSG_TYPE : %d", msg_hdr->packet);
break;
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index f458a0a..7547464 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -66,6 +66,13 @@
"Extradata input crop",
"Extradata digital zoom",
"Extradata aspect ratio",
+ "Extradata mpeg2 seqdisp",
+};
+
+static const char *const perf_level[] = {
+ "Nominal",
+ "Performance",
+ "Turbo"
};
static struct msm_vidc_ctrl msm_vdec_ctrls[] = {
@@ -203,7 +210,7 @@
.name = "Extradata Type",
.type = V4L2_CTRL_TYPE_MENU,
.minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE,
- .maximum = V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO,
+ .maximum = V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP,
.default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE,
.menu_skip_mask = ~(
(1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) |
@@ -223,11 +230,25 @@
(1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER) |
(1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_INPUT_CROP) |
(1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_DIGITAL_ZOOM) |
- (1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO)
+ (1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO) |
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP)
),
.qmenu = mpeg_video_vidc_extradata,
.step = 0,
},
+ {
+ .id = V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL,
+ .name = "Encoder Performance Level",
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL,
+ .maximum = V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO,
+ .default_value = V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL,
+ .menu_skip_mask = ~(
+ (1 << V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL) |
+ (1 << V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO)),
+ .qmenu = perf_level,
+ .step = 0,
+ },
};
#define NUM_CTRLS ARRAY_SIZE(msm_vdec_ctrls)
@@ -697,33 +718,47 @@
}
int msm_vdec_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a)
{
- u32 us_per_frame = 0;
- int rc = 0;
+ u64 us_per_frame = 0;
+ int rc = 0, fps = 0;
if (a->parm.output.timeperframe.denominator) {
switch (a->type) {
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- us_per_frame = a->parm.output.timeperframe.numerator/
- a->parm.output.timeperframe.denominator;
- break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- us_per_frame = a->parm.capture.timeperframe.numerator/
- a->parm.capture.timeperframe.denominator;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ us_per_frame = a->parm.output.timeperframe.numerator *
+ (u64)USEC_PER_SEC;
+ do_div(us_per_frame, a->parm.output.\
+ timeperframe.denominator);
break;
default:
dprintk(VIDC_ERR,
- "Scale clocks : Unknown buffer type\n");
+ "Scale clocks : Unknown buffer type %d\n",
+ a->type);
break;
}
}
+
if (!us_per_frame) {
dprintk(VIDC_ERR,
- "Failed to scale clocks : time between frames is 0\n");
+ "Failed to scale clocks : time between frames is 0\n");
rc = -EINVAL;
goto exit;
}
- inst->prop.fps = (u8) (USEC_PER_SEC / us_per_frame);
- if (inst->prop.fps) {
+
+ fps = USEC_PER_SEC;
+ do_div(fps, us_per_frame);
+
+ if ((fps % 15 == 14) || (fps % 24 == 23))
+ fps = fps + 1;
+ else if ((fps % 24 == 1) || (fps % 15 == 1))
+ fps = fps - 1;
+
+ if (inst->prop.fps != fps) {
+ dprintk(VIDC_PROF, "reported fps changed for %p: %d->%d\n",
+ inst, inst->prop.fps, fps);
+ inst->prop.fps = fps;
+ mutex_lock(&inst->core->sync_lock);
msm_comm_scale_clocks_and_bus(inst);
+ mutex_unlock(&inst->core->sync_lock);
}
exit:
return rc;
@@ -947,6 +982,7 @@
"No buffer requirement for buffer type %x\n",
HAL_BUFFER_OUTPUT);
rc = -EINVAL;
+ mutex_unlock(&inst->lock);
break;
}
if (*num_buffers && *num_buffers >
@@ -1008,7 +1044,11 @@
"Failed to set persist buffers: %d\n", rc);
goto fail_start;
}
+
+ mutex_lock(&inst->core->sync_lock);
msm_comm_scale_clocks_and_bus(inst);
+ mutex_unlock(&inst->core->sync_lock);
+
rc = msm_comm_try_state(inst, MSM_VIDC_START_DONE);
if (rc) {
dprintk(VIDC_ERR,
@@ -1099,7 +1139,10 @@
rc = -EINVAL;
break;
}
+
+ mutex_lock(&inst->core->sync_lock);
msm_comm_scale_clocks_and_bus(inst);
+ mutex_unlock(&inst->core->sync_lock);
if (rc)
dprintk(VIDC_ERR,
@@ -1119,13 +1162,25 @@
int msm_vdec_cmd(struct msm_vidc_inst *inst, struct v4l2_decoder_cmd *dec)
{
int rc = 0;
- struct v4l2_event dqevent = {0};
struct msm_vidc_core *core = inst->core;
+
+ if (!dec || !inst || !inst->core) {
+ dprintk(VIDC_ERR, "%s invalid params", __func__);
+ return -EINVAL;
+ }
switch (dec->cmd) {
case V4L2_DEC_QCOM_CMD_FLUSH:
rc = msm_comm_flush(inst, dec->flags);
break;
case V4L2_DEC_CMD_STOP:
+ if (core->state != VIDC_CORE_INVALID &&
+ inst->state == MSM_VIDC_CORE_INVALID) {
+ rc = msm_comm_recover_from_session_error(inst);
+ if (rc)
+ dprintk(VIDC_ERR,
+ "Failed to recover from session_error: %d\n",
+ rc);
+ }
rc = msm_comm_release_scratch_buffers(inst);
if (rc)
dprintk(VIDC_ERR,
@@ -1138,11 +1193,15 @@
dprintk(VIDC_ERR,
"Core %p in bad state, Sending CLOSE event\n",
core);
- dqevent.type = V4L2_EVENT_MSM_VIDC_CLOSE_DONE;
- v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+ msm_vidc_queue_v4l2_event(inst,
+ V4L2_EVENT_MSM_VIDC_CLOSE_DONE);
goto exit;
}
rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE);
+ /* Clients rely on this event for joining poll thread.
+ * This event should be returned even if firmware has
+ * failed to respond */
+ msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_CLOSE_DONE);
break;
default:
dprintk(VIDC_ERR, "Unknown Decoder Command\n");
@@ -1186,21 +1245,18 @@
inst->capability.width.min = MIN_SUPPORTED_WIDTH;
inst->capability.width.max = DEFAULT_WIDTH;
inst->prop.fps = 30;
- inst->prop.prev_time_stamp = 0;
return rc;
}
static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
{
int rc = 0;
- struct v4l2_control control;
struct hal_nal_stream_format_supported stream_format;
struct hal_enable_picture enable_picture;
struct hal_enable hal_property;/*, prop;*/
- u32 control_idx = 0;
enum hal_property property_id = 0;
u32 property_val = 0;
- void *pdata;
+ void *pdata = NULL;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
@@ -1264,8 +1320,9 @@
pdata = &hal_property;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_SECURE:
- inst->mode = VIDC_SECURE;
- dprintk(VIDC_DBG, "Setting secure mode to :%d\n", inst->mode);
+ inst->flags |= VIDC_SECURE;
+ dprintk(VIDC_DBG, "Setting secure mode to: %d\n",
+ !!(inst->flags & VIDC_SECURE));
break;
case V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA:
{
@@ -1276,16 +1333,30 @@
pdata = &extra;
break;
}
+ case V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL:
+ switch (ctrl->val) {
+ case V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL:
+ inst->flags &= ~VIDC_TURBO;
+ break;
+ case V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO:
+ inst->flags |= VIDC_TURBO;
+ break;
+ default:
+ dprintk(VIDC_ERR, "Perf mode %x not supported",
+ ctrl->val);
+ rc = -ENOTSUPP;
+ break;
+ }
+
+ break;
default:
break;
}
if (!rc && property_id) {
dprintk(VIDC_DBG,
- "Control: HAL property=%d,ctrl_id=%d,ctrl_value=%d\n",
- property_id,
- msm_vdec_ctrls[control_idx].id,
- control.value);
+ "Control: HAL property = %d, ctrl_id = 0x%x, ctrl_value = %d\n",
+ property_id, ctrl->id, ctrl->val);
rc = call_hfi_op(hdev, session_set_property, (void *)
inst->session, property_id, pdata);
}
@@ -1468,6 +1539,6 @@
kfree(curr->cluster);
kfree(curr);
}
-
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
return 0;
}
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 464cb05..7fc2595 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -20,7 +20,8 @@
#define MSM_VENC_DVC_NAME "msm_venc_8974"
#define MIN_NUM_OUTPUT_BUFFERS 4
-#define MIN_BIT_RATE 64000
+#define MIN_NUM_CAPTURE_BUFFERS 4
+#define MIN_BIT_RATE 32000
#define MAX_BIT_RATE 160000000
#define DEFAULT_BIT_RATE 64000
#define BIT_RATE_STEP 100
@@ -35,6 +36,7 @@
#define P_FRAME_QP 28
#define B_FRAME_QP 30
#define MAX_INTRA_REFRESH_MBS 300
+#define MAX_NUM_B_FRAMES 4
#define L_MODE V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY
#define CODING V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY
@@ -106,6 +108,12 @@
"Extradata aspect ratio",
};
+static const char *const perf_level[] = {
+ "Nominal",
+ "Performance",
+ "Turbo"
+};
+
enum msm_venc_ctrl_cluster {
MSM_VENC_CTRL_CLUSTER_QP = 1 << 0,
MSM_VENC_CTRL_CLUSTER_INTRA_PERIOD = 1 << 1,
@@ -122,18 +130,6 @@
static struct msm_vidc_ctrl msm_venc_ctrls[] = {
{
- .id = V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE,
- .name = "Frame Rate",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = MIN_FRAME_RATE,
- .maximum = MAX_FRAME_RATE,
- .default_value = MIN_FRAME_RATE,
- .step = 1,
- .menu_skip_mask = 0,
- .qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_TIMING,
- },
- {
.id = V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD,
.name = "IDR Period",
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -674,6 +670,19 @@
.default_value =
V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_DISABLED,
},
+ {
+ .id = V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL,
+ .name = "Encoder Performance Level",
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL,
+ .maximum = V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO,
+ .default_value = V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL,
+ .menu_skip_mask = ~(
+ (1 << V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL) |
+ (1 << V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO)),
+ .qmenu = perf_level,
+ .step = 0,
+ },
};
#define NUM_CTRLS ARRAY_SIZE(msm_venc_ctrls)
@@ -685,7 +694,7 @@
static u32 get_frame_size_nv21(int plane, u32 height, u32 width)
{
- return height * width * 2;
+ return VENUS_BUFFER_SIZE(COLOR_FMT_NV21, width, height);
}
static u32 get_frame_size_compressed(int plane, u32 height, u32 width)
@@ -761,7 +770,7 @@
struct v4l2_ctrl *ctrl = NULL;
u32 extradata = 0;
if (!q || !q->drv_priv) {
- dprintk(VIDC_ERR, "Invalid input, q = %p\n", q);
+ dprintk(VIDC_ERR, "Invalid input\n");
return -EINVAL;
}
inst = q->drv_priv;
@@ -776,16 +785,20 @@
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
*num_planes = 1;
buff_req = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
- *num_buffers = buff_req->buffer_count_actual =
+ if (buff_req) {
+ *num_buffers = buff_req->buffer_count_actual =
max(*num_buffers, buff_req->buffer_count_actual);
- if (*num_buffers > VIDEO_MAX_FRAME) {
- dprintk(VIDC_ERR,
- "Failed : No of slices requested = %d"\
- " Max supported slices = %d",
- *num_buffers, VIDEO_MAX_FRAME);
- rc = -EINVAL;
- break;
- }
+ }
+ if (*num_buffers < MIN_NUM_CAPTURE_BUFFERS)
+ *num_buffers = MIN_NUM_CAPTURE_BUFFERS;
+
+ if (*num_buffers > VIDEO_MAX_FRAME) {
+ dprintk(VIDC_ERR,
+ "Changing buffers requested, from %d to max"\
+ " supported (%d) best effort encoding\n",
+ *num_buffers, VIDEO_MAX_FRAME);
+ *num_buffers = VIDEO_MAX_FRAME;
+ }
ctrl = v4l2_ctrl_find(&inst->ctrl_handler,
V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA);
if (ctrl)
@@ -865,7 +878,10 @@
dprintk(VIDC_ERR, "Failed to set persist buffers: %d\n", rc);
goto fail_start;
}
+
+ mutex_lock(&inst->core->sync_lock);
msm_comm_scale_clocks_and_bus(inst);
+ mutex_unlock(&inst->core->sync_lock);
rc = msm_comm_try_state(inst, MSM_VIDC_START_DONE);
if (rc) {
@@ -941,7 +957,10 @@
rc = -EINVAL;
break;
}
+
+ mutex_lock(&inst->core->sync_lock);
msm_comm_scale_clocks_and_bus(inst);
+ mutex_unlock(&inst->core->sync_lock);
if (rc)
dprintk(VIDC_ERR,
@@ -1148,7 +1167,6 @@
static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
{
int rc = 0;
- struct hal_frame_rate frame_rate;
struct hal_request_iframe request_iframe;
struct hal_bitrate bitrate;
struct hal_profile_level profile_level;
@@ -1190,13 +1208,6 @@
})
switch (ctrl->id) {
- case V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE:
- property_id =
- HAL_CONFIG_FRAME_RATE;
- frame_rate.frame_rate = ctrl->val;
- frame_rate.buffer_type = HAL_BUFFER_OUTPUT;
- pdata = &frame_rate;
- break;
case V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD:
property_id =
HAL_CONFIG_VENC_IDR_PERIOD;
@@ -1237,11 +1248,24 @@
break;
case V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES:
temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES);
-
- property_id =
- HAL_CONFIG_VENC_INTRA_PERIOD;
intra_period.bframes = ctrl->val;
intra_period.pframes = temp_ctrl->val;
+ if (intra_period.bframes) {
+ u32 max_num_b_frames = MAX_NUM_B_FRAMES;
+ property_id =
+ HAL_PARAM_VENC_MAX_NUM_B_FRAMES;
+ pdata = &max_num_b_frames;
+ rc = call_hfi_op(hdev, session_set_property,
+ (void *)inst->session, property_id, pdata);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed : Setprop MAX_NUM_B_FRAMES"
+ "%d", rc);
+ break;
+ }
+ }
+ property_id =
+ HAL_CONFIG_VENC_INTRA_PERIOD;
pdata = &intra_period;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME:
@@ -1697,8 +1721,9 @@
pdata = &enable;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_SECURE:
- inst->mode = VIDC_SECURE;
- dprintk(VIDC_INFO, "Setting secure mode to :%d\n", inst->mode);
+ inst->flags |= VIDC_SECURE;
+ dprintk(VIDC_INFO, "Setting secure mode to: %d\n",
+ !!(inst->flags & VIDC_SECURE));
break;
case V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA:
{
@@ -1711,12 +1736,11 @@
}
case V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO:
{
- struct v4l2_ctrl *rc_mode, *frame_rate;
+ struct v4l2_ctrl *rc_mode;
bool cfr = false;
property_id = HAL_PARAM_VENC_H264_VUI_TIMING_INFO;
rc_mode = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL);
- frame_rate = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE);
switch (rc_mode->val) {
case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_CFR:
@@ -1733,20 +1757,9 @@
vui_timing_info.enable = 0;
break;
case V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_ENABLED:
- /* Only support this in CFR mode because we
- * don't really know how to fill out vui_timing_info.
- * time_scale in vfr mode. The assumed framerate
- * might be incorrect. */
- if (!cfr) {
- dprintk(VIDC_ERR, "Can't set %x in VFR mode\n",
- ctrl->id);
- rc = -ENOTSUPP;
- break;
- }
-
vui_timing_info.enable = 1;
vui_timing_info.fixed_frame_rate = cfr;
- vui_timing_info.time_scale = frame_rate->val;
+ vui_timing_info.time_scale = inst->prop.fps;
}
pdata = &vui_timing_info;
@@ -1769,6 +1782,22 @@
pdata = &enable;
break;
+ case V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL:
+ switch (ctrl->val) {
+ case V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL:
+ inst->flags &= ~VIDC_TURBO;
+ break;
+ case V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO:
+ inst->flags |= VIDC_TURBO;
+ break;
+ default:
+ dprintk(VIDC_ERR, "Perf mode %x not supported",
+ ctrl->val);
+ rc = -ENOTSUPP;
+ break;
+ }
+
+ break;
default:
rc = -ENOTSUPP;
break;
@@ -1862,7 +1891,6 @@
int msm_venc_cmd(struct msm_vidc_inst *inst, struct v4l2_encoder_cmd *enc)
{
int rc = 0;
- struct v4l2_event dqevent = {0};
struct msm_vidc_core *core;
core = inst->core;
switch (enc->cmd) {
@@ -1872,8 +1900,8 @@
case V4L2_ENC_CMD_STOP:
if (inst->state == MSM_VIDC_CORE_INVALID ||
core->state == VIDC_CORE_INVALID) {
- dqevent.type = V4L2_EVENT_MSM_VIDC_CLOSE_DONE;
- v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+ msm_vidc_queue_v4l2_event(inst,
+ V4L2_EVENT_MSM_VIDC_CLOSE_DONE);
return rc;
}
rc = msm_comm_release_scratch_buffers(inst);
@@ -1885,6 +1913,10 @@
dprintk(VIDC_ERR, "Failed to release persist buf:%d\n",
rc);
rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE);
+ /* Clients rely on this event for joining poll thread.
+ * This event should be returned even if firmware has
+ * failed to respond */
+ msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_CLOSE_DONE);
break;
}
if (rc)
@@ -1945,7 +1977,7 @@
{
u32 property_id = 0, us_per_frame = 0;
void *pdata;
- int rc = 0;
+ int rc = 0, fps = 0;
struct hal_frame_rate frame_rate;
struct hfi_device *hdev;
@@ -1953,32 +1985,46 @@
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
- hdev = inst->core->device;
+ hdev = inst->core->device;
property_id = HAL_CONFIG_FRAME_RATE;
+
if (a->parm.output.timeperframe.denominator) {
switch (a->type) {
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
us_per_frame = a->parm.output.timeperframe.numerator *
- USEC_PER_SEC / a->parm.output.\
- timeperframe.denominator;
+ (u64)USEC_PER_SEC;
+ do_div(us_per_frame, a->parm.output.\
+ timeperframe.denominator);
break;
default:
dprintk(VIDC_ERR,
- "Scale clocks : Unknown buffer type\n");
+ "Scale clocks : Unknown buffer type %d\n",
+ a->type);
break;
}
}
if (!us_per_frame) {
dprintk(VIDC_ERR,
- "Failed to scale clocks : time between frames is 0\n");
+ "Failed to scale clocks : time between frames is 0\n");
rc = -EINVAL;
goto exit;
}
- inst->prop.fps = (u8) (USEC_PER_SEC / us_per_frame);
- if (inst->prop.fps) {
+
+ fps = USEC_PER_SEC;
+ do_div(fps, us_per_frame);
+
+ if ((fps % 15 == 14) || (fps % 24 == 23))
+ fps = fps + 1;
+ else if ((fps % 24 == 1) || (fps % 15 == 1))
+ fps = fps - 1;
+
+ if (inst->prop.fps != fps) {
+ dprintk(VIDC_PROF, "reported fps changed for %p: %d->%d\n",
+ inst, inst->prop.fps, fps);
+ inst->prop.fps = fps;
frame_rate.frame_rate = inst->prop.fps * (0x1<<16);
frame_rate.buffer_type = HAL_BUFFER_OUTPUT;
pdata = &frame_rate;
@@ -1989,7 +2035,9 @@
dprintk(VIDC_WARN,
"Failed to set frame rate %d\n", rc);
}
+ mutex_lock(&inst->core->sync_lock);
msm_comm_scale_clocks_and_bus(inst);
+ mutex_unlock(&inst->core->sync_lock);
}
exit:
return rc;
@@ -1997,7 +2045,6 @@
int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
{
struct msm_vidc_format *fmt = NULL;
- struct hal_frame_size frame_sz;
int rc = 0;
int i;
struct hfi_device *hdev;
@@ -2025,6 +2072,9 @@
goto exit;
}
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ struct hal_uncompressed_format_select hal_fmt = {0};
+ struct hal_frame_size frame_sz;
+
inst->prop.width = f->fmt.pix_mp.width;
inst->prop.height = f->fmt.pix_mp.height;
rc = msm_vidc_check_session_supported(inst);
@@ -2063,6 +2113,29 @@
rc = -EINVAL;
goto exit;
}
+
+ switch (fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ hal_fmt.format = HAL_COLOR_FORMAT_NV12;
+ break;
+ case V4L2_PIX_FMT_NV21:
+ hal_fmt.format = HAL_COLOR_FORMAT_NV21;
+ break;
+ default:
+ /* we really shouldn't be here */
+ rc = -ENOTSUPP;
+ goto exit;
+ }
+
+ hal_fmt.buffer_type = HAL_BUFFER_INPUT;
+ rc = call_hfi_op(hdev, session_set_property, (void *)
+ inst->session, HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT,
+ &hal_fmt);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to set input color format\n");
+ goto exit;
+ }
}
if (fmt) {
@@ -2478,6 +2551,6 @@
kfree(curr->cluster);
kfree(curr);
}
-
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
return 0;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index 0fbfd72..1c43f1e 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -565,6 +565,7 @@
struct msm_vidc_core *core;
struct list_head *ptr, *next;
int rc = 0;
+ int i;
if (!inst)
return -EINVAL;
@@ -592,6 +593,9 @@
if (rc)
dprintk(VIDC_ERR,
"Failed to move video instance to uninit state\n");
+ for (i = 0; i < MAX_PORT_NUM; i++)
+ vb2_queue_release(&inst->bufq[i].vb2_bufq);
+
pr_info(VIDC_DBG_TAG "Closed video instance: %p\n", VIDC_INFO, inst);
kfree(inst);
return 0;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index d925de3..bd05180 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -22,7 +22,7 @@
#include "msm_smem.h"
#include "msm_vidc_debug.h"
-#define HW_RESPONSE_TIMEOUT 200
+#define HW_RESPONSE_TIMEOUT 1000
#define IS_ALREADY_IN_STATE(__p, __d) ({\
int __rc = (__p >= __d);\
@@ -46,8 +46,28 @@
u32 __mbs = (__h >> 4) * (__w >> 4);\
__mbs;\
})
+static bool is_turbo_requested(struct msm_vidc_core *core,
+ enum session_type type)
+{
+ struct msm_vidc_inst *inst = NULL;
-#define TIME_DIFF_THRESHOLD 200
+ list_for_each_entry(inst, &core->instances, list) {
+ bool wants_turbo = false;
+
+ mutex_lock(&inst->lock);
+ if (inst->session_type == type &&
+ inst->state >= MSM_VIDC_OPEN_DONE &&
+ inst->state < MSM_VIDC_STOP_DONE) {
+ wants_turbo = inst->flags & VIDC_TURBO;
+ }
+ mutex_unlock(&inst->lock);
+
+ if (wants_turbo)
+ return true;
+ }
+
+ return false;
+}
static int msm_comm_get_load(struct msm_vidc_core *core,
enum session_type type)
@@ -89,7 +109,10 @@
return -EINVAL;
}
- load = msm_comm_get_load(core, type);
+ if (is_turbo_requested(core, type))
+ load = core->resources.max_load;
+ else
+ load = msm_comm_get_load(core, type);
rc = call_hfi_op(hdev, scale_bus, hdev->hfi_device_data,
load, type, mtype);
@@ -288,10 +311,21 @@
static void change_inst_state(struct msm_vidc_inst *inst,
enum instance_state state)
{
+ if (!inst) {
+ dprintk(VIDC_ERR, "Invalid parameter %s", __func__);
+ return;
+ }
mutex_lock(&inst->lock);
+ if (inst->state == MSM_VIDC_CORE_INVALID) {
+ dprintk(VIDC_DBG,
+ "Inst: %p is in bad state can't change state",
+ inst);
+ goto exit;
+ }
dprintk(VIDC_DBG, "Moved inst: %p from state: %d to state: %d\n",
inst, inst->state, state);
inst->state = state;
+exit:
mutex_unlock(&inst->lock);
}
@@ -315,6 +349,7 @@
msecs_to_jiffies(HW_RESPONSE_TIMEOUT));
if (!rc) {
dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", rc);
+ msm_comm_recover_from_session_error(inst);
rc = -EIO;
} else {
rc = 0;
@@ -341,38 +376,64 @@
return rc;
}
-static void handle_session_init_done(enum command_response cmd, void *data)
-{
- struct msm_vidc_cb_cmd_done *response = data;
- struct msm_vidc_inst *inst;
- if (response) {
- struct vidc_hal_session_init_done *session_init_done =
- (struct vidc_hal_session_init_done *) response->data;
- inst = (struct msm_vidc_inst *)response->session_id;
-
- inst->capability.width = session_init_done->width;
- inst->capability.height = session_init_done->height;
- inst->capability.frame_rate =
- session_init_done->frame_rate;
- inst->capability.capability_set = true;
- signal_session_msg_receipt(cmd, inst);
- } else {
- dprintk(VIDC_ERR,
- "Failed to get valid response for session init\n");
- }
-}
-
-static void queue_v4l2_event(struct msm_vidc_inst *inst, int event_type)
+void msm_vidc_queue_v4l2_event(struct msm_vidc_inst *inst, int event_type)
{
struct v4l2_event event = {.id = 0, .type = event_type};
v4l2_event_queue_fh(&inst->event_handler, &event);
wake_up(&inst->kernel_event_queue);
}
+static void msm_comm_generate_session_error(struct msm_vidc_inst *inst)
+{
+ if (!inst) {
+ dprintk(VIDC_ERR, "%s: invalid input parameters", __func__);
+ return;
+ }
+ mutex_lock(&inst->lock);
+ inst->session = NULL;
+ inst->state = MSM_VIDC_CORE_INVALID;
+ msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_SYS_ERROR);
+ mutex_unlock(&inst->lock);
+}
+
+static void handle_session_init_done(enum command_response cmd, void *data)
+{
+ struct msm_vidc_cb_cmd_done *response = data;
+ struct msm_vidc_inst *inst = NULL;
+ if (response) {
+ struct vidc_hal_session_init_done *session_init_done =
+ (struct vidc_hal_session_init_done *)
+ response->data;
+ inst = (struct msm_vidc_inst *)response->session_id;
+ if (!inst) {
+ dprintk(VIDC_ERR, "%s: invalid input parameters",
+ __func__);
+ return;
+ }
+ if (!response->status && session_init_done) {
+ inst->capability.width = session_init_done->width;
+ inst->capability.height = session_init_done->height;
+ inst->capability.frame_rate =
+ session_init_done->frame_rate;
+ inst->capability.capability_set = true;
+ } else {
+ dprintk(VIDC_ERR,
+ "Session init response from FW : 0x%x",
+ response->status);
+ msm_comm_generate_session_error(inst);
+ }
+ signal_session_msg_receipt(cmd, inst);
+ } else {
+ dprintk(VIDC_ERR,
+ "Failed to get valid response for session init\n");
+ }
+}
+
static void handle_event_change(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
+ struct v4l2_control control = {0};
struct msm_vidc_cb_event *event_notify;
int event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
int rc = 0;
@@ -381,7 +442,15 @@
event_notify = (struct msm_vidc_cb_event *) response->data;
switch (event_notify->hal_event_type) {
case HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES:
- event = V4L2_EVENT_SEQ_CHANGED_SUFFICIENT;
+ event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
+ control.id =
+ V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER;
+ rc = v4l2_g_ctrl(&inst->ctrl_handler, &control);
+ if (rc)
+ dprintk(VIDC_WARN,
+ "Failed to get Smooth streamng flag\n");
+ if (!rc && control.value == true)
+ event = V4L2_EVENT_SEQ_CHANGED_SUFFICIENT;
break;
case HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES:
event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
@@ -399,7 +468,7 @@
}
rc = msm_vidc_check_session_supported(inst);
if (!rc) {
- queue_v4l2_event(inst, event);
+ msm_vidc_queue_v4l2_event(inst, event);
}
return;
@@ -493,7 +562,7 @@
struct msm_vidc_inst *inst;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
- queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_FLUSH_DONE);
+ msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_FLUSH_DONE);
} else {
dprintk(VIDC_ERR, "Failed to get valid response for flush\n");
}
@@ -511,7 +580,8 @@
mutex_lock(&inst->sync_lock);
inst->state = MSM_VIDC_CORE_INVALID;
mutex_unlock(&inst->sync_lock);
- queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_SYS_ERROR);
+ msm_vidc_queue_v4l2_event(inst,
+ V4L2_EVENT_MSM_VIDC_SYS_ERROR);
}
} else {
dprintk(VIDC_ERR,
@@ -523,6 +593,10 @@
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst = NULL ;
struct msm_vidc_core *core = NULL;
+ struct hfi_device *hdev = NULL;
+ int rc = 0;
+
+ subsystem_crashed("venus");
if (response) {
core = get_vidc_core(response->device_id);
dprintk(VIDC_WARN, "SYS_ERROR received for core %p\n", core);
@@ -534,9 +608,19 @@
list) {
mutex_lock(&inst->lock);
inst->state = MSM_VIDC_CORE_INVALID;
+ if (inst->core)
+ hdev = inst->core->device;
+ if (hdev && inst->session) {
+ rc = call_hfi_op(hdev, session_clean,
+ (void *) inst->session);
+ if (rc)
+ dprintk(VIDC_ERR,
+ "Sess clean failed :%p",
+ inst);
+ }
+ inst->session = NULL;
mutex_unlock(&inst->lock);
-
- queue_v4l2_event(inst,
+ msm_vidc_queue_v4l2_event(inst,
V4L2_EVENT_MSM_VIDC_SYS_ERROR);
}
} else {
@@ -554,6 +638,8 @@
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
struct msm_vidc_core *core = NULL;
+ struct hfi_device *hdev = NULL;
+ int rc = 0;
dprintk(VIDC_ERR, "Venus Subsystem crashed\n");
core = get_vidc_core(response->device_id);
if (!core) {
@@ -566,9 +652,21 @@
mutex_unlock(&core->lock);
list_for_each_entry(inst, &core->instances, list) {
if (inst) {
- queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_SYS_ERROR);
+ msm_vidc_queue_v4l2_event(inst,
+ V4L2_EVENT_MSM_VIDC_SYS_ERROR);
mutex_lock(&inst->lock);
inst->state = MSM_VIDC_CORE_INVALID;
+ if (inst->core)
+ hdev = inst->core->device;
+ if (hdev && inst->session) {
+ rc = call_hfi_op(hdev, session_clean,
+ (void *) inst->session);
+ if (rc)
+ dprintk(VIDC_ERR,
+ "Sess clean failed :%p",
+ inst);
+
+ }
inst->session = NULL;
mutex_unlock(&inst->lock);
}
@@ -582,7 +680,6 @@
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
signal_session_msg_receipt(cmd, inst);
- queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_CLOSE_DONE);
inst->session = NULL;
show_stats(inst);
} else {
@@ -639,56 +736,6 @@
}
}
-static void msm_comm_update_clocks(struct msm_vidc_inst *inst,
- u64 cur_time_stamp)
-{
- u32 new_time_diff = 0, cur_time_diff = 0;
- u8 updated_fps = 0;
- struct v4l2_ctrl *ctrl = NULL;
- u32 output_order = 0;
-
- if (inst->session_type == MSM_VIDC_ENCODER)
- goto exit;
- if (cur_time_stamp >= LLONG_MAX) {
- dprintk(VIDC_DBG,
- "Clock scaling failed : Timestamp invalid\n");
- goto exit;
- }
- ctrl = v4l2_ctrl_find(&inst->ctrl_handler,
- V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER);
- if (!ctrl) {
- dprintk(VIDC_WARN, "Unable to find output order control\n");
- dprintk(VIDC_WARN,
- "Performance might be impacted for higher fps clips\n");
- goto exit;
- }
- output_order = v4l2_ctrl_g_ctrl(ctrl);
- if (output_order == V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY) {
- new_time_diff =
- (u32)(cur_time_stamp - inst->prop.prev_time_stamp);
- inst->prop.prev_time_stamp = cur_time_stamp;
- if (!new_time_diff)
- goto exit;
- if (inst->prop.fps)
- cur_time_diff = USEC_PER_SEC / inst->prop.fps;
- cur_time_diff = cur_time_diff > new_time_diff ?
- cur_time_diff - new_time_diff :
- new_time_diff - cur_time_diff;
- if (cur_time_diff > TIME_DIFF_THRESHOLD) {
- updated_fps = (u8) (USEC_PER_SEC / new_time_diff);
- if (updated_fps && (updated_fps != inst->prop.fps)) {
- inst->prop.fps = updated_fps;
- dprintk(VIDC_DBG,
- "Updating clocks: Decoding fps = %d\n",
- inst->prop.fps);
- msm_comm_scale_clocks_and_bus(inst);
- }
- }
- }
-exit:
- return;
-}
-
static void handle_fbd(enum command_response cmd, void *data)
{
struct msm_vidc_cb_data_done *response = data;
@@ -718,7 +765,6 @@
fill_buf_done->timestamp_lo;
vb->v4l2_buf.timestamp =
ns_to_timeval(time_usec * NSEC_PER_USEC);
- msm_comm_update_clocks(inst, time_usec);
}
vb->v4l2_buf.flags = 0;
@@ -727,11 +773,18 @@
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_CODECCONFIG)
vb->v4l2_buf.flags &= ~V4L2_QCOM_BUF_FLAG_CODECCONFIG;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_SYNCFRAME)
- vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+ vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_IDRFRAME;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOSEQ)
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_EOSEQ;
+ if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DECODEONLY)
+ vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_DECODEONLY;
+ if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DATACORRUPT)
+ vb->v4l2_buf.flags |= V4L2_QCOM_BUF_DATA_CORRUPT;
switch (fill_buf_done->picture_type) {
case HAL_PICTURE_IDR:
+ vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_IDRFRAME;
+ vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+ break;
case HAL_PICTURE_I:
vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
break;
@@ -860,6 +913,7 @@
handle_release_res_done(cmd, data);
break;
case SESSION_END_DONE:
+ case SESSION_ABORT_DONE:
handle_session_close(cmd, data);
break;
case VIDC_EVENT_CHANGE:
@@ -905,8 +959,14 @@
__func__, hdev);
return -EINVAL;
}
- num_mbs_per_sec = msm_comm_get_load(core, MSM_VIDC_ENCODER);
- num_mbs_per_sec += msm_comm_get_load(core, MSM_VIDC_DECODER);
+
+ if (is_turbo_requested(core, MSM_VIDC_ENCODER) ||
+ is_turbo_requested(core, MSM_VIDC_DECODER)) {
+ num_mbs_per_sec = core->resources.max_load;
+ } else {
+ num_mbs_per_sec = msm_comm_get_load(core, MSM_VIDC_ENCODER);
+ num_mbs_per_sec += msm_comm_get_load(core, MSM_VIDC_DECODER);
+ }
dprintk(VIDC_INFO, "num_mbs_per_sec = %d\n", num_mbs_per_sec);
rc = call_hfi_op(hdev, scale_clocks,
@@ -1227,9 +1287,11 @@
}
init_completion(
&inst->completions[SESSION_MSG_INDEX(SESSION_INIT_DONE)]);
+ mutex_lock(&inst->lock);
inst->session = call_hfi_op(hdev, session_init, hdev->hfi_device_data,
(u32) inst, get_hal_domain(inst->session_type),
get_hal_codec_type(fourcc));
+ mutex_unlock(&inst->lock);
if (!inst->session) {
dprintk(VIDC_ERR,
"Failed to call session init for: %d, %d, %d, %d\n",
@@ -1255,6 +1317,12 @@
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ inst->core->state == VIDC_CORE_INVALID) {
+ dprintk(VIDC_ERR,
+ "Core is in bad state can't do load res");
+ return -EINVAL;
+ }
num_mbs_per_sec = msm_comm_get_load(inst->core, MSM_VIDC_DECODER);
num_mbs_per_sec += msm_comm_get_load(inst->core, MSM_VIDC_ENCODER);
@@ -1277,7 +1345,8 @@
}
mutex_unlock(&temp->lock);
}
-
+ inst->state = MSM_VIDC_CORE_INVALID;
+ msm_comm_recover_from_session_error(inst);
return -ENOMEM;
}
@@ -1328,6 +1397,12 @@
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ inst->core->state == VIDC_CORE_INVALID) {
+ dprintk(VIDC_ERR,
+ "Core is in bad state can't do start");
+ return -EINVAL;
+ }
hdev = inst->core->device;
@@ -1500,7 +1575,7 @@
scratch_buf->buffer_count_actual,
scratch_buf->buffer_size);
- if (inst->mode == VIDC_SECURE)
+ if (inst->flags & VIDC_SECURE)
smem_flags |= SMEM_SECURE;
if (scratch_buf->buffer_size) {
@@ -1588,7 +1663,7 @@
return rc;
}
- if (inst->mode == VIDC_SECURE)
+ if (inst->flags & VIDC_SECURE)
smem_flags |= SMEM_SECURE;
if (persist_buf->buffer_size) {
@@ -1667,6 +1742,7 @@
core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
"Core is in bad state can't change the state");
+ rc = -EINVAL;
goto exit;
}
flipped_state = get_flipped_state(inst->state, state);
@@ -1900,8 +1976,14 @@
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
- hdev = inst->core->device;
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ inst->core->state == VIDC_CORE_INVALID) {
+ dprintk(VIDC_ERR,
+ "Core is in bad state can't query get_bufreqs()");
+ return -EINVAL;
+ }
+ hdev = inst->core->device;
mutex_lock(&inst->sync_lock);
if (inst->state < MSM_VIDC_OPEN_DONE || inst->state >= MSM_VIDC_CLOSE) {
dprintk(VIDC_ERR,
@@ -1922,6 +2004,8 @@
if (!rc) {
dprintk(VIDC_ERR,
"Wait interrupted or timeout: %d\n", rc);
+ inst->state = MSM_VIDC_CORE_INVALID;
+ msm_comm_recover_from_session_error(inst);
rc = -EIO;
goto exit;
}
@@ -1982,6 +2066,13 @@
mutex_unlock(&inst->lock);
rc = wait_for_sess_signal_receipt(inst,
SESSION_RELEASE_BUFFER_DONE);
+ if (rc) {
+ mutex_lock(&inst->sync_lock);
+ inst->state = MSM_VIDC_CORE_INVALID;
+ mutex_unlock(&inst->sync_lock);
+ msm_comm_recover_from_session_error(
+ inst);
+ }
mutex_lock(&inst->lock);
}
list_del(&buf->list);
@@ -2046,6 +2137,13 @@
mutex_unlock(&inst->lock);
rc = wait_for_sess_signal_receipt(inst,
SESSION_RELEASE_BUFFER_DONE);
+ if (rc) {
+ mutex_lock(&inst->sync_lock);
+ inst->state = MSM_VIDC_CORE_INVALID;
+ mutex_unlock(&inst->sync_lock);
+ msm_comm_recover_from_session_error(
+ inst);
+ }
mutex_lock(&inst->lock);
}
list_del(&buf->list);
@@ -2178,7 +2276,8 @@
}
}
}
- queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_FLUSH_DONE);
+
+ msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_FLUSH_DONE);
return;
}
int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags)
@@ -2221,6 +2320,7 @@
"Core %p and inst %p are in bad state\n",
core, inst);
msm_comm_flush_in_invalid_state(inst);
+ return 0;
}
mutex_lock(&inst->sync_lock);
@@ -2254,7 +2354,10 @@
kfree(temp);
}
}
- rc = call_hfi_op(hdev, session_flush, inst->session,
+ /*Do not send flush in case of session_error */
+ if (!(inst->state == MSM_VIDC_CORE_INVALID &&
+ core->state != VIDC_CORE_INVALID))
+ rc = call_hfi_op(hdev, session_flush, inst->session,
HAL_FLUSH_ALL);
}
mutex_unlock(&inst->sync_lock);
@@ -2315,6 +2418,9 @@
case V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO:
ret = HAL_EXTRADATA_ASPECT_RATIO;
break;
+ case V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP:
+ ret = HAL_EXTRADATA_MPEG2_SEQDISP;
+ break;
default:
dprintk(VIDC_WARN, "Extradata not found: %d\n", index);
break;
@@ -2377,38 +2483,27 @@
{
struct msm_vidc_core_capability *capability;
int rc = 0;
- struct v4l2_event dqevent;
+ struct hfi_device *hdev;
- if (!inst) {
+ if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_WARN, "%s: Invalid parameter\n", __func__);
return -EINVAL;
}
capability = &inst->capability;
+ hdev = inst->core->device;
if (inst->capability.capability_set) {
- if (msm_vp8_low_tier &&
- inst->fmts[OUTPUT_PORT]->fourcc == V4L2_PIX_FMT_VP8) {
- capability->width.max = DEFAULT_WIDTH;
- capability->height.max = DEFAULT_HEIGHT;
- }
- if (inst->prop.width < capability->width.min ||
- inst->prop.width > capability->width.max ||
- (inst->prop.width % capability->width.step_size != 0)) {
- dprintk(VIDC_ERR,
- "Unsupported width = %d range min(%u) - max(%u) step_size(%u)",
- inst->prop.width, capability->width.min,
- capability->width.max, capability->width.step_size);
- rc = -ENOTSUPP;
- }
+ rc = call_hfi_op(hdev, capability_check,
+ inst->fmts[OUTPUT_PORT]->fourcc,
+ inst->prop.width, &capability->width.max,
+ &capability->height.max);
- if (inst->prop.height < capability->height.min ||
- inst->prop.height > capability->height.max ||
- (inst->prop.height %
- capability->height.step_size != 0)) {
+ if (!rc && (inst->prop.height * inst->prop.width >
+ capability->width.max * capability->height.max)) {
dprintk(VIDC_ERR,
- "Unsupported height = %d range min(%u) - max(%u) step_size(%u)",
- inst->prop.height, capability->height.min,
- capability->height.max, capability->height.step_size);
+ "Unsupported WxH = (%u)x(%u), Max supported is - (%u)x(%u)",
+ inst->prop.width, inst->prop.height,
+ capability->width.max, capability->height.max);
rc = -ENOTSUPP;
}
}
@@ -2416,10 +2511,61 @@
mutex_lock(&inst->sync_lock);
inst->state = MSM_VIDC_CORE_INVALID;
mutex_unlock(&inst->sync_lock);
- dqevent.type = V4L2_EVENT_MSM_VIDC_SYS_ERROR;
- dqevent.id = 0;
- v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+ msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_SYS_ERROR);
wake_up(&inst->kernel_event_queue);
}
return rc;
}
+
+static void msm_comm_generate_sys_error(struct msm_vidc_inst *inst)
+{
+ struct msm_vidc_core *core;
+ enum command_response cmd = SYS_ERROR;
+ struct msm_vidc_cb_cmd_done response = {0};
+ if (!inst || !inst->core) {
+ dprintk(VIDC_ERR, "%s: invalid input parameters", __func__);
+ return;
+ }
+ core = inst->core;
+ response.device_id = (u32) core->id;
+ handle_sys_error(cmd, (void *) &response);
+
+}
+int msm_comm_recover_from_session_error(struct msm_vidc_inst *inst)
+{
+ struct hfi_device *hdev;
+ int rc = 0;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s: invalid input parameters", __func__);
+ return -EINVAL;
+ }
+ if (!inst->session || inst->state < MSM_VIDC_OPEN_DONE) {
+ dprintk(VIDC_WARN,
+ "No corresponding FW session. No need to send Abort");
+ return rc;
+ }
+ hdev = inst->core->device;
+
+ init_completion(&inst->completions[SESSION_MSG_INDEX
+ (SESSION_ABORT_DONE)]);
+
+ /* We have received session_error. Send session_abort to firmware
+ * to clean up and release the session
+ */
+ rc = call_hfi_op(hdev, session_abort, (void *) inst->session);
+ if (rc) {
+ dprintk(VIDC_ERR, "session_abort failed rc: %d\n", rc);
+ return rc;
+ }
+ rc = wait_for_completion_timeout(
+ &inst->completions[SESSION_MSG_INDEX(SESSION_ABORT_DONE)],
+ msecs_to_jiffies(HW_RESPONSE_TIMEOUT));
+ if (!rc) {
+ dprintk(VIDC_ERR, "%s: Wait interrupted or timeout: %d\n",
+ __func__, rc);
+ msm_comm_generate_sys_error(inst);
+ } else
+ change_inst_state(inst, MSM_VIDC_CLOSE_DONE);
+ return rc;
+}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h
index 862dfab..c018345 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h
@@ -48,3 +48,4 @@
V4L2_CTRL_DRIVER_PRIV(idx))
#endif
+int msm_comm_recover_from_session_error(struct msm_vidc_inst *inst);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
index 3208df9..bea9070 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
@@ -15,7 +15,8 @@
#include "vidc_hfi_api.h"
#define MAX_DBG_BUF_SIZE 4096
-int msm_vidc_debug = 0x3;
+int msm_vidc_debug = VIDC_ERR | VIDC_WARN;
+int msm_vidc_debug_out = VIDC_OUT_PRINTK;
int msm_fw_debug = 0x18;
int msm_fw_debug_mode = 0x1;
int msm_fw_low_power_mode = 0x1;
@@ -171,6 +172,11 @@
dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
goto failed_create_dir;
}
+ if (!debugfs_create_u32("debug_output", S_IRUGO | S_IWUSR,
+ parent, &msm_vidc_debug_out)) {
+ dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
+ goto failed_create_dir;
+ }
failed_create_dir:
return dir;
}
@@ -198,6 +204,7 @@
write_str(&dbg_buf, "core: 0x%p\n", inst->core);
write_str(&dbg_buf, "height: %d\n", inst->prop.height);
write_str(&dbg_buf, "width: %d\n", inst->prop.width);
+ write_str(&dbg_buf, "fps: %d\n", inst->prop.fps);
write_str(&dbg_buf, "state: %d\n", inst->state);
write_str(&dbg_buf, "-----------Formats-------------\n");
for (i = 0; i < MAX_PORT_NUM; i++) {
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.h b/drivers/media/platform/msm/vidc/msm_vidc_debug.h
index ea6dd70..5b572c9 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.h
@@ -31,9 +31,15 @@
VIDC_INFO = 0x0004,
VIDC_DBG = 0x0008,
VIDC_PROF = 0x0010,
+ VIDC_PKT = 0x0020,
VIDC_FW = 0x1000,
};
+enum vidc_msg_out {
+ VIDC_OUT_PRINTK = 0,
+ VIDC_OUT_FTRACE,
+};
+
enum msm_vidc_debugfs_event {
MSM_VIDC_DEBUGFS_EVENT_ETB,
MSM_VIDC_DEBUGFS_EVENT_EBD,
@@ -42,6 +48,7 @@
};
extern int msm_vidc_debug;
+extern int msm_vidc_debug_out;
extern int msm_fw_debug;
extern int msm_fw_debug_mode;
extern int msm_fw_low_power_mode;
@@ -49,11 +56,18 @@
#define dprintk(__level, __fmt, arg...) \
do { \
- if (msm_vidc_debug & __level) \
- printk(KERN_DEBUG VIDC_DBG_TAG \
- __fmt, __level, ## arg); \
+ if (msm_vidc_debug & __level) { \
+ if (msm_vidc_debug_out == VIDC_OUT_PRINTK) { \
+ printk(KERN_DEBUG VIDC_DBG_TAG \
+ __fmt, __level, ## arg); \
+ } else if (msm_vidc_debug_out == VIDC_OUT_FTRACE) { \
+ trace_printk(KERN_DEBUG VIDC_DBG_TAG \
+ __fmt, __level, ## arg); \
+ } \
+ } \
} while (0)
+
struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core,
struct dentry *parent);
struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst,
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index e5696be..15f4e3f 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -130,7 +130,6 @@
u32 height;
u32 fps;
u32 bitrate;
- u64 prev_time_stamp;
};
struct buf_queue {
@@ -169,9 +168,9 @@
int samples;
};
-enum msm_vidc_mode {
- VIDC_NON_SECURE,
- VIDC_SECURE,
+enum msm_vidc_modes {
+ VIDC_SECURE = 1 << 0,
+ VIDC_TURBO = 1 << 1,
};
struct msm_vidc_core_capability {
@@ -226,7 +225,7 @@
void *priv;
struct msm_vidc_debug debug;
struct buf_count count;
- enum msm_vidc_mode mode;
+ enum msm_vidc_modes flags;
struct msm_vidc_core_capability capability;
};
@@ -255,4 +254,6 @@
int msm_vidc_trigger_ssr(struct msm_vidc_core *core,
enum hal_ssr_trigger_type type);
int msm_vidc_check_session_supported(struct msm_vidc_inst *inst);
+void msm_vidc_queue_v4l2_event(struct msm_vidc_inst *inst, int event_type);
+
#endif
diff --git a/drivers/media/platform/msm/vidc/q6_hfi.c b/drivers/media/platform/msm/vidc/q6_hfi.c
index 123b654..0678fc2 100644
--- a/drivers/media/platform/msm/vidc/q6_hfi.c
+++ b/drivers/media/platform/msm/vidc/q6_hfi.c
@@ -362,17 +362,18 @@
void q6_hfi_delete_device(void *device)
{
- struct q6_hfi_device *close, *dev;
+ struct q6_hfi_device *close, *tmp, *dev;
if (device) {
q6_hfi_deinit_resources(device);
dev = (struct q6_hfi_device *) device;
- list_for_each_entry(close, &hal_ctxt.dev_head, list) {
+ list_for_each_entry_safe(close, tmp, &hal_ctxt.dev_head, list) {
if (close->device_id == dev->device_id) {
hal_ctxt.dev_count--;
list_del(&close->list);
destroy_workqueue(close->vidc_workq);
kfree(close);
+ break;
}
}
@@ -617,6 +618,21 @@
HFI_CMD_SYS_SESSION_ABORT);
}
+static int q6_hfi_session_clean(void *session)
+{
+ struct hal_session *sess_close;
+ if (!session) {
+ dprintk(VIDC_ERR, "Invalid Params %s", __func__);
+ return -EINVAL;
+ }
+ sess_close = session;
+ dprintk(VIDC_DBG, "deleted the session: 0x%x",
+ sess_close->session_id);
+ list_del(&sess_close->list);
+ kfree(sess_close);
+ return 0;
+}
+
static int q6_hfi_session_set_buffers(void *sess,
struct vidc_buffer_addr_info *buffer_info)
{
@@ -1363,6 +1379,7 @@
hdev->session_init = q6_hfi_session_init;
hdev->session_end = q6_hfi_session_end;
hdev->session_abort = q6_hfi_session_abort;
+ hdev->session_clean = q6_hfi_session_clean;
hdev->session_set_buffers = q6_hfi_session_set_buffers;
hdev->session_release_buffers = q6_hfi_session_release_buffers;
hdev->session_load_res = q6_hfi_session_load_res;
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index ddb3063..8453b81 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -62,6 +62,23 @@
int ret;
};
+static void venus_hfi_dump_packet(u8 *packet)
+{
+ u32 c = 0, packet_size = *(u32 *)packet;
+ const int row_size = 32;
+ /* row must contain enough for 0xdeadbaad * 8 to be converted into
+ * "de ad ba ab " * 8 + '\0' */
+ char row[3 * row_size];
+
+ for (c = 0; c * row_size < packet_size; ++c) {
+ int bytes_to_read = ((c + 1) * row_size > packet_size) ?
+ packet_size % row_size : row_size;
+ hex_dump_to_buffer(packet + c * row_size, bytes_to_read,
+ row_size, 4, row, sizeof(row), false);
+ dprintk(VIDC_PKT, "%s\n", row);
+ }
+}
+
static void venus_hfi_sim_modify_cmd_packet(u8 *packet)
{
struct hfi_cmd_sys_session_init_packet *sys_init;
@@ -171,8 +188,6 @@
return -EINVAL;
}
- venus_hfi_sim_modify_cmd_packet(packet);
-
queue = (struct hfi_queue_header *) qinfo->q_hdr;
if (!queue) {
@@ -180,6 +195,13 @@
return -ENOENT;
}
+ venus_hfi_sim_modify_cmd_packet(packet);
+
+ if (msm_vidc_debug & VIDC_PKT) {
+ dprintk(VIDC_PKT, "%s: %p\n", __func__, qinfo);
+ venus_hfi_dump_packet(packet);
+ }
+
packet_size_in_words = (*(u32 *)packet) >> 2;
dprintk(VIDC_DBG, "Packet_size in words: %d", packet_size_in_words);
@@ -217,8 +239,14 @@
packet + ((packet_size_in_words - new_write_idx) << 2),
new_write_idx << 2);
}
+ /* Memory barrier to make sure packet is written before updating the
+ * write index */
+ mb();
queue->qhdr_write_idx = new_write_idx;
*rx_req_is_set = (1 == queue->qhdr_rx_req) ? 1 : 0;
+ /*Memory barrier to make sure write index is updated before an
+ * interupt is raised on venus.*/
+ mb();
dprintk(VIDC_DBG, "Out : ");
return 0;
}
@@ -282,6 +310,7 @@
u32 packet_size_in_words, new_read_idx;
u32 *read_ptr;
struct vidc_iface_q_info *qinfo;
+ int rc = 0;
if (!info || !packet || !pb_tx_req_is_set) {
dprintk(VIDC_ERR, "Invalid Params");
@@ -293,6 +322,9 @@
dprintk(VIDC_WARN, "Queues have already been freed\n");
return -EINVAL;
}
+ /*Memory barrier to make sure data is valid before
+ *reading it*/
+ mb();
queue = (struct hfi_queue_header *) qinfo->q_hdr;
if (!queue) {
@@ -317,17 +349,27 @@
new_read_idx = queue->qhdr_read_idx + packet_size_in_words;
dprintk(VIDC_DBG, "Read Ptr: %d", (u32) new_read_idx);
- if (new_read_idx < queue->qhdr_q_size) {
- memcpy(packet, read_ptr,
- packet_size_in_words << 2);
- } else {
- new_read_idx -= queue->qhdr_q_size;
- memcpy(packet, read_ptr,
+ if (((packet_size_in_words << 2) <= VIDC_IFACEQ_MED_PKT_SIZE)
+ && queue->qhdr_read_idx <= queue->qhdr_q_size) {
+ if (new_read_idx < queue->qhdr_q_size) {
+ memcpy(packet, read_ptr,
+ packet_size_in_words << 2);
+ } else {
+ new_read_idx -= queue->qhdr_q_size;
+ memcpy(packet, read_ptr,
(packet_size_in_words - new_read_idx) << 2);
- memcpy(packet + ((packet_size_in_words -
- new_read_idx) << 2),
- (u8 *)qinfo->q_array.align_virtual_addr,
- new_read_idx << 2);
+ memcpy(packet + ((packet_size_in_words -
+ new_read_idx) << 2),
+ (u8 *)qinfo->q_array.align_virtual_addr,
+ new_read_idx << 2);
+ }
+ } else {
+ dprintk(VIDC_WARN,
+ "BAD packet received, read_idx: 0x%x, pkt_size: %d\n",
+ queue->qhdr_read_idx, packet_size_in_words << 2);
+ dprintk(VIDC_WARN, "Dropping this packet\n");
+ new_read_idx = queue->qhdr_write_idx;
+ rc = -ENODATA;
}
queue->qhdr_read_idx = new_read_idx;
@@ -339,8 +381,12 @@
*pb_tx_req_is_set = (1 == queue->qhdr_tx_req) ? 1 : 0;
venus_hfi_hal_sim_modify_msg_packet(packet);
+ if (msm_vidc_debug & VIDC_PKT) {
+ dprintk(VIDC_PKT, "%s: %p\n", __func__, qinfo);
+ venus_hfi_dump_packet(packet);
+ }
dprintk(VIDC_DBG, "Out : ");
- return 0;
+ return rc;
}
static int venus_hfi_alloc(void *mem, void *clnt, u32 size, u32 align,
@@ -1072,7 +1118,6 @@
if (!(dev->intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK))
disable_irq_nosync(dev->hal_data->irq);
dev->intr_status = 0;
- venus_hfi_interface_queues_release(dev);
mutex_unlock(&dev->clock_lock);
}
dprintk(VIDC_INFO, "HAL exited\n");
@@ -1438,12 +1483,12 @@
goto err_session_init_fail;
}
- if (venus_hfi_iface_cmdq_write(dev, &pkt))
- goto err_session_init_fail;
if (venus_hfi_sys_set_debug(dev, msm_fw_debug))
dprintk(VIDC_ERR, "Setting fw_debug msg ON failed");
if (venus_hfi_sys_set_idle_message(dev, msm_fw_low_power_mode))
dprintk(VIDC_ERR, "Setting idle response ON failed");
+ if (venus_hfi_iface_cmdq_write(dev, &pkt))
+ goto err_session_init_fail;
return (void *) new_session;
err_session_init_fail:
@@ -1490,6 +1535,21 @@
HFI_CMD_SYS_SESSION_ABORT);
}
+static int venus_hfi_session_clean(void *session)
+{
+ struct hal_session *sess_close;
+ if (!session) {
+ dprintk(VIDC_ERR, "Invalid Params %s", __func__);
+ return -EINVAL;
+ }
+ sess_close = session;
+ dprintk(VIDC_DBG, "deleted the session: 0x%p",
+ sess_close);
+ list_del(&sess_close->list);
+ kfree(sess_close);
+ return 0;
+}
+
static int venus_hfi_session_set_buffers(void *sess,
struct vidc_buffer_addr_info *buffer_info)
{
@@ -1874,17 +1934,42 @@
mutex_unlock(&device->write_lock);
return rc;
}
+static void venus_hfi_process_msg_event_notify(
+ struct venus_hfi_device *device, void *packet)
+{
+ struct hfi_sfr_struct *vsfr = NULL;
+ struct hfi_msg_event_notify_packet *event_pkt;
+ struct vidc_hal_msg_pkt_hdr *msg_hdr;
+ msg_hdr = (struct vidc_hal_msg_pkt_hdr *)packet;
+ event_pkt =
+ (struct hfi_msg_event_notify_packet *)msg_hdr;
+ if (event_pkt && event_pkt->event_id ==
+ HFI_EVENT_SYS_ERROR) {
+ vsfr = (struct hfi_sfr_struct *)
+ device->sfr.align_virtual_addr;
+ if (vsfr)
+ dprintk(VIDC_ERR, "SFR Message from FW : %s",
+ vsfr->rg_data);
+ }
+}
static void venus_hfi_response_handler(struct venus_hfi_device *device)
{
u8 packet[VIDC_IFACEQ_MED_PKT_SIZE];
u32 rc = 0;
+ struct hfi_sfr_struct *vsfr = NULL;
dprintk(VIDC_INFO, "#####venus_hfi_response_handler#####\n");
if (device) {
if ((device->intr_status &
VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK)) {
dprintk(VIDC_ERR, "Received: Watchdog timeout %s",
__func__);
+ vsfr = (struct hfi_sfr_struct *)
+ device->sfr.align_virtual_addr;
+ if (vsfr)
+ dprintk(VIDC_ERR,
+ "SFR Message from FW : %s",
+ vsfr->rg_data);
venus_hfi_process_sys_watchdog_timeout(device);
}
@@ -1892,6 +1977,9 @@
rc = hfi_process_msg_packet(device->callback,
device->device_id,
(struct vidc_hal_msg_pkt_hdr *) packet);
+ if (rc == HFI_MSG_EVENT_NOTIFY)
+ venus_hfi_process_msg_event_notify(
+ device, (void *)packet);
}
while (!venus_hfi_iface_dbgq_read(device, packet)) {
struct hfi_msg_sys_debug_packet *pkt =
@@ -2566,7 +2654,7 @@
static int venus_hfi_iommu_attach(struct venus_hfi_device *device)
{
- int rc;
+ int rc = 0;
struct iommu_domain *domain;
int i;
struct iommu_set *iommu_group_set;
@@ -2752,9 +2840,11 @@
return;
}
if (device->resources.fw.cookie) {
+ flush_workqueue(device->vidc_workq);
venus_hfi_disable_clks(device);
- venus_hfi_iommu_detach(device);
subsystem_put(device->resources.fw.cookie);
+ venus_hfi_interface_queues_release(dev);
+ venus_hfi_iommu_detach(device);
device->resources.fw.cookie = NULL;
}
}
@@ -2800,6 +2890,28 @@
return 0;
}
+int venus_hfi_capability_check(u32 fourcc, u32 width,
+ u32 *max_width, u32 *max_height)
+{
+ int rc = 0;
+ if (!max_width || !max_height) {
+ dprintk(VIDC_ERR, "%s - invalid parameter\n", __func__);
+ return -EINVAL;
+ }
+
+ if (msm_vp8_low_tier && fourcc == V4L2_PIX_FMT_VP8) {
+ *max_width = DEFAULT_WIDTH;
+ *max_height = DEFAULT_HEIGHT;
+ }
+ if (width > *max_width) {
+ dprintk(VIDC_ERR,
+ "Unsupported width = %u supported max width = %u",
+ width, *max_width);
+ rc = -ENOTSUPP;
+ }
+ return rc;
+}
+
static void *venus_hfi_add_device(u32 device_id,
struct msm_vidc_platform_resources *res,
hfi_cmd_response_callback callback)
@@ -2882,12 +2994,12 @@
void venus_hfi_delete_device(void *device)
{
- struct venus_hfi_device *close, *dev;
+ struct venus_hfi_device *close, *tmp, *dev;
if (device) {
venus_hfi_deinit_resources(device);
dev = (struct venus_hfi_device *) device;
- list_for_each_entry(close, &hal_ctxt.dev_head, list) {
+ list_for_each_entry_safe(close, tmp, &hal_ctxt.dev_head, list) {
if (close->hal_data->irq == dev->hal_data->irq) {
hal_ctxt.dev_count--;
free_irq(dev->hal_data->irq, close);
@@ -2912,6 +3024,7 @@
hdev->session_init = venus_hfi_session_init;
hdev->session_end = venus_hfi_session_end;
hdev->session_abort = venus_hfi_session_abort;
+ hdev->session_clean = venus_hfi_session_clean;
hdev->session_set_buffers = venus_hfi_session_set_buffers;
hdev->session_release_buffers = venus_hfi_session_release_buffers;
hdev->session_load_res = venus_hfi_session_load_res;
@@ -2939,6 +3052,7 @@
hdev->unload_fw = venus_hfi_unload_fw;
hdev->get_fw_info = venus_hfi_get_fw_info;
hdev->get_stride_scanline = venus_hfi_get_stride_scanline;
+ hdev->capability_check = venus_hfi_capability_check;
}
int venus_hfi_initialize(struct hfi_device *hdev, u32 device_id,
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
index 075b391..bb72da7 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.h
@@ -75,7 +75,8 @@
#define HFI_EXTRADATA_PANSCAN_WINDOW 0x00000008
#define HFI_EXTRADATA_RECOVERY_POINT_SEI 0x00000009
#define HFI_EXTRADATA_CLOSED_CAPTION_UD 0x0000000A
-#define HFI_EXTRADATA_AFD_UD 0x0000000B
+#define HFI_EXTRADATA_AFD_UD 0x0000000B
+#define HFI_EXTRADATA_MPEG2_SEQDISP 0x0000000D
#define HFI_EXTRADATA_MULTISLICE_INFO 0x7F100000
#define HFI_EXTRADATA_NUM_CONCEALED_MB 0x7F100001
#define HFI_EXTRADATA_INDEX 0x7F100002
@@ -113,8 +114,6 @@
#define HFI_PROPERTY_SYS_OX_START \
(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x0000)
-#define HFI_PROPERTY_SYS_IDLE_INDICATOR \
- (HFI_PROPERTY_SYS_OX_START + 0x001)
#define HFI_PROPERTY_PARAM_OX_START \
(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x1000)
@@ -193,6 +192,8 @@
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x014)
#define HFI_PROPERTY_PARAM_VDEC_AVC_SESSION_SELECT \
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x015)
+#define HFI_PROPERTY_PARAM_VDEC_MPEG2_SEQDISP_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x016)
#define HFI_PROPERTY_CONFIG_VDEC_OX_START \
(HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x0000)
@@ -333,7 +334,6 @@
#define HFI_MSG_SYS_OX_START \
(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_MSG_START_OFFSET + 0x0000)
-#define HFI_MSG_SYS_IDLE (HFI_MSG_SYS_OX_START + 0x1)
#define HFI_MSG_SYS_PING_ACK (HFI_MSG_SYS_OX_START + 0x2)
#define HFI_MSG_SYS_PROPERTY_INFO (HFI_MSG_SYS_OX_START + 0x3)
#define HFI_MSG_SYS_SESSION_ABORT_DONE (HFI_MSG_SYS_OX_START + 0x4)
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 5ba191f..389c13f 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -95,6 +95,7 @@
HAL_EXTRADATA_NUM_CONCEALED_MB,
HAL_EXTRADATA_METADATA_FILLER,
HAL_EXTRADATA_ASPECT_RATIO,
+ HAL_EXTRADATA_MPEG2_SEQDISP
};
enum hal_property {
@@ -170,6 +171,7 @@
HAL_CONFIG_VENC_MAX_BITRATE,
HAL_PARAM_VENC_H264_VUI_TIMING_INFO,
HAL_PARAM_VENC_H264_GENERATE_AUDNAL,
+ HAL_PARAM_VENC_MAX_NUM_B_FRAMES,
};
enum hal_domain {
@@ -389,20 +391,20 @@
};
enum hal_uncompressed_format {
- HAL_COLOR_FORMAT_MONOCHROME,
- HAL_COLOR_FORMAT_NV12,
- HAL_COLOR_FORMAT_NV21,
- HAL_COLOR_FORMAT_NV12_4x4TILE,
- HAL_COLOR_FORMAT_NV21_4x4TILE,
- HAL_COLOR_FORMAT_YUYV,
- HAL_COLOR_FORMAT_YVYU,
- HAL_COLOR_FORMAT_UYVY,
- HAL_COLOR_FORMAT_VYUY,
- HAL_COLOR_FORMAT_RGB565,
- HAL_COLOR_FORMAT_BGR565,
- HAL_COLOR_FORMAT_RGB888,
- HAL_COLOR_FORMAT_BGR888,
- HAL_UNUSED_COLOR = 0x10000000,
+ HAL_COLOR_FORMAT_MONOCHROME = 0x00000001,
+ HAL_COLOR_FORMAT_NV12 = 0x00000002,
+ HAL_COLOR_FORMAT_NV21 = 0x00000004,
+ HAL_COLOR_FORMAT_NV12_4x4TILE = 0x00000008,
+ HAL_COLOR_FORMAT_NV21_4x4TILE = 0x00000010,
+ HAL_COLOR_FORMAT_YUYV = 0x00000020,
+ HAL_COLOR_FORMAT_YVYU = 0x00000040,
+ HAL_COLOR_FORMAT_UYVY = 0x00000080,
+ HAL_COLOR_FORMAT_VYUY = 0x00000100,
+ HAL_COLOR_FORMAT_RGB565 = 0x00000200,
+ HAL_COLOR_FORMAT_BGR565 = 0x00000400,
+ HAL_COLOR_FORMAT_RGB888 = 0x00000800,
+ HAL_COLOR_FORMAT_BGR888 = 0x00001000,
+ HAL_UNUSED_COLOR = 0x10000000,
};
enum hal_ssr_trigger_type {
@@ -481,7 +483,7 @@
HAL_PICTURE_I = 0x01,
HAL_PICTURE_P = 0x02,
HAL_PICTURE_B = 0x04,
- HAL_PICTURE_IDR = 0x7F001000,
+ HAL_PICTURE_IDR = 0x08,
HAL_FRAME_NOTCODED = 0x7F002000,
HAL_FRAME_YUV = 0x7F004000,
HAL_UNUSED_PICT = 0x10000000,
@@ -752,6 +754,10 @@
u32 nal_stream_format_supported;
};
+struct hal_nal_stream_format_select {
+ u32 nal_stream_format_select;
+};
+
struct hal_multi_view_format {
u32 views;
u32 rg_view_order[1];
@@ -1019,7 +1025,7 @@
};
#define call_hfi_op(q, op, args...) \
- (((q)->op) ? ((q)->op(args)) : 0)
+ (((q) && (q)->op) ? ((q)->op(args)) : 0)
struct hfi_device {
void *hfi_device_data;
@@ -1073,6 +1079,9 @@
int (*get_fw_info)(void *dev, enum fw_info info);
int (*get_stride_scanline)(int color_fmt, int width,
int height, int *stride, int *scanlines);
+ int (*capability_check)(u32 fourcc, u32 width,
+ u32 *max_width, u32 *max_height);
+ int (*session_clean)(void *sess);
};
typedef void (*hfi_cmd_response_callback) (enum command_response cmd,
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index 2d0c3bd..2b6d6bb 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -195,6 +195,8 @@
(HFI_PROPERTY_SYS_COMMON_START + 0x002)
#define HFI_PROPERTY_SYS_CONFIG_VCODEC_CLKFREQ \
(HFI_PROPERTY_SYS_COMMON_START + 0x003)
+#define HFI_PROPERTY_SYS_IDLE_INDICATOR \
+ (HFI_PROPERTY_SYS_COMMON_START + 0x004)
#define HFI_PROPERTY_PARAM_COMMON_START \
(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x1000)
@@ -304,7 +306,8 @@
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01E)
#define HFI_PROPERTY_PARAM_VENC_VC1_PERF_CFG \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01F)
-
+#define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES \
+ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x020)
#define HFI_PROPERTY_CONFIG_VENC_COMMON_START \
(HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x6000)
#define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE \
@@ -424,6 +427,10 @@
u32 idr_period;
};
+struct hfi_max_num_b_frames {
+ u32 max_num_b_frames;
+};
+
struct hfi_intra_period {
u32 pframes;
u32 bframes;
@@ -469,6 +476,9 @@
u32 nal_stream_format_supported;
};
+struct hfi_nal_stream_format_select {
+ u32 nal_stream_format_select;
+};
#define HFI_PICTURE_TYPE_I 0x01
#define HFI_PICTURE_TYPE_P 0x02
#define HFI_PICTURE_TYPE_B 0x04
@@ -692,6 +702,7 @@
#define HFI_MSG_SYS_DEBUG (HFI_MSG_SYS_COMMON_START + 0x4)
#define HFI_MSG_SYS_SESSION_INIT_DONE (HFI_MSG_SYS_COMMON_START + 0x6)
#define HFI_MSG_SYS_SESSION_END_DONE (HFI_MSG_SYS_COMMON_START + 0x7)
+#define HFI_MSG_SYS_IDLE (HFI_MSG_SYS_COMMON_START + 0x8)
#define HFI_MSG_SESSION_COMMON_START \
(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + \
diff --git a/drivers/media/platform/msm/wfd/Kconfig b/drivers/media/platform/msm/wfd/Kconfig
index 6050d73..4fd668e 100644
--- a/drivers/media/platform/msm/wfd/Kconfig
+++ b/drivers/media/platform/msm/wfd/Kconfig
@@ -4,3 +4,9 @@
---help---
Enables the Wifi Display driver.
+menuconfig MSM_WFD_DEBUG
+ bool "Qualcomm MSM Wifi Display Driver Debug Mode"
+ depends on MSM_WFD
+ ---help---
+ Enables the Wifi Display driver in debug mode
+
diff --git a/drivers/media/platform/msm/wfd/Makefile b/drivers/media/platform/msm/wfd/Makefile
index 813bc64..c05f517 100644
--- a/drivers/media/platform/msm/wfd/Makefile
+++ b/drivers/media/platform/msm/wfd/Makefile
@@ -2,6 +2,8 @@
obj-y += wfd-ioctl.o
obj-y += wfd-util.o
obj-y += vsg-subdev.o
+
+ #pick up the MDP subdevice
ifeq ($(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL),y)
obj-y += mdp-4-subdev.o
else ifeq ($(CONFIG_FB_MSM_MDSS_WRITEBACK),y)
@@ -9,6 +11,14 @@
else
obj-y += mdp-dummy-subdev.o
endif
- obj-$(CONFIG_MSM_VIDC_1080P) += enc-mfc-subdev.o
- obj-$(CONFIG_MSM_VIDC_V4L2) += enc-venus-subdev.o
+
+ #pick up the Encoder subdevice
+ ifeq ($(CONFIG_MSM_VIDC_1080P),y)
+ obj-y += enc-mfc-subdev.o
+ else ifeq ($(CONFIG_MSM_VIDC_V4L2),y)
+ obj-y += enc-venus-subdev.o
+ else
+ obj-y += enc-dummy-subdev.o
+ endif
+
endif
diff --git a/drivers/media/platform/msm/wfd/enc-dummy-subdev.c b/drivers/media/platform/msm/wfd/enc-dummy-subdev.c
new file mode 100644
index 0000000..3acb6f8
--- /dev/null
+++ b/drivers/media/platform/msm/wfd/enc-dummy-subdev.c
@@ -0,0 +1,575 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <media/msm_media_info.h>
+#include <media/v4l2-subdev.h>
+#include "enc-subdev.h"
+#include "wfd-util.h"
+
+#ifndef CONFIG_MSM_WFD_DEBUG
+#error "Dummy subdevice must only be used when CONFIG_MSM_WFD_DEBUG=y"
+#endif
+
+#define DEFAULT_WIDTH 1920
+#define DEFAULT_HEIGHT 1080
+#define ALLOC_SIZE ALIGN((DEFAULT_WIDTH * DEFAULT_HEIGHT * 3) / 2, SZ_1M)
+#define FILL_SIZE ((DEFAULT_WIDTH * DEFAULT_HEIGHT * 3) / 2)
+
+static struct ion_client *venc_ion_client;
+struct venc_inst {
+ struct mutex lock;
+ struct venc_msg_ops vmops;
+ struct mem_region output_bufs, input_bufs;
+ struct workqueue_struct *wq;
+};
+
+struct encode_task {
+ struct venc_inst *inst;
+ struct work_struct work;
+};
+
+int venc_load_fw(struct v4l2_subdev *sd)
+{
+ return 0;
+}
+
+int venc_init(struct v4l2_subdev *sd, u32 val)
+{
+ if (!venc_ion_client)
+ venc_ion_client = msm_ion_client_create(-1, "wfd_enc_subdev");
+
+ return venc_ion_client ? 0 : -ENOMEM;
+}
+
+static long venc_open(struct v4l2_subdev *sd, void *arg)
+{
+ struct venc_inst *inst = NULL;
+ struct venc_msg_ops *vmops = arg;
+ int rc = 0;
+
+ if (!vmops) {
+ WFD_MSG_ERR("Callbacks required for %s\n", __func__);
+ rc = -EINVAL;
+ goto venc_open_fail;
+ } else if (!sd) {
+ WFD_MSG_ERR("Subdevice required for %s\n", __func__);
+ rc = -EINVAL;
+ goto venc_open_fail;
+ }
+
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+ if (!inst) {
+ WFD_MSG_ERR("Failed to allocate memory\n");
+ rc = -EINVAL;
+ goto venc_open_fail;
+ }
+
+ INIT_LIST_HEAD(&inst->output_bufs.list);
+ INIT_LIST_HEAD(&inst->input_bufs.list);
+ mutex_init(&inst->lock);
+ inst->wq = create_workqueue("venc-dummy-subdev");
+ inst->vmops = *vmops;
+ sd->dev_priv = inst;
+ vmops->cookie = inst;
+ return 0;
+venc_open_fail:
+ return rc;
+}
+
+static long venc_close(struct v4l2_subdev *sd, void *arg)
+{
+ struct venc_inst *inst = NULL;
+ int rc = 0;
+
+ if (!sd) {
+ WFD_MSG_ERR("Subdevice required for %s\n", __func__);
+ rc = -EINVAL;
+ goto venc_close_fail;
+ }
+
+ inst = (struct venc_inst *)sd->dev_priv;
+ destroy_workqueue(inst->wq);
+ kfree(inst);
+ sd->dev_priv = inst = NULL;
+venc_close_fail:
+ return rc;
+
+}
+
+static long venc_get_buffer_req(struct v4l2_subdev *sd, void *arg)
+{
+ int rc = 0;
+ struct bufreq *bufreq = arg;
+ if (!sd) {
+ WFD_MSG_ERR("Subdevice required for %s\n", __func__);
+ rc = -EINVAL;
+ goto venc_buf_req_fail;
+ } else if (!arg) {
+ WFD_MSG_ERR("Invalid buffer requirements\n");
+ rc = -EINVAL;
+ goto venc_buf_req_fail;
+ }
+
+
+ bufreq->count = 3;
+ bufreq->size = ALLOC_SIZE;
+venc_buf_req_fail:
+ return rc;
+}
+
+static long venc_set_buffer_req(struct v4l2_subdev *sd, void *arg)
+{
+ int rc = 0;
+ struct bufreq *bufreq = arg;
+
+ if (!sd) {
+ WFD_MSG_ERR("Subdevice required for %s\n", __func__);
+ rc = -EINVAL;
+ goto venc_buf_req_fail;
+ } else if (!arg) {
+ WFD_MSG_ERR("Invalid buffer requirements\n");
+ rc = -EINVAL;
+ goto venc_buf_req_fail;
+ }
+
+ bufreq->size = ALLOC_SIZE;
+venc_buf_req_fail:
+ return rc;
+
+}
+
+static long venc_start(struct v4l2_subdev *sd)
+{
+ return 0;
+}
+
+static long venc_stop(struct v4l2_subdev *sd)
+{
+ struct venc_inst *inst = NULL;
+
+ if (!sd) {
+ WFD_MSG_ERR("Subdevice required for %s\n", __func__);
+ return -EINVAL;
+ }
+
+ inst = (struct venc_inst *)sd->dev_priv;
+ flush_workqueue(inst->wq);
+ return 0;
+}
+
+static long venc_set_input_buffer(struct v4l2_subdev *sd, void *arg)
+{
+ return 0;
+}
+
+static long venc_set_output_buffer(struct v4l2_subdev *sd, void *arg)
+{
+ return 0;
+}
+
+static long venc_set_format(struct v4l2_subdev *sd, void *arg)
+{
+ struct venc_inst *inst = NULL;
+ struct v4l2_format *fmt = arg;
+
+ if (!sd) {
+ WFD_MSG_ERR("Subdevice required for %s\n", __func__);
+ return -EINVAL;
+ } else if (!fmt) {
+ WFD_MSG_ERR("Invalid format\n");
+ return -EINVAL;
+ } else if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ WFD_MSG_ERR("Invalid buffer type %d\n", fmt->type);
+ return -ENOTSUPP;
+ }
+
+ inst = (struct venc_inst *)sd->dev_priv;
+ fmt->fmt.pix.sizeimage = ALLOC_SIZE;
+ return 0;
+}
+
+static long venc_set_framerate(struct v4l2_subdev *sd, void *arg)
+{
+ return 0;
+}
+
+static int venc_map_user_to_kernel(struct venc_inst *inst,
+ struct mem_region *mregion)
+{
+ int rc = 0;
+ unsigned long flags = 0;
+ if (!mregion) {
+ rc = -EINVAL;
+ goto venc_map_fail;
+ }
+
+ mregion->ion_handle = ion_import_dma_buf(venc_ion_client, mregion->fd);
+ if (IS_ERR_OR_NULL(mregion->ion_handle)) {
+ rc = PTR_ERR(mregion->ion_handle);
+ WFD_MSG_ERR("Failed to get handle: %p, %d, %d, %d\n",
+ venc_ion_client, mregion->fd, mregion->offset, rc);
+ mregion->ion_handle = NULL;
+ goto venc_map_fail;
+ }
+
+ rc = ion_handle_get_flags(venc_ion_client, mregion->ion_handle, &flags);
+ if (rc) {
+ WFD_MSG_ERR("Failed to get ion flags %d\n", rc);
+ goto venc_map_fail;
+ }
+
+ mregion->paddr = mregion->kvaddr = ion_map_kernel(venc_ion_client,
+ mregion->ion_handle);
+
+ if (IS_ERR_OR_NULL(mregion->kvaddr)) {
+ WFD_MSG_ERR("Failed to map buffer into kernel\n");
+ rc = PTR_ERR(mregion->kvaddr);
+ mregion->kvaddr = NULL;
+ goto venc_map_fail;
+ }
+
+venc_map_fail:
+ return rc;
+}
+
+static int venc_unmap_user_to_kernel(struct venc_inst *inst,
+ struct mem_region *mregion)
+{
+ if (!mregion || !mregion->ion_handle)
+ return 0;
+
+ if (mregion->kvaddr) {
+ ion_unmap_kernel(venc_ion_client, mregion->ion_handle);
+ mregion->kvaddr = NULL;
+ }
+
+
+ return 0;
+}
+
+#ifdef CONFIG_MSM_VIDC_V4L2
+static int encode_memcpy(uint8_t *dst, uint8_t *src, int size)
+{
+ int y_stride = VENUS_Y_STRIDE(COLOR_FMT_NV12, DEFAULT_WIDTH),
+ y_scan = VENUS_Y_SCANLINES(COLOR_FMT_NV12, DEFAULT_HEIGHT),
+ uv_stride = VENUS_UV_STRIDE(COLOR_FMT_NV12, DEFAULT_WIDTH),
+ uv_scan = VENUS_UV_SCANLINES(COLOR_FMT_NV12, DEFAULT_HEIGHT);
+ int y_size = y_stride * y_scan;
+ int c = 0, dst_offset = 0, src_offset = 0;
+
+ /* copy the luma */
+ for (c = 0; c < DEFAULT_HEIGHT; ++c) {
+ memcpy(dst + dst_offset, src + src_offset, DEFAULT_WIDTH);
+ src_offset += y_stride;
+ dst_offset += DEFAULT_WIDTH;
+ }
+
+ /* skip over padding between luma and chroma */
+ src_offset = y_size;
+ /* now do the chroma */
+ for (c = 0; c < DEFAULT_HEIGHT / 2; ++c) {
+ memcpy(dst + dst_offset, src + src_offset, DEFAULT_WIDTH);
+ src_offset += uv_stride;
+ dst_offset += DEFAULT_WIDTH;
+ }
+
+ (void)uv_scan;
+ return dst_offset;
+}
+#else
+static int encode_memcpy(uint8_t *dst, uint8_t *src, int size)
+{
+ memcpy(dst, src, size);
+ return size;
+}
+#endif
+
+static void encode(struct work_struct *work)
+{
+ struct encode_task *et = NULL;
+ struct venc_inst *inst = NULL;
+ struct mem_region *input, *output;
+ struct vb2_buffer *vb;
+ int bytes_copied = 0;
+ if (!work)
+ return;
+
+ et = container_of(work, struct encode_task, work);
+ inst = et->inst;
+
+ mutex_lock(&inst->lock);
+ if (list_empty(&inst->input_bufs.list)) {
+ WFD_MSG_ERR("Can't find an input buffer");
+ mutex_unlock(&inst->lock);
+ return;
+ }
+
+ if (list_empty(&inst->output_bufs.list)) {
+ WFD_MSG_ERR("Can't find an output buffer");
+ mutex_unlock(&inst->lock);
+ return;
+ }
+
+ /* Grab an i/p & o/p buffer pair */
+ input = list_first_entry(&inst->input_bufs.list,
+ struct mem_region, list);
+ list_del(&input->list);
+
+ output = list_first_entry(&inst->output_bufs.list,
+ struct mem_region, list);
+ list_del(&output->list);
+ mutex_unlock(&inst->lock);
+
+ /* This is our "encode" */
+ bytes_copied = encode_memcpy(output->kvaddr, input->kvaddr,
+ min(output->size, input->size));
+
+ vb = (struct vb2_buffer *)output->cookie;
+ vb->v4l2_planes[0].bytesused = bytes_copied;
+ inst->vmops.op_buffer_done(
+ inst->vmops.cbdata, 0, vb);
+
+ inst->vmops.ip_buffer_done(
+ inst->vmops.cbdata,
+ 0, input);
+
+ venc_unmap_user_to_kernel(inst, output);
+ kfree(input);
+ kfree(output);
+ kfree(et);
+}
+
+static void prepare_for_encode(struct venc_inst *inst)
+{
+ struct encode_task *et = kzalloc(sizeof(*et), GFP_KERNEL);
+ et->inst = inst;
+ INIT_WORK(&et->work, encode);
+ queue_work(inst->wq, &et->work);
+}
+
+static long venc_fill_outbuf(struct v4l2_subdev *sd, void *arg)
+{
+ struct venc_inst *inst = NULL;
+ struct mem_region *mregion = NULL;
+
+ if (!sd) {
+ WFD_MSG_ERR("Subdevice required for %s\n", __func__);
+ return -EINVAL;
+ } else if (!arg) {
+ WFD_MSG_ERR("Invalid output buffer in %s", __func__);
+ return -EINVAL;
+ }
+
+ inst = (struct venc_inst *)sd->dev_priv;
+
+ /* check for dupes */
+ list_for_each_entry(mregion, &inst->output_bufs.list, list) {
+ struct mem_region *temp = arg;
+ if (mem_region_equals(temp, mregion)) {
+ WFD_MSG_ERR("Attempt to queue dupe buffer\n");
+ return -EEXIST;
+ }
+ }
+
+ mregion = kzalloc(sizeof(*mregion), GFP_KERNEL);
+ if (!mregion) {
+ WFD_MSG_ERR("Invalid output buffer in %s", __func__);
+ return -EINVAL;
+ }
+
+ *mregion = *(struct mem_region *)arg;
+
+ if (venc_map_user_to_kernel(inst, mregion)) {
+ WFD_MSG_ERR("unable to map buffer into kernel\n");
+ return -EFAULT;
+ }
+
+ mutex_lock(&inst->lock);
+ list_add_tail(&mregion->list, &inst->output_bufs.list);
+
+ if (!list_empty(&inst->input_bufs.list))
+ prepare_for_encode(inst);
+ mutex_unlock(&inst->lock);
+
+ return 0;
+}
+
+static long venc_encode_frame(struct v4l2_subdev *sd, void *arg)
+{
+ struct venc_inst *inst = NULL;
+ struct venc_buf_info *venc_buf = arg;
+ struct mem_region *mregion = NULL;
+
+ if (!sd) {
+ WFD_MSG_ERR("Subdevice required for %s\n", __func__);
+ return -EINVAL;
+ } else if (!venc_buf) {
+ WFD_MSG_ERR("Invalid output buffer ot fill\n");
+ return -EINVAL;
+ }
+
+ mregion = kzalloc(sizeof(*mregion), GFP_KERNEL);
+ if (!mregion) {
+ WFD_MSG_ERR("Invalid output buffer in %s", __func__);
+ return -EINVAL;
+ }
+
+ inst = (struct venc_inst *)sd->dev_priv;
+ *mregion = *venc_buf->mregion;
+
+ mutex_lock(&inst->lock);
+ list_add_tail(&mregion->list, &inst->input_bufs.list);
+
+ if (!list_empty(&inst->output_bufs.list))
+ prepare_for_encode(inst);
+ mutex_unlock(&inst->lock);
+ return 0;
+}
+
+static long venc_alloc_recon_buffers(struct v4l2_subdev *sd, void *arg)
+{
+ return 0;
+}
+
+static long venc_free_output_buffer(struct v4l2_subdev *sd, void *arg)
+{
+ return 0;
+}
+
+static long venc_flush_buffers(struct v4l2_subdev *sd, void *arg)
+{
+ return 0;
+}
+
+static long venc_free_input_buffer(struct v4l2_subdev *sd, void *arg)
+{
+ return 0;
+}
+
+static long venc_free_recon_buffers(struct v4l2_subdev *sd, void *arg)
+{
+ return 0;
+}
+
+static long venc_set_property(struct v4l2_subdev *sd, void *arg)
+{
+ return 0;
+}
+
+static long venc_get_property(struct v4l2_subdev *sd, void *arg)
+{
+ return 0;
+}
+
+static long venc_mmap(struct v4l2_subdev *sd, void *arg)
+{
+ struct mem_region_map *mmap = arg;
+ struct mem_region *mregion;
+
+ mregion = mmap->mregion;
+ mregion->paddr = mregion->kvaddr;
+ return 0;
+}
+
+static long venc_munmap(struct v4l2_subdev *sd, void *arg)
+{
+ return 0;
+}
+
+static long venc_set_framerate_mode(struct v4l2_subdev *sd,
+ void *arg)
+{
+ return 0;
+}
+
+long venc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ long rc = 0;
+ switch (cmd) {
+ case OPEN:
+ rc = venc_open(sd, arg);
+ break;
+ case CLOSE:
+ rc = venc_close(sd, arg);
+ break;
+ case ENCODE_START:
+ rc = venc_start(sd);
+ break;
+ case ENCODE_FRAME:
+ venc_encode_frame(sd, arg);
+ break;
+ case ENCODE_STOP:
+ rc = venc_stop(sd);
+ break;
+ case SET_PROP:
+ rc = venc_set_property(sd, arg);
+ break;
+ case GET_PROP:
+ rc = venc_get_property(sd, arg);
+ break;
+ case GET_BUFFER_REQ:
+ rc = venc_get_buffer_req(sd, arg);
+ break;
+ case SET_BUFFER_REQ:
+ rc = venc_set_buffer_req(sd, arg);
+ break;
+ case FREE_BUFFER:
+ break;
+ case FILL_OUTPUT_BUFFER:
+ rc = venc_fill_outbuf(sd, arg);
+ break;
+ case SET_FORMAT:
+ rc = venc_set_format(sd, arg);
+ break;
+ case SET_FRAMERATE:
+ rc = venc_set_framerate(sd, arg);
+ break;
+ case SET_INPUT_BUFFER:
+ rc = venc_set_input_buffer(sd, arg);
+ break;
+ case SET_OUTPUT_BUFFER:
+ rc = venc_set_output_buffer(sd, arg);
+ break;
+ case ALLOC_RECON_BUFFERS:
+ rc = venc_alloc_recon_buffers(sd, arg);
+ break;
+ case FREE_OUTPUT_BUFFER:
+ rc = venc_free_output_buffer(sd, arg);
+ break;
+ case FREE_INPUT_BUFFER:
+ rc = venc_free_input_buffer(sd, arg);
+ break;
+ case FREE_RECON_BUFFERS:
+ rc = venc_free_recon_buffers(sd, arg);
+ break;
+ case ENCODE_FLUSH:
+ rc = venc_flush_buffers(sd, arg);
+ break;
+ case ENC_MMAP:
+ rc = venc_mmap(sd, arg);
+ break;
+ case ENC_MUNMAP:
+ rc = venc_munmap(sd, arg);
+ break;
+ case SET_FRAMERATE_MODE:
+ rc = venc_set_framerate_mode(sd, arg);
+ break;
+ default:
+ WFD_MSG_ERR("Unknown ioctl %d to enc-subdev\n", cmd);
+ rc = -ENOTSUPP;
+ break;
+ }
+ return rc;
+}
diff --git a/drivers/media/platform/msm/wfd/enc-mfc-subdev.c b/drivers/media/platform/msm/wfd/enc-mfc-subdev.c
index ceb0149..26e24da 100644
--- a/drivers/media/platform/msm/wfd/enc-mfc-subdev.c
+++ b/drivers/media/platform/msm/wfd/enc-mfc-subdev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -20,6 +20,7 @@
#include <media/msm/vcd_property.h>
#include <linux/time.h>
#include <linux/ktime.h>
+#include <linux/slab.h>
#define VID_ENC_MAX_ENCODER_CLIENTS 1
#define MAX_NUM_CTRLS 20
diff --git a/drivers/media/platform/msm/wfd/enc-venus-subdev.c b/drivers/media/platform/msm/wfd/enc-venus-subdev.c
index 4f7fb44..8121471 100644
--- a/drivers/media/platform/msm/wfd/enc-venus-subdev.c
+++ b/drivers/media/platform/msm/wfd/enc-venus-subdev.c
@@ -18,6 +18,8 @@
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
#include <mach/iommu_domains.h>
#include <media/msm_vidc.h>
#include <media/v4l2-subdev.h>
@@ -47,6 +49,13 @@
bool callback_thread_running;
struct completion dq_complete, cmd_complete;
bool secure;
+ struct workqueue_struct *fill_buf_wq;
+};
+
+struct fill_buf_work {
+ struct venc_inst *inst;
+ struct mem_region *mregion;
+ struct work_struct work;
};
static const int subscribed_events[] = {
@@ -69,6 +78,26 @@
return venc_ion_client ? 0 : -ENOMEM;
}
+static int invalidate_cache(struct ion_client *client,
+ struct mem_region *mregion)
+{
+ if (!client || !mregion) {
+ WFD_MSG_ERR(
+ "Failed to flush ion buffer: invalid client or region\n");
+ return -EINVAL;
+ } else if (!mregion->ion_handle) {
+ WFD_MSG_ERR(
+ "Failed to flush ion buffer: not an ion buffer\n");
+ return -EINVAL;
+ }
+
+ return msm_ion_do_cache_op(client,
+ mregion->ion_handle,
+ mregion->kvaddr,
+ mregion->size,
+ ION_IOC_INV_CACHES);
+
+}
static int next_free_index(struct index_bitmap *index_bitmap)
{
int index = find_first_zero_bit(index_bitmap->bitmap,
@@ -228,6 +257,16 @@
vb->v4l2_planes[0].bytesused =
buffer.m.planes[0].bytesused;
+ /* Buffer is on its way to userspace, so
+ * invalidate the cache */
+ rc = invalidate_cache(venc_ion_client, mregion);
+ if (rc) {
+ WFD_MSG_WARN(
+ "Failed to invalidate cache %d\n",
+ rc);
+ /* Not fatal, move on */
+ }
+
inst->vmops.op_buffer_done(
inst->vmops.cbdata, 0, vb);
} else if (buffer.type == BUF_TYPE_INPUT &&
@@ -257,13 +296,24 @@
static long set_default_properties(struct venc_inst *inst)
{
struct v4l2_control ctrl = {0};
+ int rc;
/* Set the IDR period as 1. The venus core doesn't give
* the sps/pps for I-frames, only IDR. */
ctrl.id = V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD;
ctrl.value = 1;
+ rc = msm_vidc_s_ctrl(inst->vidc_context, &ctrl);
+ if (rc)
+ WFD_MSG_WARN("Failed to set IDR period\n");
- return msm_vidc_s_ctrl(inst->vidc_context, &ctrl);
+ /* Set the default rc mode to VBR/VFR, client can change later */
+ ctrl.id = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL;
+ ctrl.value = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_VFR;
+ rc = msm_vidc_s_ctrl(inst->vidc_context, &ctrl);
+ if (rc)
+ WFD_MSG_WARN("Failed to set rc mode\n");
+
+ return 0;
}
static int subscribe_events(struct venc_inst *inst)
@@ -330,6 +380,14 @@
init_completion(&inst->dq_complete);
init_completion(&inst->cmd_complete);
mutex_init(&inst->lock);
+
+ inst->fill_buf_wq = create_singlethread_workqueue("venc_vidc_ftb_wq");
+ if (!inst->fill_buf_wq) {
+ WFD_MSG_ERR("Failed to create ftb wq\n");
+ rc = -ENOMEM;
+ goto vidc_wq_create_fail;
+ }
+
inst->vidc_context = msm_vidc_open(MSM_VIDC_CORE_0, MSM_VIDC_ENCODER);
if (!inst->vidc_context) {
WFD_MSG_ERR("Failed to create vidc context\n");
@@ -361,6 +419,8 @@
vidc_subscribe_fail:
msm_vidc_close(inst->vidc_context);
vidc_open_fail:
+ destroy_workqueue(inst->fill_buf_wq);
+vidc_wq_create_fail:
kfree(inst);
venc_open_fail:
return rc;
@@ -384,6 +444,7 @@
wait_for_completion(&inst->cmd_complete);
+ destroy_workqueue(inst->fill_buf_wq);
if (inst->callback_thread && inst->callback_thread_running)
kthread_stop(inst->callback_thread);
@@ -550,6 +611,7 @@
inst = (struct venc_inst *)sd->dev_priv;
+ flush_workqueue(inst->fill_buf_wq);
rc = msm_vidc_streamoff(inst->vidc_context, BUF_TYPE_INPUT);
if (rc) {
WFD_MSG_ERR("Failed to streamoff vidc's input port");
@@ -645,6 +707,33 @@
return rc;
}
+#ifdef CONFIG_MSM_WFD_DEBUG
+static void *venc_map_kernel(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ return ion_map_kernel(client, handle);
+}
+
+static void venc_unmap_kernel(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ ion_unmap_kernel(client, handle);
+}
+#else
+
+static void *venc_map_kernel(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ return NULL;
+}
+
+static void venc_unmap_kernel(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ return;
+}
+#endif
+
static int venc_map_user_to_kernel(struct venc_inst *inst,
struct mem_region *mregion)
{
@@ -679,18 +768,8 @@
goto venc_map_fail;
}
- if (!inst->secure) {
- mregion->kvaddr = ion_map_kernel(venc_ion_client,
- mregion->ion_handle);
- if (IS_ERR_OR_NULL(mregion->kvaddr)) {
- WFD_MSG_ERR("Failed to map buffer into kernel\n");
- rc = PTR_ERR(mregion->kvaddr);
- mregion->kvaddr = NULL;
- goto venc_map_fail;
- }
- } else {
- mregion->kvaddr = NULL;
- }
+ mregion->kvaddr = inst->secure ? NULL :
+ venc_map_kernel(venc_ion_client, mregion->ion_handle);
if (inst->secure) {
rc = msm_ion_secure_buffer(venc_ion_client,
@@ -727,8 +806,8 @@
if (inst->secure)
msm_ion_unsecure_buffer(venc_ion_client, mregion->ion_handle);
venc_map_iommu_map_fail:
- if (!inst->secure)
- ion_unmap_kernel(venc_ion_client, mregion->ion_handle);
+ if (!inst->secure && !IS_ERR_OR_NULL(mregion->kvaddr))
+ venc_unmap_kernel(venc_ion_client, mregion->ion_handle);
venc_map_fail:
return rc;
}
@@ -761,8 +840,8 @@
mregion->paddr = NULL;
}
- if (mregion->kvaddr) {
- ion_unmap_kernel(venc_ion_client, mregion->ion_handle);
+ if (!IS_ERR_OR_NULL(mregion->kvaddr)) {
+ venc_unmap_kernel(venc_ion_client, mregion->ion_handle);
mregion->kvaddr = NULL;
}
@@ -937,25 +1016,12 @@
return msm_vidc_s_parm(inst->vidc_context, &p);
}
-static long venc_fill_outbuf(struct v4l2_subdev *sd, void *arg)
+static long fill_outbuf(struct venc_inst *inst, struct mem_region *mregion)
{
- struct venc_inst *inst = NULL;
- struct mem_region *mregion = NULL;
struct v4l2_buffer buffer = {0};
struct v4l2_plane plane = {0};
int index = 0, rc = 0;
- if (!sd) {
- WFD_MSG_ERR("Subdevice required for %s\n", __func__);
- return -EINVAL;
- } else if (!arg) {
- WFD_MSG_ERR("Invalid output buffer ot fill\n");
- return -EINVAL;
- }
-
- inst = (struct venc_inst *)sd->dev_priv;
- mregion = get_registered_mregion(&inst->registered_output_bufs, arg);
-
if (!mregion) {
WFD_MSG_ERR("Output buffer not registered\n");
return -ENOENT;
@@ -993,8 +1059,77 @@
mark_index_busy(&inst->free_output_indices, index);
mutex_unlock(&inst->lock);
}
- return rc;
+ return rc;
+}
+
+static void fill_outbuf_helper(struct work_struct *work)
+{
+ int rc;
+ struct fill_buf_work *fbw =
+ container_of(work, struct fill_buf_work, work);
+
+ rc = fill_outbuf(fbw->inst, fbw->mregion);
+ if (rc) {
+ struct vb2_buffer *vb = NULL;
+
+ WFD_MSG_ERR("Failed to fill buffer async\n");
+ vb = (struct vb2_buffer *)fbw->mregion->cookie;
+ vb->v4l2_buf.flags = 0;
+ vb->v4l2_buf.timestamp = ns_to_timeval(-1);
+ vb->v4l2_planes[0].bytesused = 0;
+
+ fbw->inst->vmops.op_buffer_done(
+ fbw->inst->vmops.cbdata, rc, vb);
+ }
+
+ kfree(fbw);
+}
+
+static long venc_fill_outbuf(struct v4l2_subdev *sd, void *arg)
+{
+ struct fill_buf_work *fbw;
+ struct venc_inst *inst = NULL;
+ struct mem_region *mregion;
+
+ if (!sd) {
+ WFD_MSG_ERR("Subdevice required for %s\n", __func__);
+ return -EINVAL;
+ } else if (!arg) {
+ WFD_MSG_ERR("Invalid output buffer ot fill\n");
+ return -EINVAL;
+ }
+
+ inst = (struct venc_inst *)sd->dev_priv;
+ mregion = get_registered_mregion(&inst->registered_output_bufs, arg);
+ if (!mregion) {
+ WFD_MSG_ERR("Output buffer not registered\n");
+ return -ENOENT;
+ }
+
+ fbw = kzalloc(sizeof(*fbw), GFP_KERNEL);
+ if (!fbw) {
+ WFD_MSG_ERR("Couldn't allocate memory\n");
+ return -ENOMEM;
+ }
+
+ INIT_WORK(&fbw->work, fill_outbuf_helper);
+ fbw->inst = inst;
+ fbw->mregion = mregion;
+ /* XXX: The need for a wq to qbuf to vidc is necessitated as a
+ * workaround for a bug in the v4l2 framework. VIDIOC_QBUF from
+ * triggers a down_read(current->mm->mmap_sem). There is another
+ * _read(..) as msm_vidc_qbuf() depends on videobuf2 framework
+ * as well. However, a _write(..) after the first _read() by a
+ * different driver will prevent the second _read(...) from
+ * suceeding.
+ *
+ * As we can't modify the framework, we're working around by issue
+ * by queuing in a different thread effectively.
+ */
+ queue_work(inst->fill_buf_wq, &fbw->work);
+
+ return 0;
}
static long venc_encode_frame(struct v4l2_subdev *sd, void *arg)
@@ -1126,6 +1261,8 @@
inst = (struct venc_inst *)sd->dev_priv;
+ flush_workqueue(inst->fill_buf_wq);
+
enc_cmd.cmd = V4L2_ENC_QCOM_CMD_FLUSH;
enc_cmd.flags = V4L2_QCOM_CMD_FLUSH_OUTPUT |
V4L2_QCOM_CMD_FLUSH_CAPTURE;
diff --git a/drivers/media/platform/msm/wfd/mdp-4-subdev.c b/drivers/media/platform/msm/wfd/mdp-4-subdev.c
index 465ec21..2d79622 100644
--- a/drivers/media/platform/msm/wfd/mdp-4-subdev.c
+++ b/drivers/media/platform/msm/wfd/mdp-4-subdev.c
@@ -11,6 +11,7 @@
*
*/
#include <linux/msm_mdp.h>
+#include <linux/slab.h>
#include <mach/iommu_domains.h>
#include <media/videobuf2-core.h>
#include "enc-subdev.h"
diff --git a/drivers/media/platform/msm/wfd/mdp-5-subdev.c b/drivers/media/platform/msm/wfd/mdp-5-subdev.c
index 3c546d0..97204ae 100644
--- a/drivers/media/platform/msm/wfd/mdp-5-subdev.c
+++ b/drivers/media/platform/msm/wfd/mdp-5-subdev.c
@@ -11,6 +11,7 @@
*
*/
#include <linux/msm_mdp.h>
+#include <linux/slab.h>
#include <mach/iommu_domains.h>
#include <media/videobuf2-core.h>
#include "enc-subdev.h"
@@ -72,7 +73,7 @@
mops->cookie = inst;
return 0;
mdp_secure_fail:
- msm_fb_writeback_terminate(inst->mdp);
+ msm_fb_writeback_terminate(fbi);
mdp_open_fail:
kfree(inst);
return rc;
@@ -82,19 +83,12 @@
{
struct mdp_instance *inst = arg;
int rc = 0;
- struct fb_info *fbi = NULL;
if (inst) {
rc = msm_fb_writeback_start(inst->mdp);
if (rc) {
WFD_MSG_ERR("Failed to start MDP mode\n");
goto exit;
}
- fbi = msm_fb_get_writeback_fb();
- if (!fbi) {
- WFD_MSG_ERR("Failed to acquire mdp instance\n");
- rc = -ENODEV;
- goto exit;
- }
}
exit:
return rc;
@@ -104,7 +98,6 @@
{
struct mdp_instance *inst = arg;
int rc = 0;
- struct fb_info *fbi = NULL;
if (inst) {
rc = msm_fb_writeback_stop(inst->mdp);
if (rc) {
@@ -112,7 +105,6 @@
return rc;
}
- fbi = (struct fb_info *)inst->mdp;
}
return 0;
}
@@ -120,12 +112,10 @@
static int mdp_close(struct v4l2_subdev *sd, void *arg)
{
struct mdp_instance *inst = arg;
- struct fb_info *fbi = NULL;
if (inst) {
- fbi = (struct fb_info *)inst->mdp;
if (inst->secure)
msm_fb_writeback_set_secure(inst->mdp, false);
- msm_fb_writeback_terminate(fbi);
+ msm_fb_writeback_terminate(inst->mdp);
kfree(inst);
}
return 0;
diff --git a/drivers/media/platform/msm/wfd/mdp-dummy-subdev.c b/drivers/media/platform/msm/wfd/mdp-dummy-subdev.c
index 2242c76..46977a6 100644
--- a/drivers/media/platform/msm/wfd/mdp-dummy-subdev.c
+++ b/drivers/media/platform/msm/wfd/mdp-dummy-subdev.c
@@ -12,12 +12,17 @@
*/
#include <linux/list.h>
#include <linux/msm_mdp.h>
+#include <linux/slab.h>
#include <media/videobuf2-core.h>
#include "enc-subdev.h"
#include "mdp-subdev.h"
#include "wfd-util.h"
+#ifndef CONFIG_MSM_WFD_DEBUG
+#error "Dummy subdevice must only be used when CONFIG_MSM_WFD_DEBUG=y"
+#endif
+
struct mdp_buf_queue {
struct mdp_buf_info mdp_buf_info;
struct list_head node;
diff --git a/drivers/media/platform/msm/wfd/vsg-subdev.c b/drivers/media/platform/msm/wfd/vsg-subdev.c
index 90b1957..e0a46cc 100644
--- a/drivers/media/platform/msm/wfd/vsg-subdev.c
+++ b/drivers/media/platform/msm/wfd/vsg-subdev.c
@@ -13,6 +13,7 @@
#include <linux/hrtimer.h>
#include <linux/time.h>
#include <linux/list.h>
+#include <linux/slab.h>
#include <media/videobuf2-core.h>
#include "enc-subdev.h"
#include "vsg-subdev.h"
diff --git a/drivers/media/platform/msm/wfd/wfd-ioctl.c b/drivers/media/platform/msm/wfd/wfd-ioctl.c
index af3cd69..30a666d 100644
--- a/drivers/media/platform/msm/wfd/wfd-ioctl.c
+++ b/drivers/media/platform/msm/wfd/wfd-ioctl.c
@@ -14,14 +14,14 @@
#include <linux/types.h>
#include <linux/list.h>
#include <linux/ioctl.h>
-#include <linux/spinlock.h>
+#include <linux/mutex.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/platform_device.h>
-
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/time.h>
+#include <linux/slab.h>
#include <mach/board.h>
#include <media/v4l2-dev.h>
@@ -78,7 +78,8 @@
struct wfd_inst {
struct vb2_queue vid_bufq;
- spinlock_t inst_lock;
+ struct mutex lock;
+ struct mutex vb2_lock;
u32 buf_count;
struct task_struct *mdp_task;
void *mdp_inst;
@@ -114,7 +115,6 @@
{
struct file *priv_data = (struct file *)(q->drv_priv);
struct wfd_inst *inst = file_to_inst(priv_data);
- unsigned long flags;
int i;
WFD_MSG_DBG("In %s\n", __func__);
@@ -122,12 +122,12 @@
return -EINVAL;
*num_planes = 1;
- spin_lock_irqsave(&inst->inst_lock, flags);
+ mutex_lock(&inst->lock);
for (i = 0; i < *num_planes; ++i) {
sizes[i] = inst->out_buf_size;
alloc_ctxs[i] = inst;
}
- spin_unlock_irqrestore(&inst->inst_lock, flags);
+ mutex_unlock(&inst->lock);
return 0;
}
@@ -158,11 +158,36 @@
return (unsigned long)NULL;
}
+#ifdef CONFIG_MSM_WFD_DEBUG
+static void *wfd_map_kernel(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ return ion_map_kernel(client, handle);
+}
+
+static void wfd_unmap_kernel(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ ion_unmap_kernel(client, handle);
+}
+#else
+static void *wfd_map_kernel(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ return NULL;
+}
+
+static void wfd_unmap_kernel(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ return;
+}
+#endif
+
static int wfd_allocate_ion_buffer(struct ion_client *client,
bool secure, struct mem_region *mregion)
{
struct ion_handle *handle = NULL;
- void *kvaddr = NULL;
unsigned int alloc_regions = 0, ion_flags = 0, align = 0;
int rc = 0;
@@ -184,26 +209,14 @@
goto alloc_fail;
}
- if (!secure) {
- kvaddr = ion_map_kernel(client, handle);
-
- if (IS_ERR_OR_NULL(kvaddr)) {
- WFD_MSG_ERR("Failed to get virtual addr\n");
- rc = PTR_ERR(kvaddr);
- goto alloc_fail;
- }
- } else {
- kvaddr = NULL;
- }
-
- mregion->kvaddr = kvaddr;
+ mregion->kvaddr = secure ? NULL :
+ wfd_map_kernel(client, handle);
mregion->ion_handle = handle;
-
return rc;
alloc_fail:
if (!IS_ERR_OR_NULL(handle)) {
- if (!IS_ERR_OR_NULL(kvaddr))
- ion_unmap_kernel(client, handle);
+ if (!IS_ERR_OR_NULL(mregion->kvaddr))
+ wfd_unmap_kernel(client, handle);
ion_free(client, handle);
@@ -223,8 +236,10 @@
"Invalid client or region");
return -EINVAL;
}
- if (mregion->kvaddr)
- ion_unmap_kernel(client, mregion->ion_handle);
+
+ if (!IS_ERR_OR_NULL(mregion->kvaddr))
+ wfd_unmap_kernel(client, mregion->ion_handle);
+
ion_free(client, mregion->ion_handle);
return 0;
}
@@ -257,16 +272,15 @@
struct mem_region *enc_mregion, *mdp_mregion;
struct mem_region_pair *mpair;
int rc;
- unsigned long flags;
struct mdp_buf_info mdp_buf = {0};
struct mem_region_map mmap_context = {0};
- spin_lock_irqsave(&inst->inst_lock, flags);
+ mutex_lock(&inst->lock);
if (inst->input_bufs_allocated) {
- spin_unlock_irqrestore(&inst->inst_lock, flags);
+ mutex_unlock(&inst->lock);
return 0;
}
inst->input_bufs_allocated = true;
- spin_unlock_irqrestore(&inst->inst_lock, flags);
+ mutex_unlock(&inst->lock);
for (i = 0; i < VENC_INPUT_BUFFERS; ++i) {
mpair = kzalloc(sizeof(*mpair), GFP_KERNEL);
@@ -409,15 +423,14 @@
{
struct list_head *ptr, *next;
struct mem_region_pair *mpair;
- unsigned long flags;
int rc = 0;
- spin_lock_irqsave(&inst->inst_lock, flags);
+ mutex_lock(&inst->lock);
if (!inst->input_bufs_allocated) {
- spin_unlock_irqrestore(&inst->inst_lock, flags);
+ mutex_unlock(&inst->lock);
return;
}
inst->input_bufs_allocated = false;
- spin_unlock_irqrestore(&inst->inst_lock, flags);
+ mutex_unlock(&inst->lock);
if (!list_empty(&inst->input_mem_list)) {
list_for_each_safe(ptr, next,
&inst->input_mem_list) {
@@ -470,8 +483,7 @@
{
struct mem_info_entry *temp;
struct mem_info *ret = NULL;
- unsigned long flags;
- spin_lock_irqsave(&inst->inst_lock, flags);
+ mutex_lock(&inst->lock);
if (!list_empty(&inst->minfo_list)) {
list_for_each_entry(temp, &inst->minfo_list, list) {
if (temp && temp->userptr == userptr) {
@@ -480,7 +492,7 @@
}
}
}
- spin_unlock_irqrestore(&inst->inst_lock, flags);
+ mutex_unlock(&inst->lock);
return ret;
}
@@ -489,8 +501,7 @@
{
struct list_head *ptr, *next;
struct mem_info_entry *temp;
- unsigned long flags;
- spin_lock_irqsave(&inst->inst_lock, flags);
+ mutex_lock(&inst->lock);
if (!list_empty(&inst->minfo_list)) {
list_for_each_safe(ptr, next,
&inst->minfo_list) {
@@ -502,7 +513,7 @@
}
}
}
- spin_unlock_irqrestore(&inst->inst_lock, flags);
+ mutex_unlock(&inst->lock);
}
static void wfd_unregister_out_buf(struct wfd_inst *inst,
struct mem_info *minfo)
@@ -806,7 +817,6 @@
struct v4l2_format *fmt)
{
struct wfd_inst *inst = file_to_inst(filp);
- unsigned long flags;
if (!fmt) {
WFD_MSG_ERR("Invalid argument\n");
return -EINVAL;
@@ -815,13 +825,13 @@
WFD_MSG_ERR("Only V4L2_BUF_TYPE_VIDEO_CAPTURE is supported\n");
return -EINVAL;
}
- spin_lock_irqsave(&inst->inst_lock, flags);
+ mutex_lock(&inst->lock);
fmt->fmt.pix.width = inst->width;
fmt->fmt.pix.height = inst->height;
fmt->fmt.pix.pixelformat = inst->pixelformat;
fmt->fmt.pix.sizeimage = inst->out_buf_size;
fmt->fmt.pix.priv = 0;
- spin_unlock_irqrestore(&inst->inst_lock, flags);
+ mutex_unlock(&inst->lock);
return 0;
}
@@ -832,7 +842,6 @@
struct wfd_inst *inst = file_to_inst(filp);
struct wfd_device *wfd_dev = video_drvdata(filp);
struct mdp_prop prop;
- unsigned long flags;
struct bufreq breq;
if (!fmt) {
WFD_MSG_ERR("Invalid argument\n");
@@ -864,13 +873,13 @@
WFD_MSG_ERR("Failed to set buffer reqs on encoder\n");
return rc;
}
- spin_lock_irqsave(&inst->inst_lock, flags);
+ mutex_lock(&inst->lock);
inst->input_buf_size = breq.size;
inst->out_buf_size = fmt->fmt.pix.sizeimage;
prop.height = inst->height = fmt->fmt.pix.height;
prop.width = inst->width = fmt->fmt.pix.width;
prop.inst = inst->mdp_inst;
- spin_unlock_irqrestore(&inst->inst_lock, flags);
+ mutex_unlock(&inst->lock);
rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl, MDP_SET_PROP,
(void *)&prop);
if (rc)
@@ -882,7 +891,6 @@
{
struct wfd_inst *inst = file_to_inst(filp);
struct wfd_device *wfd_dev = video_drvdata(filp);
- unsigned long flags;
int rc = 0;
if (b->type != V4L2_CAP_VIDEO_CAPTURE ||
@@ -897,9 +905,9 @@
WFD_MSG_ERR("Failed to get buf reqs from encoder\n");
return rc;
}
- spin_lock_irqsave(&inst->inst_lock, flags);
+ mutex_lock(&inst->lock);
inst->buf_count = b->count;
- spin_unlock_irqrestore(&inst->inst_lock, flags);
+ mutex_unlock(&inst->lock);
rc = vb2_reqbufs(&inst->vid_bufq, b);
return rc;
}
@@ -908,7 +916,6 @@
{
struct mem_info_entry *minfo_entry;
struct mem_info *minfo;
- unsigned long flags;
if (!b || !inst || !b->reserved) {
WFD_MSG_ERR("Invalid arguments\n");
return -EINVAL;
@@ -924,9 +931,9 @@
return -EINVAL;
}
minfo_entry->userptr = b->m.userptr;
- spin_lock_irqsave(&inst->inst_lock, flags);
+ mutex_lock(&inst->lock);
list_add_tail(&minfo_entry->list, &inst->minfo_list);
- spin_unlock_irqrestore(&inst->inst_lock, flags);
+ mutex_unlock(&inst->lock);
} else
WFD_MSG_DBG("Buffer already registered\n");
@@ -948,7 +955,10 @@
return rc;
}
+ mutex_lock(&inst->vb2_lock);
rc = vb2_qbuf(&inst->vid_bufq, b);
+ mutex_unlock(&inst->vb2_lock);
+
if (rc)
WFD_MSG_ERR("Failed to queue buffer\n");
else
@@ -962,16 +972,15 @@
{
int rc = 0;
struct wfd_inst *inst = file_to_inst(filp);
- unsigned long flags;
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
WFD_MSG_ERR("stream on for buffer type = %d is not "
"supported.\n", i);
return -EINVAL;
}
- spin_lock_irqsave(&inst->inst_lock, flags);
+ mutex_lock(&inst->lock);
inst->streamoff = false;
- spin_unlock_irqrestore(&inst->inst_lock, flags);
+ mutex_unlock(&inst->lock);
rc = vb2_streamon(&inst->vid_bufq, i);
if (rc) {
@@ -988,7 +997,6 @@
enum v4l2_buf_type i)
{
struct wfd_inst *inst = file_to_inst(filp);
- unsigned long flags;
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
WFD_MSG_ERR("stream off for buffer type = %d is not "
@@ -996,16 +1004,17 @@
return -EINVAL;
}
- spin_lock_irqsave(&inst->inst_lock, flags);
+ mutex_lock(&inst->lock);
if (inst->streamoff) {
WFD_MSG_ERR("Module is already in streamoff state\n");
- spin_unlock_irqrestore(&inst->inst_lock, flags);
+ mutex_unlock(&inst->lock);
return -EINVAL;
}
inst->streamoff = true;
- spin_unlock_irqrestore(&inst->inst_lock, flags);
+ mutex_unlock(&inst->lock);
WFD_MSG_DBG("Calling videobuf_streamoff\n");
vb2_streamoff(&inst->vid_bufq, i);
+ wake_up(&inst->event_handler.wait);
return 0;
}
static int wfdioc_dqbuf(struct file *filp, void *fh,
@@ -1015,7 +1024,10 @@
int rc;
WFD_MSG_DBG("Waiting to dequeue buffer\n");
- rc = vb2_dqbuf(&inst->vid_bufq, b, 0);
+
+ /* XXX: If we switch to non-blocking mode in the future,
+ * we'll need to lock this with vb2_lock */
+ rc = vb2_dqbuf(&inst->vid_bufq, b, false /* blocking */);
if (rc)
WFD_MSG_ERR("Failed to dequeue buffer\n");
@@ -1232,7 +1244,6 @@
};
static int wfd_set_default_properties(struct file *filp)
{
- unsigned long flags;
struct v4l2_format fmt;
struct v4l2_control ctrl;
struct wfd_inst *inst = file_to_inst(filp);
@@ -1240,13 +1251,13 @@
WFD_MSG_ERR("Invalid argument\n");
return -EINVAL;
}
- spin_lock_irqsave(&inst->inst_lock, flags);
+ mutex_lock(&inst->lock);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.height = inst->height = DEFAULT_WFD_HEIGHT;
fmt.fmt.pix.width = inst->width = DEFAULT_WFD_WIDTH;
fmt.fmt.pix.pixelformat = inst->pixelformat
= V4L2_PIX_FMT_H264;
- spin_unlock_irqrestore(&inst->inst_lock, flags);
+ mutex_unlock(&inst->lock);
wfdioc_s_fmt(filp, filp->private_data, &fmt);
ctrl.id = V4L2_CID_MPEG_VIDEO_HEADER_MODE;
@@ -1257,8 +1268,13 @@
static void venc_op_buffer_done(void *cookie, u32 status,
struct vb2_buffer *buf)
{
+ struct file *filp = cookie;
+ struct wfd_inst *inst = file_to_inst(filp);
+
WFD_MSG_DBG("yay!! got callback\n");
- vb2_buffer_done(buf, VB2_BUF_STATE_DONE);
+ mutex_lock(&inst->vb2_lock);
+ vb2_buffer_done(buf, status ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+ mutex_unlock(&inst->vb2_lock);
}
static void venc_ip_buffer_done(void *cookie, u32 status,
@@ -1429,7 +1445,8 @@
goto err_mdp_open;
}
filp->private_data = &inst->event_handler;
- spin_lock_init(&inst->inst_lock);
+ mutex_init(&inst->lock);
+ mutex_init(&inst->vb2_lock);
INIT_LIST_HEAD(&inst->input_mem_list);
INIT_LIST_HEAD(&inst->minfo_list);
@@ -1530,6 +1547,8 @@
wfd_stats_deinit(&inst->stats);
v4l2_fh_del(&inst->event_handler);
+ mutex_destroy(&inst->lock);
+ mutex_destroy(&inst->vb2_lock);
kfree(inst);
}
@@ -1545,12 +1564,19 @@
unsigned int wfd_poll(struct file *filp, struct poll_table_struct *pt)
{
struct wfd_inst *inst = file_to_inst(filp);
- unsigned int flags = 0;
+ unsigned long flags = 0;
+ bool streamoff = false;
poll_wait(filp, &inst->event_handler.wait, pt);
+ mutex_lock(&inst->lock);
+ streamoff = inst->streamoff;
+ mutex_unlock(&inst->lock);
+
if (v4l2_event_pending(&inst->event_handler))
flags |= POLLPRI;
+ if (streamoff)
+ flags |= POLLERR;
return flags;
}
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
index 153552d..d673713 100644
--- a/drivers/media/radio/radio-iris.c
+++ b/drivers/media/radio/radio-iris.c
@@ -3790,6 +3790,27 @@
if (retval < 0)
FMDERR("set CF0 Threshold failed\n");
break;
+ case V4L2_CID_PRIVATE_RXREPEATCOUNT:
+ rd.mode = RDS_PS0_XFR_MODE;
+ rd.length = RDS_PS0_LEN;
+ rd.param_len = 0;
+ rd.param = 0;
+
+ retval = hci_def_data_read(&rd, radio->fm_hdev);
+ if (retval < 0) {
+ FMDERR("default data read failed for PS0 %x", retval);
+ return retval;
+ }
+ wrd.mode = RDS_PS0_XFR_MODE;
+ wrd.length = RDS_PS0_LEN;
+ memcpy(&wrd.data, &radio->default_data.data,
+ radio->default_data.ret_data_len);
+ wrd.data[RX_REPEATE_BYTE_OFFSET] = ctrl->value;
+
+ retval = hci_def_data_write(&wrd, radio->fm_hdev);
+ if (retval < 0)
+ FMDERR("set RxRePeat count failed\n");
+ break;
default:
retval = -EINVAL;
}
diff --git a/drivers/media/radio/radio-tavarua.c b/drivers/media/radio/radio-tavarua.c
index 0c5534c..ea7032b 100644
--- a/drivers/media/radio/radio-tavarua.c
+++ b/drivers/media/radio/radio-tavarua.c
@@ -1001,9 +1001,15 @@
FMDBG("Search list has %d stations\n",
radio->registers[XFRCTRL+1]);
radio->xfr_bytes_left = radio->registers[XFRCTRL+1]*2;
- if (radio->xfr_bytes_left > 14) {
+ if (!radio->registers[XFRCTRL+1]) {
copy_from_xfr(radio, TAVARUA_BUF_SRCH_LIST,
- XFR_REG_NUM);
+ 1);
+ tavarua_q_event(radio,
+ TAVARUA_EVT_NEW_SRCH_LIST);
+ radio->xfr_in_progress = 0;
+ } else if (radio->xfr_bytes_left > 14) {
+ copy_from_xfr(radio, TAVARUA_BUF_SRCH_LIST,
+ RX_STATIONS0_LEN);
request_read_xfr(radio, RX_STATIONS_1);
} else if (radio->xfr_bytes_left) {
FMDBG("In else RX_STATIONS_0\n");
diff --git a/drivers/mfd/pm8xxx-pwm.c b/drivers/mfd/pm8xxx-pwm.c
index caaba81..0388484 100644
--- a/drivers/mfd/pm8xxx-pwm.c
+++ b/drivers/mfd/pm8xxx-pwm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, 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
@@ -58,10 +58,11 @@
#define PM8XXX_PWM_DISABLE 0x3F
/* PM8XXX LPG PWM */
+#define SSBI_REG_ADDR_LPG_BANK_LOW_EN 0x130
#define SSBI_REG_ADDR_LPG_CTL_BASE 0x13C
#define SSBI_REG_ADDR_LPG_CTL(n) (SSBI_REG_ADDR_LPG_CTL_BASE + (n))
#define SSBI_REG_ADDR_LPG_BANK_SEL 0x143
-#define SSBI_REG_ADDR_LPG_BANK_EN 0x144
+#define SSBI_REG_ADDR_LPG_BANK_HIGH_EN 0x144
#define SSBI_REG_ADDR_LPG_LUT_CFG0 0x145
#define SSBI_REG_ADDR_LPG_LUT_CFG1 0x146
#define SSBI_REG_ADDR_LPG_TEST 0x147
@@ -206,13 +207,15 @@
struct pm8xxx_pwm_chip *chip;
int bypass_lut;
int dtest_mode_supported;
+ int banks;
};
struct pm8xxx_pwm_chip {
struct pwm_device *pwm_dev;
u8 pwm_channels;
u8 pwm_total_pre_divs;
- u8 bank_mask;
+ u8 lo_bank_mask;
+ u8 hi_bank_mask;
struct mutex pwm_mutex;
struct device *dev;
bool is_lpg_supported;
@@ -259,18 +262,37 @@
chip = pwm->chip;
- if (enable)
- reg = chip->bank_mask | (1 << pwm->pwm_id);
- else
- reg = chip->bank_mask & ~(1 << pwm->pwm_id);
+ if (pwm->banks & PM_PWM_BANK_LO) {
+ if (enable)
+ reg = chip->lo_bank_mask | (1 << pwm->pwm_id);
+ else
+ reg = chip->lo_bank_mask & ~(1 << pwm->pwm_id);
- rc = pm8xxx_writeb(chip->dev->parent, SSBI_REG_ADDR_LPG_BANK_EN, reg);
- if (rc) {
- pr_err("pm8xxx_writeb(): rc=%d (Enable LPG Bank)\n", rc);
- return rc;
+ rc = pm8xxx_writeb(chip->dev->parent,
+ SSBI_REG_ADDR_LPG_BANK_LOW_EN, reg);
+ if (rc) {
+ pr_err("pm8xxx_writeb(): Enable Bank Low =%d\n", rc);
+ return rc;
+ }
+
+ chip->lo_bank_mask = reg;
}
- chip->bank_mask = reg;
+ if (pwm->banks & PM_PWM_BANK_HI) {
+ if (enable)
+ reg = chip->hi_bank_mask | (1 << pwm->pwm_id);
+ else
+ reg = chip->hi_bank_mask & ~(1 << pwm->pwm_id);
+
+ rc = pm8xxx_writeb(chip->dev->parent,
+ SSBI_REG_ADDR_LPG_BANK_HIGH_EN, reg);
+ if (rc) {
+ pr_err("pm8xxx_writeb(): Enable Bank High =%d\n", rc);
+ return rc;
+ }
+
+ chip->hi_bank_mask = reg;
+ }
return 0;
}
@@ -1010,6 +1032,16 @@
period = &pwm->period;
mutex_lock(&pwm->chip->pwm_mutex);
+ if (flags & PM_PWM_BANK_HI)
+ pwm->banks = PM_PWM_BANK_HI;
+
+ if (flags & PM_PWM_BANK_LO)
+ pwm->banks |= PM_PWM_BANK_LO;
+
+ /*Enable both banks if banks information is not shared.*/
+ if (!pwm->banks)
+ pwm->banks |= (PM_PWM_BANK_LO | PM_PWM_BANK_HI);
+
if (!pwm->in_use) {
pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
rc = -EINVAL;
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index 046faac..3d0abce 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -70,8 +70,7 @@
static int wcd9xxx_read(struct wcd9xxx *wcd9xxx, unsigned short reg,
int bytes, void *dest, bool interface_reg)
{
- int ret;
- u8 *buf = dest;
+ int i, ret;
if (bytes <= 0) {
dev_err(wcd9xxx->dev, "Invalid byte read length %d\n", bytes);
@@ -82,9 +81,11 @@
if (ret < 0) {
dev_err(wcd9xxx->dev, "Codec read failed\n");
return ret;
- } else
- dev_dbg(wcd9xxx->dev, "Read 0x%02x from 0x%x\n",
- *buf, reg);
+ } else {
+ for (i = 0; i < bytes; i++)
+ dev_dbg(wcd9xxx->dev, "Read 0x%02x from 0x%x\n",
+ ((u8 *)dest)[i], reg + i);
+ }
return 0;
}
@@ -107,15 +108,16 @@
static int wcd9xxx_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
int bytes, void *src, bool interface_reg)
{
- u8 *buf = src;
+ int i;
if (bytes <= 0) {
pr_err("%s: Error, invalid write length\n", __func__);
return -EINVAL;
}
- dev_dbg(wcd9xxx->dev, "Write %02x to 0x%x\n",
- *buf, reg);
+ for (i = 0; i < bytes; i++)
+ dev_dbg(wcd9xxx->dev, "Write %02x to 0x%x\n", ((u8 *)src)[i],
+ reg + i);
return wcd9xxx->write_dev(wcd9xxx, reg, bytes, src, interface_reg);
}
@@ -281,14 +283,6 @@
},
};
-
-enum wcd9xxx_chipid_major {
- TABLA_MAJOR = cpu_to_le16(0x100),
- SITAR_MAJOR = cpu_to_le16(0x101),
- TAIKO_MAJOR = cpu_to_le16(0x102),
- TAPAN_MAJOR = cpu_to_le16(0x103),
-};
-
static const struct wcd9xxx_codec_type wcd9xxx_codecs[] = {
{
TABLA_MAJOR, cpu_to_le16(0x1), tabla1x_devs,
@@ -1147,6 +1141,10 @@
(of_property_read_bool(dev->of_node, "qcom,cdc-micbias4-ext-cap") ?
MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+ micbias->bias2_is_headset_only =
+ of_property_read_bool(dev->of_node,
+ "qcom,cdc-micbias2-headset-only");
+
dev_dbg(dev, "ldoh_v %u cfilt1_mv %u cfilt2_mv %u cfilt3_mv %u",
(u32)micbias->ldoh_v, (u32)micbias->cfilt1_mv,
(u32)micbias->cfilt2_mv, (u32)micbias->cfilt3_mv);
@@ -1162,6 +1160,8 @@
dev_dbg(dev, "bias3_ext_cap %d bias4_ext_cap %d\n",
micbias->bias3_cap_mode, micbias->bias4_cap_mode);
+ dev_dbg(dev, "bias2_is_headset_only %d\n",
+ micbias->bias2_is_headset_only);
return 0;
}
@@ -1721,6 +1721,7 @@
.id_table = tapan_slimtest_id,
.resume = wcd9xxx_slim_resume,
.suspend = wcd9xxx_slim_suspend,
+ .device_up = wcd9xxx_slim_device_up,
};
static struct i2c_device_id wcd9xxx_id_table[] = {
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
index 356aecb..062351d 100644
--- a/drivers/mfd/wcd9xxx-irq.c
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -25,6 +25,7 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
+#include <linux/ratelimit.h>
#include <mach/cpuidle.h>
#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE))
@@ -192,10 +193,24 @@
mutex_unlock(&wcd9xxx->nested_irq_lock);
}
-static void wcd9xxx_irq_dispatch(struct wcd9xxx *wcd9xxx, int irqbit)
+static bool wcd9xxx_is_mbhc_irq(struct wcd9xxx *wcd9xxx, int irqbit)
{
if ((irqbit <= WCD9XXX_IRQ_MBHC_INSERTION) &&
- (irqbit >= WCD9XXX_IRQ_MBHC_REMOVAL)) {
+ (irqbit >= WCD9XXX_IRQ_MBHC_REMOVAL))
+ return true;
+ else if (wcd9xxx->codec_type->id_major == TAIKO_MAJOR &&
+ irqbit == WCD9320_IRQ_MBHC_JACK_SWITCH)
+ return true;
+ else if (wcd9xxx->codec_type->id_major == TAPAN_MAJOR &&
+ irqbit == WCD9306_IRQ_MBHC_JACK_SWITCH)
+ return true;
+ else
+ return false;
+}
+
+static void wcd9xxx_irq_dispatch(struct wcd9xxx *wcd9xxx, int irqbit)
+{
+ if (wcd9xxx_is_mbhc_irq(wcd9xxx, irqbit)) {
wcd9xxx_nested_irq_lock(wcd9xxx);
wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0 +
BIT_BYTE(irqbit),
@@ -226,9 +241,11 @@
{
int ret;
int i;
+ char linebuf[128];
struct wcd9xxx *wcd9xxx = data;
int num_irq_regs = wcd9xxx_num_irq_regs(wcd9xxx);
- u8 status[num_irq_regs];
+ u8 status[num_irq_regs], status1[num_irq_regs];
+ static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 1);
if (unlikely(wcd9xxx_lock_sleep(wcd9xxx) == false)) {
dev_err(wcd9xxx->dev, "Failed to hold suspend\n");
@@ -250,12 +267,17 @@
for (i = 0; i < num_irq_regs; i++)
status[i] &= ~wcd9xxx->irq_masks_cur[i];
+ memcpy(status1, status, sizeof(status1));
+
/* Find out which interrupt was triggered and call that interrupt's
* handler function
*/
if (status[BIT_BYTE(WCD9XXX_IRQ_SLIMBUS)] &
- BYTE_BIT_MASK(WCD9XXX_IRQ_SLIMBUS))
+ BYTE_BIT_MASK(WCD9XXX_IRQ_SLIMBUS)) {
wcd9xxx_irq_dispatch(wcd9xxx, WCD9XXX_IRQ_SLIMBUS);
+ status1[BIT_BYTE(WCD9XXX_IRQ_SLIMBUS)] &=
+ ~BYTE_BIT_MASK(WCD9XXX_IRQ_SLIMBUS);
+ }
/* Since codec has only one hardware irq line which is shared by
* codec's different internal interrupts, so it's possible master irq
@@ -264,13 +286,43 @@
* machine's order */
for (i = WCD9XXX_IRQ_MBHC_INSERTION;
i >= WCD9XXX_IRQ_MBHC_REMOVAL; i--) {
- if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
+ if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i)) {
wcd9xxx_irq_dispatch(wcd9xxx, i);
+ status1[BIT_BYTE(i)] &= ~BYTE_BIT_MASK(i);
+ }
}
for (i = WCD9XXX_IRQ_BG_PRECHARGE; i < wcd9xxx->codec_type->num_irqs;
i++) {
- if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
+ if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i)) {
wcd9xxx_irq_dispatch(wcd9xxx, i);
+ status1[BIT_BYTE(i)] &= ~BYTE_BIT_MASK(i);
+ }
+ }
+
+ /*
+ * As a failsafe if unhandled irq is found, clear it to prevent
+ * interrupt storm.
+ * Note that we can say there was an unhandled irq only when no irq
+ * handled by nested irq handler since Taiko supports qdsp as irqs'
+ * destination for few irqs. Therefore driver shouldn't clear pending
+ * irqs when few handled while few others not.
+ */
+ if (unlikely(!memcmp(status, status1, sizeof(status)))) {
+ if (__ratelimit(&ratelimit)) {
+ pr_warn("%s: Unhandled irq found\n", __func__);
+ hex_dump_to_buffer(status, sizeof(status), 16, 1,
+ linebuf, sizeof(linebuf), false);
+ pr_warn("%s: status0 : %s\n", __func__, linebuf);
+ hex_dump_to_buffer(status1, sizeof(status1), 16, 1,
+ linebuf, sizeof(linebuf), false);
+ pr_warn("%s: status1 : %s\n", __func__, linebuf);
+ }
+
+ memset(status, 0xff, num_irq_regs);
+ wcd9xxx_bulk_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0,
+ num_irq_regs, status);
+ if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02);
}
wcd9xxx_unlock_sleep(wcd9xxx);
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 2573a16..a7932ba 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -51,10 +51,6 @@
To compile this driver as a module, choose M here: the
module will be called ad525x_dpot-spi.
-config ANDROID_PMEM
- bool "Android pmem allocator"
- default y
-
config ATMEL_PWM
tristate "Atmel AT32/AT91 PWM support"
depends on HAVE_CLK
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 327d1ec..760f768 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -19,7 +19,6 @@
obj-$(CONFIG_SENSORS_BH1780) += bh1780gli.o
obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o
obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o
-obj-$(CONFIG_ANDROID_PMEM) += pmem.o
obj-$(CONFIG_SGI_IOC4) += ioc4.o
obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
obj-$(CONFIG_KGDB_TESTS) += kgdbts.o
diff --git a/drivers/misc/pmem.c b/drivers/misc/pmem.c
deleted file mode 100644
index e91db7a..0000000
--- a/drivers/misc/pmem.c
+++ /dev/null
@@ -1,2961 +0,0 @@
-/* drivers/android/pmem.c
- *
- * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/export.h>
-#include <linux/miscdevice.h>
-#include <linux/platform_device.h>
-#include <linux/fs.h>
-#include <linux/file.h>
-#include <linux/fmem.h>
-#include <linux/mm.h>
-#include <linux/list.h>
-#include <linux/debugfs.h>
-#include <linux/android_pmem.h>
-#include <linux/mempolicy.h>
-#include <linux/sched.h>
-#include <linux/kobject.h>
-#include <linux/pm_runtime.h>
-#include <linux/memory_alloc.h>
-#include <linux/vmalloc.h>
-#include <linux/io.h>
-#include <linux/mm_types.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/cacheflush.h>
-#include <asm/sizes.h>
-#include <asm/mach/map.h>
-#include <asm/page.h>
-
-#define PMEM_MAX_DEVICES (10)
-
-#define PMEM_MAX_ORDER (128)
-#define PMEM_MIN_ALLOC PAGE_SIZE
-
-#define PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS (64)
-
-#define PMEM_32BIT_WORD_ORDER (5)
-#define PMEM_BITS_PER_WORD_MASK (BITS_PER_LONG - 1)
-
-#ifdef CONFIG_ANDROID_PMEM_DEBUG
-#define PMEM_DEBUG 1
-#else
-#define PMEM_DEBUG 0
-#endif
-
-#define SYSTEM_ALLOC_RETRY 10
-
-/* indicates that a refernce to this file has been taken via get_pmem_file,
- * the file should not be released until put_pmem_file is called */
-#define PMEM_FLAGS_BUSY 0x1
-/* indicates that this is a suballocation of a larger master range */
-#define PMEM_FLAGS_CONNECTED 0x1 << 1
-/* indicates this is a master and not a sub allocation and that it is mmaped */
-#define PMEM_FLAGS_MASTERMAP 0x1 << 2
-/* submap and unsubmap flags indicate:
- * 00: subregion has never been mmaped
- * 10: subregion has been mmaped, reference to the mm was taken
- * 11: subretion has ben released, refernece to the mm still held
- * 01: subretion has been released, reference to the mm has been released
- */
-#define PMEM_FLAGS_SUBMAP 0x1 << 3
-#define PMEM_FLAGS_UNSUBMAP 0x1 << 4
-
-struct pmem_data {
- /* in alloc mode: an index into the bitmap
- * in no_alloc mode: the size of the allocation */
- int index;
- /* see flags above for descriptions */
- unsigned int flags;
- /* protects this data field, if the mm_mmap sem will be held at the
- * same time as this sem, the mm sem must be taken first (as this is
- * the order for vma_open and vma_close ops */
- struct rw_semaphore sem;
- /* info about the mmaping process */
- struct vm_area_struct *vma;
- /* task struct of the mapping process */
- struct task_struct *task;
- /* process id of teh mapping process */
- pid_t pid;
- /* file descriptor of the master */
- int master_fd;
- /* file struct of the master */
- struct file *master_file;
- /* a list of currently available regions if this is a suballocation */
- struct list_head region_list;
- /* a linked list of data so we can access them for debugging */
- struct list_head list;
-#if PMEM_DEBUG
- int ref;
-#endif
-};
-
-struct pmem_bits {
- unsigned allocated:1; /* 1 if allocated, 0 if free */
- unsigned order:7; /* size of the region in pmem space */
-};
-
-struct pmem_region_node {
- struct pmem_region region;
- struct list_head list;
-};
-
-#define PMEM_DEBUG_MSGS 0
-#if PMEM_DEBUG_MSGS
-#define DLOG(fmt,args...) \
- do { pr_debug("[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \
- ##args); } \
- while (0)
-#else
-#define DLOG(x...) do {} while (0)
-#endif
-
-enum pmem_align {
- PMEM_ALIGN_4K,
- PMEM_ALIGN_1M,
-};
-
-#define PMEM_NAME_SIZE 16
-
-struct alloc_list {
- void *addr; /* physical addr of allocation */
- void *aaddr; /* aligned physical addr */
- unsigned int size; /* total size of allocation */
- unsigned char __iomem *vaddr; /* Virtual addr */
- struct list_head allocs;
-};
-
-struct pmem_info {
- struct miscdevice dev;
- /* physical start address of the remaped pmem space */
- unsigned long base;
- /* vitual start address of the remaped pmem space */
- unsigned char __iomem *vbase;
- /* total size of the pmem space */
- unsigned long size;
- /* number of entries in the pmem space */
- unsigned long num_entries;
- /* pfn of the garbage page in memory */
- unsigned long garbage_pfn;
- /* which memory type (i.e. SMI, EBI1) this PMEM device is backed by */
- unsigned memory_type;
-
- char name[PMEM_NAME_SIZE];
-
- /* index of the garbage page in the pmem space */
- int garbage_index;
- /* reserved virtual address range */
- struct vm_struct *area;
-
- enum pmem_allocator_type allocator_type;
-
- int (*allocate)(const int,
- const unsigned long,
- const unsigned int);
- int (*free)(int, int);
- int (*free_space)(int, struct pmem_freespace *);
- unsigned long (*len)(int, struct pmem_data *);
- unsigned long (*start_addr)(int, struct pmem_data *);
-
- /* actual size of memory element, e.g.: (4 << 10) is 4K */
- unsigned int quantum;
-
- /* indicates maps of this region should be cached, if a mix of
- * cached and uncached is desired, set this and open the device with
- * O_SYNC to get an uncached region */
- unsigned cached;
- unsigned buffered;
- union {
- struct {
- /* in all_or_nothing allocator mode the first mapper
- * gets the whole space and sets this flag */
- unsigned allocated;
- } all_or_nothing;
-
- struct {
- /* the buddy allocator bitmap for the region
- * indicating which entries are allocated and which
- * are free.
- */
-
- struct pmem_bits *buddy_bitmap;
- } buddy_bestfit;
-
- struct {
- unsigned int bitmap_free; /* # of zero bits/quanta */
- uint32_t *bitmap;
- int32_t bitmap_allocs;
- struct {
- short bit;
- unsigned short quanta;
- } *bitm_alloc;
- } bitmap;
-
- struct {
- unsigned long used; /* Bytes currently allocated */
- struct list_head alist; /* List of allocations */
- } system_mem;
- } allocator;
-
- int id;
- struct kobject kobj;
-
- /* for debugging, creates a list of pmem file structs, the
- * data_list_mutex should be taken before pmem_data->sem if both are
- * needed */
- struct mutex data_list_mutex;
- struct list_head data_list;
- /* arena_mutex protects the global allocation arena
- *
- * IF YOU TAKE BOTH LOCKS TAKE THEM IN THIS ORDER:
- * down(pmem_data->sem) => mutex_lock(arena_mutex)
- */
- struct mutex arena_mutex;
-
- long (*ioctl)(struct file *, unsigned int, unsigned long);
- int (*release)(struct inode *, struct file *);
- /* reference count of allocations */
- atomic_t allocation_cnt;
- /*
- * request function for a region when the allocation count goes
- * from 0 -> 1
- */
- int (*mem_request)(void *);
- /*
- * release function for a region when the allocation count goes
- * from 1 -> 0
- */
- int (*mem_release)(void *);
- /*
- * private data for the request/release callback
- */
- void *region_data;
- /*
- * map and unmap as needed
- */
- int map_on_demand;
- /*
- * memory will be reused through fmem
- */
- int reusable;
-};
-#define to_pmem_info_id(a) (container_of(a, struct pmem_info, kobj)->id)
-
-static void ioremap_pmem(int id);
-static void pmem_put_region(int id);
-static int pmem_get_region(int id);
-
-static struct pmem_info pmem[PMEM_MAX_DEVICES];
-static int id_count;
-
-#define PMEM_SYSFS_DIR_NAME "pmem_regions" /* under /sys/kernel/ */
-static struct kset *pmem_kset;
-
-#define PMEM_IS_FREE_BUDDY(id, index) \
- (!(pmem[id].allocator.buddy_bestfit.buddy_bitmap[index].allocated))
-#define PMEM_BUDDY_ORDER(id, index) \
- (pmem[id].allocator.buddy_bestfit.buddy_bitmap[index].order)
-#define PMEM_BUDDY_INDEX(id, index) \
- (index ^ (1 << PMEM_BUDDY_ORDER(id, index)))
-#define PMEM_BUDDY_NEXT_INDEX(id, index) \
- (index + (1 << PMEM_BUDDY_ORDER(id, index)))
-#define PMEM_OFFSET(index) (index * pmem[id].quantum)
-#define PMEM_START_ADDR(id, index) \
- (PMEM_OFFSET(index) + pmem[id].base)
-#define PMEM_BUDDY_LEN(id, index) \
- ((1 << PMEM_BUDDY_ORDER(id, index)) * pmem[id].quantum)
-#define PMEM_END_ADDR(id, index) \
- (PMEM_START_ADDR(id, index) + PMEM_LEN(id, index))
-#define PMEM_START_VADDR(id, index) \
- (PMEM_OFFSET(id, index) + pmem[id].vbase)
-#define PMEM_END_VADDR(id, index) \
- (PMEM_START_VADDR(id, index) + PMEM_LEN(id, index))
-#define PMEM_REVOKED(data) (data->flags & PMEM_FLAGS_REVOKED)
-#define PMEM_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK)))
-#define PMEM_IS_SUBMAP(data) \
- ((data->flags & PMEM_FLAGS_SUBMAP) && \
- (!(data->flags & PMEM_FLAGS_UNSUBMAP)))
-
-static int pmem_release(struct inode *, struct file *);
-static int pmem_mmap(struct file *, struct vm_area_struct *);
-static int pmem_open(struct inode *, struct file *);
-static long pmem_ioctl(struct file *, unsigned int, unsigned long);
-
-struct file_operations pmem_fops = {
- .release = pmem_release,
- .mmap = pmem_mmap,
- .open = pmem_open,
- .unlocked_ioctl = pmem_ioctl,
-};
-
-#define PMEM_ATTR(_name, _mode, _show, _store) { \
- .attr = {.name = __stringify(_name), .mode = _mode }, \
- .show = _show, \
- .store = _store, \
-}
-
-struct pmem_attr {
- struct attribute attr;
- ssize_t(*show) (const int id, char * const);
- ssize_t(*store) (const int id, const char * const, const size_t count);
-};
-#define to_pmem_attr(a) container_of(a, struct pmem_attr, attr)
-
-#define RW_PMEM_ATTR(name) \
-static struct pmem_attr pmem_attr_## name = \
- PMEM_ATTR(name, S_IRUGO | S_IWUSR, show_pmem_## name, store_pmem_## name)
-
-#define RO_PMEM_ATTR(name) \
-static struct pmem_attr pmem_attr_## name = \
- PMEM_ATTR(name, S_IRUGO, show_pmem_## name, NULL)
-
-#define WO_PMEM_ATTR(name) \
-static struct pmem_attr pmem_attr_## name = \
- PMEM_ATTR(name, S_IWUSR, NULL, store_pmem_## name)
-
-static ssize_t show_pmem(struct kobject *kobj,
- struct attribute *attr,
- char *buf)
-{
- struct pmem_attr *a = to_pmem_attr(attr);
- return a->show ? a->show(to_pmem_info_id(kobj), buf) : -EIO;
-}
-
-static ssize_t store_pmem(struct kobject *kobj, struct attribute *attr,
- const char *buf, size_t count)
-{
- struct pmem_attr *a = to_pmem_attr(attr);
- return a->store ? a->store(to_pmem_info_id(kobj), buf, count) : -EIO;
-}
-
-static struct sysfs_ops pmem_ops = {
- .show = show_pmem,
- .store = store_pmem,
-};
-
-static ssize_t show_pmem_base(int id, char *buf)
-{
- return scnprintf(buf, PAGE_SIZE, "%lu(%#lx)\n",
- pmem[id].base, pmem[id].base);
-}
-RO_PMEM_ATTR(base);
-
-static ssize_t show_pmem_size(int id, char *buf)
-{
- return scnprintf(buf, PAGE_SIZE, "%lu(%#lx)\n",
- pmem[id].size, pmem[id].size);
-}
-RO_PMEM_ATTR(size);
-
-static ssize_t show_pmem_allocator_type(int id, char *buf)
-{
- switch (pmem[id].allocator_type) {
- case PMEM_ALLOCATORTYPE_ALLORNOTHING:
- return scnprintf(buf, PAGE_SIZE, "%s\n", "All or Nothing");
- case PMEM_ALLOCATORTYPE_BUDDYBESTFIT:
- return scnprintf(buf, PAGE_SIZE, "%s\n", "Buddy Bestfit");
- case PMEM_ALLOCATORTYPE_BITMAP:
- return scnprintf(buf, PAGE_SIZE, "%s\n", "Bitmap");
- case PMEM_ALLOCATORTYPE_SYSTEM:
- return scnprintf(buf, PAGE_SIZE, "%s\n", "System heap");
- default:
- return scnprintf(buf, PAGE_SIZE,
- "??? Invalid allocator type (%d) for this region! "
- "Something isn't right.\n",
- pmem[id].allocator_type);
- }
-}
-RO_PMEM_ATTR(allocator_type);
-
-static ssize_t show_pmem_mapped_regions(int id, char *buf)
-{
- struct list_head *elt;
- int ret;
-
- ret = scnprintf(buf, PAGE_SIZE,
- "pid #: mapped regions (offset, len) (offset,len)...\n");
-
- mutex_lock(&pmem[id].data_list_mutex);
- list_for_each(elt, &pmem[id].data_list) {
- struct pmem_data *data =
- list_entry(elt, struct pmem_data, list);
- struct list_head *elt2;
-
- down_read(&data->sem);
- ret += scnprintf(buf + ret, PAGE_SIZE - ret, "pid %u:",
- data->pid);
- list_for_each(elt2, &data->region_list) {
- struct pmem_region_node *region_node = list_entry(elt2,
- struct pmem_region_node,
- list);
- ret += scnprintf(buf + ret, PAGE_SIZE - ret,
- "(%lx,%lx) ",
- region_node->region.offset,
- region_node->region.len);
- }
- up_read(&data->sem);
- ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
- }
- mutex_unlock(&pmem[id].data_list_mutex);
- return ret;
-}
-RO_PMEM_ATTR(mapped_regions);
-
-#define PMEM_COMMON_SYSFS_ATTRS \
- &pmem_attr_base.attr, \
- &pmem_attr_size.attr, \
- &pmem_attr_allocator_type.attr, \
- &pmem_attr_mapped_regions.attr
-
-
-static ssize_t show_pmem_allocated(int id, char *buf)
-{
- ssize_t ret;
-
- mutex_lock(&pmem[id].arena_mutex);
- ret = scnprintf(buf, PAGE_SIZE, "%s\n",
- pmem[id].allocator.all_or_nothing.allocated ?
- "is allocated" : "is NOT allocated");
- mutex_unlock(&pmem[id].arena_mutex);
- return ret;
-}
-RO_PMEM_ATTR(allocated);
-
-static struct attribute *pmem_allornothing_attrs[] = {
- PMEM_COMMON_SYSFS_ATTRS,
-
- &pmem_attr_allocated.attr,
-
- NULL
-};
-
-static struct kobj_type pmem_allornothing_ktype = {
- .sysfs_ops = &pmem_ops,
- .default_attrs = pmem_allornothing_attrs,
-};
-
-static ssize_t show_pmem_total_entries(int id, char *buf)
-{
- return scnprintf(buf, PAGE_SIZE, "%lu\n", pmem[id].num_entries);
-}
-RO_PMEM_ATTR(total_entries);
-
-static ssize_t show_pmem_quantum_size(int id, char *buf)
-{
- return scnprintf(buf, PAGE_SIZE, "%u (%#x)\n",
- pmem[id].quantum, pmem[id].quantum);
-}
-RO_PMEM_ATTR(quantum_size);
-
-static ssize_t show_pmem_buddy_bitmap_dump(int id, char *buf)
-{
- int ret, i;
-
- mutex_lock(&pmem[id].data_list_mutex);
- ret = scnprintf(buf, PAGE_SIZE, "index\torder\tlength\tallocated\n");
-
- for (i = 0; i < pmem[id].num_entries && (PAGE_SIZE - ret);
- i = PMEM_BUDDY_NEXT_INDEX(id, i))
- ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%d\t%d\t%d\t%d\n",
- i, PMEM_BUDDY_ORDER(id, i),
- PMEM_BUDDY_LEN(id, i),
- !PMEM_IS_FREE_BUDDY(id, i));
-
- mutex_unlock(&pmem[id].data_list_mutex);
- return ret;
-}
-RO_PMEM_ATTR(buddy_bitmap_dump);
-
-#define PMEM_BITMAP_BUDDY_BESTFIT_COMMON_SYSFS_ATTRS \
- &pmem_attr_quantum_size.attr, \
- &pmem_attr_total_entries.attr
-
-static struct attribute *pmem_buddy_bestfit_attrs[] = {
- PMEM_COMMON_SYSFS_ATTRS,
-
- PMEM_BITMAP_BUDDY_BESTFIT_COMMON_SYSFS_ATTRS,
-
- &pmem_attr_buddy_bitmap_dump.attr,
-
- NULL
-};
-
-static struct kobj_type pmem_buddy_bestfit_ktype = {
- .sysfs_ops = &pmem_ops,
- .default_attrs = pmem_buddy_bestfit_attrs,
-};
-
-static ssize_t show_pmem_free_quanta(int id, char *buf)
-{
- ssize_t ret;
-
- mutex_lock(&pmem[id].arena_mutex);
- ret = scnprintf(buf, PAGE_SIZE, "%u\n",
- pmem[id].allocator.bitmap.bitmap_free);
- mutex_unlock(&pmem[id].arena_mutex);
- return ret;
-}
-RO_PMEM_ATTR(free_quanta);
-
-static ssize_t show_pmem_bits_allocated(int id, char *buf)
-{
- ssize_t ret;
- unsigned int i;
-
- mutex_lock(&pmem[id].arena_mutex);
-
- ret = scnprintf(buf, PAGE_SIZE,
- "id: %d\nbitnum\tindex\tquanta allocated\n", id);
-
- for (i = 0; i < pmem[id].allocator.bitmap.bitmap_allocs; i++)
- if (pmem[id].allocator.bitmap.bitm_alloc[i].bit != -1)
- ret += scnprintf(buf + ret, PAGE_SIZE - ret,
- "%u\t%u\t%u\n",
- i,
- pmem[id].allocator.bitmap.bitm_alloc[i].bit,
- pmem[id].allocator.bitmap.bitm_alloc[i].quanta
- );
-
- mutex_unlock(&pmem[id].arena_mutex);
- return ret;
-}
-RO_PMEM_ATTR(bits_allocated);
-
-static struct attribute *pmem_bitmap_attrs[] = {
- PMEM_COMMON_SYSFS_ATTRS,
-
- PMEM_BITMAP_BUDDY_BESTFIT_COMMON_SYSFS_ATTRS,
-
- &pmem_attr_free_quanta.attr,
- &pmem_attr_bits_allocated.attr,
-
- NULL
-};
-
-static struct attribute *pmem_system_attrs[] = {
- PMEM_COMMON_SYSFS_ATTRS,
-
- NULL
-};
-
-static struct kobj_type pmem_bitmap_ktype = {
- .sysfs_ops = &pmem_ops,
- .default_attrs = pmem_bitmap_attrs,
-};
-
-static struct kobj_type pmem_system_ktype = {
- .sysfs_ops = &pmem_ops,
- .default_attrs = pmem_system_attrs,
-};
-
-static int pmem_allocate_from_id(const int id, const unsigned long size,
- const unsigned int align)
-{
- int ret;
- ret = pmem_get_region(id);
-
- if (ret)
- return -1;
-
- ret = pmem[id].allocate(id, size, align);
-
- if (ret < 0)
- pmem_put_region(id);
-
- return ret;
-}
-
-static int pmem_free_from_id(const int id, const int index)
-{
- pmem_put_region(id);
- return pmem[id].free(id, index);
-}
-
-static int pmem_get_region(int id)
-{
- /* Must be called with arena mutex locked */
- atomic_inc(&pmem[id].allocation_cnt);
- if (!pmem[id].vbase) {
- DLOG("PMEMDEBUG: mapping for %s", pmem[id].name);
- if (pmem[id].mem_request) {
- int ret = pmem[id].mem_request(pmem[id].region_data);
- if (ret) {
- atomic_dec(&pmem[id].allocation_cnt);
- return 1;
- }
- }
- ioremap_pmem(id);
- }
-
- if (pmem[id].vbase) {
- return 0;
- } else {
- if (pmem[id].mem_release)
- pmem[id].mem_release(pmem[id].region_data);
- atomic_dec(&pmem[id].allocation_cnt);
- return 1;
- }
-}
-
-static void pmem_put_region(int id)
-{
- /* Must be called with arena mutex locked */
- if (atomic_dec_and_test(&pmem[id].allocation_cnt)) {
- DLOG("PMEMDEBUG: unmapping for %s", pmem[id].name);
- BUG_ON(!pmem[id].vbase);
- if (pmem[id].map_on_demand) {
- /* unmap_kernel_range() flushes the caches
- * and removes the page table entries
- */
- unmap_kernel_range((unsigned long)pmem[id].vbase,
- pmem[id].size);
- pmem[id].vbase = NULL;
- if (pmem[id].mem_release) {
- int ret = pmem[id].mem_release(
- pmem[id].region_data);
- WARN(ret, "mem_release failed");
- }
-
- }
- }
-}
-
-static int get_id(struct file *file)
-{
- return MINOR(file->f_dentry->d_inode->i_rdev);
-}
-
-static char *get_name(struct file *file)
-{
- int id = get_id(file);
- return pmem[id].name;
-}
-
-static int is_pmem_file(struct file *file)
-{
- int id;
-
- if (unlikely(!file || !file->f_dentry || !file->f_dentry->d_inode))
- return 0;
-
- id = get_id(file);
- return (unlikely(id >= PMEM_MAX_DEVICES ||
- file->f_dentry->d_inode->i_rdev !=
- MKDEV(MISC_MAJOR, pmem[id].dev.minor))) ? 0 : 1;
-}
-
-static int has_allocation(struct file *file)
-{
- /* must be called with at least read lock held on
- * ((struct pmem_data *)(file->private_data))->sem which
- * means that file is guaranteed not to be NULL upon entry!!
- * check is_pmem_file first if not accessed via pmem_file_ops */
- struct pmem_data *pdata = file->private_data;
- return pdata && pdata->index != -1;
-}
-
-static int is_master_owner(struct file *file)
-{
- struct file *master_file;
- struct pmem_data *data = file->private_data;
- int put_needed, ret = 0;
-
- if (!has_allocation(file))
- return 0;
- if (PMEM_FLAGS_MASTERMAP & data->flags)
- return 1;
- master_file = fget_light(data->master_fd, &put_needed);
- if (master_file && data->master_file == master_file)
- ret = 1;
- if (master_file)
- fput_light(master_file, put_needed);
- return ret;
-}
-
-static int pmem_free_all_or_nothing(int id, int index)
-{
- /* caller should hold the lock on arena_mutex! */
- DLOG("index %d\n", index);
-
- pmem[id].allocator.all_or_nothing.allocated = 0;
- return 0;
-}
-
-static int pmem_free_space_all_or_nothing(int id,
- struct pmem_freespace *fs)
-{
- /* caller should hold the lock on arena_mutex! */
- fs->total = (unsigned long)
- pmem[id].allocator.all_or_nothing.allocated == 0 ?
- pmem[id].size : 0;
-
- fs->largest = fs->total;
- return 0;
-}
-
-
-static int pmem_free_buddy_bestfit(int id, int index)
-{
- /* caller should hold the lock on arena_mutex! */
- int curr = index;
- DLOG("index %d\n", index);
-
-
- /* clean up the bitmap, merging any buddies */
- pmem[id].allocator.buddy_bestfit.buddy_bitmap[curr].allocated = 0;
- /* find a slots buddy Buddy# = Slot# ^ (1 << order)
- * if the buddy is also free merge them
- * repeat until the buddy is not free or end of the bitmap is reached
- */
- do {
- int buddy = PMEM_BUDDY_INDEX(id, curr);
- if (buddy < pmem[id].num_entries &&
- PMEM_IS_FREE_BUDDY(id, buddy) &&
- PMEM_BUDDY_ORDER(id, buddy) ==
- PMEM_BUDDY_ORDER(id, curr)) {
- PMEM_BUDDY_ORDER(id, buddy)++;
- PMEM_BUDDY_ORDER(id, curr)++;
- curr = min(buddy, curr);
- } else {
- break;
- }
- } while (curr < pmem[id].num_entries);
-
- return 0;
-}
-
-
-static int pmem_free_space_buddy_bestfit(int id,
- struct pmem_freespace *fs)
-{
- /* caller should hold the lock on arena_mutex! */
- int curr;
- unsigned long size;
- fs->total = 0;
- fs->largest = 0;
-
- for (curr = 0; curr < pmem[id].num_entries;
- curr = PMEM_BUDDY_NEXT_INDEX(id, curr)) {
- if (PMEM_IS_FREE_BUDDY(id, curr)) {
- size = PMEM_BUDDY_LEN(id, curr);
- if (size > fs->largest)
- fs->largest = size;
- fs->total += size;
- }
- }
- return 0;
-}
-
-
-static inline uint32_t start_mask(int bit_start)
-{
- return (uint32_t)(~0) << (bit_start & PMEM_BITS_PER_WORD_MASK);
-}
-
-static inline uint32_t end_mask(int bit_end)
-{
- return (uint32_t)(~0) >>
- ((BITS_PER_LONG - bit_end) & PMEM_BITS_PER_WORD_MASK);
-}
-
-static inline int compute_total_words(int bit_end, int word_index)
-{
- return ((bit_end + BITS_PER_LONG - 1) >>
- PMEM_32BIT_WORD_ORDER) - word_index;
-}
-
-static void bitmap_bits_clear_all(uint32_t *bitp, int bit_start, int bit_end)
-{
- int word_index = bit_start >> PMEM_32BIT_WORD_ORDER, total_words;
-
- total_words = compute_total_words(bit_end, word_index);
- if (total_words > 0) {
- if (total_words == 1) {
- bitp[word_index] &=
- ~(start_mask(bit_start) & end_mask(bit_end));
- } else {
- bitp[word_index++] &= ~start_mask(bit_start);
- if (total_words > 2) {
- int total_bytes;
-
- total_words -= 2;
- total_bytes = total_words << 2;
-
- memset(&bitp[word_index], 0, total_bytes);
- word_index += total_words;
- }
- bitp[word_index] &= ~end_mask(bit_end);
- }
- }
-}
-
-static int pmem_free_bitmap(int id, int bitnum)
-{
- /* caller should hold the lock on arena_mutex! */
- int i;
- char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
-
- DLOG("bitnum %d\n", bitnum);
-
- for (i = 0; i < pmem[id].allocator.bitmap.bitmap_allocs; i++) {
- const int curr_bit =
- pmem[id].allocator.bitmap.bitm_alloc[i].bit;
-
- if (curr_bit == bitnum) {
- const int curr_quanta =
- pmem[id].allocator.bitmap.bitm_alloc[i].quanta;
-
- bitmap_bits_clear_all(pmem[id].allocator.bitmap.bitmap,
- curr_bit, curr_bit + curr_quanta);
- pmem[id].allocator.bitmap.bitmap_free += curr_quanta;
- pmem[id].allocator.bitmap.bitm_alloc[i].bit = -1;
- pmem[id].allocator.bitmap.bitm_alloc[i].quanta = 0;
- return 0;
- }
- }
- printk(KERN_ALERT "pmem: %s: Attempt to free unallocated index %d, id"
- " %d, pid %d(%s)\n", __func__, bitnum, id, current->pid,
- get_task_comm(currtask_name, current));
-
- return -1;
-}
-
-static int pmem_free_system(int id, int index)
-{
- /* caller should hold the lock on arena_mutex! */
- struct alloc_list *item;
-
- DLOG("index %d\n", index);
- if (index != 0)
- item = (struct alloc_list *)index;
- else
- return 0;
-
- if (item->vaddr != NULL) {
- iounmap(item->vaddr);
- kfree(__va(item->addr));
- list_del(&item->allocs);
- kfree(item);
- }
-
- return 0;
-}
-
-static int pmem_free_space_bitmap(int id, struct pmem_freespace *fs)
-{
- int i, j;
- int max_allocs = pmem[id].allocator.bitmap.bitmap_allocs;
- int alloc_start = 0;
- int next_alloc;
- unsigned long size = 0;
-
- fs->total = 0;
- fs->largest = 0;
-
- for (i = 0; i < max_allocs; i++) {
-
- int alloc_quanta = 0;
- int alloc_idx = 0;
- next_alloc = pmem[id].num_entries;
-
- /* Look for the lowest bit where next allocation starts */
- for (j = 0; j < max_allocs; j++) {
- const int curr_alloc = pmem[id].allocator.
- bitmap.bitm_alloc[j].bit;
- if (curr_alloc != -1) {
- if (alloc_start == curr_alloc)
- alloc_idx = j;
- if (alloc_start >= curr_alloc)
- continue;
- if (curr_alloc < next_alloc)
- next_alloc = curr_alloc;
- }
- }
- alloc_quanta = pmem[id].allocator.bitmap.
- bitm_alloc[alloc_idx].quanta;
- size = (next_alloc - (alloc_start + alloc_quanta)) *
- pmem[id].quantum;
-
- if (size > fs->largest)
- fs->largest = size;
- fs->total += size;
-
- if (next_alloc == pmem[id].num_entries)
- break;
- else
- alloc_start = next_alloc;
- }
-
- return 0;
-}
-
-static int pmem_free_space_system(int id, struct pmem_freespace *fs)
-{
- fs->total = pmem[id].size;
- fs->largest = pmem[id].size;
-
- return 0;
-}
-
-static void pmem_revoke(struct file *file, struct pmem_data *data);
-
-static int pmem_release(struct inode *inode, struct file *file)
-{
- struct pmem_data *data = file->private_data;
- struct pmem_region_node *region_node;
- struct list_head *elt, *elt2;
- int id = get_id(file), ret = 0;
-
-#if PMEM_DEBUG_MSGS
- char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
-#endif
- DLOG("releasing memory pid %u(%s) file %p(%ld) dev %s(id: %d)\n",
- current->pid, get_task_comm(currtask_name, current),
- file, file_count(file), get_name(file), id);
- mutex_lock(&pmem[id].data_list_mutex);
- /* if this file is a master, revoke all the memory in the connected
- * files */
- if (PMEM_FLAGS_MASTERMAP & data->flags) {
- list_for_each(elt, &pmem[id].data_list) {
- struct pmem_data *sub_data =
- list_entry(elt, struct pmem_data, list);
- int is_master;
-
- down_read(&sub_data->sem);
- is_master = (PMEM_IS_SUBMAP(sub_data) &&
- file == sub_data->master_file);
- up_read(&sub_data->sem);
-
- if (is_master)
- pmem_revoke(file, sub_data);
- }
- }
- list_del(&data->list);
- mutex_unlock(&pmem[id].data_list_mutex);
-
- down_write(&data->sem);
-
- /* if it is not a connected file and it has an allocation, free it */
- if (!(PMEM_FLAGS_CONNECTED & data->flags) && has_allocation(file)) {
- mutex_lock(&pmem[id].arena_mutex);
- ret = pmem_free_from_id(id, data->index);
- mutex_unlock(&pmem[id].arena_mutex);
- }
-
- /* if this file is a submap (mapped, connected file), downref the
- * task struct */
- if (PMEM_FLAGS_SUBMAP & data->flags)
- if (data->task) {
- put_task_struct(data->task);
- data->task = NULL;
- }
-
- file->private_data = NULL;
-
- list_for_each_safe(elt, elt2, &data->region_list) {
- region_node = list_entry(elt, struct pmem_region_node, list);
- list_del(elt);
- kfree(region_node);
- }
- BUG_ON(!list_empty(&data->region_list));
-
- up_write(&data->sem);
- kfree(data);
- if (pmem[id].release)
- ret = pmem[id].release(inode, file);
-
- return ret;
-}
-
-static int pmem_open(struct inode *inode, struct file *file)
-{
- struct pmem_data *data;
- int id = get_id(file);
- int ret = 0;
-#if PMEM_DEBUG_MSGS
- char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
-#endif
-
- DLOG("pid %u(%s) file %p(%ld) dev %s(id: %d)\n",
- current->pid, get_task_comm(currtask_name, current),
- file, file_count(file), get_name(file), id);
- data = kmalloc(sizeof(struct pmem_data), GFP_KERNEL);
- if (!data) {
- printk(KERN_ALERT "pmem: %s: unable to allocate memory for "
- "pmem metadata.", __func__);
- return -1;
- }
- data->flags = 0;
- data->index = -1;
- data->task = NULL;
- data->vma = NULL;
- data->pid = 0;
- data->master_file = NULL;
-#if PMEM_DEBUG
- data->ref = 0;
-#endif
- INIT_LIST_HEAD(&data->region_list);
- init_rwsem(&data->sem);
-
- file->private_data = data;
- INIT_LIST_HEAD(&data->list);
-
- mutex_lock(&pmem[id].data_list_mutex);
- list_add(&data->list, &pmem[id].data_list);
- mutex_unlock(&pmem[id].data_list_mutex);
- return ret;
-}
-
-static unsigned long pmem_order(unsigned long len, int id)
-{
- int i;
-
- len = (len + pmem[id].quantum - 1)/pmem[id].quantum;
- len--;
- for (i = 0; i < sizeof(len)*8; i++)
- if (len >> i == 0)
- break;
- return i;
-}
-
-static int pmem_allocator_all_or_nothing(const int id,
- const unsigned long len,
- const unsigned int align)
-{
- /* caller should hold the lock on arena_mutex! */
- DLOG("all or nothing\n");
- if ((len > pmem[id].size) ||
- pmem[id].allocator.all_or_nothing.allocated)
- return -1;
- pmem[id].allocator.all_or_nothing.allocated = 1;
- return len;
-}
-
-static int pmem_allocator_buddy_bestfit(const int id,
- const unsigned long len,
- unsigned int align)
-{
- /* caller should hold the lock on arena_mutex! */
- int curr;
- int best_fit = -1;
- unsigned long order;
-
- DLOG("buddy bestfit\n");
- order = pmem_order(len, id);
- if (order > PMEM_MAX_ORDER)
- goto out;
-
- DLOG("order %lx\n", order);
-
- /* Look through the bitmap.
- * If a free slot of the correct order is found, use it.
- * Otherwise, use the best fit (smallest with size > order) slot.
- */
- for (curr = 0;
- curr < pmem[id].num_entries;
- curr = PMEM_BUDDY_NEXT_INDEX(id, curr))
- if (PMEM_IS_FREE_BUDDY(id, curr)) {
- if (PMEM_BUDDY_ORDER(id, curr) ==
- (unsigned char)order) {
- /* set the not free bit and clear others */
- best_fit = curr;
- break;
- }
- if (PMEM_BUDDY_ORDER(id, curr) >
- (unsigned char)order &&
- (best_fit < 0 ||
- PMEM_BUDDY_ORDER(id, curr) <
- PMEM_BUDDY_ORDER(id, best_fit)))
- best_fit = curr;
- }
-
- /* if best_fit < 0, there are no suitable slots; return an error */
- if (best_fit < 0) {
-#if PMEM_DEBUG
- printk(KERN_ALERT "pmem: %s: no space left to allocate!\n",
- __func__);
-#endif
- goto out;
- }
-
- /* now partition the best fit:
- * split the slot into 2 buddies of order - 1
- * repeat until the slot is of the correct order
- */
- while (PMEM_BUDDY_ORDER(id, best_fit) > (unsigned char)order) {
- int buddy;
- PMEM_BUDDY_ORDER(id, best_fit) -= 1;
- buddy = PMEM_BUDDY_INDEX(id, best_fit);
- PMEM_BUDDY_ORDER(id, buddy) = PMEM_BUDDY_ORDER(id, best_fit);
- }
- pmem[id].allocator.buddy_bestfit.buddy_bitmap[best_fit].allocated = 1;
-out:
- return best_fit;
-}
-
-
-static inline unsigned long paddr_from_bit(const int id, const int bitnum)
-{
- return pmem[id].base + pmem[id].quantum * bitnum;
-}
-
-static inline unsigned long bit_from_paddr(const int id,
- const unsigned long paddr)
-{
- return (paddr - pmem[id].base) / pmem[id].quantum;
-}
-
-static void bitmap_bits_set_all(uint32_t *bitp, int bit_start, int bit_end)
-{
- int word_index = bit_start >> PMEM_32BIT_WORD_ORDER, total_words;
-
- total_words = compute_total_words(bit_end, word_index);
- if (total_words > 0) {
- if (total_words == 1) {
- bitp[word_index] |=
- (start_mask(bit_start) & end_mask(bit_end));
- } else {
- bitp[word_index++] |= start_mask(bit_start);
- if (total_words > 2) {
- int total_bytes;
-
- total_words -= 2;
- total_bytes = total_words << 2;
-
- memset(&bitp[word_index], ~0, total_bytes);
- word_index += total_words;
- }
- bitp[word_index] |= end_mask(bit_end);
- }
- }
-}
-
-static int
-bitmap_allocate_contiguous(uint32_t *bitp, int num_bits_to_alloc,
- int total_bits, int spacing, int start_bit)
-{
- int bit_start, last_bit, word_index;
-
- if (num_bits_to_alloc <= 0)
- return -1;
-
- for (bit_start = start_bit; ;
- bit_start = ((last_bit +
- (word_index << PMEM_32BIT_WORD_ORDER) + spacing - 1)
- & ~(spacing - 1)) + start_bit) {
- int bit_end = bit_start + num_bits_to_alloc, total_words;
-
- if (bit_end > total_bits)
- return -1; /* out of contiguous memory */
-
- word_index = bit_start >> PMEM_32BIT_WORD_ORDER;
- total_words = compute_total_words(bit_end, word_index);
-
- if (total_words <= 0)
- return -1;
-
- if (total_words == 1) {
- last_bit = fls(bitp[word_index] &
- (start_mask(bit_start) &
- end_mask(bit_end)));
- if (last_bit)
- continue;
- } else {
- int end_word = word_index + (total_words - 1);
- last_bit =
- fls(bitp[word_index] & start_mask(bit_start));
- if (last_bit)
- continue;
-
- for (word_index++;
- word_index < end_word;
- word_index++) {
- last_bit = fls(bitp[word_index]);
- if (last_bit)
- break;
- }
- if (last_bit)
- continue;
-
- last_bit = fls(bitp[word_index] & end_mask(bit_end));
- if (last_bit)
- continue;
- }
- bitmap_bits_set_all(bitp, bit_start, bit_end);
- return bit_start;
- }
- return -1;
-}
-
-static int reserve_quanta(const unsigned int quanta_needed,
- const int id,
- unsigned int align)
-{
- /* alignment should be a valid power of 2 */
- int ret = -1, start_bit = 0, spacing = 1;
-
- /* Sanity check */
- if (quanta_needed > pmem[id].allocator.bitmap.bitmap_free) {
-#if PMEM_DEBUG
- printk(KERN_ALERT "pmem: %s: request (%d) too big for"
- " available free (%d)\n", __func__, quanta_needed,
- pmem[id].allocator.bitmap.bitmap_free);
-#endif
- return -1;
- }
-
- start_bit = bit_from_paddr(id,
- (pmem[id].base + align - 1) & ~(align - 1));
- if (start_bit <= -1) {
-#if PMEM_DEBUG
- printk(KERN_ALERT
- "pmem: %s: bit_from_paddr fails for"
- " %u alignment.\n", __func__, align);
-#endif
- return -1;
- }
- spacing = align / pmem[id].quantum;
- spacing = spacing > 1 ? spacing : 1;
-
- ret = bitmap_allocate_contiguous(pmem[id].allocator.bitmap.bitmap,
- quanta_needed,
- (pmem[id].size + pmem[id].quantum - 1) / pmem[id].quantum,
- spacing,
- start_bit);
-
-#if PMEM_DEBUG
- if (ret < 0)
- printk(KERN_ALERT "pmem: %s: not enough contiguous bits free "
- "in bitmap! Region memory is either too fragmented or"
- " request is too large for available memory.\n",
- __func__);
-#endif
-
- return ret;
-}
-
-static int pmem_allocator_bitmap(const int id,
- const unsigned long len,
- const unsigned int align)
-{
- /* caller should hold the lock on arena_mutex! */
- int bitnum, i;
- unsigned int quanta_needed;
-
- DLOG("bitmap id %d, len %ld, align %u\n", id, len, align);
- if (!pmem[id].allocator.bitmap.bitm_alloc) {
-#if PMEM_DEBUG
- printk(KERN_ALERT "pmem: bitm_alloc not present! id: %d\n",
- id);
-#endif
- return -1;
- }
-
- quanta_needed = (len + pmem[id].quantum - 1) / pmem[id].quantum;
- DLOG("quantum size %u quanta needed %u free %u id %d\n",
- pmem[id].quantum, quanta_needed,
- pmem[id].allocator.bitmap.bitmap_free, id);
-
- if (pmem[id].allocator.bitmap.bitmap_free < quanta_needed) {
-#if PMEM_DEBUG
- printk(KERN_ALERT "pmem: memory allocation failure. "
- "PMEM memory region exhausted, id %d."
- " Unable to comply with allocation request.\n", id);
-#endif
- return -1;
- }
-
- bitnum = reserve_quanta(quanta_needed, id, align);
- if (bitnum == -1)
- goto leave;
-
- for (i = 0;
- i < pmem[id].allocator.bitmap.bitmap_allocs &&
- pmem[id].allocator.bitmap.bitm_alloc[i].bit != -1;
- i++)
- ;
-
- if (i >= pmem[id].allocator.bitmap.bitmap_allocs) {
- void *temp;
- int32_t new_bitmap_allocs =
- pmem[id].allocator.bitmap.bitmap_allocs << 1;
- int j;
-
- if (!new_bitmap_allocs) { /* failed sanity check!! */
-#if PMEM_DEBUG
- pr_alert("pmem: bitmap_allocs number"
- " wrapped around to zero! Something "
- "is VERY wrong.\n");
-#endif
- return -1;
- }
-
- if (new_bitmap_allocs > pmem[id].num_entries) {
- /* failed sanity check!! */
-#if PMEM_DEBUG
- pr_alert("pmem: required bitmap_allocs"
- " number exceeds maximum entries possible"
- " for current quanta\n");
-#endif
- return -1;
- }
-
- temp = krealloc(pmem[id].allocator.bitmap.bitm_alloc,
- new_bitmap_allocs *
- sizeof(*pmem[id].allocator.bitmap.bitm_alloc),
- GFP_KERNEL);
- if (!temp) {
-#if PMEM_DEBUG
- pr_alert("pmem: can't realloc bitmap_allocs,"
- "id %d, current num bitmap allocs %d\n",
- id, pmem[id].allocator.bitmap.bitmap_allocs);
-#endif
- return -1;
- }
- pmem[id].allocator.bitmap.bitmap_allocs = new_bitmap_allocs;
- pmem[id].allocator.bitmap.bitm_alloc = temp;
-
- for (j = i; j < new_bitmap_allocs; j++) {
- pmem[id].allocator.bitmap.bitm_alloc[j].bit = -1;
- pmem[id].allocator.bitmap.bitm_alloc[i].quanta = 0;
- }
-
- DLOG("increased # of allocated regions to %d for id %d\n",
- pmem[id].allocator.bitmap.bitmap_allocs, id);
- }
-
- DLOG("bitnum %d, bitm_alloc index %d\n", bitnum, i);
-
- pmem[id].allocator.bitmap.bitmap_free -= quanta_needed;
- pmem[id].allocator.bitmap.bitm_alloc[i].bit = bitnum;
- pmem[id].allocator.bitmap.bitm_alloc[i].quanta = quanta_needed;
-leave:
- return bitnum;
-}
-
-static int pmem_allocator_system(const int id,
- const unsigned long len,
- const unsigned int align)
-{
- /* caller should hold the lock on arena_mutex! */
- struct alloc_list *list;
- unsigned long aligned_len;
- int count = SYSTEM_ALLOC_RETRY;
- void *buf;
-
- DLOG("system id %d, len %ld, align %u\n", id, len, align);
-
- if ((pmem[id].allocator.system_mem.used + len) > pmem[id].size) {
- DLOG("requested size would be larger than quota\n");
- return -1;
- }
-
- /* Handle alignment */
- aligned_len = len + align;
-
- /* Attempt allocation */
- list = kmalloc(sizeof(struct alloc_list), GFP_KERNEL);
- if (list == NULL) {
- printk(KERN_ERR "pmem: failed to allocate system metadata\n");
- return -1;
- }
- list->vaddr = NULL;
-
- buf = NULL;
- while ((buf == NULL) && count--) {
- buf = kmalloc((aligned_len), GFP_KERNEL);
- if (buf == NULL) {
- DLOG("pmem: kmalloc %d temporarily failed len= %ld\n",
- count, aligned_len);
- }
- }
- if (!buf) {
- printk(KERN_CRIT "pmem: kmalloc failed for id= %d len= %ld\n",
- id, aligned_len);
- kfree(list);
- return -1;
- }
- list->size = aligned_len;
- list->addr = (void *)__pa(buf);
- list->aaddr = (void *)(((unsigned int)(list->addr) + (align - 1)) &
- ~(align - 1));
-
- if (!pmem[id].cached)
- list->vaddr = ioremap(__pa(buf), aligned_len);
- else
- list->vaddr = ioremap_cached(__pa(buf), aligned_len);
-
- INIT_LIST_HEAD(&list->allocs);
- list_add(&list->allocs, &pmem[id].allocator.system_mem.alist);
-
- return (int)list;
-}
-
-static pgprot_t pmem_phys_mem_access_prot(struct file *file, pgprot_t vma_prot)
-{
- int id = get_id(file);
-#ifdef pgprot_writecombine
- if (pmem[id].cached == 0 || file->f_flags & O_SYNC)
- /* on ARMv6 and ARMv7 this expands to Normal Noncached */
- return pgprot_writecombine(vma_prot);
-#endif
-#ifdef pgprot_ext_buffered
- else if (pmem[id].buffered)
- return pgprot_ext_buffered(vma_prot);
-#endif
- return vma_prot;
-}
-
-static unsigned long pmem_start_addr_all_or_nothing(int id,
- struct pmem_data *data)
-{
- return PMEM_START_ADDR(id, 0);
-}
-
-static unsigned long pmem_start_addr_buddy_bestfit(int id,
- struct pmem_data *data)
-{
- return PMEM_START_ADDR(id, data->index);
-}
-
-static unsigned long pmem_start_addr_bitmap(int id, struct pmem_data *data)
-{
- return data->index * pmem[id].quantum + pmem[id].base;
-}
-
-static unsigned long pmem_start_addr_system(int id, struct pmem_data *data)
-{
- return (unsigned long)(((struct alloc_list *)(data->index))->aaddr);
-}
-
-static void *pmem_start_vaddr(int id, struct pmem_data *data)
-{
- if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_SYSTEM)
- return ((struct alloc_list *)(data->index))->vaddr;
- else
- return pmem[id].start_addr(id, data) - pmem[id].base + pmem[id].vbase;
-}
-
-static unsigned long pmem_len_all_or_nothing(int id, struct pmem_data *data)
-{
- return data->index;
-}
-
-static unsigned long pmem_len_buddy_bestfit(int id, struct pmem_data *data)
-{
- return PMEM_BUDDY_LEN(id, data->index);
-}
-
-static unsigned long pmem_len_bitmap(int id, struct pmem_data *data)
-{
- int i;
- unsigned long ret = 0;
-
- mutex_lock(&pmem[id].arena_mutex);
-
- for (i = 0; i < pmem[id].allocator.bitmap.bitmap_allocs; i++)
- if (pmem[id].allocator.bitmap.bitm_alloc[i].bit ==
- data->index) {
- ret = pmem[id].allocator.bitmap.bitm_alloc[i].quanta *
- pmem[id].quantum;
- break;
- }
-
- mutex_unlock(&pmem[id].arena_mutex);
-#if PMEM_DEBUG
- if (i >= pmem[id].allocator.bitmap.bitmap_allocs)
- pr_alert("pmem: %s: can't find bitnum %d in "
- "alloc'd array!\n", __func__, data->index);
-#endif
- return ret;
-}
-
-static unsigned long pmem_len_system(int id, struct pmem_data *data)
-{
- unsigned long ret = 0;
-
- mutex_lock(&pmem[id].arena_mutex);
-
- ret = ((struct alloc_list *)data->index)->size;
- mutex_unlock(&pmem[id].arena_mutex);
-
- return ret;
-}
-
-static int pmem_map_garbage(int id, struct vm_area_struct *vma,
- struct pmem_data *data, unsigned long offset,
- unsigned long len)
-{
- int i, garbage_pages = len >> PAGE_SHIFT;
-
- vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP | VM_SHARED | VM_WRITE;
- for (i = 0; i < garbage_pages; i++) {
- if (vm_insert_pfn(vma, vma->vm_start + offset + (i * PAGE_SIZE),
- pmem[id].garbage_pfn))
- return -EAGAIN;
- }
- return 0;
-}
-
-static int pmem_unmap_pfn_range(int id, struct vm_area_struct *vma,
- struct pmem_data *data, unsigned long offset,
- unsigned long len)
-{
- int garbage_pages;
- DLOG("unmap offset %lx len %lx\n", offset, len);
-
- BUG_ON(!PMEM_IS_PAGE_ALIGNED(len));
-
- garbage_pages = len >> PAGE_SHIFT;
- zap_page_range(vma, vma->vm_start + offset, len, NULL);
- pmem_map_garbage(id, vma, data, offset, len);
- return 0;
-}
-
-static int pmem_map_pfn_range(int id, struct vm_area_struct *vma,
- struct pmem_data *data, unsigned long offset,
- unsigned long len)
-{
- int ret;
- DLOG("map offset %lx len %lx\n", offset, len);
- BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_start));
- BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_end));
- BUG_ON(!PMEM_IS_PAGE_ALIGNED(len));
- BUG_ON(!PMEM_IS_PAGE_ALIGNED(offset));
-
- ret = io_remap_pfn_range(vma, vma->vm_start + offset,
- (pmem[id].start_addr(id, data) + offset) >> PAGE_SHIFT,
- len, vma->vm_page_prot);
- if (ret) {
-#if PMEM_DEBUG
- pr_alert("pmem: %s: io_remap_pfn_range fails with "
- "return value: %d!\n", __func__, ret);
-#endif
-
- ret = -EAGAIN;
- }
- return ret;
-}
-
-static int pmem_remap_pfn_range(int id, struct vm_area_struct *vma,
- struct pmem_data *data, unsigned long offset,
- unsigned long len)
-{
- /* hold the mm semp for the vma you are modifying when you call this */
- BUG_ON(!vma);
- zap_page_range(vma, vma->vm_start + offset, len, NULL);
- return pmem_map_pfn_range(id, vma, data, offset, len);
-}
-
-static void pmem_vma_open(struct vm_area_struct *vma)
-{
- struct file *file = vma->vm_file;
- struct pmem_data *data = file->private_data;
- int id = get_id(file);
-
-#if PMEM_DEBUG_MSGS
- char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
-#endif
- DLOG("Dev %s(id: %d) pid %u(%s) ppid %u file %p count %ld\n",
- get_name(file), id, current->pid,
- get_task_comm(currtask_name, current),
- current->parent->pid, file, file_count(file));
- /* this should never be called as we don't support copying pmem
- * ranges via fork */
- down_read(&data->sem);
- BUG_ON(!has_allocation(file));
- /* remap the garbage pages, forkers don't get access to the data */
- pmem_unmap_pfn_range(id, vma, data, 0, vma->vm_start - vma->vm_end);
- up_read(&data->sem);
-}
-
-static void pmem_vma_close(struct vm_area_struct *vma)
-{
- struct file *file = vma->vm_file;
- struct pmem_data *data = file->private_data;
-
-#if PMEM_DEBUG_MSGS
- char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
-#endif
- DLOG("Dev %s(id: %d) pid %u(%s) ppid %u file %p count %ld\n",
- get_name(file), get_id(file), current->pid,
- get_task_comm(currtask_name, current),
- current->parent->pid, file, file_count(file));
-
- if (unlikely(!is_pmem_file(file))) {
- pr_warning("pmem: something is very wrong, you are "
- "closing a vm backing an allocation that doesn't "
- "exist!\n");
- return;
- }
-
- down_write(&data->sem);
- if (unlikely(!has_allocation(file))) {
- up_write(&data->sem);
- pr_warning("pmem: something is very wrong, you are "
- "closing a vm backing an allocation that doesn't "
- "exist!\n");
- return;
- }
- if (data->vma == vma) {
- data->vma = NULL;
- if ((data->flags & PMEM_FLAGS_CONNECTED) &&
- (data->flags & PMEM_FLAGS_SUBMAP))
- data->flags |= PMEM_FLAGS_UNSUBMAP;
- }
- /* the kernel is going to free this vma now anyway */
- up_write(&data->sem);
-}
-
-static struct vm_operations_struct vm_ops = {
- .open = pmem_vma_open,
- .close = pmem_vma_close,
-};
-
-static int pmem_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct pmem_data *data = file->private_data;
- int index = -1;
- unsigned long vma_size = vma->vm_end - vma->vm_start;
- int ret = 0, id = get_id(file);
-#if PMEM_DEBUG_MSGS
- char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
-#endif
-
- if (!data) {
- pr_err("pmem: Invalid file descriptor, no private data\n");
- return -EINVAL;
- }
- DLOG("pid %u(%s) mmap vma_size %lu on dev %s(id: %d)\n", current->pid,
- get_task_comm(currtask_name, current), vma_size,
- get_name(file), id);
- if (vma->vm_pgoff || !PMEM_IS_PAGE_ALIGNED(vma_size)) {
-#if PMEM_DEBUG
- pr_err("pmem: mmaps must be at offset zero, aligned"
- " and a multiple of pages_size.\n");
-#endif
- return -EINVAL;
- }
-
- down_write(&data->sem);
- /* check this file isn't already mmaped, for submaps check this file
- * has never been mmaped */
- if ((data->flags & PMEM_FLAGS_SUBMAP) ||
- (data->flags & PMEM_FLAGS_UNSUBMAP)) {
-#if PMEM_DEBUG
- pr_err("pmem: you can only mmap a pmem file once, "
- "this file is already mmaped. %x\n", data->flags);
-#endif
- ret = -EINVAL;
- goto error;
- }
- /* if file->private_data == unalloced, alloc*/
- if (data->index == -1) {
- mutex_lock(&pmem[id].arena_mutex);
- index = pmem_allocate_from_id(id,
- vma->vm_end - vma->vm_start,
- SZ_4K);
- mutex_unlock(&pmem[id].arena_mutex);
- /* either no space was available or an error occured */
- if (index == -1) {
- pr_err("pmem: mmap unable to allocate memory"
- "on %s\n", get_name(file));
- ret = -ENOMEM;
- goto error;
- }
- /* store the index of a successful allocation */
- data->index = index;
- }
-
- if (pmem[id].len(id, data) < vma_size) {
-#if PMEM_DEBUG
- pr_err("pmem: mmap size [%lu] does not match"
- " size of backing region [%lu].\n", vma_size,
- pmem[id].len(id, data));
-#endif
- ret = -EINVAL;
- goto error;
- }
-
- vma->vm_pgoff = pmem[id].start_addr(id, data) >> PAGE_SHIFT;
-
- vma->vm_page_prot = pmem_phys_mem_access_prot(file, vma->vm_page_prot);
-
- if (data->flags & PMEM_FLAGS_CONNECTED) {
- struct pmem_region_node *region_node;
- struct list_head *elt;
- if (pmem_map_garbage(id, vma, data, 0, vma_size)) {
- pr_alert("pmem: mmap failed in kernel!\n");
- ret = -EAGAIN;
- goto error;
- }
- list_for_each(elt, &data->region_list) {
- region_node = list_entry(elt, struct pmem_region_node,
- list);
- DLOG("remapping file: %p %lx %lx\n", file,
- region_node->region.offset,
- region_node->region.len);
- if (pmem_remap_pfn_range(id, vma, data,
- region_node->region.offset,
- region_node->region.len)) {
- ret = -EAGAIN;
- goto error;
- }
- }
- data->flags |= PMEM_FLAGS_SUBMAP;
- get_task_struct(current->group_leader);
- data->task = current->group_leader;
- data->vma = vma;
-#if PMEM_DEBUG
- data->pid = current->pid;
-#endif
- DLOG("submmapped file %p vma %p pid %u\n", file, vma,
- current->pid);
- } else {
- if (pmem_map_pfn_range(id, vma, data, 0, vma_size)) {
- pr_err("pmem: mmap failed in kernel!\n");
- ret = -EAGAIN;
- goto error;
- }
- data->flags |= PMEM_FLAGS_MASTERMAP;
- data->pid = current->pid;
- }
- vma->vm_ops = &vm_ops;
-error:
- up_write(&data->sem);
- return ret;
-}
-
-/* the following are the api for accessing pmem regions by other drivers
- * from inside the kernel */
-int get_pmem_user_addr(struct file *file, unsigned long *start,
- unsigned long *len)
-{
- int ret = -1;
-
- if (is_pmem_file(file)) {
- struct pmem_data *data = file->private_data;
-
- down_read(&data->sem);
- if (has_allocation(file)) {
- if (data->vma) {
- *start = data->vma->vm_start;
- *len = data->vma->vm_end - data->vma->vm_start;
- } else {
- *start = *len = 0;
-#if PMEM_DEBUG
- pr_err("pmem: %s: no vma present.\n",
- __func__);
-#endif
- }
- ret = 0;
- }
- up_read(&data->sem);
- }
-
-#if PMEM_DEBUG
- if (ret)
- pr_err("pmem: %s: requested pmem data from invalid"
- "file.\n", __func__);
-#endif
- return ret;
-}
-
-int get_pmem_addr(struct file *file, unsigned long *start,
- unsigned long *vstart, unsigned long *len)
-{
- int ret = -1;
-
- if (is_pmem_file(file)) {
- struct pmem_data *data = file->private_data;
-
- down_read(&data->sem);
- if (has_allocation(file)) {
- int id = get_id(file);
-
- *start = pmem[id].start_addr(id, data);
- *len = pmem[id].len(id, data);
- *vstart = (unsigned long)
- pmem_start_vaddr(id, data);
- up_read(&data->sem);
-#if PMEM_DEBUG
- down_write(&data->sem);
- data->ref++;
- up_write(&data->sem);
-#endif
- DLOG("returning start %#lx len %lu "
- "vstart %#lx\n",
- *start, *len, *vstart);
- ret = 0;
- } else {
- up_read(&data->sem);
- }
- }
- return ret;
-}
-
-int get_pmem_file(unsigned int fd, unsigned long *start, unsigned long *vstart,
- unsigned long *len, struct file **filp)
-{
- int ret = -1;
- struct file *file = fget(fd);
-
- if (unlikely(file == NULL)) {
- pr_err("pmem: %s: requested data from file "
- "descriptor that doesn't exist.\n", __func__);
- } else {
-#if PMEM_DEBUG_MSGS
- char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
-#endif
- DLOG("filp %p rdev %d pid %u(%s) file %p(%ld)"
- " dev %s(id: %d)\n", filp,
- file->f_dentry->d_inode->i_rdev,
- current->pid, get_task_comm(currtask_name, current),
- file, file_count(file), get_name(file), get_id(file));
-
- if (!get_pmem_addr(file, start, vstart, len)) {
- if (filp)
- *filp = file;
- ret = 0;
- } else {
- fput(file);
- }
- }
- return ret;
-}
-EXPORT_SYMBOL(get_pmem_file);
-
-int get_pmem_fd(int fd, unsigned long *start, unsigned long *len)
-{
- unsigned long vstart;
- return get_pmem_file(fd, start, &vstart, len, NULL);
-}
-EXPORT_SYMBOL(get_pmem_fd);
-
-void put_pmem_file(struct file *file)
-{
-#if PMEM_DEBUG_MSGS
- char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
-#endif
- DLOG("rdev %d pid %u(%s) file %p(%ld)" " dev %s(id: %d)\n",
- file->f_dentry->d_inode->i_rdev, current->pid,
- get_task_comm(currtask_name, current), file,
- file_count(file), get_name(file), get_id(file));
- if (is_pmem_file(file)) {
-#if PMEM_DEBUG
- struct pmem_data *data = file->private_data;
-
- down_write(&data->sem);
- if (!data->ref--) {
- data->ref++;
- pr_alert("pmem: pmem_put > pmem_get %s "
- "(pid %d)\n",
- pmem[get_id(file)].dev.name, data->pid);
- BUG();
- }
- up_write(&data->sem);
-#endif
- fput(file);
- }
-}
-EXPORT_SYMBOL(put_pmem_file);
-
-void put_pmem_fd(int fd)
-{
- int put_needed;
- struct file *file = fget_light(fd, &put_needed);
-
- if (file) {
- put_pmem_file(file);
- fput_light(file, put_needed);
- }
-}
-
-void flush_pmem_fd(int fd, unsigned long offset, unsigned long len)
-{
- int fput_needed;
- struct file *file = fget_light(fd, &fput_needed);
-
- if (file) {
- flush_pmem_file(file, offset, len);
- fput_light(file, fput_needed);
- }
-}
-
-void flush_pmem_file(struct file *file, unsigned long offset, unsigned long len)
-{
- struct pmem_data *data;
- int id;
- void *vaddr;
- struct pmem_region_node *region_node;
- struct list_head *elt;
- void *flush_start, *flush_end;
-#ifdef CONFIG_OUTER_CACHE
- unsigned long phy_start, phy_end;
-#endif
- if (!is_pmem_file(file))
- return;
-
- id = get_id(file);
- if (!pmem[id].cached)
- return;
-
- /* is_pmem_file fails if !file */
- data = file->private_data;
-
- down_read(&data->sem);
- if (!has_allocation(file))
- goto end;
-
- vaddr = pmem_start_vaddr(id, data);
-
- if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_SYSTEM) {
- dmac_flush_range(vaddr,
- (void *)((unsigned long)vaddr +
- ((struct alloc_list *)(data->index))->size));
-#ifdef CONFIG_OUTER_CACHE
- phy_start = pmem_start_addr_system(id, data);
-
- phy_end = phy_start +
- ((struct alloc_list *)(data->index))->size;
-
- outer_flush_range(phy_start, phy_end);
-#endif
- goto end;
- }
- /* if this isn't a submmapped file, flush the whole thing */
- if (unlikely(!(data->flags & PMEM_FLAGS_CONNECTED))) {
- dmac_flush_range(vaddr, vaddr + pmem[id].len(id, data));
-#ifdef CONFIG_OUTER_CACHE
- phy_start = (unsigned long)vaddr -
- (unsigned long)pmem[id].vbase + pmem[id].base;
-
- phy_end = phy_start + pmem[id].len(id, data);
-
- outer_flush_range(phy_start, phy_end);
-#endif
- goto end;
- }
- /* otherwise, flush the region of the file we are drawing */
- list_for_each(elt, &data->region_list) {
- region_node = list_entry(elt, struct pmem_region_node, list);
- if ((offset >= region_node->region.offset) &&
- ((offset + len) <= (region_node->region.offset +
- region_node->region.len))) {
- flush_start = vaddr + region_node->region.offset;
- flush_end = flush_start + region_node->region.len;
- dmac_flush_range(flush_start, flush_end);
-#ifdef CONFIG_OUTER_CACHE
-
- phy_start = (unsigned long)flush_start -
- (unsigned long)pmem[id].vbase + pmem[id].base;
-
- phy_end = phy_start + region_node->region.len;
-
- outer_flush_range(phy_start, phy_end);
-#endif
- break;
- }
- }
-end:
- up_read(&data->sem);
-}
-
-int pmem_cache_maint(struct file *file, unsigned int cmd,
- struct pmem_addr *pmem_addr)
-{
- struct pmem_data *data;
- int id;
- unsigned long vaddr, paddr, length, offset,
- pmem_len, pmem_start_addr;
-
- /* Called from kernel-space so file may be NULL */
- if (!file)
- return -EBADF;
-
- /*
- * check that the vaddr passed for flushing is valid
- * so that you don't crash the kernel
- */
- if (!pmem_addr->vaddr)
- return -EINVAL;
-
- data = file->private_data;
- id = get_id(file);
-
- if (!pmem[id].cached)
- return 0;
-
- offset = pmem_addr->offset;
- length = pmem_addr->length;
-
- down_read(&data->sem);
- if (!has_allocation(file)) {
- up_read(&data->sem);
- return -EINVAL;
- }
- pmem_len = pmem[id].len(id, data);
- pmem_start_addr = pmem[id].start_addr(id, data);
- up_read(&data->sem);
-
- if (offset + length > pmem_len)
- return -EINVAL;
-
- vaddr = pmem_addr->vaddr;
- paddr = pmem_start_addr + offset;
-
- DLOG("pmem cache maint on dev %s(id: %d)"
- "(vaddr %lx paddr %lx len %lu bytes)\n",
- get_name(file), id, vaddr, paddr, length);
- if (cmd == PMEM_CLEAN_INV_CACHES)
- clean_and_invalidate_caches(vaddr,
- length, paddr);
- else if (cmd == PMEM_CLEAN_CACHES)
- clean_caches(vaddr, length, paddr);
- else if (cmd == PMEM_INV_CACHES)
- invalidate_caches(vaddr, length, paddr);
-
- return 0;
-}
-EXPORT_SYMBOL(pmem_cache_maint);
-
-static int pmem_connect(unsigned long connect, struct file *file)
-{
- int ret = 0, put_needed;
- struct file *src_file;
-
- if (!file) {
- pr_err("pmem: %s: NULL file pointer passed in, "
- "bailing out!\n", __func__);
- ret = -EINVAL;
- goto leave;
- }
-
- src_file = fget_light(connect, &put_needed);
-
- if (!src_file) {
- pr_err("pmem: %s: src file not found!\n", __func__);
- ret = -EBADF;
- goto leave;
- }
-
- if (src_file == file) { /* degenerative case, operator error */
- pr_err("pmem: %s: src_file and passed in file are "
- "the same; refusing to connect to self!\n", __func__);
- ret = -EINVAL;
- goto put_src_file;
- }
-
- if (unlikely(!is_pmem_file(src_file))) {
- pr_err("pmem: %s: src file is not a pmem file!\n",
- __func__);
- ret = -EINVAL;
- goto put_src_file;
- } else {
- struct pmem_data *src_data = src_file->private_data;
-
- if (!src_data) {
- pr_err("pmem: %s: src file pointer has no"
- "private data, bailing out!\n", __func__);
- ret = -EINVAL;
- goto put_src_file;
- }
-
- down_read(&src_data->sem);
-
- if (unlikely(!has_allocation(src_file))) {
- up_read(&src_data->sem);
- pr_err("pmem: %s: src file has no allocation!\n",
- __func__);
- ret = -EINVAL;
- } else {
- struct pmem_data *data;
- int src_index = src_data->index;
-
- up_read(&src_data->sem);
-
- data = file->private_data;
- if (!data) {
- pr_err("pmem: %s: passed in file "
- "pointer has no private data, bailing"
- " out!\n", __func__);
- ret = -EINVAL;
- goto put_src_file;
- }
-
- down_write(&data->sem);
- if (has_allocation(file) &&
- (data->index != src_index)) {
- up_write(&data->sem);
-
- pr_err("pmem: %s: file is already "
- "mapped but doesn't match this "
- "src_file!\n", __func__);
- ret = -EINVAL;
- } else {
- data->index = src_index;
- data->flags |= PMEM_FLAGS_CONNECTED;
- data->master_fd = connect;
- data->master_file = src_file;
-
- up_write(&data->sem);
-
- DLOG("connect %p to %p\n", file, src_file);
- }
- }
- }
-put_src_file:
- fput_light(src_file, put_needed);
-leave:
- return ret;
-}
-
-static void pmem_unlock_data_and_mm(struct pmem_data *data,
- struct mm_struct *mm)
-{
- up_write(&data->sem);
- if (mm != NULL) {
- up_write(&mm->mmap_sem);
- mmput(mm);
- }
-}
-
-static int pmem_lock_data_and_mm(struct file *file, struct pmem_data *data,
- struct mm_struct **locked_mm)
-{
- int ret = 0;
- struct mm_struct *mm = NULL;
-#if PMEM_DEBUG_MSGS
- char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
-#endif
- DLOG("pid %u(%s) file %p(%ld)\n",
- current->pid, get_task_comm(currtask_name, current),
- file, file_count(file));
-
- *locked_mm = NULL;
-lock_mm:
- down_read(&data->sem);
- if (PMEM_IS_SUBMAP(data)) {
- mm = get_task_mm(data->task);
- if (!mm) {
- up_read(&data->sem);
-#if PMEM_DEBUG
- pr_alert("pmem: can't remap - task is gone!\n");
-#endif
- return -1;
- }
- }
- up_read(&data->sem);
-
- if (mm)
- down_write(&mm->mmap_sem);
-
- down_write(&data->sem);
- /* check that the file didn't get mmaped before we could take the
- * data sem, this should be safe b/c you can only submap each file
- * once */
- if (PMEM_IS_SUBMAP(data) && !mm) {
- pmem_unlock_data_and_mm(data, mm);
- DLOG("mapping contention, repeating mmap op\n");
- goto lock_mm;
- }
- /* now check that vma.mm is still there, it could have been
- * deleted by vma_close before we could get the data->sem */
- if ((data->flags & PMEM_FLAGS_UNSUBMAP) && (mm != NULL)) {
- /* might as well release this */
- if (data->flags & PMEM_FLAGS_SUBMAP) {
- put_task_struct(data->task);
- data->task = NULL;
- /* lower the submap flag to show the mm is gone */
- data->flags &= ~(PMEM_FLAGS_SUBMAP);
- }
- pmem_unlock_data_and_mm(data, mm);
-#if PMEM_DEBUG
- pr_alert("pmem: vma.mm went away!\n");
-#endif
- return -1;
- }
- *locked_mm = mm;
- return ret;
-}
-
-int pmem_remap(struct pmem_region *region, struct file *file,
- unsigned operation)
-{
- int ret;
- struct pmem_region_node *region_node;
- struct mm_struct *mm = NULL;
- struct list_head *elt, *elt2;
- int id = get_id(file);
- struct pmem_data *data;
-
- DLOG("operation %#x, region offset %ld, region len %ld\n",
- operation, region->offset, region->len);
-
- if (!is_pmem_file(file)) {
-#if PMEM_DEBUG
- pr_err("pmem: remap request for non-pmem file descriptor\n");
-#endif
- return -EINVAL;
- }
-
- /* is_pmem_file fails if !file */
- data = file->private_data;
-
- /* pmem region must be aligned on a page boundry */
- if (unlikely(!PMEM_IS_PAGE_ALIGNED(region->offset) ||
- !PMEM_IS_PAGE_ALIGNED(region->len))) {
-#if PMEM_DEBUG
- pr_err("pmem: request for unaligned pmem"
- "suballocation %lx %lx\n",
- region->offset, region->len);
-#endif
- return -EINVAL;
- }
-
- /* if userspace requests a region of len 0, there's nothing to do */
- if (region->len == 0)
- return 0;
-
- /* lock the mm and data */
- ret = pmem_lock_data_and_mm(file, data, &mm);
- if (ret)
- return 0;
-
- /* only the owner of the master file can remap the client fds
- * that back in it */
- if (!is_master_owner(file)) {
-#if PMEM_DEBUG
- pr_err("pmem: remap requested from non-master process\n");
-#endif
- ret = -EINVAL;
- goto err;
- }
-
- /* check that the requested range is within the src allocation */
- if (unlikely((region->offset > pmem[id].len(id, data)) ||
- (region->len > pmem[id].len(id, data)) ||
- (region->offset + region->len > pmem[id].len(id, data)))) {
-#if PMEM_DEBUG
- pr_err("pmem: suballoc doesn't fit in src_file!\n");
-#endif
- ret = -EINVAL;
- goto err;
- }
-
- if (operation == PMEM_MAP) {
- region_node = kmalloc(sizeof(struct pmem_region_node),
- GFP_KERNEL);
- if (!region_node) {
- ret = -ENOMEM;
-#if PMEM_DEBUG
- pr_alert("pmem: No space to allocate remap metadata!");
-#endif
- goto err;
- }
- region_node->region = *region;
- list_add(®ion_node->list, &data->region_list);
- } else if (operation == PMEM_UNMAP) {
- int found = 0;
- list_for_each_safe(elt, elt2, &data->region_list) {
- region_node = list_entry(elt, struct pmem_region_node,
- list);
- if (region->len == 0 ||
- (region_node->region.offset == region->offset &&
- region_node->region.len == region->len)) {
- list_del(elt);
- kfree(region_node);
- found = 1;
- }
- }
- if (!found) {
-#if PMEM_DEBUG
- pr_err("pmem: Unmap region does not map any"
- " mapped region!");
-#endif
- ret = -EINVAL;
- goto err;
- }
- }
-
- if (data->vma && PMEM_IS_SUBMAP(data)) {
- if (operation == PMEM_MAP)
- ret = pmem_remap_pfn_range(id, data->vma, data,
- region->offset, region->len);
- else if (operation == PMEM_UNMAP)
- ret = pmem_unmap_pfn_range(id, data->vma, data,
- region->offset, region->len);
- }
-
-err:
- pmem_unlock_data_and_mm(data, mm);
- return ret;
-}
-
-static void pmem_revoke(struct file *file, struct pmem_data *data)
-{
- struct pmem_region_node *region_node;
- struct list_head *elt, *elt2;
- struct mm_struct *mm = NULL;
- int id = get_id(file);
- int ret = 0;
-
- data->master_file = NULL;
- ret = pmem_lock_data_and_mm(file, data, &mm);
- /* if lock_data_and_mm fails either the task that mapped the fd, or
- * the vma that mapped it have already gone away, nothing more
- * needs to be done */
- if (ret)
- return;
- /* unmap everything */
- /* delete the regions and region list nothing is mapped any more */
- if (data->vma)
- list_for_each_safe(elt, elt2, &data->region_list) {
- region_node = list_entry(elt, struct pmem_region_node,
- list);
- pmem_unmap_pfn_range(id, data->vma, data,
- region_node->region.offset,
- region_node->region.len);
- list_del(elt);
- kfree(region_node);
- }
- /* delete the master file */
- pmem_unlock_data_and_mm(data, mm);
-}
-
-static void pmem_get_size(struct pmem_region *region, struct file *file)
-{
- /* called via ioctl file op, so file guaranteed to be not NULL */
- struct pmem_data *data = file->private_data;
- int id = get_id(file);
-
- down_read(&data->sem);
- if (!has_allocation(file)) {
- region->offset = 0;
- region->len = 0;
- } else {
- region->offset = pmem[id].start_addr(id, data);
- region->len = pmem[id].len(id, data);
- }
- up_read(&data->sem);
- DLOG("offset 0x%lx len 0x%lx\n", region->offset, region->len);
-}
-
-
-static long pmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- /* called from user space as file op, so file guaranteed to be not
- * NULL
- */
- struct pmem_data *data = file->private_data;
- int id = get_id(file);
-#if PMEM_DEBUG_MSGS
- char currtask_name[
- FIELD_SIZEOF(struct task_struct, comm) + 1];
-#endif
-
- DLOG("pid %u(%s) file %p(%ld) cmd %#x, dev %s(id: %d)\n",
- current->pid, get_task_comm(currtask_name, current),
- file, file_count(file), cmd, get_name(file), id);
-
- switch (cmd) {
- case PMEM_GET_PHYS:
- {
- struct pmem_region region;
-
- DLOG("get_phys\n");
- down_read(&data->sem);
- if (!has_allocation(file)) {
- region.offset = 0;
- region.len = 0;
- } else {
- region.offset = pmem[id].start_addr(id, data);
- region.len = pmem[id].len(id, data);
- }
- up_read(&data->sem);
-
- if (copy_to_user((void __user *)arg, ®ion,
- sizeof(struct pmem_region)))
- return -EFAULT;
-
- DLOG("pmem: successful request for "
- "physical address of pmem region id %d, "
- "offset 0x%lx, len 0x%lx\n",
- id, region.offset, region.len);
-
- break;
- }
- case PMEM_MAP:
- {
- struct pmem_region region;
- DLOG("map\n");
- if (copy_from_user(®ion, (void __user *)arg,
- sizeof(struct pmem_region)))
- return -EFAULT;
- return pmem_remap(®ion, file, PMEM_MAP);
- }
- break;
- case PMEM_UNMAP:
- {
- struct pmem_region region;
- DLOG("unmap\n");
- if (copy_from_user(®ion, (void __user *)arg,
- sizeof(struct pmem_region)))
- return -EFAULT;
- return pmem_remap(®ion, file, PMEM_UNMAP);
- break;
- }
- case PMEM_GET_SIZE:
- {
- struct pmem_region region;
- DLOG("get_size\n");
- pmem_get_size(®ion, file);
- if (copy_to_user((void __user *)arg, ®ion,
- sizeof(struct pmem_region)))
- return -EFAULT;
- break;
- }
- case PMEM_GET_TOTAL_SIZE:
- {
- struct pmem_region region;
- DLOG("get total size\n");
- region.offset = 0;
- get_id(file);
- region.len = pmem[id].size;
- if (copy_to_user((void __user *)arg, ®ion,
- sizeof(struct pmem_region)))
- return -EFAULT;
- break;
- }
- case PMEM_GET_FREE_SPACE:
- {
- struct pmem_freespace fs;
- DLOG("get freespace on %s(id: %d)\n",
- get_name(file), id);
-
- mutex_lock(&pmem[id].arena_mutex);
- pmem[id].free_space(id, &fs);
- mutex_unlock(&pmem[id].arena_mutex);
-
- DLOG("%s(id: %d) total free %lu, largest %lu\n",
- get_name(file), id, fs.total, fs.largest);
-
- if (copy_to_user((void __user *)arg, &fs,
- sizeof(struct pmem_freespace)))
- return -EFAULT;
- break;
- }
-
- case PMEM_ALLOCATE:
- {
- int ret = 0;
- DLOG("allocate, id %d\n", id);
- down_write(&data->sem);
- if (has_allocation(file)) {
- pr_err("pmem: Existing allocation found on "
- "this file descrpitor\n");
- up_write(&data->sem);
- return -EINVAL;
- }
-
- mutex_lock(&pmem[id].arena_mutex);
- data->index = pmem_allocate_from_id(id,
- arg,
- SZ_4K);
- mutex_unlock(&pmem[id].arena_mutex);
- ret = data->index == -1 ? -ENOMEM :
- data->index;
- up_write(&data->sem);
- return ret;
- }
- case PMEM_ALLOCATE_ALIGNED:
- {
- struct pmem_allocation alloc;
- int ret = 0;
-
- if (copy_from_user(&alloc, (void __user *)arg,
- sizeof(struct pmem_allocation)))
- return -EFAULT;
- DLOG("allocate id align %d %u\n", id, alloc.align);
- down_write(&data->sem);
- if (has_allocation(file)) {
- pr_err("pmem: Existing allocation found on "
- "this file descrpitor\n");
- up_write(&data->sem);
- return -EINVAL;
- }
-
- if (alloc.align & (alloc.align - 1)) {
- pr_err("pmem: Alignment is not a power of 2\n");
- return -EINVAL;
- }
-
- if (alloc.align != SZ_4K &&
- (pmem[id].allocator_type !=
- PMEM_ALLOCATORTYPE_BITMAP)) {
- pr_err("pmem: Non 4k alignment requires bitmap"
- " allocator on %s\n", pmem[id].name);
- return -EINVAL;
- }
-
- if (alloc.align > SZ_1M ||
- alloc.align < SZ_4K) {
- pr_err("pmem: Invalid Alignment (%u) "
- "specified\n", alloc.align);
- return -EINVAL;
- }
-
- mutex_lock(&pmem[id].arena_mutex);
- data->index = pmem_allocate_from_id(id,
- alloc.size,
- alloc.align);
- mutex_unlock(&pmem[id].arena_mutex);
- ret = data->index == -1 ? -ENOMEM :
- data->index;
- up_write(&data->sem);
- return ret;
- }
- case PMEM_CONNECT:
- DLOG("connect\n");
- return pmem_connect(arg, file);
- case PMEM_CLEAN_INV_CACHES:
- case PMEM_CLEAN_CACHES:
- case PMEM_INV_CACHES:
- {
- struct pmem_addr pmem_addr;
-
- if (copy_from_user(&pmem_addr, (void __user *)arg,
- sizeof(struct pmem_addr)))
- return -EFAULT;
-
- return pmem_cache_maint(file, cmd, &pmem_addr);
- }
- default:
- if (pmem[id].ioctl)
- return pmem[id].ioctl(file, cmd, arg);
-
- DLOG("ioctl invalid (%#x)\n", cmd);
- return -EINVAL;
- }
- return 0;
-}
-
-static void ioremap_pmem(int id)
-{
- unsigned long addr;
- const struct mem_type *type;
-
- DLOG("PMEMDEBUG: ioremaping for %s\n", pmem[id].name);
- if (pmem[id].map_on_demand) {
- addr = (unsigned long)pmem[id].area->addr;
- if (pmem[id].cached)
- type = get_mem_type(MT_DEVICE_CACHED);
- else
- type = get_mem_type(MT_DEVICE);
- DLOG("PMEMDEBUG: Remap phys %lx to virt %lx on %s\n",
- pmem[id].base, addr, pmem[id].name);
- if (ioremap_pages(addr, pmem[id].base, pmem[id].size, type)) {
- pr_err("pmem: Failed to map pages\n");
- BUG();
- }
- pmem[id].vbase = pmem[id].area->addr;
- /* Flush the cache after installing page table entries to avoid
- * aliasing when these pages are remapped to user space.
- */
- flush_cache_vmap(addr, addr + pmem[id].size);
- } else {
- if (pmem[id].cached)
- pmem[id].vbase = ioremap_cached(pmem[id].base,
- pmem[id].size);
- #ifdef ioremap_ext_buffered
- else if (pmem[id].buffered)
- pmem[id].vbase = ioremap_ext_buffered(pmem[id].base,
- pmem[id].size);
- #endif
- else
- pmem[id].vbase = ioremap(pmem[id].base, pmem[id].size);
- }
-}
-
-int pmem_setup(struct android_pmem_platform_data *pdata,
- long (*ioctl)(struct file *, unsigned int, unsigned long),
- int (*release)(struct inode *, struct file *))
-{
- int i, index = 0, id;
- struct vm_struct *pmem_vma = NULL;
- struct page *page;
-
- if (id_count >= PMEM_MAX_DEVICES) {
- pr_alert("pmem: %s: unable to register driver(%s) - no more "
- "devices available!\n", __func__, pdata->name);
- goto err_no_mem;
- }
-
- if (!pdata->size) {
- pr_alert("pmem: %s: unable to register pmem driver(%s) - zero "
- "size passed in!\n", __func__, pdata->name);
- goto err_no_mem;
- }
-
- id = id_count++;
-
- pmem[id].id = id;
-
- if (pmem[id].allocate) {
- pr_alert("pmem: %s: unable to register pmem driver - "
- "duplicate registration of %s!\n",
- __func__, pdata->name);
- goto err_no_mem;
- }
-
- pmem[id].allocator_type = pdata->allocator_type;
-
- /* 'quantum' is a "hidden" variable that defaults to 0 in the board
- * files */
- pmem[id].quantum = pdata->quantum ?: PMEM_MIN_ALLOC;
- if (pmem[id].quantum < PMEM_MIN_ALLOC ||
- !is_power_of_2(pmem[id].quantum)) {
- pr_alert("pmem: %s: unable to register pmem driver %s - "
- "invalid quantum value (%#x)!\n",
- __func__, pdata->name, pmem[id].quantum);
- goto err_reset_pmem_info;
- }
-
- if (pdata->size % pmem[id].quantum) {
- /* bad alignment for size! */
- pr_alert("pmem: %s: Unable to register driver %s - "
- "memory region size (%#lx) is not a multiple of "
- "quantum size(%#x)!\n", __func__, pdata->name,
- pdata->size, pmem[id].quantum);
- goto err_reset_pmem_info;
- }
-
- pmem[id].cached = pdata->cached;
- pmem[id].buffered = pdata->buffered;
- pmem[id].size = pdata->size;
- pmem[id].memory_type = pdata->memory_type;
- strlcpy(pmem[id].name, pdata->name, PMEM_NAME_SIZE);
-
- pmem[id].num_entries = pmem[id].size / pmem[id].quantum;
-
- memset(&pmem[id].kobj, 0, sizeof(pmem[0].kobj));
- pmem[id].kobj.kset = pmem_kset;
-
- switch (pmem[id].allocator_type) {
- case PMEM_ALLOCATORTYPE_ALLORNOTHING:
- pmem[id].allocate = pmem_allocator_all_or_nothing;
- pmem[id].free = pmem_free_all_or_nothing;
- pmem[id].free_space = pmem_free_space_all_or_nothing;
- pmem[id].len = pmem_len_all_or_nothing;
- pmem[id].start_addr = pmem_start_addr_all_or_nothing;
- pmem[id].num_entries = 1;
- pmem[id].quantum = pmem[id].size;
- pmem[id].allocator.all_or_nothing.allocated = 0;
-
- if (kobject_init_and_add(&pmem[id].kobj,
- &pmem_allornothing_ktype, NULL,
- "%s", pdata->name))
- goto out_put_kobj;
-
- break;
-
- case PMEM_ALLOCATORTYPE_BUDDYBESTFIT:
- pmem[id].allocator.buddy_bestfit.buddy_bitmap = kmalloc(
- pmem[id].num_entries * sizeof(struct pmem_bits),
- GFP_KERNEL);
- if (!pmem[id].allocator.buddy_bestfit.buddy_bitmap)
- goto err_reset_pmem_info;
-
- memset(pmem[id].allocator.buddy_bestfit.buddy_bitmap, 0,
- sizeof(struct pmem_bits) * pmem[id].num_entries);
-
- for (i = sizeof(pmem[id].num_entries) * 8 - 1; i >= 0; i--)
- if ((pmem[id].num_entries) & 1<<i) {
- PMEM_BUDDY_ORDER(id, index) = i;
- index = PMEM_BUDDY_NEXT_INDEX(id, index);
- }
- pmem[id].allocate = pmem_allocator_buddy_bestfit;
- pmem[id].free = pmem_free_buddy_bestfit;
- pmem[id].free_space = pmem_free_space_buddy_bestfit;
- pmem[id].len = pmem_len_buddy_bestfit;
- pmem[id].start_addr = pmem_start_addr_buddy_bestfit;
- if (kobject_init_and_add(&pmem[id].kobj,
- &pmem_buddy_bestfit_ktype, NULL,
- "%s", pdata->name))
- goto out_put_kobj;
-
- break;
-
- case PMEM_ALLOCATORTYPE_BITMAP: /* 0, default if not explicit */
- pmem[id].allocator.bitmap.bitm_alloc = kmalloc(
- PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS *
- sizeof(*pmem[id].allocator.bitmap.bitm_alloc),
- GFP_KERNEL);
- if (!pmem[id].allocator.bitmap.bitm_alloc) {
- pr_alert("pmem: %s: Unable to register pmem "
- "driver %s - can't allocate "
- "bitm_alloc!\n",
- __func__, pdata->name);
- goto err_reset_pmem_info;
- }
-
- if (kobject_init_and_add(&pmem[id].kobj,
- &pmem_bitmap_ktype, NULL,
- "%s", pdata->name))
- goto out_put_kobj;
-
- for (i = 0; i < PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS; i++) {
- pmem[id].allocator.bitmap.bitm_alloc[i].bit = -1;
- pmem[id].allocator.bitmap.bitm_alloc[i].quanta = 0;
- }
-
- pmem[id].allocator.bitmap.bitmap_allocs =
- PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS;
-
- pmem[id].allocator.bitmap.bitmap =
- kcalloc((pmem[id].num_entries + 31) / 32,
- sizeof(unsigned int), GFP_KERNEL);
- if (!pmem[id].allocator.bitmap.bitmap) {
- pr_alert("pmem: %s: Unable to register pmem "
- "driver - can't allocate bitmap!\n",
- __func__);
- goto err_cant_register_device;
- }
- pmem[id].allocator.bitmap.bitmap_free = pmem[id].num_entries;
-
- pmem[id].allocate = pmem_allocator_bitmap;
- pmem[id].free = pmem_free_bitmap;
- pmem[id].free_space = pmem_free_space_bitmap;
- pmem[id].len = pmem_len_bitmap;
- pmem[id].start_addr = pmem_start_addr_bitmap;
-
- DLOG("bitmap allocator id %d (%s), num_entries %u, raw size "
- "%lu, quanta size %u\n",
- id, pdata->name, pmem[id].allocator.bitmap.bitmap_free,
- pmem[id].size, pmem[id].quantum);
- break;
-
- case PMEM_ALLOCATORTYPE_SYSTEM:
-
- INIT_LIST_HEAD(&pmem[id].allocator.system_mem.alist);
-
- pmem[id].allocator.system_mem.used = 0;
- pmem[id].vbase = NULL;
-
- if (kobject_init_and_add(&pmem[id].kobj,
- &pmem_system_ktype, NULL,
- "%s", pdata->name))
- goto out_put_kobj;
-
- pmem[id].allocate = pmem_allocator_system;
- pmem[id].free = pmem_free_system;
- pmem[id].free_space = pmem_free_space_system;
- pmem[id].len = pmem_len_system;
- pmem[id].start_addr = pmem_start_addr_system;
- pmem[id].num_entries = 0;
- pmem[id].quantum = PAGE_SIZE;
-
- DLOG("system allocator id %d (%s), raw size %lu\n",
- id, pdata->name, pmem[id].size);
- break;
-
- default:
- pr_alert("Invalid allocator type (%d) for pmem driver\n",
- pdata->allocator_type);
- goto err_reset_pmem_info;
- }
-
- pmem[id].ioctl = ioctl;
- pmem[id].release = release;
- mutex_init(&pmem[id].arena_mutex);
- mutex_init(&pmem[id].data_list_mutex);
- INIT_LIST_HEAD(&pmem[id].data_list);
-
- pmem[id].dev.name = pdata->name;
- pmem[id].dev.minor = id;
- pmem[id].dev.fops = &pmem_fops;
- pmem[id].reusable = pdata->reusable;
- pr_info("pmem: Initializing %s as %s\n",
- pdata->name, pdata->cached ? "cached" : "non-cached");
-
- if (misc_register(&pmem[id].dev)) {
- pr_alert("Unable to register pmem driver!\n");
- goto err_cant_register_device;
- }
-
- if (!pmem[id].reusable) {
- pmem[id].base = allocate_contiguous_memory_nomap(pmem[id].size,
- pmem[id].memory_type, PAGE_SIZE);
- if (!pmem[id].base) {
- pr_err("pmem: Cannot allocate from reserved memory for %s\n",
- pdata->name);
- goto err_misc_deregister;
- }
- }
-
- /* reusable pmem requires map on demand */
- pmem[id].map_on_demand = pdata->map_on_demand || pdata->reusable;
- if (pmem[id].map_on_demand) {
- if (pmem[id].reusable) {
- const struct fmem_data *fmem_info = fmem_get_info();
- pmem[id].area = fmem_info->area;
- pmem[id].base = fmem_info->phys;
- } else {
- pmem_vma = get_vm_area(pmem[id].size, VM_IOREMAP);
- if (!pmem_vma) {
- pr_err("pmem: Failed to allocate virtual space for "
- "%s\n", pdata->name);
- goto err_free;
- }
- pr_err("pmem: Reserving virtual address range %lx - %lx for"
- " %s\n", (unsigned long) pmem_vma->addr,
- (unsigned long) pmem_vma->addr + pmem[id].size,
- pdata->name);
- pmem[id].area = pmem_vma;
- }
- } else
- pmem[id].area = NULL;
-
- page = alloc_page(GFP_KERNEL);
- if (!page) {
- pr_err("pmem: Failed to allocate page for %s\n", pdata->name);
- goto cleanup_vm;
- }
- pmem[id].garbage_pfn = page_to_pfn(page);
- atomic_set(&pmem[id].allocation_cnt, 0);
-
- if (pdata->setup_region)
- pmem[id].region_data = pdata->setup_region();
-
- if (pdata->request_region)
- pmem[id].mem_request = pdata->request_region;
-
- if (pdata->release_region)
- pmem[id].mem_release = pdata->release_region;
-
- pr_info("allocating %lu bytes at %lx physical for %s\n",
- pmem[id].size, pmem[id].base, pmem[id].name);
-
- return 0;
-
-cleanup_vm:
- if (!pmem[id].reusable)
- remove_vm_area(pmem_vma);
-err_free:
- if (!pmem[id].reusable)
- free_contiguous_memory_by_paddr(pmem[id].base);
-err_misc_deregister:
- misc_deregister(&pmem[id].dev);
-err_cant_register_device:
-out_put_kobj:
- kobject_put(&pmem[id].kobj);
- if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_BUDDYBESTFIT)
- kfree(pmem[id].allocator.buddy_bestfit.buddy_bitmap);
- else if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_BITMAP) {
- kfree(pmem[id].allocator.bitmap.bitmap);
- kfree(pmem[id].allocator.bitmap.bitm_alloc);
- }
-err_reset_pmem_info:
- pmem[id].allocate = 0;
- pmem[id].dev.minor = -1;
-err_no_mem:
- return -1;
-}
-
-static int pmem_probe(struct platform_device *pdev)
-{
- struct android_pmem_platform_data *pdata;
-
- if (!pdev || !pdev->dev.platform_data) {
- pr_alert("Unable to probe pmem!\n");
- return -1;
- }
- pdata = pdev->dev.platform_data;
-
- pm_runtime_set_active(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
-
- return pmem_setup(pdata, NULL, NULL);
-}
-
-static int pmem_remove(struct platform_device *pdev)
-{
- int id = pdev->id;
- __free_page(pfn_to_page(pmem[id].garbage_pfn));
- pm_runtime_disable(&pdev->dev);
- if (pmem[id].vbase)
- iounmap(pmem[id].vbase);
- if (pmem[id].map_on_demand && !pmem[id].reusable && pmem[id].area)
- free_vm_area(pmem[id].area);
- if (pmem[id].base)
- free_contiguous_memory_by_paddr(pmem[id].base);
- kobject_put(&pmem[id].kobj);
- if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_BUDDYBESTFIT)
- kfree(pmem[id].allocator.buddy_bestfit.buddy_bitmap);
- else if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_BITMAP) {
- kfree(pmem[id].allocator.bitmap.bitmap);
- kfree(pmem[id].allocator.bitmap.bitm_alloc);
- }
- misc_deregister(&pmem[id].dev);
- return 0;
-}
-
-static int pmem_runtime_suspend(struct device *dev)
-{
- dev_dbg(dev, "pm_runtime: suspending...\n");
- return 0;
-}
-
-static int pmem_runtime_resume(struct device *dev)
-{
- dev_dbg(dev, "pm_runtime: resuming...\n");
- return 0;
-}
-
-static const struct dev_pm_ops pmem_dev_pm_ops = {
- .runtime_suspend = pmem_runtime_suspend,
- .runtime_resume = pmem_runtime_resume,
-};
-
-static struct platform_driver pmem_driver = {
- .probe = pmem_probe,
- .remove = pmem_remove,
- .driver = { .name = "android_pmem",
- .pm = &pmem_dev_pm_ops,
- }
-};
-
-
-static int __init pmem_init(void)
-{
- /* create /sys/kernel/<PMEM_SYSFS_DIR_NAME> directory */
- pmem_kset = kset_create_and_add(PMEM_SYSFS_DIR_NAME,
- NULL, kernel_kobj);
- if (!pmem_kset) {
- pr_err("pmem(%s):kset_create_and_add fail\n", __func__);
- return -ENOMEM;
- }
-
- return platform_driver_register(&pmem_driver);
-}
-
-static void __exit pmem_exit(void)
-{
- platform_driver_unregister(&pmem_driver);
-}
-
-module_init(pmem_init);
-module_exit(pmem_exit);
-
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 55e3e4e..c60537a 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -45,13 +45,14 @@
#include "qseecom_kernel.h"
#define QSEECOM_DEV "qseecom"
-#define QSEOS_VERSION_13 0x13
#define QSEOS_VERSION_14 0x14
#define QSEEE_VERSION_00 0x400000
#define QSEE_VERSION_01 0x401000
#define QSEE_VERSION_02 0x402000
#define QSEE_VERSION_03 0x403000
#define QSEE_VERSION_04 0x404000
+#define QSEE_VERSION_05 0x405000
+
#define QSEOS_CHECK_VERSION_CMD 0x00001803
@@ -88,11 +89,6 @@
static dev_t qseecom_device_no;
static struct cdev qseecom_cdev;
-/* Data structures used in legacy support */
-static void *pil;
-static uint32_t pil_ref_cnt;
-static DEFINE_MUTEX(pil_access_lock);
-
static DEFINE_MUTEX(qsee_bw_mutex);
static DEFINE_MUTEX(app_access_lock);
static DEFINE_MUTEX(clk_access_lock);
@@ -276,63 +272,24 @@
svc->sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt, svc->ihandle);
svc->sb_phys = pa;
- if (qseecom.qseos_version == QSEOS_VERSION_14) {
- req.qsee_cmd_id = QSEOS_REGISTER_LISTENER;
- req.listener_id = svc->svc.listener_id;
- req.sb_len = svc->sb_length;
- req.sb_ptr = (void *)svc->sb_phys;
+ req.qsee_cmd_id = QSEOS_REGISTER_LISTENER;
+ req.listener_id = svc->svc.listener_id;
+ req.sb_len = svc->sb_length;
+ req.sb_ptr = (void *)svc->sb_phys;
- resp.result = QSEOS_RESULT_INCOMPLETE;
+ resp.result = QSEOS_RESULT_INCOMPLETE;
- ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
+ ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
sizeof(req), &resp, sizeof(resp));
- if (ret) {
- pr_err("qseecom_scm_call failed with err: %d\n", ret);
- return -EINVAL;
- }
+ if (ret) {
+ pr_err("qseecom_scm_call failed with err: %d\n", ret);
+ return -EINVAL;
+ }
- if (resp.result != QSEOS_RESULT_SUCCESS) {
- pr_err("Error SB registration req: resp.result = %d\n",
- resp.result);
- return -EPERM;
- }
- } else {
- struct qseecom_command cmd;
- struct qseecom_response resp;
- struct qse_pr_init_sb_req_s sb_init_req;
- struct qse_pr_init_sb_rsp_s sb_init_rsp;
-
- svc->sb_reg_req = kzalloc((sizeof(sb_init_req) +
- sizeof(sb_init_rsp)), GFP_KERNEL);
-
- sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
- sb_init_req.listener_id = svc->svc.listener_id;
- sb_init_req.sb_len = svc->sb_length;
- sb_init_req.sb_ptr = svc->sb_phys;
-
- memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
-
- /* It will always be a new cmd from this method */
- cmd.cmd_type = TZ_SCHED_CMD_NEW;
- cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
- cmd.sb_in_cmd_len = sizeof(sb_init_req);
-
- resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
-
- ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd)
- , &resp, sizeof(resp));
-
- if (ret) {
- pr_err("qseecom_scm_call failed with err: %d\n", ret);
- return -EINVAL;
- }
-
- if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
- pr_err("SB registration fail resp.cmd_status %d\n",
- resp.cmd_status);
- return -EINVAL;
- }
- memset(svc->sb_virt, 0, svc->sb_length);
+ if (resp.result != QSEOS_RESULT_SUCCESS) {
+ pr_err("Error SB registration req: resp.result = %d\n",
+ resp.result);
+ return -EPERM;
}
return 0;
}
@@ -394,56 +351,24 @@
struct qseecom_command_scm_resp resp;
struct ion_handle *ihandle = NULL; /* Retrieve phy addr */
- if (qseecom.qseos_version == QSEOS_VERSION_14) {
- req.qsee_cmd_id = QSEOS_DEREGISTER_LISTENER;
- req.listener_id = data->listener.id;
- resp.result = QSEOS_RESULT_INCOMPLETE;
+ req.qsee_cmd_id = QSEOS_DEREGISTER_LISTENER;
+ req.listener_id = data->listener.id;
+ resp.result = QSEOS_RESULT_INCOMPLETE;
- ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
+ ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
sizeof(req), &resp, sizeof(resp));
- if (ret) {
- pr_err("scm_call() failed with err: %d (lstnr id=%d)\n",
- ret, data->listener.id);
- return ret;
- }
-
- if (resp.result != QSEOS_RESULT_SUCCESS) {
- pr_err("Failed resp.result=%d,(lstnr id=%d)\n",
- resp.result, data->listener.id);
- return -EPERM;
- }
- } else {
- struct qse_pr_init_sb_req_s sb_init_req;
- struct qseecom_command cmd;
- struct qseecom_response resp;
- struct qseecom_registered_listener_list *svc;
-
- svc = __qseecom_find_svc(data->listener.id);
- sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
- sb_init_req.listener_id = data->listener.id;
- sb_init_req.sb_len = 0;
- sb_init_req.sb_ptr = 0;
-
- memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
-
- /* It will always be a new cmd from this method */
- cmd.cmd_type = TZ_SCHED_CMD_NEW;
- cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
- cmd.sb_in_cmd_len = sizeof(sb_init_req);
- resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
-
- ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd),
- &resp, sizeof(resp));
- if (ret) {
- pr_err("qseecom_scm_call failed with err: %d\n", ret);
- return ret;
- }
- kzfree(svc->sb_reg_req);
- if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
- pr_err("Error with SB initialization\n");
- return -EPERM;
- }
+ if (ret) {
+ pr_err("scm_call() failed with err: %d (lstnr id=%d)\n",
+ ret, data->listener.id);
+ return ret;
}
+
+ if (resp.result != QSEOS_RESULT_SUCCESS) {
+ pr_err("Failed resp.result=%d,(lstnr id=%d)\n",
+ resp.result, data->listener.id);
+ return -EPERM;
+ }
+
data->abort = 1;
spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
list_for_each_entry(ptr_svc, &qseecom.registered_listener_list_head,
@@ -538,6 +463,8 @@
unsigned long flags;
struct qseecom_client_listener_data_irsp send_data_rsp;
struct qseecom_registered_listener_list *ptr_svc = NULL;
+ sigset_t new_sigset;
+ sigset_t old_sigset;
while (resp->result == QSEOS_RESULT_INCOMPLETE) {
lstnr = resp->data;
@@ -562,17 +489,24 @@
}
pr_debug("waking up rcv_req_wq and "
"waiting for send_resp_wq\n");
- if (wait_event_freezable(qseecom.send_resp_wq,
- __qseecom_listener_has_sent_rsp(data))) {
- pr_warning("Interrupted: exiting send_cmd loop\n");
- ret = -ERESTARTSYS;
- }
- if ((data->abort) || (ret == -ERESTARTSYS)) {
+ /* initialize the new signal mask with all signals*/
+ sigfillset(&new_sigset);
+ /* block all signals */
+ sigprocmask(SIG_SETMASK, &new_sigset, &old_sigset);
+
+ do {
+ if (!wait_event_freezable(qseecom.send_resp_wq,
+ __qseecom_listener_has_sent_rsp(data)))
+ break;
+ } while (1);
+
+ /* restore signal mask */
+ sigprocmask(SIG_SETMASK, &old_sigset, NULL);
+ if (data->abort) {
pr_err("Abort clnt %d waiting on lstnr svc %d, ret %d",
data->client.app_id, lstnr, ret);
- if (data->abort)
- rc = -ENODEV;
+ rc = -ENODEV;
send_data_rsp.status = QSEOS_RESULT_FAILURE;
} else {
send_data_rsp.status = QSEOS_RESULT_SUCCESS;
@@ -819,8 +753,7 @@
bool unload = false;
bool found_app = false;
- if ((qseecom.qseos_version == QSEOS_VERSION_14) &&
- (data->client.app_id > 0)) {
+ if (data->client.app_id > 0) {
spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
list) {
@@ -846,7 +779,7 @@
}
}
- if ((unload) && (qseecom.qseos_version == QSEOS_VERSION_14)) {
+ if (unload) {
struct qseecom_unload_app_ireq req;
__qseecom_cleanup_app(data);
@@ -879,19 +812,6 @@
}
}
}
-
- if (qseecom.qseos_version == QSEOS_VERSION_13) {
- data->abort = 1;
- wake_up_all(&qseecom.send_resp_wq);
- while (atomic_read(&data->ioctl_count) > 0) {
- if (wait_event_freezable(data->abort_wq,
- atomic_read(&data->ioctl_count) <= 0)) {
- pr_err("Interrupted from abort\n");
- ret = -ERESTARTSYS;
- break;
- }
- }
- }
qseecom_unmap_ion_allocated_memory(data);
data->released = true;
return ret;
@@ -903,98 +823,6 @@
return data->client.sb_phys + (virt - data->client.user_virt_sb_base);
}
-static int __qseecom_send_cmd_legacy(struct qseecom_dev_handle *data,
- struct qseecom_send_cmd_req *req)
-{
- int ret = 0;
- unsigned long flags;
- u32 reqd_len_sb_in = 0;
- struct qseecom_command cmd;
- struct qseecom_response resp;
-
-
- if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
- pr_err("cmd buffer or response buffer is null\n");
- return -EINVAL;
- }
-
- if (req->cmd_req_len <= 0 ||
- req->resp_len <= 0 ||
- req->cmd_req_len > data->client.sb_length ||
- req->resp_len > data->client.sb_length) {
- pr_err("cmd buffer length or "
- "response buffer length not valid\n");
- return -EINVAL;
- }
-
- reqd_len_sb_in = req->cmd_req_len + req->resp_len;
- if (reqd_len_sb_in > data->client.sb_length) {
- pr_debug("Not enough memory to fit cmd_buf and "
- "resp_buf. Required: %u, Available: %u\n",
- reqd_len_sb_in, data->client.sb_length);
- return -ENOMEM;
- }
- cmd.cmd_type = TZ_SCHED_CMD_NEW;
- cmd.sb_in_cmd_addr = (u8 *) data->client.sb_phys;
- cmd.sb_in_cmd_len = req->cmd_req_len;
-
- resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
- resp.sb_in_rsp_addr = (u8 *)data->client.sb_phys + req->cmd_req_len;
- resp.sb_in_rsp_len = req->resp_len;
-
- ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
- sizeof(cmd), &resp, sizeof(resp));
-
- if (ret) {
- pr_err("qseecom_scm_call_legacy failed with err: %d\n", ret);
- return ret;
- }
-
- while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
- /*
- * If cmd is incomplete, get the callback cmd out from SB out
- * and put it on the list
- */
- struct qseecom_registered_listener_list *ptr_svc = NULL;
- /*
- * We don't know which service can handle the command. so we
- * wake up all blocking services and let them figure out if
- * they can handle the given command.
- */
- spin_lock_irqsave(&qseecom.registered_listener_list_lock,
- flags);
- list_for_each_entry(ptr_svc,
- &qseecom.registered_listener_list_head, list) {
- ptr_svc->rcv_req_flag = 1;
- wake_up_interruptible(&ptr_svc->rcv_req_wq);
- }
- spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
- flags);
-
- pr_debug("waking up rcv_req_wq and "
- "waiting for send_resp_wq\n");
- if (wait_event_freezable(qseecom.send_resp_wq,
- __qseecom_listener_has_sent_rsp(data))) {
- pr_warning("qseecom Interrupted: exiting send_cmd loop\n");
- return -ERESTARTSYS;
- }
-
- if (data->abort) {
- pr_err("Aborting driver\n");
- return -ENODEV;
- }
- qseecom.send_resp_flag = 0;
- cmd.cmd_type = TZ_SCHED_CMD_PENDING;
- ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
- sizeof(cmd), &resp, sizeof(resp));
- if (ret) {
- pr_err("qseecom_scm_call failed with err: %d\n", ret);
- return ret;
- }
- }
- return ret;
-}
-
int __qseecom_process_rpmb_svc_cmd(struct qseecom_dev_handle *data_ptr,
struct qseecom_send_svc_cmd_req *req_ptr,
struct qseecom_client_send_service_ireq *send_svc_ireq_ptr)
@@ -1160,10 +988,8 @@
pr_err("copy_from_user failed\n");
return ret;
}
- if (qseecom.qseos_version == QSEOS_VERSION_14)
- ret = __qseecom_send_cmd(data, &req);
- else
- ret = __qseecom_send_cmd_legacy(data, &req);
+ ret = __qseecom_send_cmd(data, &req);
+
if (ret)
return ret;
@@ -1172,27 +998,8 @@
return ret;
}
-static int __qseecom_send_cmd_req_clean_up(
- struct qseecom_send_modfd_cmd_req *req)
-{
- char *field;
- uint32_t *update;
- int ret = 0;
- int i = 0;
-
- for (i = 0; i < MAX_ION_FD; i++) {
- if (req->ifd_data[i].fd > 0) {
- field = (char *)req->cmd_req_buf +
- req->ifd_data[i].cmd_buf_offset;
- update = (uint32_t *) field;
- *update = 0;
- }
- }
- return ret;
-}
-
-static int __qseecom_update_with_phy_addr(
- struct qseecom_send_modfd_cmd_req *req)
+static int __qseecom_update_cmd_buf(struct qseecom_send_modfd_cmd_req *req,
+ bool cleanup)
{
struct ion_handle *ihandle;
char *field;
@@ -1231,7 +1038,11 @@
if (sg_ptr->nents == 1) {
uint32_t *update;
update = (uint32_t *) field;
- *update = (uint32_t)sg_dma_address(sg_ptr->sgl);
+ if (cleanup)
+ *update = 0;
+ else
+ *update = (uint32_t)sg_dma_address(
+ sg_ptr->sgl);
} else {
struct qseecom_sg_entry *update;
struct scatterlist *sg;
@@ -1239,9 +1050,14 @@
update = (struct qseecom_sg_entry *) field;
sg = sg_ptr->sgl;
for (j = 0; j < sg_ptr->nents; j++) {
- update->phys_addr = (uint32_t)
- sg_dma_address(sg);
- update->len = (uint32_t)sg->length;
+ if (cleanup) {
+ update->phys_addr = 0;
+ update->len = 0;
+ } else {
+ update->phys_addr = (uint32_t)
+ sg_dma_address(sg);
+ update->len = sg->length;
+ }
update++;
sg = sg_next(sg);
}
@@ -1275,18 +1091,15 @@
send_cmd_req.resp_buf = req.resp_buf;
send_cmd_req.resp_len = req.resp_len;
- ret = __qseecom_update_with_phy_addr(&req);
+ ret = __qseecom_update_cmd_buf(&req, false);
if (ret)
return ret;
- if (qseecom.qseos_version == QSEOS_VERSION_14)
- ret = __qseecom_send_cmd(data, &send_cmd_req);
- else
- ret = __qseecom_send_cmd_legacy(data, &send_cmd_req);
- __qseecom_send_cmd_req_clean_up(&req);
-
+ ret = __qseecom_send_cmd(data, &send_cmd_req);
if (ret)
return ret;
-
+ ret = __qseecom_update_cmd_buf(&req, true);
+ if (ret)
+ return ret;
pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
req.resp_len, req.resp_buf);
return ret;
@@ -1322,12 +1135,7 @@
return -ENODEV;
}
this_lstnr->rcv_req_flag = 0;
- if (qseecom.qseos_version == QSEOS_VERSION_13) {
- if (*((uint32_t *)this_lstnr->sb_virt) != 0)
- break;
- } else {
- break;
- }
+ break;
}
return ret;
}
@@ -1625,11 +1433,6 @@
uint32_t len;
ion_phys_addr_t pa;
- if (qseecom.qseos_version == QSEOS_VERSION_13) {
- pr_err("This functionality is UNSUPPORTED in version 1.3\n");
- return -EINVAL;
- }
-
*handle = kzalloc(sizeof(struct qseecom_handle), GFP_KERNEL);
if (!(*handle)) {
pr_err("failed to allocate memory for kernel client handle\n");
@@ -1648,7 +1451,6 @@
data->abort = 0;
data->type = QSEECOM_CLIENT_APP;
data->released = false;
- data->client.app_id = ret;
data->client.sb_length = size;
data->client.user_virt_sb_base = 0;
data->client.ihandle = NULL;
@@ -1665,35 +1467,27 @@
*handle = NULL;
return -EINVAL;
}
-
+ mutex_lock(&app_access_lock);
if (qseecom.qsee_version > QSEEE_VERSION_00) {
- mutex_lock(&app_access_lock);
if (qseecom.commonlib_loaded == false) {
ret = qseecom_load_commonlib_image(data);
if (ret == 0)
qseecom.commonlib_loaded = true;
}
- mutex_unlock(&app_access_lock);
}
-
if (ret) {
pr_err("Failed to load commonlib image\n");
- kfree(data);
- kfree(*handle);
- *handle = NULL;
- return -EIO;
+ ret = -EIO;
+ goto err;
}
app_ireq.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
memcpy(app_ireq.app_name, app_name, MAX_APP_NAME_SIZE);
ret = __qseecom_check_app_exists(app_ireq);
- if (ret < 0) {
- kzfree(data);
- kfree(*handle);
- *handle = NULL;
- return -EINVAL;
- }
+ if (ret < 0)
+ goto err;
+ data->client.app_id = ret;
if (ret > 0) {
pr_warn("App id %d for [%s] app exists\n", ret,
(char *)app_ireq.app_name);
@@ -1715,26 +1509,17 @@
/* load the app and get the app_id */
pr_debug("%s: Loading app for the first time'\n",
qseecom.pdev->init_name);
- mutex_lock(&app_access_lock);
ret = __qseecom_load_fw(data, app_name);
- mutex_unlock(&app_access_lock);
-
- if (ret < 0) {
- kfree(*handle);
- kfree(data);
- *handle = NULL;
- return ret;
- }
+ if (ret < 0)
+ goto err;
data->client.app_id = ret;
}
if (!found_app) {
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
- pr_err("kmalloc failed\n");
- kfree(data);
- kfree(*handle);
- *handle = NULL;
- return -ENOMEM;
+ pr_err("kmalloc for app entry failed\n");
+ ret = -ENOMEM;
+ goto err;
}
entry->app_id = ret;
entry->ref_cnt = 1;
@@ -1759,7 +1544,8 @@
kclient_entry = kzalloc(sizeof(*kclient_entry), GFP_KERNEL);
if (!kclient_entry) {
pr_err("kmalloc failed\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err;
}
kclient_entry->handle = *handle;
@@ -1768,7 +1554,15 @@
&qseecom.registered_kclient_list_head);
spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
+ mutex_unlock(&app_access_lock);
return 0;
+
+err:
+ kfree(data);
+ kfree(*handle);
+ *handle = NULL;
+ mutex_unlock(&app_access_lock);
+ return ret;
}
EXPORT_SYMBOL(qseecom_start_app);
@@ -1781,10 +1575,6 @@
unsigned long flags = 0;
bool found_handle = false;
- if (qseecom.qseos_version == QSEOS_VERSION_13) {
- pr_err("This functionality is UNSUPPORTED in version 1.3\n");
- return -EINVAL;
- }
if ((handle == NULL) || (*handle == NULL)) {
pr_err("Handle is not initialized\n");
return -EINVAL;
@@ -1825,11 +1615,6 @@
struct qseecom_send_cmd_req req = {0, 0, 0, 0};
struct qseecom_dev_handle *data;
- if (qseecom.qseos_version == QSEOS_VERSION_13) {
- pr_err("This functionality is UNSUPPORTED in version 1.3\n");
- return -EINVAL;
- }
-
if (handle == NULL) {
pr_err("Handle is not initialized\n");
return -EINVAL;
@@ -1917,6 +1702,10 @@
qclk = &qseecom.ce_drv;
mutex_lock(&clk_access_lock);
+
+ if (qclk->clk_access_cnt == ULONG_MAX)
+ goto err;
+
if (qclk->clk_access_cnt > 0) {
qclk->clk_access_cnt++;
mutex_unlock(&clk_access_lock);
@@ -1964,6 +1753,12 @@
qclk = &qseecom.ce_drv;
mutex_lock(&clk_access_lock);
+
+ if (qclk->clk_access_cnt == 0) {
+ mutex_unlock(&clk_access_lock);
+ return;
+ }
+
if (qclk->clk_access_cnt == 1) {
if (qclk->ce_clk != NULL)
clk_disable_unprepare(qclk->ce_clk);
@@ -2382,9 +2177,11 @@
memcpy(ireq.key_id, key_id, QSEECOM_KEY_ID_SIZE);
ireq.flags = flags;
+ ireq.qsee_command_id = QSEOS_GENERATE_KEY;
__qseecom_enable_clk(CLK_QSEE);
- ret = scm_call(SCM_SVC_CRYPTO, QSEOS_GENERATE_KEY,
+
+ ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
&ireq, sizeof(struct qseecom_key_generate_ireq),
&resp, sizeof(resp));
if (ret) {
@@ -2396,10 +2193,20 @@
switch (resp.result) {
case QSEOS_RESULT_SUCCESS:
break;
+ case QSEOS_RESULT_FAIL_KEY_ID_EXISTS:
+ pr_debug("process_incomplete_cmd return Key ID exists.\n");
+ break;
case QSEOS_RESULT_INCOMPLETE:
ret = __qseecom_process_incomplete_cmd(data, &resp);
- if (ret)
- pr_err("process_incomplete_cmd FAILED\n");
+ if (ret) {
+ if (resp.result == QSEOS_RESULT_FAIL_KEY_ID_EXISTS) {
+ pr_debug("process_incomplete_cmd return Key ID exists.\n");
+ ret = 0;
+ } else {
+ pr_err("process_incomplete_cmd FAILED, resp.result %d\n",
+ resp.result);
+ }
+ }
break;
case QSEOS_RESULT_FAILURE:
default:
@@ -2426,9 +2233,11 @@
memcpy(ireq.key_id, key_id, QSEECOM_KEY_ID_SIZE);
ireq.flags = flags;
+ ireq.qsee_command_id = QSEOS_DELETE_KEY;
__qseecom_enable_clk(CLK_QSEE);
- ret = scm_call(SCM_SVC_CRYPTO, QSEOS_DELETE_KEY,
+
+ ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
&ireq, sizeof(struct qseecom_key_delete_ireq),
&resp, sizeof(struct qseecom_command_scm_resp));
if (ret) {
@@ -2443,7 +2252,8 @@
case QSEOS_RESULT_INCOMPLETE:
ret = __qseecom_process_incomplete_cmd(data, &resp);
if (ret)
- pr_err("process_incomplete_cmd FAILED\n");
+ pr_err("process_incomplete_cmd FAILED, resp.result %d\n",
+ resp.result);
break;
case QSEOS_RESULT_FAILURE:
default:
@@ -2469,16 +2279,19 @@
return -EFAULT;
}
- if (qseecom.qsee.instance == qseecom.ce_drv.instance)
- __qseecom_enable_clk(CLK_QSEE);
- else
+ __qseecom_enable_clk(CLK_QSEE);
+ if (qseecom.qsee.instance != qseecom.ce_drv.instance)
__qseecom_enable_clk(CLK_CE_DRV);
memcpy(ireq.key_id, set_key_para->key_id, QSEECOM_KEY_ID_SIZE);
+ ireq.qsee_command_id = QSEOS_SET_KEY;
ireq.ce = set_key_para->ce_hw;
ireq.pipe = set_key_para->pipe;
ireq.flags = set_key_para->flags;
+ /* set both PIPE_ENC and PIPE_ENC_XTS*/
+ ireq.pipe_type = QSEOS_PIPE_ENC|QSEOS_PIPE_ENC_XTS;
+
if (set_key_para->set_clear_key_flag ==
QSEECOM_SET_CE_KEY_CMD)
memcpy((void *)ireq.hash, (void *)set_key_para->hash32,
@@ -2486,11 +2299,14 @@
else
memset((void *)ireq.hash, 0, QSEECOM_HASH_SIZE);
- ret = scm_call(SCM_SVC_CRYPTO, QSEOS_SET_KEY,
+ ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
&ireq, sizeof(struct qseecom_key_select_ireq),
&resp, sizeof(struct qseecom_command_scm_resp));
if (ret) {
- pr_err("scm call to set key failed : %d\n", ret);
+ pr_err("scm call to set QSEOS_PIPE_ENC key failed : %d\n", ret);
+ __qseecom_disable_clk(CLK_QSEE);
+ if (qseecom.qsee.instance != qseecom.ce_drv.instance)
+ __qseecom_disable_clk(CLK_CE_DRV);
return ret;
}
@@ -2500,7 +2316,8 @@
case QSEOS_RESULT_INCOMPLETE:
ret = __qseecom_process_incomplete_cmd(data, &resp);
if (ret)
- pr_err("process_incomplete_cmd FAILED\n");
+ pr_err("process_incomplete_cmd FAILED, resp.result %d\n",
+ resp.result);
break;
case QSEOS_RESULT_FAILURE:
default:
@@ -2509,9 +2326,8 @@
break;
}
- if (qseecom.qsee.instance == qseecom.ce_drv.instance)
- __qseecom_disable_clk(CLK_QSEE);
- else
+ __qseecom_disable_clk(CLK_QSEE);
+ if (qseecom.qsee.instance != qseecom.ce_drv.instance)
__qseecom_disable_clk(CLK_CE_DRV);
return ret;
@@ -2827,11 +2643,6 @@
}
case QSEECOM_IOCTL_LOAD_EXTERNAL_ELF_REQ: {
data->released = true;
- if (qseecom.qseos_version == QSEOS_VERSION_13) {
- pr_err("Loading External elf image unsupported in rev 0x13\n");
- ret = -EINVAL;
- break;
- }
mutex_lock(&app_access_lock);
atomic_inc(&data->ioctl_count);
ret = qseecom_load_external_elf(data, argp);
@@ -2843,11 +2654,6 @@
}
case QSEECOM_IOCTL_UNLOAD_EXTERNAL_ELF_REQ: {
data->released = true;
- if (qseecom.qseos_version == QSEOS_VERSION_13) {
- pr_err("Unloading External elf image unsupported in rev 0x13\n");
- ret = -EINVAL;
- break;
- }
mutex_lock(&app_access_lock);
atomic_inc(&data->ioctl_count);
ret = qseecom_unload_external_elf(data);
@@ -2879,6 +2685,11 @@
break;
}
case QSEECOM_IOCTL_CREATE_KEY_REQ: {
+ if (qseecom.qsee_version < QSEE_VERSION_05) {
+ pr_err("Create Key feature not supported in qsee version %u\n",
+ qseecom.qsee_version);
+ return -EINVAL;
+ }
data->released = true;
mutex_lock(&app_access_lock);
atomic_inc(&data->ioctl_count);
@@ -2891,6 +2702,11 @@
break;
}
case QSEECOM_IOCTL_WIPE_KEY_REQ: {
+ if (qseecom.qsee_version < QSEE_VERSION_05) {
+ pr_err("Wipe Key feature not supported in qsee version %u\n",
+ qseecom.qsee_version);
+ return -EINVAL;
+ }
data->released = true;
mutex_lock(&app_access_lock);
atomic_inc(&data->ioctl_count);
@@ -2941,23 +2757,7 @@
data->released = false;
init_waitqueue_head(&data->abort_wq);
atomic_set(&data->ioctl_count, 0);
- if (qseecom.qseos_version == QSEOS_VERSION_13) {
- int pil_error;
- mutex_lock(&pil_access_lock);
- if (pil_ref_cnt == 0) {
- pil = subsystem_get("tzapps");
- if (IS_ERR(pil)) {
- pr_err("Playready PIL image load failed\n");
- pil_error = PTR_ERR(pil);
- pil = NULL;
- pr_debug("tzapps image load FAILED\n");
- mutex_unlock(&pil_access_lock);
- return pil_error;
- }
- }
- pil_ref_cnt++;
- mutex_unlock(&pil_access_lock);
- }
+
return ret;
}
@@ -2995,13 +2795,6 @@
if (data->perf_enabled == true)
qsee_disable_clock_vote(data, CLK_DFAB);
- if (qseecom.qseos_version == QSEOS_VERSION_13) {
- mutex_lock(&pil_access_lock);
- if (pil_ref_cnt == 1)
- subsystem_put(pil);
- pil_ref_cnt--;
- mutex_unlock(&pil_access_lock);
- }
kfree(data);
return ret;
@@ -3206,10 +2999,10 @@
}
qseecom.qseos_version = QSEOS_VERSION_14;
} else {
- qseecom.qseos_version = QSEOS_VERSION_13;
- qseecom.qsee_version = 0;
- pil = NULL;
- pil_ref_cnt = 0;
+ pr_err("QSEE legacy version is not supported:");
+ pr_err("Support for TZ1.3 and earlier is deprecated\n");
+ rc = -EINVAL;
+ goto err;
}
qseecom.commonlib_loaded = false;
qseecom.pdev = class_dev;
diff --git a/drivers/misc/tspp.c b/drivers/misc/tspp.c
index 73cae32..a395b7c 100644
--- a/drivers/misc/tspp.c
+++ b/drivers/misc/tspp.c
@@ -33,6 +33,7 @@
#include <linux/wait.h> /* wait() macros, sleeping */
#include <linux/tspp.h> /* tspp functions */
#include <linux/bitops.h> /* BIT() macro */
+#include <linux/regulator/consumer.h>
#include <mach/sps.h> /* BAM stuff */
#include <mach/gpio.h>
#include <linux/wakelock.h> /* Locking functions */
@@ -40,10 +41,12 @@
#include <linux/jiffies.h> /* Jiffies counter */
#include <mach/dma.h>
#include <mach/msm_tspp.h>
+#include <mach/rpm-regulator-smd.h>
#include <linux/debugfs.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/string.h>
+#include <mach/msm_bus.h>
/*
* General defines
@@ -60,9 +63,8 @@
/*
* BAM descriptor FIFO size (in number of descriptors).
* Max number of descriptors allowed by SPS which is 8K-1.
- * Restrict it to half of this to save DMA memory.
*/
-#define TSPP_SPS_DESCRIPTOR_COUNT (4 * 1024 - 1)
+#define TSPP_SPS_DESCRIPTOR_COUNT (8 * 1024 - 1)
#define TSPP_PACKET_LENGTH 188
#define TSPP_MIN_BUFFER_SIZE (TSPP_PACKET_LENGTH)
@@ -436,6 +438,7 @@
struct list_head devlist; /* list of all devices */
struct platform_device *pdev;
void __iomem *base;
+ uint32_t tsif_bus_client;
unsigned int tspp_irq;
unsigned int bam_irq;
u32 bam_handle;
@@ -447,6 +450,8 @@
/* clocks */
struct clk *tsif_pclk;
struct clk *tsif_ref_clk;
+ /* regulators */
+ struct regulator *tsif_vreg;
/* data */
struct tspp_pid_filter_table *filters[TSPP_FILTER_TABLES];
struct tspp_channel channels[TSPP_NUM_CHANNELS];
@@ -763,13 +768,47 @@
/*** Clock functions ***/
static int tspp_clock_start(struct tspp_device *device)
{
+ int rc;
+
if (device == NULL) {
pr_err("tspp: Can't start clocks, invalid device\n");
return -EINVAL;
}
+ if (device->tsif_bus_client) {
+ rc = msm_bus_scale_client_update_request(
+ device->tsif_bus_client, 1);
+ if (rc) {
+ pr_err("tspp: Can't enable bus\n");
+ return -EBUSY;
+ }
+ }
+
+ if (device->tsif_vreg) {
+ rc = regulator_set_voltage(device->tsif_vreg,
+ RPM_REGULATOR_CORNER_SUPER_TURBO,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ if (rc) {
+ pr_err("Unable to set CX voltage.\n");
+ if (device->tsif_bus_client)
+ msm_bus_scale_client_update_request(
+ device->tsif_bus_client, 0);
+ return rc;
+ }
+ }
+
if (device->tsif_pclk && clk_prepare_enable(device->tsif_pclk) != 0) {
pr_err("tspp: Can't start pclk");
+
+ if (device->tsif_vreg) {
+ regulator_set_voltage(device->tsif_vreg,
+ RPM_REGULATOR_CORNER_SVS_SOC,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ }
+
+ if (device->tsif_bus_client)
+ msm_bus_scale_client_update_request(
+ device->tsif_bus_client, 0);
return -EBUSY;
}
@@ -777,6 +816,15 @@
clk_prepare_enable(device->tsif_ref_clk) != 0) {
pr_err("tspp: Can't start ref clk");
clk_disable_unprepare(device->tsif_pclk);
+ if (device->tsif_vreg) {
+ regulator_set_voltage(device->tsif_vreg,
+ RPM_REGULATOR_CORNER_SVS_SOC,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ }
+
+ if (device->tsif_bus_client)
+ msm_bus_scale_client_update_request(
+ device->tsif_bus_client, 0);
return -EBUSY;
}
@@ -785,6 +833,8 @@
static void tspp_clock_stop(struct tspp_device *device)
{
+ int rc;
+
if (device == NULL) {
pr_err("tspp: Can't stop clocks, invalid device\n");
return;
@@ -795,6 +845,21 @@
if (device->tsif_ref_clk)
clk_disable_unprepare(device->tsif_ref_clk);
+
+ if (device->tsif_vreg) {
+ rc = regulator_set_voltage(device->tsif_vreg,
+ RPM_REGULATOR_CORNER_SVS_SOC,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ if (rc)
+ pr_err("Unable to set CX voltage.\n");
+ }
+
+ if (device->tsif_bus_client) {
+ rc = msm_bus_scale_client_update_request(
+ device->tsif_bus_client, 0);
+ if (rc)
+ pr_err("tspp: Can't disable bus\n");
+ }
}
/*** TSIF functions ***/
@@ -2199,6 +2264,12 @@
return -EINVAL;
}
+ if (count > TSPP_NUM_BUFFERS) {
+ pr_err("%s: tspp requires a maximum of %i buffers\n",
+ __func__, TSPP_NUM_BUFFERS);
+ return -EINVAL;
+ }
+
channel = &pdev->channels[channel_id];
/* allow buffer allocation only if there was no previous buffer
@@ -2667,6 +2738,7 @@
struct device_node *node = pdev->dev.of_node;
struct msm_tspp_platform_data *data;
struct msm_gpio *gpios;
+ struct property *prop;
int i, rc;
int gpio;
u32 gpio_func;
@@ -2691,6 +2763,11 @@
return NULL;
}
+ data->tsif_vreg_present = 0;
+ prop = of_find_property(node, "vdd_cx-supply", NULL);
+ if (prop)
+ data->tsif_vreg_present = 1;
+
data->num_gpios = of_gpio_count(node);
if (data->num_gpios == 0) {
pr_err("tspp: Could not find GPIO definitions\n");
@@ -2803,6 +2880,7 @@
struct resource *mem_tspp;
struct resource *mem_bam;
struct tspp_channel *channel;
+ struct msm_bus_scale_pdata *tspp_bus_pdata = NULL;
if (pdev->dev.of_node) {
/* get information from device tree */
@@ -2814,9 +2892,12 @@
pdev->id = -1;
pdev->dev.platform_data = data;
+
+ tspp_bus_pdata = msm_bus_cl_get_pdata(pdev);
} else {
/* must have platform data */
data = pdev->dev.platform_data;
+ tspp_bus_pdata = NULL;
}
if (!data) {
pr_err("tspp: Platform data not available");
@@ -2843,6 +2924,41 @@
device->pdev = pdev;
platform_set_drvdata(pdev, device);
+ /* register bus client */
+ if (tspp_bus_pdata) {
+ device->tsif_bus_client =
+ msm_bus_scale_register_client(tspp_bus_pdata);
+ if (!device->tsif_bus_client)
+ pr_err("tspp: Unable to register bus client\n");
+ } else {
+ device->tsif_bus_client = 0;
+ }
+
+ /* map regulators */
+ if (data->tsif_vreg_present) {
+ device->tsif_vreg = devm_regulator_get(&pdev->dev, "vdd_cx");
+ if (IS_ERR(device->tsif_vreg)) {
+ rc = PTR_ERR(device->tsif_vreg);
+ device->tsif_vreg = NULL;
+ goto err_regultaor;
+ }
+
+ /* Set an initial voltage and enable the regulator */
+ rc = regulator_set_voltage(device->tsif_vreg,
+ RPM_REGULATOR_CORNER_SVS_SOC,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ if (rc) {
+ dev_err(&pdev->dev, "Unable to set CX voltage.\n");
+ goto err_regultaor;
+ }
+
+ rc = regulator_enable(device->tsif_vreg);
+ if (rc) {
+ dev_err(&pdev->dev, "Unable to enable CX regulator.\n");
+ goto err_regultaor;
+ }
+ }
+
/* map clocks */
if (data->tsif_pclk) {
device->tsif_pclk = clk_get(&pdev->dev, data->tsif_pclk);
@@ -3032,6 +3148,11 @@
if (device->tsif_pclk)
clk_put(device->tsif_pclk);
err_pclock:
+ if (device->tsif_vreg)
+ regulator_disable(device->tsif_vreg);
+err_regultaor:
+ if (device->tsif_bus_client)
+ msm_bus_scale_unregister_client(device->tsif_bus_client);
kfree(device);
out:
@@ -3067,6 +3188,9 @@
free_irq(device->tsif[i].tsif_irq, &device->tsif[i]);
}
+ if (device->tsif_bus_client)
+ msm_bus_scale_unregister_client(device->tsif_bus_client);
+
wake_lock_destroy(&device->wake_lock);
free_irq(device->tspp_irq, device);
@@ -3081,6 +3205,9 @@
if (device->tsif_pclk)
clk_put(device->tsif_pclk);
+ if (device->tsif_vreg)
+ regulator_disable(device->tsif_vreg);
+
pm_runtime_disable(&pdev->dev);
kfree(device);
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 5b7f08f..f01ddab 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -67,12 +67,19 @@
(rq_data_dir(req) == WRITE))
#define PACKED_CMD_VER 0x01
#define PACKED_CMD_WR 0x02
+#define PACKED_TRIGGER_MAX_ELEMENTS 5000
#define MMC_BLK_UPDATE_STOP_REASON(stats, reason) \
do { \
if (stats->enabled) \
stats->pack_stop_reason[reason]++; \
} while (0)
+#define PCKD_TRGR_INIT_MEAN_POTEN 17
+#define PCKD_TRGR_POTEN_LOWER_BOUND 5
+#define PCKD_TRGR_URGENT_PENALTY 2
+#define PCKD_TRGR_LOWER_BOUND 5
+#define PCKD_TRGR_PRECISION_MULTIPLIER 100
+
static DEFINE_MUTEX(block_mutex);
/*
@@ -221,6 +228,7 @@
md = mmc_blk_get(dev_to_disk(dev));
card = md->queue.card;
+ mmc_rpm_hold(card->host, &card->dev);
mmc_claim_host(card->host);
ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP,
@@ -233,6 +241,7 @@
card->ext_csd.boot_ro_lock |= EXT_CSD_BOOT_WP_B_PWR_WP_EN;
mmc_release_host(card->host);
+ mmc_rpm_release(card->host, &card->dev);
if (!ret) {
pr_info("%s: Locking boot partition ro until next power on\n",
@@ -595,7 +604,7 @@
md = mmc_blk_get(bdev->bd_disk);
if (!md) {
err = -EINVAL;
- goto cmd_done;
+ goto blk_err;
}
card = md->queue.card;
@@ -700,6 +709,7 @@
cmd_done:
mmc_blk_put(md);
+blk_err:
kfree(idata->buf);
kfree(idata);
return err;
@@ -1862,6 +1872,80 @@
}
EXPORT_SYMBOL(mmc_blk_disable_wr_packing);
+static int get_packed_trigger(int potential, struct mmc_card *card,
+ struct request *req, int curr_trigger)
+{
+ static int num_mean_elements = 1;
+ static unsigned long mean_potential = PCKD_TRGR_INIT_MEAN_POTEN;
+ unsigned int trigger = curr_trigger;
+ unsigned int pckd_trgr_upper_bound = card->ext_csd.max_packed_writes;
+
+ /* scale down the upper bound to 75% */
+ pckd_trgr_upper_bound = (pckd_trgr_upper_bound * 3) / 4;
+
+ /*
+ * since the most common calls for this function are with small
+ * potential write values and since we don't want these calls to affect
+ * the packed trigger, set a lower bound and ignore calls with
+ * potential lower than that bound
+ */
+ if (potential <= PCKD_TRGR_POTEN_LOWER_BOUND)
+ return trigger;
+
+ /*
+ * this is to prevent integer overflow in the following calculation:
+ * once every PACKED_TRIGGER_MAX_ELEMENTS reset the algorithm
+ */
+ if (num_mean_elements > PACKED_TRIGGER_MAX_ELEMENTS) {
+ num_mean_elements = 1;
+ mean_potential = PCKD_TRGR_INIT_MEAN_POTEN;
+ }
+
+ /*
+ * get next mean value based on previous mean value and current
+ * potential packed writes. Calculation is as follows:
+ * mean_pot[i+1] =
+ * ((mean_pot[i] * num_mean_elem) + potential)/(num_mean_elem + 1)
+ */
+ mean_potential *= num_mean_elements;
+ /*
+ * add num_mean_elements so that the division of two integers doesn't
+ * lower mean_potential too much
+ */
+ if (potential > mean_potential)
+ mean_potential += num_mean_elements;
+ mean_potential += potential;
+ /* this is for gaining more precision when dividing two integers */
+ mean_potential *= PCKD_TRGR_PRECISION_MULTIPLIER;
+ /* this completes the mean calculation */
+ mean_potential /= ++num_mean_elements;
+ mean_potential /= PCKD_TRGR_PRECISION_MULTIPLIER;
+
+ /*
+ * if current potential packed writes is greater than the mean potential
+ * then the heuristic is that the following workload will contain many
+ * write requests, therefore we lower the packed trigger. In the
+ * opposite case we want to increase the trigger in order to get less
+ * packing events.
+ */
+ if (potential >= mean_potential)
+ trigger = (trigger <= PCKD_TRGR_LOWER_BOUND) ?
+ PCKD_TRGR_LOWER_BOUND : trigger - 1;
+ else
+ trigger = (trigger >= pckd_trgr_upper_bound) ?
+ pckd_trgr_upper_bound : trigger + 1;
+
+ /*
+ * an urgent read request indicates a packed list being interrupted
+ * by this read, therefore we aim for less packing, hence the trigger
+ * gets increased
+ */
+ if (req && (req->cmd_flags & REQ_URGENT) && (rq_data_dir(req) == READ))
+ trigger += PCKD_TRGR_URGENT_PENALTY;
+
+ return trigger;
+}
+
static void mmc_blk_write_packing_control(struct mmc_queue *mq,
struct request *req)
{
@@ -1889,6 +1973,10 @@
if (mq->num_of_potential_packed_wr_reqs >
mq->num_wr_reqs_to_start_packing)
mq->wr_packing_enabled = true;
+ mq->num_wr_reqs_to_start_packing =
+ get_packed_trigger(mq->num_of_potential_packed_wr_reqs,
+ mq->card, req,
+ mq->num_wr_reqs_to_start_packing);
mq->num_of_potential_packed_wr_reqs = 0;
return;
}
@@ -1897,6 +1985,12 @@
if (data_dir == READ) {
mmc_blk_disable_wr_packing(mq);
+ mq->num_wr_reqs_to_start_packing =
+ get_packed_trigger(mq->num_of_potential_packed_wr_reqs,
+ mq->card, req,
+ mq->num_wr_reqs_to_start_packing);
+ mq->num_of_potential_packed_wr_reqs = 0;
+ mq->wr_packing_enabled = false;
return;
} else if (data_dir == WRITE) {
mq->num_of_potential_packed_wr_reqs++;
@@ -3028,6 +3122,37 @@
#endif
}
+static void mmc_blk_shutdown(struct mmc_card *card)
+{
+ struct mmc_blk_data *part_md;
+ struct mmc_blk_data *md = mmc_get_drvdata(card);
+ int rc;
+
+ /* Silent the block layer */
+ if (md) {
+ rc = mmc_queue_suspend(&md->queue);
+ if (rc)
+ goto suspend_error;
+ list_for_each_entry(part_md, &md->part, part) {
+ rc = mmc_queue_suspend(&part_md->queue);
+ if (rc)
+ goto suspend_error;
+ }
+ }
+
+ /* send power off notification */
+ if (mmc_card_mmc(card)) {
+ mmc_rpm_hold(card->host, &card->dev);
+ mmc_send_long_pon(card);
+ mmc_rpm_release(card->host, &card->dev);
+ }
+ return;
+
+suspend_error:
+ pr_err("%s: mmc_queue_suspend returned error = %d",
+ mmc_hostname(card->host), rc);
+}
+
#ifdef CONFIG_PM
static int mmc_blk_suspend(struct mmc_card *card)
{
@@ -3087,6 +3212,7 @@
.remove = mmc_blk_remove,
.suspend = mmc_blk_suspend,
.resume = mmc_blk_resume,
+ .shutdown = mmc_blk_shutdown,
};
static int __init mmc_blk_init(void)
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 7b9e133..a990f43 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -123,6 +123,25 @@
return 0;
}
+static void mmc_bus_shutdown(struct device *dev)
+{
+ struct mmc_driver *drv = to_mmc_driver(dev->driver);
+ struct mmc_card *card = mmc_dev_to_card(dev);
+
+ if (!drv) {
+ pr_debug("%s: %s: drv is NULL\n", dev_name(dev), __func__);
+ return;
+ }
+
+ if (!card) {
+ pr_debug("%s: %s: card is NULL\n", dev_name(dev), __func__);
+ return;
+ }
+
+ if (drv->shutdown)
+ drv->shutdown(card);
+}
+
#ifdef CONFIG_PM_SLEEP
static int mmc_bus_suspend(struct device *dev)
{
@@ -153,10 +172,18 @@
{
struct mmc_card *card = mmc_dev_to_card(dev);
- if (mmc_use_core_runtime_pm(card->host))
- return 0;
- else
+ if (mmc_use_core_runtime_pm(card->host)) {
+ /*
+ * If idle time bkops is running on the card, let's not get
+ * into suspend.
+ */
+ if (mmc_card_doing_bkops(card) && mmc_card_is_prog_state(card))
+ return -EBUSY;
+ else
+ return 0;
+ } else {
return mmc_power_save_host(card->host);
+ }
}
static int mmc_runtime_resume(struct device *dev)
@@ -238,6 +265,7 @@
.uevent = mmc_bus_uevent,
.probe = mmc_bus_probe,
.remove = mmc_bus_remove,
+ .shutdown = mmc_bus_shutdown,
.pm = &mmc_bus_pm_ops,
};
diff --git a/drivers/mmc/core/cd-gpio.c b/drivers/mmc/core/cd-gpio.c
index 2c14be7..14dd313 100644
--- a/drivers/mmc/core/cd-gpio.c
+++ b/drivers/mmc/core/cd-gpio.c
@@ -20,12 +20,44 @@
struct mmc_cd_gpio {
unsigned int gpio;
char label[0];
+ bool status;
};
+static int mmc_cd_get_status(struct mmc_host *host)
+{
+ int ret = -ENOSYS;
+ struct mmc_cd_gpio *cd = host->hotplug.handler_priv;
+
+ if (!cd || !gpio_is_valid(cd->gpio))
+ goto out;
+
+ ret = !gpio_get_value_cansleep(cd->gpio) ^
+ !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);
+out:
+ return ret;
+}
+
static irqreturn_t mmc_cd_gpio_irqt(int irq, void *dev_id)
{
- /* Schedule a card detection after a debounce timeout */
- mmc_detect_change(dev_id, msecs_to_jiffies(100));
+ struct mmc_host *host = dev_id;
+ struct mmc_cd_gpio *cd = host->hotplug.handler_priv;
+ int status;
+
+ status = mmc_cd_get_status(host);
+ if (unlikely(status < 0))
+ goto out;
+
+ if (status ^ cd->status) {
+ pr_info("%s: slot status change detected (%d -> %d), GPIO_ACTIVE_%s\n",
+ mmc_hostname(host), cd->status, status,
+ (host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH) ?
+ "HIGH" : "LOW");
+ cd->status = status;
+
+ /* Schedule a card detection after a debounce timeout */
+ mmc_detect_change(host, msecs_to_jiffies(100));
+ }
+out:
return IRQ_HANDLED;
}
@@ -49,16 +81,22 @@
if (ret < 0)
goto egpioreq;
+ cd->gpio = gpio;
+ host->hotplug.irq = irq;
+ host->hotplug.handler_priv = cd;
+
+ ret = mmc_cd_get_status(host);
+ if (ret < 0)
+ goto eirqreq;
+
+ cd->status = ret;
+
ret = request_threaded_irq(irq, NULL, mmc_cd_gpio_irqt,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
cd->label, host);
if (ret < 0)
goto eirqreq;
- cd->gpio = gpio;
- host->hotplug.irq = irq;
- host->hotplug.handler_priv = cd;
-
return 0;
eirqreq:
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 73a1b41..04687fa 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -487,11 +487,6 @@
mmc_card_clr_need_bkops(card);
mmc_card_set_doing_bkops(card);
- pr_debug("%s: %s: starting the polling thread\n",
- mmc_hostname(card->host), __func__);
- queue_work(system_nrt_wq,
- &card->bkops_info.poll_for_completion);
-
out:
mmc_release_host(card->host);
mmc_rpm_release(card->host, &card->dev);
@@ -499,81 +494,6 @@
EXPORT_SYMBOL(mmc_start_bkops);
/**
- * mmc_bkops_completion_polling() - Poll on the card status to
- * wait for the non-blocking BKOPS completion
- * @work: The completion polling work
- *
- * The on-going reading of the card status will prevent the card
- * from getting into suspend while it is in the middle of
- * performing BKOPS.
- * Since the non blocking BKOPS can be interrupted by a fetched
- * request we also check IF mmc_card_doing_bkops in each
- * iteration.
- */
-void mmc_bkops_completion_polling(struct work_struct *work)
-{
- struct mmc_card *card = container_of(work, struct mmc_card,
- bkops_info.poll_for_completion);
- unsigned long timeout_jiffies = jiffies +
- msecs_to_jiffies(BKOPS_COMPLETION_POLLING_TIMEOUT_MS);
- u32 status;
- int err;
-
- /*
- * Wait for the BKOPs to complete. Keep reading the status to prevent
- * the host from getting into suspend
- */
- do {
- mmc_rpm_hold(card->host, &card->dev);
- mmc_claim_host(card->host);
-
- if (!mmc_card_doing_bkops(card))
- goto out;
-
- err = mmc_send_status(card, &status);
- if (err) {
- pr_err("%s: error %d requesting status\n",
- mmc_hostname(card->host), err);
- goto out;
- }
-
- /*
- * Some cards mishandle the status bits, so make sure to check
- * both the busy indication and the card state.
- */
- if ((status & R1_READY_FOR_DATA) &&
- (R1_CURRENT_STATE(status) != R1_STATE_PRG)) {
- pr_debug("%s: %s: completed BKOPs, exit polling\n",
- mmc_hostname(card->host), __func__);
- mmc_card_clr_doing_bkops(card);
- card->bkops_info.sectors_changed = 0;
- goto out;
- }
-
- mmc_release_host(card->host);
- mmc_rpm_release(card->host, &card->dev);
-
- /*
- * Sleep before checking the card status again to allow the
- * card to complete the BKOPs operation
- */
- msleep(BKOPS_COMPLETION_POLLING_INTERVAL_MS);
- } while (time_before(jiffies, timeout_jiffies));
-
- pr_err("%s: %s: exit polling due to timeout, stop bkops\n",
- mmc_hostname(card->host), __func__);
- err = mmc_stop_bkops(card);
- if (err)
- pr_err("%s: %s: mmc_stop_bkops failed, err=%d\n",
- mmc_hostname(card->host), __func__, err);
-
- return;
-out:
- mmc_release_host(card->host);
- mmc_rpm_release(card->host, &card->dev);
-}
-
-/**
* mmc_start_idle_time_bkops() - check if a non urgent BKOPS is
* needed
* @work: The idle time BKOPS work
@@ -1066,6 +986,36 @@
}
EXPORT_SYMBOL(mmc_wait_for_req);
+bool mmc_card_is_prog_state(struct mmc_card *card)
+{
+ bool rc;
+ struct mmc_command cmd;
+
+ mmc_claim_host(card->host);
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = MMC_SEND_STATUS;
+ if (!mmc_host_is_spi(card->host))
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+ rc = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (rc) {
+ pr_err("%s: Get card status fail. rc=%d\n",
+ mmc_hostname(card->host), rc);
+ rc = false;
+ goto out;
+ }
+
+ if (R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG)
+ rc = true;
+ else
+ rc = false;
+out:
+ mmc_release_host(card->host);
+ return rc;
+}
+EXPORT_SYMBOL(mmc_card_is_prog_state);
+
/**
* mmc_interrupt_hpi - Issue for High priority Interrupt
* @card: the MMC card associated with the HPI transfer
@@ -1123,8 +1073,13 @@
if (!err && R1_CURRENT_STATE(status) == R1_STATE_TRAN)
break;
- if (time_after(jiffies, prg_wait))
- err = -ETIMEDOUT;
+ if (time_after(jiffies, prg_wait)) {
+ err = mmc_send_status(card, &status);
+ if (!err && R1_CURRENT_STATE(status) != R1_STATE_TRAN)
+ err = -ETIMEDOUT;
+ else
+ break;
+ }
} while (!err);
out:
@@ -1189,6 +1144,17 @@
if (!mmc_card_doing_bkops(card))
goto out;
+ /*
+ * If idle time bkops is running on the card, let's not get into
+ * suspend.
+ */
+ if (!mmc_use_core_runtime_pm(card->host) && mmc_card_doing_bkops(card)
+ && (card->host->parent->power.runtime_status == RPM_SUSPENDING)
+ && mmc_card_is_prog_state(card)) {
+ err = -EBUSY;
+ goto out;
+ }
+
err = mmc_interrupt_hpi(card);
/*
@@ -2864,6 +2830,40 @@
return ret;
}
+static int mmc_clk_update_freq(struct mmc_host *host,
+ unsigned long freq, enum mmc_load state)
+{
+ int err = 0;
+
+ if (host->ops->notify_load) {
+ err = host->ops->notify_load(host, state);
+ if (err)
+ goto out;
+ }
+
+ if (freq != host->clk_scaling.curr_freq) {
+ if (!mmc_is_vaild_state_for_clk_scaling(host)) {
+ err = -EAGAIN;
+ goto error;
+ }
+
+ err = host->bus_ops->change_bus_speed(host, &freq);
+ if (!err)
+ host->clk_scaling.curr_freq = freq;
+ else
+ pr_err("%s: %s: failed (%d) at freq=%lu\n",
+ mmc_hostname(host), __func__, err, freq);
+ }
+error:
+ if (err) {
+ /* restore previous state */
+ if (host->ops->notify_load)
+ host->ops->notify_load(host, host->clk_scaling.state);
+ }
+out:
+ return err;
+}
+
/**
* mmc_clk_scaling() - clock scaling decision algorithm
* @host: pointer to mmc host structure
@@ -2889,6 +2889,7 @@
unsigned int up_threshold = host->clk_scaling.up_threshold;
unsigned int down_threshold = host->clk_scaling.down_threshold;
bool queue_scale_down_work = false;
+ enum mmc_load state;
if (!card || !host->bus_ops || !host->bus_ops->change_bus_speed) {
pr_err("%s: %s: invalid entry\n", mmc_hostname(host), __func__);
@@ -2916,6 +2917,7 @@
busy_time_ms = host->clk_scaling.busy_time_us / USEC_PER_MSEC;
freq = host->clk_scaling.curr_freq;
+ state = host->clk_scaling.state;
/*
* Note that the max. and min. frequency should be based
@@ -2924,28 +2926,24 @@
*/
if ((busy_time_ms * 100 > total_time_ms * up_threshold)) {
freq = mmc_get_max_frequency(host);
+ state = MMC_LOAD_HIGH;
} else if ((busy_time_ms * 100 < total_time_ms * down_threshold)) {
if (!from_wq)
queue_scale_down_work = true;
freq = mmc_get_min_frequency(host);
+ state = MMC_LOAD_LOW;
}
- if (freq != host->clk_scaling.curr_freq) {
+ if (state != host->clk_scaling.state) {
if (!queue_scale_down_work) {
if (!from_wq)
cancel_delayed_work_sync(
&host->clk_scaling.work);
-
- if (!mmc_is_vaild_state_for_clk_scaling(host))
- goto bypass_scaling;
-
- err = host->bus_ops->change_bus_speed(host, &freq);
+ err = mmc_clk_update_freq(host, freq, state);
if (!err)
- host->clk_scaling.curr_freq = freq;
- else
- pr_err("%s: %s: failed (%d) at freq=%lu\n",
- mmc_hostname(host), __func__, err,
- freq);
+ host->clk_scaling.state = state;
+ else if (err == -EAGAIN)
+ goto no_reset_stats;
} else {
/*
* We hold claim host while queueing the scale down
@@ -2954,13 +2952,12 @@
*/
queue_delayed_work(system_nrt_wq,
&host->clk_scaling.work, 1);
- host->clk_scaling.in_progress = false;
- goto out;
+ goto no_reset_stats;
}
}
mmc_reset_clk_scale_stats(host);
-bypass_scaling:
+no_reset_stats:
host->clk_scaling.in_progress = false;
out:
return;
@@ -3007,6 +3004,9 @@
INIT_DELAYED_WORK(&host->clk_scaling.work, mmc_clk_scale_work);
host->clk_scaling.curr_freq = mmc_get_max_frequency(host);
+ if (host->ops->notify_load)
+ host->ops->notify_load(host, MMC_LOAD_HIGH);
+ host->clk_scaling.state = MMC_LOAD_HIGH;
mmc_reset_clk_scale_stats(host);
host->clk_scaling.enable = true;
host->clk_scaling.initialized = true;
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 8b7e0bd..903decf 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -176,9 +176,11 @@
if (val > host->f_max)
return -EINVAL;
+ mmc_rpm_hold(host, &host->class_dev);
mmc_claim_host(host);
mmc_set_clock(host, (unsigned int) val);
mmc_release_host(host);
+ mmc_rpm_release(host, &host->class_dev);
return 0;
}
@@ -208,6 +210,7 @@
if (!host || (val < host->f_min))
goto out;
+ mmc_rpm_hold(host, &host->class_dev);
mmc_claim_host(host);
if (host->bus_ops && host->bus_ops->change_bus_speed) {
old_freq = host->f_max;
@@ -219,6 +222,7 @@
host->f_max = old_freq;
}
mmc_release_host(host);
+ mmc_rpm_release(host, &host->class_dev);
out:
return err;
}
@@ -286,6 +290,7 @@
u32 status;
int ret;
+ mmc_rpm_hold(card->host, &card->dev);
mmc_claim_host(card->host);
ret = mmc_send_status(data, &status);
@@ -293,6 +298,7 @@
*val = status;
mmc_release_host(card->host);
+ mmc_rpm_release(card->host, &card->dev);
return ret;
}
@@ -319,9 +325,11 @@
goto out_free;
}
+ mmc_rpm_hold(card->host, &card->dev);
mmc_claim_host(card->host);
err = mmc_send_ext_csd(card, ext_csd);
mmc_release_host(card->host);
+ mmc_rpm_release(card->host, &card->dev);
if (err)
goto out_free;
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index c0a4cef..edd6a5d 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -253,11 +253,19 @@
* mmc_host_may_gate_card - check if this card may be gated
* @card: card to check.
*/
-static bool mmc_host_may_gate_card(struct mmc_card *card)
+bool mmc_host_may_gate_card(struct mmc_card *card)
{
/* If there is no card we may gate it */
if (!card)
return true;
+
+ /*
+ * SDIO3.0 card allows the clock to be gated off so check if
+ * that is the case or not.
+ */
+ if (mmc_card_sdio(card) && card->cccr.async_intr_sup)
+ return true;
+
/*
* Don't gate SDIO cards! These need to be clocked at all times
* since they may be independent systems generating interrupts
@@ -466,12 +474,8 @@
goto err;
if (value && !mmc_can_scale_clk(host)) {
- if (mmc_card_ddr_mode(host->card) ||
- mmc_card_hs200(host->card) ||
- mmc_card_uhs(host->card)) {
- host->caps2 |= MMC_CAP2_CLK_SCALE;
- mmc_init_clk_scaling(host);
- }
+ host->caps2 |= MMC_CAP2_CLK_SCALE;
+ mmc_init_clk_scaling(host);
if (!mmc_can_scale_clk(host)) {
host->caps2 &= ~MMC_CAP2_CLK_SCALE;
@@ -487,6 +491,10 @@
if (host->bus_ops->change_bus_speed(host, &freq))
goto err;
}
+ if (host->ops->notify_load &&
+ host->ops->notify_load(host, MMC_LOAD_HIGH))
+ goto err;
+ host->clk_scaling.state = MMC_LOAD_HIGH;
host->clk_scaling.initialized = false;
}
retval = count;
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 8a866cf..9b9c1ed 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -18,6 +18,7 @@
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include <linux/pm_runtime.h>
+#include <linux/reboot.h>
#include "core.h"
#include "bus.h"
@@ -900,6 +901,20 @@
return err;
}
+static int mmc_reboot_notify(struct notifier_block *notify_block,
+ unsigned long event, void *unused)
+{
+ struct mmc_card *card = container_of(
+ notify_block, struct mmc_card, reboot_notify);
+
+ if (event != SYS_RESTART)
+ card->issue_long_pon = true;
+ else
+ card->issue_long_pon = false;
+
+ return NOTIFY_OK;
+}
+
/*
* Handle the detection and initialisation of a card.
*
@@ -979,6 +994,7 @@
card->type = MMC_TYPE_MMC;
card->rca = 1;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+ card->reboot_notify.notifier_call = mmc_reboot_notify;
}
/*
@@ -1400,8 +1416,6 @@
if (card->ext_csd.bkops_en) {
INIT_DELAYED_WORK(&card->bkops_info.dw,
mmc_start_idle_time_bkops);
- INIT_WORK(&card->bkops_info.poll_for_completion,
- mmc_bkops_completion_polling);
/*
* Calculate the time to start the BKOPs checking.
@@ -1462,6 +1476,22 @@
return err;
}
+int mmc_send_long_pon(struct mmc_card *card)
+{
+ int err = 0;
+ struct mmc_host *host = card->host;
+
+ mmc_claim_host(host);
+ if (card->issue_long_pon && mmc_can_poweroff_notify(card)) {
+ err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_LONG);
+ if (err)
+ pr_warning("%s: error %d sending Long PON",
+ mmc_hostname(host), err);
+ }
+ mmc_release_host(host);
+ return err;
+}
+
/*
* Host is being removed. Free up the current card.
*/
@@ -1470,6 +1500,7 @@
BUG_ON(!host);
BUG_ON(!host->card);
+ unregister_reboot_notifier(&host->card->reboot_notify);
mmc_remove_card(host->card);
mmc_claim_host(host);
@@ -1734,9 +1765,9 @@
if (err)
goto remove_card;
- /* Initialize clock scaling only for high frequency modes */
- if (mmc_card_hs200(host->card) || mmc_card_ddr_mode(host->card))
- mmc_init_clk_scaling(host);
+ mmc_init_clk_scaling(host);
+
+ register_reboot_notifier(&host->card->reboot_notify);
return 0;
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 60e0640..ddf9a87 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1415,9 +1415,7 @@
if (err)
goto remove_card;
- /* Initialize clock scaling only for high frequency modes */
- if (mmc_card_uhs(host->card))
- mmc_init_clk_scaling(host);
+ mmc_init_clk_scaling(host);
return 0;
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 4e76f61..91e23ca 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -187,6 +187,23 @@
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_C;
if (data & SDIO_DRIVE_SDTD)
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_D;
+
+ ret = mmc_io_rw_direct(card, 0, 0,
+ SDIO_CCCR_INTERRUPT_EXTENSION, 0, &data);
+ if (ret)
+ goto out;
+ if (data & SDIO_SUPPORT_ASYNC_INTR) {
+ if (card->host->caps2 &
+ MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE) {
+ data |= SDIO_ENABLE_ASYNC_INTR;
+ ret = mmc_io_rw_direct(card, 1, 0,
+ SDIO_CCCR_INTERRUPT_EXTENSION,
+ data, NULL);
+ if (ret)
+ goto out;
+ card->cccr.async_intr_sup = 1;
+ }
+ }
}
/* if no uhs mode ensure we check for high speed */
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 9c30cd1..3986cdd 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -87,6 +87,10 @@
static int msmsdcc_prep_xfer(struct msmsdcc_host *host, struct mmc_data
*data);
+static void msmsdcc_msm_bus_cancel_work_and_set_vote(struct msmsdcc_host *host,
+ struct mmc_ios *ios);
+static void msmsdcc_msm_bus_queue_work(struct msmsdcc_host *host);
+
static u64 dma_mask = DMA_BIT_MASK(32);
static unsigned int msmsdcc_pwrsave = 1;
@@ -958,7 +962,7 @@
struct msmsdcc_nc_dmadata *nc;
dmov_box *box;
uint32_t rows;
- unsigned int n;
+ int n;
int i, err = 0, box_cmd_cnt = 0;
struct scatterlist *sg = data->sg;
unsigned int len, offset;
@@ -2731,12 +2735,14 @@
int rc = 0;
if (enable && !atomic_read(&host->clks_on)) {
+ msmsdcc_msm_bus_cancel_work_and_set_vote(host, &host->mmc->ios);
+
if (!IS_ERR_OR_NULL(host->bus_clk)) {
rc = clk_prepare_enable(host->bus_clk);
if (rc) {
pr_err("%s: %s: failed to enable the bus-clock with error %d\n",
mmc_hostname(host->mmc), __func__, rc);
- goto out;
+ goto remove_vote;
}
}
if (!IS_ERR(host->pclk)) {
@@ -2764,6 +2770,18 @@
clk_disable_unprepare(host->pclk);
if (!IS_ERR_OR_NULL(host->bus_clk))
clk_disable_unprepare(host->bus_clk);
+
+ /*
+ * If clock gating is enabled, then remove the vote
+ * immediately because clocks will be disabled only
+ * after MSM_MMC_CLK_GATE_DELAY and thus no additional
+ * delay is required to remove the bus vote.
+ */
+ if (host->mmc->clkgate_delay)
+ msmsdcc_msm_bus_cancel_work_and_set_vote(host, NULL);
+ else
+ msmsdcc_msm_bus_queue_work(host);
+
atomic_set(&host->clks_on, 0);
}
goto out;
@@ -2774,6 +2792,8 @@
disable_bus:
if (!IS_ERR_OR_NULL(host->bus_clk))
clk_disable_unprepare(host->bus_clk);
+remove_vote:
+ msmsdcc_msm_bus_cancel_work_and_set_vote(host, NULL);
out:
return rc;
}
@@ -3388,6 +3408,14 @@
(1 + ((3 * USEC_PER_SEC) /
(host->clk_rate ? host->clk_rate :
msmsdcc_get_min_sup_clk_rate(host))));
+ spin_unlock_irqrestore(&host->lock, flags);
+ /*
+ * Update bus vote incase of frequency change due to
+ * clock scaling.
+ */
+ msmsdcc_msm_bus_cancel_work_and_set_vote(host,
+ &mmc->ios);
+ spin_lock_irqsave(&host->lock, flags);
}
/*
* give atleast 2 MCLK cycles delay for clocks
@@ -3404,7 +3432,7 @@
else
clk |= MCI_CLK_WIDEBUS_1;
- if (msmsdcc_is_pwrsave(host))
+ if (msmsdcc_is_pwrsave(host) && mmc_host_may_gate_card(host->mmc->card))
clk |= MCI_CLK_PWRSAVE;
clk |= MCI_CLK_FLOWENA;
@@ -3625,7 +3653,6 @@
return rc;
}
out:
- msmsdcc_msm_bus_cancel_work_and_set_vote(host, &mmc->ios);
return 0;
}
@@ -3654,7 +3681,6 @@
}
out:
- msmsdcc_msm_bus_queue_work(host);
return rc;
}
#else
@@ -3690,7 +3716,6 @@
msmsdcc_pm_qos_update_latency(host, 0);
return rc;
}
- msmsdcc_msm_bus_cancel_work_and_set_vote(host, &mmc->ios);
return 0;
}
@@ -3713,7 +3738,6 @@
return rc;
}
out:
- msmsdcc_msm_bus_queue_work(host);
return rc;
}
#endif
@@ -4393,6 +4417,39 @@
return data_cnt;
}
+static int msmsdcc_notify_load(struct mmc_host *mmc, enum mmc_load state)
+{
+ int err = 0;
+ unsigned long rate;
+ struct msmsdcc_host *host = mmc_priv(mmc);
+
+ if (IS_ERR_OR_NULL(host->bus_clk))
+ goto out;
+
+ switch (state) {
+ case MMC_LOAD_HIGH:
+ rate = MSMSDCC_BUS_VOTE_MAX_RATE;
+ break;
+ case MMC_LOAD_LOW:
+ rate = MSMSDCC_BUS_VOTE_MIN_RATE;
+ break;
+ default:
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (rate != host->bus_clk_rate) {
+ err = clk_set_rate(host->bus_clk, rate);
+ if (err)
+ pr_err("%s: %s: bus clk set rate %lu Hz err %d\n",
+ mmc_hostname(mmc), __func__, rate, err);
+ else
+ host->bus_clk_rate = rate;
+ }
+out:
+ return err;
+}
+
static const struct mmc_host_ops msmsdcc_ops = {
.enable = msmsdcc_enable,
.disable = msmsdcc_disable,
@@ -4407,6 +4464,7 @@
.hw_reset = msmsdcc_hw_reset,
.stop_request = msmsdcc_stop_request,
.get_xfer_remain = msmsdcc_get_xfer_remain,
+ .notify_load = msmsdcc_notify_load,
};
static void msmsdcc_enable_status_gpio(struct msmsdcc_host *host)
@@ -4683,10 +4741,10 @@
}
/* Now save the sps pipe handle */
ep->pipe_handle = sps_pipe_handle;
- pr_debug("%s: %s, success !!! %s: pipe_handle=0x%x,"
- " desc_fifo.phys_base=0x%x\n", mmc_hostname(host->mmc),
+ pr_debug("%s: %s, success !!! %s: pipe_handle=0x%x,"\
+ " desc_fifo.phys_base=%pa\n", mmc_hostname(host->mmc),
__func__, is_producer ? "READ" : "WRITE",
- (u32)sps_pipe_handle, sps_config->desc.phys_base);
+ (u32)sps_pipe_handle, &sps_config->desc.phys_base);
goto out;
reg_event_err:
@@ -4929,11 +4987,8 @@
host->bam_base = ioremap(host->bam_memres->start,
resource_size(host->bam_memres));
if (!host->bam_base) {
- pr_err("%s: BAM ioremap() failed!!! phys_addr=0x%x,"
- " size=0x%x", mmc_hostname(host->mmc),
- host->bam_memres->start,
- (host->bam_memres->end -
- host->bam_memres->start));
+ pr_err("%s: BAM ioremap() failed!!! resource: %pr\n",
+ mmc_hostname(host->mmc), host->bam_memres);
rc = -ENOMEM;
goto out;
}
@@ -4954,7 +5009,7 @@
*/
bam.summing_threshold = SPS_MIN_XFER_SIZE;
/* SPS driver wll handle the SDCC BAM IRQ */
- bam.irq = (u32)host->bam_irqres->start;
+ bam.irq = host->bam_irqres->start;
bam.manage = SPS_BAM_MGR_LOCAL;
bam.callback = msmsdcc_sps_bam_global_irq_cb;
bam.user = (void *)host;
@@ -4990,10 +5045,8 @@
if (rc)
goto cons_conn_err;
- pr_info("%s: Qualcomm MSM SDCC-BAM at 0x%016llx irq %d\n",
- mmc_hostname(host->mmc),
- (unsigned long long)host->bam_memres->start,
- (unsigned int)host->bam_irqres->start);
+ pr_info("%s: Qualcomm MSM SDCC-BAM at %pr %pr\n",
+ mmc_hostname(host->mmc), host->bam_memres, host->bam_irqres);
goto out;
cons_conn_err:
@@ -5180,15 +5233,16 @@
}
static void msmsdcc_print_regs(const char *name, void __iomem *base,
- u32 phys_base, unsigned int no_of_regs)
+ resource_size_t phys_base,
+ unsigned int no_of_regs)
{
unsigned int i;
if (!base)
return;
- pr_err("===== %s: Register Dumps @phys_base=0x%x, @virt_base=0x%x"
- " =====\n", name, phys_base, (u32)base);
+ pr_err("===== %s: Register Dumps @phys_base=%pa, @virt_base=0x%x"\
+ " =====\n", name, &phys_base, (u32)base);
for (i = 0; i < no_of_regs; i = i + 4) {
pr_err("Reg=0x%.2x: 0x%.8x, 0x%.8x, 0x%.8x, 0x%.8x\n", i*4,
(u32)readl_relaxed(base + i*4),
@@ -5979,12 +6033,13 @@
host->bus_clk = clk_get(&pdev->dev, "bus_clk");
if (!IS_ERR_OR_NULL(host->bus_clk)) {
/* Vote for max. clk rate for max. performance */
- ret = clk_set_rate(host->bus_clk, INT_MAX);
+ ret = clk_set_rate(host->bus_clk, MSMSDCC_BUS_VOTE_MAX_RATE);
if (ret)
goto bus_clk_put;
ret = clk_prepare_enable(host->bus_clk);
if (ret)
goto bus_clk_put;
+ host->bus_clk_rate = MSMSDCC_BUS_VOTE_MAX_RATE;
}
/*
@@ -6034,6 +6089,17 @@
msmsdcc_get_min_sup_clk_rate(host)));
atomic_set(&host->clks_on, 1);
+
+ ret = msmsdcc_msm_bus_register(host);
+ if (ret)
+ goto clk_disable;
+
+ if (host->msm_bus_vote.client_handle)
+ INIT_DELAYED_WORK(&host->msm_bus_vote.vote_work,
+ msmsdcc_msm_bus_work);
+
+ msmsdcc_msm_bus_cancel_work_and_set_vote(host, &mmc->ios);
+
/* Apply Hard reset to SDCC to put it in power on default state */
msmsdcc_hard_reset(host);
@@ -6046,18 +6112,10 @@
pm_qos_add_request(&host->pm_qos_req_dma,
PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
- ret = msmsdcc_msm_bus_register(host);
- if (ret)
- goto pm_qos_remove;
-
- if (host->msm_bus_vote.client_handle)
- INIT_DELAYED_WORK(&host->msm_bus_vote.vote_work,
- msmsdcc_msm_bus_work);
-
ret = msmsdcc_vreg_init(host, true);
if (ret) {
pr_err("%s: msmsdcc_vreg_init() failed (%d)\n", __func__, ret);
- goto clk_disable;
+ goto pm_qos_remove;
}
@@ -6116,6 +6174,7 @@
mmc->caps2 |= MMC_CAP2_CACHE_CTRL;
mmc->caps2 |= MMC_CAP2_POWEROFF_NOTIFY;
mmc->caps2 |= MMC_CAP2_STOP_REQUEST;
+ mmc->caps2 |= MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE;
if (plat->nonremovable)
mmc->caps |= MMC_CAP_NONREMOVABLE;
@@ -6275,10 +6334,8 @@
mmc->clk_scaling.polling_delay_ms = 100;
mmc->caps2 |= MMC_CAP2_CLK_SCALE;
- pr_info("%s: Qualcomm MSM SDCC-core at 0x%016llx irq %d,%d dma %d"
- " dmacrcri %d\n", mmc_hostname(mmc),
- (unsigned long long)core_memres->start,
- (unsigned int) core_irqres->start,
+ pr_info("%s: Qualcomm MSM SDCC-core %pr %pr,%d dma %d dmacrcri %d\n",
+ mmc_hostname(mmc), core_memres, core_irqres,
(unsigned int) plat->status_irq, host->dma.channel,
host->dma.crci);
@@ -6300,11 +6357,11 @@
if (is_dma_mode(host) && host->dma.channel != -1
&& host->dma.crci != -1) {
- pr_info("%s: DM non-cached buffer at %p, dma_addr 0x%.8x\n",
- mmc_hostname(mmc), host->dma.nc, host->dma.nc_busaddr);
- pr_info("%s: DM cmd busaddr 0x%.8x, cmdptr busaddr 0x%.8x\n",
- mmc_hostname(mmc), host->dma.cmd_busaddr,
- host->dma.cmdptr_busaddr);
+ pr_info("%s: DM non-cached buffer at %p, dma_addr: %pa\n",
+ mmc_hostname(mmc), host->dma.nc, &host->dma.nc_busaddr);
+ pr_info("%s: DM cmd busaddr: %pa, cmdptr busaddr: %pa\n",
+ mmc_hostname(mmc), &host->dma.cmd_busaddr,
+ &host->dma.cmdptr_busaddr);
} else if (is_sps_mode(host)) {
pr_info("%s: SPS-BAM data transfer mode available\n",
mmc_hostname(mmc));
@@ -6409,12 +6466,13 @@
msmsdcc_sps_exit(host);
vreg_deinit:
msmsdcc_vreg_init(host, false);
- clk_disable:
- clk_disable_unprepare(host->clk);
- msmsdcc_msm_bus_unregister(host);
pm_qos_remove:
if (host->cpu_dma_latency)
pm_qos_remove_request(&host->pm_qos_req_dma);
+ msmsdcc_msm_bus_cancel_work_and_set_vote(host, NULL);
+ msmsdcc_msm_bus_unregister(host);
+ clk_disable:
+ clk_disable_unprepare(host->clk);
clk_put:
clk_put(host->clk);
pclk_disable:
@@ -6735,8 +6793,13 @@
}
pr_debug("%s: %s: ends with err=%d\n", mmc_hostname(mmc), __func__, rc);
out:
- /* set bus bandwidth to 0 immediately */
- msmsdcc_msm_bus_cancel_work_and_set_vote(host, NULL);
+ /*
+ * Remove the vote immediately only if clocks are off in which
+ * case we might have queued work to remove vote but it may not
+ * be completed before runtime suspend or system suspend.
+ */
+ if (!atomic_read(&host->clks_on))
+ msmsdcc_msm_bus_cancel_work_and_set_vote(host, NULL);
msmsdcc_print_pm_stats(host, start, __func__, rc);
return rc;
}
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index 4ed2d96..bcfde57 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -260,6 +260,12 @@
#define MMC_MAX_DMA_CMDS (MAX_NR_SG_DMA_PIO * (MMC_MAX_REQ_SIZE / \
MMC_MAX_DMA_BOX_LENGTH))
+/*
+ * Peripheral bus clock scaling vote rates
+ */
+#define MSMSDCC_BUS_VOTE_MAX_RATE 64000000 /* Hz */
+#define MSMSDCC_BUS_VOTE_MIN_RATE 32000000 /* Hz */
+
struct clk;
struct msmsdcc_nc_dmadata {
@@ -360,6 +366,7 @@
struct clk *clk; /* main MMC bus clock */
struct clk *pclk; /* SDCC peripheral bus clock */
struct clk *bus_clk; /* SDCC bus voter clock */
+ unsigned long bus_clk_rate; /* peripheral bus clk rate */
atomic_t clks_on; /* set if clocks are enabled */
unsigned int eject; /* eject state */
diff --git a/drivers/mmc/host/msm_sdcc_dml.c b/drivers/mmc/host/msm_sdcc_dml.c
index 91ab7e3..2562436 100644
--- a/drivers/mmc/host/msm_sdcc_dml.c
+++ b/drivers/mmc/host/msm_sdcc_dml.c
@@ -166,17 +166,13 @@
host->dml_base = ioremap(host->dml_memres->start,
resource_size(host->dml_memres));
if (!host->dml_base) {
- pr_err("%s: DML ioremap() failed!!! phys_addr=0x%x,"
- " size=0x%x", mmc_hostname(host->mmc),
- host->dml_memres->start,
- (host->dml_memres->end -
- host->dml_memres->start));
+ pr_err("%s: DML ioremap() failed!!! %pr\n",
+ mmc_hostname(host->mmc), host->dml_memres);
rc = -ENOMEM;
goto out;
}
- pr_info("%s: Qualcomm MSM SDCC-DML at 0x%016llx\n",
- mmc_hostname(host->mmc),
- (unsigned long long)host->dml_memres->start);
+ pr_info("%s: Qualcomm MSM SDCC-DML %pr\n",
+ mmc_hostname(host->mmc), host->dml_memres);
}
dml_base = host->dml_base;
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 49222b9..c73bf01 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -38,6 +38,7 @@
#include <linux/dma-mapping.h>
#include <mach/gpio.h>
#include <mach/msm_bus.h>
+#include <linux/iopoll.h>
#include "sdhci-pltfm.h"
@@ -81,6 +82,31 @@
#define CORE_CLK_PWRSAVE (1 << 1)
#define CORE_IO_PAD_PWR_SWITCH (1 << 16)
+#define CORE_MCI_DATA_CTRL 0x2C
+#define CORE_MCI_DPSM_ENABLE (1 << 0)
+
+#define CORE_TESTBUS_CONFIG 0x0CC
+#define CORE_TESTBUS_ENA (1 << 3)
+#define CORE_TESTBUS_SEL2 (1 << 4)
+
+/*
+ * Waiting until end of potential AHB access for data:
+ * 16 AHB cycles (160ns for 100MHz and 320ns for 50MHz) +
+ * delay on AHB (2us) = maximum 2.32us
+ * Taking x10 times margin
+ */
+#define CORE_AHB_DATA_DELAY_US 23
+/* Waiting until end of potential AHB access for descriptor:
+ * Single (1 AHB cycle) + delay on AHB bus = max 2us
+ * INCR4 (4 AHB cycles) + delay on AHB bus = max 2us
+ * Single (1 AHB cycle) + delay on AHB bus = max 2us
+ * Total 8 us delay with margin
+ */
+#define CORE_AHB_DESC_DELAY_US 8
+
+#define CORE_SDCC_DEBUG_REG 0x124
+#define CORE_DEBUG_REG_AHB_HTRANS (3 << 12)
+
/* 8KB descriptors */
#define SDHCI_MSM_MAX_SEGMENTS (1 << 13)
#define SDHCI_MSM_MMC_CLK_GATE_DELAY 200 /* msecs */
@@ -1093,6 +1119,7 @@
int len, i;
int clk_table_len;
u32 *clk_table = NULL;
+ enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
@@ -1100,7 +1127,9 @@
goto out;
}
- pdata->status_gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, 0);
+ pdata->status_gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags);
+ if (gpio_is_valid(pdata->status_gpio) & !(flags & OF_GPIO_ACTIVE_LOW))
+ pdata->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
of_property_read_u32(np, "qcom,bus-width", &bus_width);
if (bus_width == 8)
@@ -1392,10 +1421,20 @@
return;
bw = sdhci_get_bw_required(host, ios);
- if (enable)
+ if (enable) {
sdhci_msm_bus_cancel_work_and_set_vote(host, bw);
- else
- sdhci_msm_bus_queue_work(host);
+ } else {
+ /*
+ * If clock gating is enabled, then remove the vote
+ * immediately because clocks will be disabled only
+ * after SDHCI_MSM_MMC_CLK_GATE_DELAY and thus no
+ * additional delay is required to remove the bus vote.
+ */
+ if (host->mmc->clkgate_delay)
+ sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
+ else
+ sdhci_msm_bus_queue_work(host);
+ }
}
/* Regulator utility functions */
@@ -1916,12 +1955,15 @@
if (enable && !atomic_read(&msm_host->clks_on)) {
pr_debug("%s: request to enable clocks\n",
mmc_hostname(host->mmc));
+
+ sdhci_msm_bus_voting(host, 1);
+
if (!IS_ERR_OR_NULL(msm_host->bus_clk)) {
rc = clk_prepare_enable(msm_host->bus_clk);
if (rc) {
pr_err("%s: %s: failed to enable the bus-clock with error %d\n",
mmc_hostname(host->mmc), __func__, rc);
- goto out;
+ goto remove_vote;
}
}
if (!IS_ERR(msm_host->pclk)) {
@@ -1950,6 +1992,8 @@
clk_disable_unprepare(msm_host->pclk);
if (!IS_ERR_OR_NULL(msm_host->bus_clk))
clk_disable_unprepare(msm_host->bus_clk);
+
+ sdhci_msm_bus_voting(host, 0);
}
atomic_set(&msm_host->clks_on, enable);
goto out;
@@ -1959,6 +2003,9 @@
disable_bus_clk:
if (!IS_ERR_OR_NULL(msm_host->bus_clk))
clk_disable_unprepare(msm_host->bus_clk);
+remove_vote:
+ if (msm_host->msm_bus_vote.client_handle)
+ sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
out:
return rc;
}
@@ -2005,6 +2052,11 @@
}
msm_host->clk_rate = sup_clock;
host->clock = clock;
+ /*
+ * Update the bus vote in case of frequency change due to
+ * clock scaling.
+ */
+ sdhci_msm_bus_voting(host, 1);
}
}
@@ -2044,6 +2096,51 @@
return 0;
}
+/*
+ * sdhci_msm_disable_data_xfer - disable undergoing AHB bus data transfer
+ *
+ * Write 0 to bit 0 in MCI_DATA_CTL (offset 0x2C) - clearing TxActive bit by
+ * access to legacy registers. It will stop current burst and prevent start of
+ * the next on.
+ *
+ * Polling CORE_AHB_DATA_DELAY_US timeout, by reading bit 13:12 until they are 0
+ * in CORE_SDCC_DEBUG_REG (offset 0x124) will validate that AHB burst was
+ * completed and a new one didn't start.
+ *
+ * Waiting for 4us while AHB finishes descriptors fetch.
+ */
+static void sdhci_msm_disable_data_xfer(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ u32 value;
+ int ret;
+
+ value = readl_relaxed(msm_host->core_mem + CORE_MCI_DATA_CTRL);
+ value &= ~(u32)CORE_MCI_DPSM_ENABLE;
+ writel_relaxed(value, msm_host->core_mem + CORE_MCI_DATA_CTRL);
+
+ /* Enable the test bus for device slot */
+ writel_relaxed(CORE_TESTBUS_ENA | CORE_TESTBUS_SEL2,
+ msm_host->core_mem + CORE_TESTBUS_CONFIG);
+
+ ret = readl_poll_timeout_noirq(msm_host->core_mem
+ + CORE_SDCC_DEBUG_REG, value,
+ !(value & CORE_DEBUG_REG_AHB_HTRANS),
+ CORE_AHB_DATA_DELAY_US, 1);
+ if (ret) {
+ pr_err("%s: %s: can't stop ongoing AHB bus access by ADMA\n",
+ mmc_hostname(host->mmc), __func__);
+ BUG();
+ }
+ /* Disable the test bus for device slot */
+ value = readl_relaxed(msm_host->core_mem + CORE_TESTBUS_CONFIG);
+ value &= ~CORE_TESTBUS_ENA;
+ writel_relaxed(value, msm_host->core_mem + CORE_TESTBUS_CONFIG);
+
+ udelay(CORE_AHB_DESC_DELAY_US);
+}
+
static struct sdhci_ops sdhci_msm_ops = {
.set_uhs_signaling = sdhci_msm_set_uhs_signaling,
.check_power_status = sdhci_msm_check_power_status,
@@ -2051,9 +2148,9 @@
.toggle_cdr = sdhci_msm_toggle_cdr,
.get_max_segments = sdhci_msm_max_segs,
.set_clock = sdhci_msm_set_clock,
- .platform_bus_voting = sdhci_msm_bus_voting,
.get_min_clock = sdhci_msm_get_min_clock,
.get_max_clock = sdhci_msm_get_max_clock,
+ .disable_data_xfer = sdhci_msm_disable_data_xfer,
};
static int __devinit sdhci_msm_probe(struct platform_device *pdev)
@@ -2064,7 +2161,8 @@
struct resource *core_memres = NULL;
int ret = 0, dead = 0;
u32 vdd_max_current;
- u32 host_version;
+ u16 host_version;
+ u32 pwr, irq_status, irq_ctl;
pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__);
msm_host = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_msm_host),
@@ -2140,24 +2238,33 @@
goto pclk_disable;
}
- ret = clk_prepare_enable(msm_host->clk);
- if (ret)
- goto pclk_disable;
-
/* Set to the minimum supported clock frequency */
ret = clk_set_rate(msm_host->clk, sdhci_msm_get_min_clock(host));
if (ret) {
dev_err(&pdev->dev, "MClk rate set failed (%d)\n", ret);
- goto clk_disable;
+ goto pclk_disable;
}
+ ret = clk_prepare_enable(msm_host->clk);
+ if (ret)
+ goto pclk_disable;
+
msm_host->clk_rate = sdhci_msm_get_min_clock(host);
atomic_set(&msm_host->clks_on, 1);
+ ret = sdhci_msm_bus_register(msm_host, pdev);
+ if (ret)
+ goto clk_disable;
+
+ if (msm_host->msm_bus_vote.client_handle)
+ INIT_DELAYED_WORK(&msm_host->msm_bus_vote.vote_work,
+ sdhci_msm_bus_work);
+ sdhci_msm_bus_voting(host, 1);
+
/* Setup regulators */
ret = sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, true);
if (ret) {
dev_err(&pdev->dev, "Regulator setup failed (%d)\n", ret);
- goto clk_disable;
+ goto bus_unregister;
}
/* Reset the core and Enable SDHC mode */
@@ -2173,11 +2280,46 @@
}
/* Set SW_RST bit in POWER register (Offset 0x0) */
- writel_relaxed(CORE_SW_RST, msm_host->core_mem + CORE_POWER);
+ writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_POWER) |
+ CORE_SW_RST, msm_host->core_mem + CORE_POWER);
+ /*
+ * SW reset can take upto 10HCLK + 15MCLK cycles.
+ * Calculating based on min clk rates (hclk = 27MHz,
+ * mclk = 400KHz) it comes to ~40us. Let's poll for
+ * max. 1ms for reset completion.
+ */
+ ret = readl_poll_timeout(msm_host->core_mem + CORE_POWER,
+ pwr, !(pwr & CORE_SW_RST), 100, 10);
+
+ if (ret) {
+ dev_err(&pdev->dev, "reset failed (%d)\n", ret);
+ goto vreg_deinit;
+ }
/* Set HC_MODE_EN bit in HC_MODE register */
writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
/*
+ * CORE_SW_RST above may trigger power irq if previous status of PWRCTL
+ * was either BUS_ON or IO_HIGH_V. So before we enable the power irq
+ * interrupt in GIC (by registering the interrupt handler), we need to
+ * ensure that any pending power irq interrupt status is acknowledged
+ * otherwise power irq interrupt handler would be fired prematurely.
+ */
+ irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS);
+ writel_relaxed(irq_status, (msm_host->core_mem + CORE_PWRCTL_CLEAR));
+ irq_ctl = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL);
+ if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF))
+ irq_ctl |= CORE_PWRCTL_BUS_SUCCESS;
+ if (irq_status & (CORE_PWRCTL_IO_HIGH | CORE_PWRCTL_IO_LOW))
+ irq_ctl |= CORE_PWRCTL_IO_SUCCESS;
+ writel_relaxed(irq_ctl, (msm_host->core_mem + CORE_PWRCTL_CTL));
+ /*
+ * Ensure that above writes are propogated before interrupt enablement
+ * in GIC.
+ */
+ mb();
+
+ /*
* Following are the deviations from SDHC spec v3.0 -
* 1. Card detection is handled using separate GPIO.
* 2. Bus power control is handled by interacting with PMIC.
@@ -2192,7 +2334,7 @@
host->quirks2 |= SDHCI_QUIRK2_BROKEN_PRESET_VALUE;
host->quirks2 |= SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT;
- host_version = readl_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
+ host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n",
host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
SDHCI_VENDOR_VER_SHIFT));
@@ -2269,14 +2411,6 @@
host->cpu_dma_latency_us = msm_host->pdata->cpu_dma_latency_us;
- ret = sdhci_msm_bus_register(msm_host, pdev);
- if (ret)
- goto vreg_deinit;
-
- if (msm_host->msm_bus_vote.client_handle)
- INIT_DELAYED_WORK(&msm_host->msm_bus_vote.vote_work,
- sdhci_msm_bus_work);
-
init_completion(&msm_host->pwr_irq_completion);
if (gpio_is_valid(msm_host->pdata->status_gpio)) {
@@ -2285,7 +2419,7 @@
if (ret) {
dev_err(&pdev->dev, "%s: Failed to request card detection IRQ %d\n",
__func__, ret);
- goto bus_unregister;
+ goto vreg_deinit;
}
}
@@ -2328,10 +2462,12 @@
free_cd_gpio:
if (gpio_is_valid(msm_host->pdata->status_gpio))
mmc_cd_gpio_free(msm_host->mmc);
-bus_unregister:
- sdhci_msm_bus_unregister(msm_host);
vreg_deinit:
sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false);
+bus_unregister:
+ if (msm_host->msm_bus_vote.client_handle)
+ sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
+ sdhci_msm_bus_unregister(msm_host);
clk_disable:
if (!IS_ERR(msm_host->clk))
clk_disable_unprepare(msm_host->clk);
@@ -2387,6 +2523,16 @@
disable_irq(host->irq);
disable_irq(msm_host->pwr_irq);
+ /*
+ * Remove the vote immediately only if clocks are off in which
+ * case we might have queued work to remove vote but it may not
+ * be completed before runtime suspend or system suspend.
+ */
+ if (!atomic_read(&msm_host->clks_on)) {
+ if (msm_host->msm_bus_vote.client_handle)
+ sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
+ }
+
return 0;
}
@@ -2420,9 +2566,6 @@
goto out;
}
- if (msm_host->msm_bus_vote.client_handle)
- sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
-
return sdhci_msm_runtime_suspend(dev);
out:
return ret;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 0549b4a..4f9bbad 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -51,6 +51,7 @@
static void sdhci_finish_command(struct sdhci_host *);
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
static void sdhci_tuning_timer(unsigned long data);
+static bool sdhci_check_state(struct sdhci_host *);
#ifdef CONFIG_PM_RUNTIME
static int sdhci_runtime_pm_get(struct sdhci_host *host);
@@ -292,7 +293,7 @@
spin_lock_irqsave(&host->lock, flags);
- if (host->runtime_suspended)
+ if (host->runtime_suspended || sdhci_check_state(host))
goto out;
if (brightness == LED_OFF)
@@ -1395,7 +1396,8 @@
struct mmc_host *mmc = host->mmc;
if (!host->clock || !host->pwr ||
- pm_runtime_suspended(mmc->parent))
+ (mmc_use_core_runtime_pm(mmc) ?
+ pm_runtime_suspended(mmc->parent) : 0))
return true;
else
return false;
@@ -2126,7 +2128,49 @@
static int sdhci_stop_request(struct mmc_host *mmc)
{
- return -ENOSYS;
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+ struct mmc_data *data;
+
+ spin_lock_irqsave(&host->lock, flags);
+ if (!host->mrq || !host->data)
+ goto out;
+
+ data = host->data;
+
+ if (host->ops->disable_data_xfer)
+ host->ops->disable_data_xfer(host);
+
+ sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+
+ if (host->flags & SDHCI_REQ_USE_DMA) {
+ if (host->flags & SDHCI_USE_ADMA) {
+ sdhci_adma_table_post(host, data);
+ } else {
+ if (!data->host_cookie)
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len,
+ (data->flags & MMC_DATA_READ) ?
+ DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ }
+ }
+ del_timer(&host->timer);
+ host->mrq = NULL;
+ host->cmd = NULL;
+ host->data = NULL;
+out:
+ spin_unlock_irqrestore(&host->lock, flags);
+ return 0;
+}
+
+static unsigned int sdhci_get_xfer_remain(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u32 present_state = 0;
+
+ present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
+
+ return present_state & SDHCI_DOING_WRITE;
}
static const struct mmc_host_ops sdhci_ops = {
@@ -2143,6 +2187,7 @@
.enable = sdhci_enable,
.disable = sdhci_disable,
.stop_request = sdhci_stop_request,
+ .get_xfer_remain = sdhci_get_xfer_remain,
};
/*****************************************************************************\
@@ -2844,6 +2889,8 @@
host = mmc_priv(mmc);
host->mmc = mmc;
+ spin_lock_init(&host->lock);
+
return host;
}
@@ -3189,8 +3236,6 @@
return -ENODEV;
}
- spin_lock_init(&host->lock);
-
/*
* Maximum number of segments. Depends on if the hardware
* can do scatter/gather or not.
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index c6bef8a..a3d8442 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -286,6 +286,7 @@
void (*toggle_cdr)(struct sdhci_host *host, bool enable);
unsigned int (*get_max_segments)(void);
void (*platform_bus_voting)(struct sdhci_host *host, u32 enable);
+ void (*disable_data_xfer)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
diff --git a/drivers/mtd/devices/msm_qpic_nand.c b/drivers/mtd/devices/msm_qpic_nand.c
index 570c257..efa09f6 100644
--- a/drivers/mtd/devices/msm_qpic_nand.c
+++ b/drivers/mtd/devices/msm_qpic_nand.c
@@ -30,7 +30,7 @@
#include <linux/of.h>
#include <linux/ctype.h>
#include <mach/sps.h>
-#include <mach/msm_smsm.h>
+#include <mach/msm_smem.h>
#define PAGE_SIZE_2K 2048
#define PAGE_SIZE_4K 4096
#define WRITE 1
@@ -89,6 +89,7 @@
/* QPIC NANDc (NAND Controller) Register Set */
#define MSM_NAND_REG(info, off) (info->nand_phys + off)
+#define MSM_NAND_QPIC_VERSION(info) MSM_NAND_REG(info, 0x20100)
#define MSM_NAND_FLASH_CMD(info) MSM_NAND_REG(info, 0x30000)
#define MSM_NAND_ADDR0(info) MSM_NAND_REG(info, 0x30004)
#define MSM_NAND_ADDR1(info) MSM_NAND_REG(info, 0x30008)
@@ -138,7 +139,7 @@
#define MSM_NAND_CTRL(info) MSM_NAND_REG(info, 0x30F00)
#define BAM_MODE_EN 0
-
+#define MSM_NAND_VERSION(info) MSM_NAND_REG(info, 0x30F08)
#define MSM_NAND_READ_LOCATION_0(info) MSM_NAND_REG(info, 0x30F20)
#define MSM_NAND_READ_LOCATION_1(info) MSM_NAND_REG(info, 0x30F24)
@@ -152,6 +153,12 @@
#define MSM_NAND_CMD_BLOCK_ERASE 0x3A
#define MSM_NAND_CMD_FETCH_ID 0x0B
+/* Version Mask */
+#define MSM_NAND_VERSION_MAJOR_MASK 0xF0000000
+#define MSM_NAND_VERSION_MAJOR_SHIFT 28
+#define MSM_NAND_VERSION_MINOR_MASK 0x0FFF0000
+#define MSM_NAND_VERSION_MINOR_SHIFT 16
+
/* Structure that defines a NAND SPS command element */
struct msm_nand_sps_cmd {
struct sps_command_element ce;
@@ -623,6 +630,48 @@
uint32_t ecc_bch_cfg;
};
+struct version {
+ uint16_t nand_major;
+ uint16_t nand_minor;
+ uint16_t qpic_major;
+ uint16_t qpic_minor;
+};
+
+static int msm_nand_version_check(struct msm_nand_info *info,
+ struct version *nandc_version)
+{
+ uint32_t qpic_ver = 0, nand_ver = 0;
+ int err = 0;
+
+ /* Lookup the version to identify supported features */
+ err = msm_nand_flash_rd_reg(info, MSM_NAND_VERSION(info),
+ &nand_ver);
+ if (err) {
+ pr_err("Failed to read NAND_VERSION, err=%d\n", err);
+ goto out;
+ }
+ nandc_version->nand_major = (nand_ver & MSM_NAND_VERSION_MAJOR_MASK) >>
+ MSM_NAND_VERSION_MAJOR_SHIFT;
+ nandc_version->nand_minor = (nand_ver & MSM_NAND_VERSION_MINOR_MASK) >>
+ MSM_NAND_VERSION_MINOR_SHIFT;
+
+ err = msm_nand_flash_rd_reg(info, MSM_NAND_QPIC_VERSION(info),
+ &qpic_ver);
+ if (err) {
+ pr_err("Failed to read QPIC_VERSION, err=%d\n", err);
+ goto out;
+ }
+ nandc_version->qpic_major = (qpic_ver & MSM_NAND_VERSION_MAJOR_MASK) >>
+ MSM_NAND_VERSION_MAJOR_SHIFT;
+ nandc_version->qpic_minor = (qpic_ver & MSM_NAND_VERSION_MINOR_MASK) >>
+ MSM_NAND_VERSION_MINOR_SHIFT;
+ pr_info("nand_major:%d, nand_minor:%d, qpic_major:%d, qpic_minor:%d\n",
+ nandc_version->nand_major, nandc_version->nand_minor,
+ nandc_version->qpic_major, nandc_version->qpic_minor);
+out:
+ return err;
+}
+
/*
* Function to identify whether the attached NAND flash device is
* complaint to ONFI spec or not. If yes, then it reads the ONFI parameter
@@ -661,6 +710,18 @@
uint32_t flash_status;
} *dma_buffer;
+
+ /* Lookup the version to identify supported features */
+ struct version nandc_version = {0};
+
+ ret = msm_nand_version_check(info, &nandc_version);
+ if (!ret && !(nandc_version.nand_major == 1 &&
+ nandc_version.nand_minor == 1 &&
+ nandc_version.qpic_major == 1 &&
+ nandc_version.qpic_minor == 1)) {
+ ret = -EPERM;
+ goto out;
+ }
wait_event(chip->dma_wait_queue, (onfi_param_info_buf =
msm_nand_get_dma_buffer(chip, ONFI_PARAM_INFO_LENGTH)));
dma_addr_param_info = msm_virt_to_dma(chip, onfi_param_info_buf);
@@ -691,9 +752,9 @@
/* Lookup the 'APPS' partition's first page address */
for (i = 0; i < FLASH_PTABLE_MAX_PARTS_V4; i++) {
- if (!strncmp("apps", ptable.part_entry[i].name,
- strlen(ptable.part_entry[i].name))) {
- page_address = ptable.part_entry[i].offset << 6;
+ if (!strncmp("apps", mtd_part[i].name,
+ strlen(mtd_part[i].name))) {
+ page_address = mtd_part[i].offset << 6;
break;
}
}
@@ -839,6 +900,7 @@
msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
msm_nand_release_dma_buffer(chip, onfi_param_info_buf,
ONFI_PARAM_INFO_LENGTH);
+out:
return ret;
}
@@ -1458,7 +1520,7 @@
int ret;
struct mtd_oob_ops ops;
- ops.mode = MTD_OPS_PLACE_OOB;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.len = len;
ops.retlen = 0;
ops.ooblen = 0;
@@ -1643,7 +1705,7 @@
int ret;
struct mtd_oob_ops ops;
- ops.mode = MTD_OPS_PLACE_OOB;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.len = len;
ops.retlen = 0;
ops.ooblen = 0;
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 4e12bb7..147e378 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -1127,6 +1127,33 @@
}
#endif
+static inline unsigned long get_vm_size(struct vm_area_struct *vma)
+{
+ return vma->vm_end - vma->vm_start;
+}
+
+static inline resource_size_t get_vm_offset(struct vm_area_struct *vma)
+{
+ return (resource_size_t) vma->vm_pgoff << PAGE_SHIFT;
+}
+
+/*
+ * Set a new vm offset.
+ *
+ * Verify that the incoming offset really works as a page offset,
+ * and that the offset and size fit in a resource_size_t.
+ */
+static inline int set_vm_offset(struct vm_area_struct *vma, resource_size_t off)
+{
+ pgoff_t pgoff = off >> PAGE_SHIFT;
+ if (off != (resource_size_t) pgoff << PAGE_SHIFT)
+ return -EINVAL;
+ if (off + get_vm_size(vma) - 1 < off)
+ return -EINVAL;
+ vma->vm_pgoff = pgoff;
+ return 0;
+}
+
/*
* set up a mapping for shared memory segments
*/
@@ -1136,20 +1163,29 @@
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
struct map_info *map = mtd->priv;
- unsigned long start;
- unsigned long off;
- u32 len;
+ resource_size_t start, off;
+ unsigned long len, vma_len;
if (mtd->type == MTD_RAM || mtd->type == MTD_ROM) {
- off = vma->vm_pgoff << PAGE_SHIFT;
+ off = get_vm_offset(vma);
start = map->phys;
len = PAGE_ALIGN((start & ~PAGE_MASK) + map->size);
start &= PAGE_MASK;
- if ((vma->vm_end - vma->vm_start + off) > len)
+ vma_len = get_vm_size(vma);
+
+ /* Overflow in off+len? */
+ if (vma_len + off < off)
+ return -EINVAL;
+ /* Does it fit in the mapping? */
+ if (vma_len + off > len)
return -EINVAL;
off += start;
- vma->vm_pgoff = off >> PAGE_SHIFT;
+ /* Did that overflow? */
+ if (off < start)
+ return -EINVAL;
+ if (set_vm_offset(vma, off) < 0)
+ return -EINVAL;
vma->vm_flags |= VM_IO | VM_RESERVED;
#ifdef pgprot_noncached
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index af4fe8c..72ced9d 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -161,6 +161,7 @@
BBT_AUTO_REFRESH
},
+ {"NAND 4GiB 1,8V 8-bit", 0xAC, 2048, 4096, 0x20000, 0},
{NULL,}
};
diff --git a/drivers/net/ethernet/msm/ecm_ipa.c b/drivers/net/ethernet/msm/ecm_ipa.c
index 114b23d..f000df7 100644
--- a/drivers/net/ethernet/msm/ecm_ipa.c
+++ b/drivers/net/ethernet/msm/ecm_ipa.c
@@ -22,13 +22,13 @@
#include <mach/ecm_ipa.h>
#define DRIVER_NAME "ecm_ipa"
-#define DRIVER_VERSION "20-Mar-2013"
#define ECM_IPA_IPV4_HDR_NAME "ecm_eth_ipv4"
#define ECM_IPA_IPV6_HDR_NAME "ecm_eth_ipv6"
#define IPA_TO_USB_CLIENT IPA_CLIENT_USB_CONS
#define INACTIVITY_MSEC_DELAY 100
#define DEFAULT_OUTSTANDING_HIGH 64
#define DEFAULT_OUTSTANDING_LOW 32
+#define DEBUGFS_TEMP_BUF_SIZE 4
#define ECM_IPA_ERROR(fmt, args...) \
pr_err(DRIVER_NAME "@%s@%d@ctx:%s: "\
@@ -47,19 +47,59 @@
#define ECM_IPA_LOG_EXIT() pr_debug("end\n")
/**
+ * enum ecm_ipa_state - specify the current driver internal state
+ * which is guarded by a state machine.
+ *
+ * The driver internal state changes due to its external API usage.
+ * The driver saves its internal state to guard from caller illegal
+ * call sequence.
+ * states:
+ * UNLOADED is the first state which is the default one and is also the state
+ * after the driver gets unloaded(cleanup).
+ * INITIALIZED is the driver state once it finished registering
+ * the network device and all internal data struct were initialized
+ * CONNECTED is the driver state once the USB pipes were connected to IPA
+ * UP is the driver state after the interface mode was set to UP but the
+ * pipes are not connected yet - this state is meta-stable state.
+ * CONNECTED_AND_UP is the driver state when the pipe were connected and
+ * the interface got UP request from the network stack. this is the driver
+ * idle operation state which allows it to transmit/receive data.
+ * INVALID is a state which is not allowed.
+ */
+enum ecm_ipa_state {
+ ECM_IPA_UNLOADED = 0,
+ ECM_IPA_INITIALIZED,
+ ECM_IPA_CONNECTED,
+ ECM_IPA_UP,
+ ECM_IPA_CONNECTED_AND_UP,
+ ECM_IPA_INVALID,
+};
+
+/**
+ * enum ecm_ipa_operation - enumerations used to descibe the API operation
+ *
+ * Those enums are used as input for the driver state machine.
+ */
+enum ecm_ipa_operation {
+ ECM_IPA_INITIALIZE,
+ ECM_IPA_CONNECT,
+ ECM_IPA_OPEN,
+ ECM_IPA_STOP,
+ ECM_IPA_DISCONNECT,
+ ECM_IPA_CLEANUP,
+};
+
+#define ECM_IPA_STATE_DEBUG(ecm_ipa_ctx) \
+ pr_debug("Driver state - %s", ecm_ipa_state_string(ecm_ipa_ctx->state));
+
+/**
* struct ecm_ipa_dev - main driver context parameters
* @net: network interface struct implemented by this driver
- * @folder: debugfs folder for various debuging switches
+ * @directory: debugfs directory for various debuging switches
* @tx_enable: flag that enable/disable Tx path to continue to IPA
* @rx_enable: flag that enable/disable Rx path to continue to IPA
* @rm_enable: flag that enable/disable Resource manager request prior to Tx
* @dma_enable: flag that allow on-the-fly DMA mode for IPA
- * @tx_file: saved debugfs entry to allow cleanup
- * @rx_file: saved debugfs entry to allow cleanup
- * @rm_file: saved debugfs entry to allow cleanup
- * @outstanding_high_file saved debugfs entry to allow cleanup
- * @outstanding_low_file saved debugfs entry to allow cleanup
- * @dma_file: saved debugfs entry to allow cleanup
* @eth_ipv4_hdr_hdl: saved handle for ipv4 header-insertion table
* @eth_ipv6_hdr_hdl: saved handle for ipv6 header-insertion table
* @usb_to_ipa_hdl: save handle for IPA pipe operations
@@ -68,20 +108,15 @@
* @outstanding_high: number of outstanding packets allowed
* @outstanding_low: number of outstanding packets which shall cause
* to netdev queue start (after stopped due to outstanding_high reached)
+ * @state: current state of ecm_ipa driver
*/
struct ecm_ipa_dev {
struct net_device *net;
- bool tx_enable;
- bool rx_enable;
- bool rm_enable;
+ u32 tx_enable;
+ u32 rx_enable;
+ u32 rm_enable;
bool dma_enable;
- struct dentry *folder;
- struct dentry *tx_file;
- struct dentry *rx_file;
- struct dentry *rm_file;
- struct dentry *outstanding_high_file;
- struct dentry *outstanding_low_file;
- struct dentry *dma_file;
+ struct dentry *directory;
uint32_t eth_ipv4_hdr_hdl;
uint32_t eth_ipv6_hdr_hdl;
u32 usb_to_ipa_hdl;
@@ -89,54 +124,52 @@
atomic_t outstanding_pkts;
u8 outstanding_high;
u8 outstanding_low;
+ enum ecm_ipa_state state;
};
-/**
- * struct ecm_ipa_ctx - saved pointer for the std ecm network device
- * which allow ecm_ipa to be a singleton
- */
-static struct ecm_ipa_dev *ecm_ipa_ctx;
-
-static int ecm_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl);
-static int ecm_ipa_set_device_ethernet_addr(
- u8 *dev_ethaddr, u8 device_ethaddr[]);
-static void ecm_ipa_packet_receive_notify(void *priv,
- enum ipa_dp_evt_type evt,
- unsigned long data);
-static void ecm_ipa_tx_complete_notify(void *priv,
- enum ipa_dp_evt_type evt,
- unsigned long data);
-static int ecm_ipa_ep_registers_dma_cfg(u32 usb_to_ipa_hdl);
static int ecm_ipa_open(struct net_device *net);
+static void ecm_ipa_packet_receive_notify(void *priv,
+ enum ipa_dp_evt_type evt, unsigned long data);
+static void ecm_ipa_tx_complete_notify(void *priv,
+ enum ipa_dp_evt_type evt, unsigned long data);
static int ecm_ipa_stop(struct net_device *net);
-static netdev_tx_t ecm_ipa_start_xmit(struct sk_buff *skb,
- struct net_device *net);
-static void ecm_ipa_rm_notify(void *user_data, enum ipa_rm_event event,
- unsigned long data);
-static int ecm_ipa_create_rm_resource(struct ecm_ipa_dev *dev);
-static void ecm_ipa_destory_rm_resource(struct ecm_ipa_dev *dev);
-static bool rx_filter(struct sk_buff *skb);
-static bool tx_filter(struct sk_buff *skb);
-static bool rm_enabled(struct ecm_ipa_dev *dev);
-
-static int ecm_ipa_rules_cfg(struct ecm_ipa_dev *dev,
+static int ecm_ipa_rules_cfg(struct ecm_ipa_dev *ecm_ipa_ctx,
const void *dst_mac, const void *src_mac);
+static void ecm_ipa_rules_destroy(struct ecm_ipa_dev *ecm_ipa_ctx);
static int ecm_ipa_register_properties(void);
static void ecm_ipa_deregister_properties(void);
-static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *dev);
-static void ecm_ipa_debugfs_destroy(struct ecm_ipa_dev *dev);
-static int ecm_ipa_debugfs_tx_open(struct inode *inode, struct file *file);
-static int ecm_ipa_debugfs_rx_open(struct inode *inode, struct file *file);
-static int ecm_ipa_debugfs_rm_open(struct inode *inode, struct file *file);
-static int ecm_ipa_debugfs_dma_open(struct inode *inode, struct file *file);
-static ssize_t ecm_ipa_debugfs_enable_read(struct file *file,
- char __user *ubuf, size_t count, loff_t *ppos);
-static ssize_t ecm_ipa_debugfs_enable_write(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos);
+static void ecm_ipa_rm_notify(void *user_data, enum ipa_rm_event event,
+ unsigned long data);
+static int ecm_ipa_create_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx);
+static void ecm_ipa_destory_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx);
+static bool rx_filter(struct sk_buff *skb);
+static bool tx_filter(struct sk_buff *skb);
+static bool rm_enabled(struct ecm_ipa_dev *ecm_ipa_ctx);
+static int resource_request(struct ecm_ipa_dev *ecm_ipa_ctx);
+static void resource_release(struct ecm_ipa_dev *ecm_ipa_ctx);
+static netdev_tx_t ecm_ipa_start_xmit(struct sk_buff *skb,
+ struct net_device *net);
+static int ecm_ipa_debugfs_atomic_open(struct inode *inode, struct file *file);
static ssize_t ecm_ipa_debugfs_enable_write_dma(struct file *file,
const char __user *buf, size_t count, loff_t *ppos);
-static void eth_get_drvinfo(struct net_device *net,
- struct ethtool_drvinfo *drv_info);
+static int ecm_ipa_debugfs_dma_open(struct inode *inode, struct file *file);
+static ssize_t ecm_ipa_debugfs_enable_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos);
+static ssize_t ecm_ipa_debugfs_enable_read(struct file *file,
+ char __user *ubuf, size_t count, loff_t *ppos);
+static ssize_t ecm_ipa_debugfs_atomic_read(struct file *file,
+ char __user *ubuf, size_t count, loff_t *ppos);
+static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx);
+static void ecm_ipa_debugfs_destroy(struct ecm_ipa_dev *ecm_ipa_ctx);
+static int ecm_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl);
+static int ecm_ipa_ep_registers_dma_cfg(u32 usb_to_ipa_hdl);
+static int ecm_ipa_set_device_ethernet_addr(u8 *dev_ethaddr,
+ u8 device_ethaddr[]);
+static enum ecm_ipa_state ecm_ipa_next_state(enum ecm_ipa_state current_state,
+ enum ecm_ipa_operation operation);
+static const char *ecm_ipa_state_string(enum ecm_ipa_state state);
+static int ecm_ipa_init_module(void);
+static void ecm_ipa_cleanup_module(void);
static const struct net_device_ops ecm_ipa_netdev_ops = {
.ndo_open = ecm_ipa_open,
@@ -144,106 +177,508 @@
.ndo_start_xmit = ecm_ipa_start_xmit,
.ndo_set_mac_address = eth_mac_addr,
};
-static const struct ethtool_ops ops = {
- .get_drvinfo = eth_get_drvinfo,
- .get_link = ethtool_op_get_link,
-};
-const struct file_operations ecm_ipa_debugfs_tx_ops = {
- .open = ecm_ipa_debugfs_tx_open,
- .read = ecm_ipa_debugfs_enable_read,
- .write = ecm_ipa_debugfs_enable_write,
-};
-const struct file_operations ecm_ipa_debugfs_rx_ops = {
- .open = ecm_ipa_debugfs_rx_open,
- .read = ecm_ipa_debugfs_enable_read,
- .write = ecm_ipa_debugfs_enable_write,
-};
-const struct file_operations ecm_ipa_debugfs_rm_ops = {
- .open = ecm_ipa_debugfs_rm_open,
- .read = ecm_ipa_debugfs_enable_read,
- .write = ecm_ipa_debugfs_enable_write,
-};
+
const struct file_operations ecm_ipa_debugfs_dma_ops = {
.open = ecm_ipa_debugfs_dma_open,
.read = ecm_ipa_debugfs_enable_read,
.write = ecm_ipa_debugfs_enable_write_dma,
};
+const struct file_operations ecm_ipa_debugfs_atomic_ops = {
+ .open = ecm_ipa_debugfs_atomic_open,
+ .read = ecm_ipa_debugfs_atomic_read,
+};
+
/**
- * ecm_ipa_init() - initializes internal data structures
- * @ecm_ipa_rx_dp_notify: supplied callback to be called by the IPA
- * driver upon data packets received from USB pipe into IPA core.
- * @ecm_ipa_rt_dp_notify: supplied callback to be called by the IPA
- * driver upon exception packets sent from IPA pipe into USB core.
- * @priv: should be passed later on to ecm_ipa_configure, hold the network
- * structure allocated for STD ECM interface.
+ * ecm_ipa_init() - create network device and initializes internal
+ * data structures
+ * @params: in/out parameters required for ecm_ipa initialization
*
* Shall be called prior to pipe connection.
* The out parameters (the callbacks) shall be supplied to ipa_connect.
* Detailed description:
- * - set the callbacks to be used by the caller upon ipa_connect
* - allocate the network device
- * - set the priv argument with a reference to the network device
+ * - set default values for driver internals
+ * - create debugfs folder and files
+ * - create IPA resource manager client
+ * - add header insertion rules for IPA driver (based on host/device
+ * Ethernet addresses given in input params)
+ * - register tx/rx properties to IPA driver (will be later used
+ * by IPA configuration manager to configure reset of the IPA rules)
+ * - set the carrier state to "off" (until ecm_ipa_connect is called)
+ * - register the network device
+ * - set the out parameters
*
* Returns negative errno, or zero on success
*/
-int ecm_ipa_init(ecm_ipa_callback *ecm_ipa_rx_dp_notify,
- ecm_ipa_callback *ecm_ipa_tx_dp_notify,
- void **priv)
+int ecm_ipa_init(struct ecm_ipa_params *params)
{
- int ret = 0;
+ int result = 0;
struct net_device *net;
- struct ecm_ipa_dev *dev;
+ struct ecm_ipa_dev *ecm_ipa_ctx;
+
ECM_IPA_LOG_ENTRY();
- pr_debug("%s version %s\n", DRIVER_NAME, DRIVER_VERSION);
- NULL_CHECK(ecm_ipa_rx_dp_notify);
- NULL_CHECK(ecm_ipa_tx_dp_notify);
- NULL_CHECK(priv);
+ pr_debug("%s initializing\n", DRIVER_NAME);
+ NULL_CHECK(params);
+
+ pr_debug("host_ethaddr=%pM, device_ethaddr=%pM\n",
+ params->host_ethaddr,
+ params->device_ethaddr);
+
net = alloc_etherdev(sizeof(struct ecm_ipa_dev));
if (!net) {
- ret = -ENOMEM;
+ result = -ENOMEM;
ECM_IPA_ERROR("fail to allocate etherdev\n");
goto fail_alloc_etherdev;
}
- pr_debug("etherdev was successfully allocated\n");
- dev = netdev_priv(net);
- memset(dev, 0, sizeof(*dev));
- dev->tx_enable = true;
- dev->rx_enable = true;
- atomic_set(&dev->outstanding_pkts, 0);
- dev->outstanding_high = DEFAULT_OUTSTANDING_HIGH;
- dev->outstanding_low = DEFAULT_OUTSTANDING_LOW;
- dev->net = net;
- ecm_ipa_ctx = dev;
- *priv = (void *)dev;
+ pr_debug("network device was successfully allocated\n");
+
+ ecm_ipa_ctx = netdev_priv(net);
+ memset(ecm_ipa_ctx, 0, sizeof(*ecm_ipa_ctx));
+ ecm_ipa_ctx->net = net;
+ ecm_ipa_ctx->tx_enable = true;
+ ecm_ipa_ctx->rx_enable = true;
+ ecm_ipa_ctx->rm_enable = true;
+ ecm_ipa_ctx->outstanding_high = DEFAULT_OUTSTANDING_HIGH;
+ ecm_ipa_ctx->outstanding_low = DEFAULT_OUTSTANDING_LOW;
+ atomic_set(&ecm_ipa_ctx->outstanding_pkts, 0);
snprintf(net->name, sizeof(net->name), "%s%%d", "ecm");
net->netdev_ops = &ecm_ipa_netdev_ops;
- pr_debug("internal data structures were intialized\n");
- ret = ecm_ipa_debugfs_init(dev);
- if (ret)
+ pr_debug("internal data structures were intialized and defaults set\n");
+
+ result = ecm_ipa_debugfs_init(ecm_ipa_ctx);
+ if (result)
goto fail_debugfs;
pr_debug("debugfs entries were created\n");
- *ecm_ipa_rx_dp_notify = ecm_ipa_packet_receive_notify;
- *ecm_ipa_tx_dp_notify = ecm_ipa_tx_complete_notify;
+
+ result = ecm_ipa_create_rm_resource(ecm_ipa_ctx);
+ if (result) {
+ ECM_IPA_ERROR("fail on RM create\n");
+ goto fail_create_rm;
+ }
+ pr_debug("RM resource was created\n");
+
+ result = ecm_ipa_set_device_ethernet_addr(net->dev_addr,
+ params->device_ethaddr);
+ if (result) {
+ ECM_IPA_ERROR("set device MAC failed\n");
+ goto fail_set_device_ethernet;
+ }
+ pr_debug("Device Ethernet address set %pM\n", net->dev_addr);
+
+ result = ecm_ipa_rules_cfg(ecm_ipa_ctx, params->host_ethaddr,
+ params->device_ethaddr);
+ if (result) {
+ ECM_IPA_ERROR("fail on ipa rules set\n");
+ goto fail_rules_cfg;
+ }
+ pr_debug("Ethernet header insertion set\n");
+
+ result = ecm_ipa_register_properties();
+ if (result) {
+ ECM_IPA_ERROR("fail on properties set\n");
+ goto fail_register_tx;
+ }
+ pr_debug("ecm_ipa 2 Tx and 2 Rx properties were registered\n");
+
+ netif_carrier_off(net);
+ pr_debug("set carrier off\n");
+
+ result = register_netdev(net);
+ if (result) {
+ ECM_IPA_ERROR("register_netdev failed: %d\n", result);
+ goto fail_register_netdev;
+ }
+ pr_debug("register_netdev succeeded\n");
+
+ params->ecm_ipa_rx_dp_notify = ecm_ipa_packet_receive_notify;
+ params->ecm_ipa_tx_dp_notify = ecm_ipa_tx_complete_notify;
+ params->private = (void *)ecm_ipa_ctx;
+ ecm_ipa_ctx->state = ECM_IPA_INITIALIZED;
+ ECM_IPA_STATE_DEBUG(ecm_ipa_ctx);
+
ECM_IPA_LOG_EXIT();
+
return 0;
+
+fail_register_netdev:
+ ecm_ipa_deregister_properties();
+fail_register_tx:
+ ecm_ipa_rules_destroy(ecm_ipa_ctx);
+fail_set_device_ethernet:
+fail_rules_cfg:
+ ecm_ipa_destory_rm_resource(ecm_ipa_ctx);
+fail_create_rm:
+ ecm_ipa_debugfs_destroy(ecm_ipa_ctx);
fail_debugfs:
free_netdev(net);
fail_alloc_etherdev:
- return ret;
+ return result;
}
EXPORT_SYMBOL(ecm_ipa_init);
/**
+ * ecm_ipa_connect() - notify ecm_ipa for IPA<->USB pipes connection
+ * @usb_to_ipa_hdl: handle of IPA driver client for USB->IPA
+ * @ipa_to_usb_hdl: handle of IPA driver client for IPA->USB
+ * @priv: same value that was set by ecm_ipa_init(), this
+ * parameter holds the network device pointer.
+ *
+ * Once USB driver finishes the pipe connection between IPA core
+ * and USB core this method shall be called in order to
+ * allow ecm_ipa complete the data path configurations.
+ * Detailed description:
+ * - configure the IPA end-points register
+ * - notify the Linux kernel for "carrier_on"
+ * After this function is done the driver state changes to "Connected".
+ * This API is expected to be called after ecm_ipa_init() or
+ * after a call to ecm_ipa_disconnect.
+ */
+int ecm_ipa_connect(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl,
+ void *priv)
+{
+ struct ecm_ipa_dev *ecm_ipa_ctx = priv;
+ int next_state;
+
+ ECM_IPA_LOG_ENTRY();
+ NULL_CHECK(priv);
+ pr_debug("usb_to_ipa_hdl = %d, ipa_to_usb_hdl = %d, priv=0x%p\n",
+ usb_to_ipa_hdl, ipa_to_usb_hdl, priv);
+
+ next_state = ecm_ipa_next_state(ecm_ipa_ctx->state, ECM_IPA_CONNECT);
+ if (next_state == ECM_IPA_INVALID) {
+ ECM_IPA_ERROR("can't call connect before calling initialize\n");
+ return -EPERM;
+ }
+ ecm_ipa_ctx->state = next_state;
+ ECM_IPA_STATE_DEBUG(ecm_ipa_ctx);
+
+ if (!usb_to_ipa_hdl || usb_to_ipa_hdl >= IPA_CLIENT_MAX) {
+ ECM_IPA_ERROR("usb_to_ipa_hdl(%d) is not a valid ipa handle\n",
+ usb_to_ipa_hdl);
+ return -EINVAL;
+ }
+ if (!ipa_to_usb_hdl || ipa_to_usb_hdl >= IPA_CLIENT_MAX) {
+ ECM_IPA_ERROR("ipa_to_usb_hdl(%d) is not a valid ipa handle\n",
+ ipa_to_usb_hdl);
+ return -EINVAL;
+ }
+ ecm_ipa_ctx->ipa_to_usb_hdl = ipa_to_usb_hdl;
+ ecm_ipa_ctx->usb_to_ipa_hdl = usb_to_ipa_hdl;
+ ecm_ipa_ep_registers_cfg(usb_to_ipa_hdl, ipa_to_usb_hdl);
+ pr_debug("end-point configured\n");
+
+ netif_carrier_on(ecm_ipa_ctx->net);
+ if (!netif_carrier_ok(ecm_ipa_ctx->net)) {
+ ECM_IPA_ERROR("netif_carrier_ok error\n");
+ return -EBUSY;
+ }
+ pr_debug("carrier_on notified, ecm_ipa is operational\n");
+
+ if (ecm_ipa_ctx->state == ECM_IPA_CONNECTED_AND_UP) {
+ netif_start_queue(ecm_ipa_ctx->net);
+ pr_debug("queue started\n");
+ }
+
+ ECM_IPA_LOG_EXIT();
+
+ return 0;
+}
+EXPORT_SYMBOL(ecm_ipa_connect);
+
+/**
+ * ecm_ipa_open() - notify Linux network stack to start sending packets
+ * @net: the network interface supplied by the network stack
+ *
+ * Linux uses this API to notify the driver that the network interface
+ * transitions to the up state.
+ * The driver will instruct the Linux network stack to start
+ * delivering data packets.
+ */
+static int ecm_ipa_open(struct net_device *net)
+{
+ struct ecm_ipa_dev *ecm_ipa_ctx;
+ int next_state;
+
+ ECM_IPA_LOG_ENTRY();
+
+ ecm_ipa_ctx = netdev_priv(net);
+
+ next_state = ecm_ipa_next_state(ecm_ipa_ctx->state, ECM_IPA_OPEN);
+ if (next_state == ECM_IPA_INVALID) {
+ ECM_IPA_ERROR("can't bring driver up before initialize\n");
+ return -EPERM;
+ }
+ ecm_ipa_ctx->state = next_state;
+ ECM_IPA_STATE_DEBUG(ecm_ipa_ctx);
+
+ if (ecm_ipa_ctx->state == ECM_IPA_CONNECTED_AND_UP) {
+ netif_start_queue(net);
+ pr_debug("queue started\n");
+ } else {
+ pr_debug("queue was not started due to meta-stabilie state\n");
+ }
+
+ ECM_IPA_LOG_EXIT();
+
+ return 0;
+}
+
+/**
+ * ecm_ipa_start_xmit() - send data from APPs to USB core via IPA core
+ * @skb: packet received from Linux network stack
+ * @net: the network device being used to send this packet
+ *
+ * Several conditions needed in order to send the packet to IPA:
+ * - Transmit queue for the network driver is currently
+ * in "send" state
+ * - The driver internal state is in "UP" state.
+ * - Filter Tx switch is turned off
+ * - The IPA resource manager state for the driver producer client
+ * is "Granted" which implies that all the resources in the dependency
+ * graph are valid for data flow.
+ * - outstanding high boundary did not reach.
+ *
+ * In case all of the above conditions are met, the network driver will
+ * send the packet by using the IPA API for Tx.
+ * In case the outstanding packet high boundary is reached, the driver will
+ * stop the send queue until enough packet were proceeded by the IPA core.
+ */
+static netdev_tx_t ecm_ipa_start_xmit(struct sk_buff *skb,
+ struct net_device *net)
+{
+ int ret;
+ netdev_tx_t status = NETDEV_TX_BUSY;
+ struct ecm_ipa_dev *ecm_ipa_ctx = netdev_priv(net);
+
+ if (unlikely(netif_queue_stopped(net))) {
+ ECM_IPA_ERROR("interface queue is stopped\n");
+ goto out;
+ }
+
+ if (unlikely(ecm_ipa_ctx->state != ECM_IPA_CONNECTED_AND_UP)) {
+ ECM_IPA_ERROR("Missing pipe connected and/or iface up\n");
+ return -NETDEV_TX_BUSY;
+ }
+
+ if (unlikely(tx_filter(skb))) {
+ dev_kfree_skb_any(skb);
+ pr_debug("packet got filtered out on Tx path\n");
+ status = NETDEV_TX_OK;
+ goto out;
+ }
+ ret = resource_request(ecm_ipa_ctx);
+ if (ret) {
+ pr_debug("Waiting to resource\n");
+ netif_stop_queue(net);
+ goto resource_busy;
+ }
+
+ if (atomic_read(&ecm_ipa_ctx->outstanding_pkts) >=
+ ecm_ipa_ctx->outstanding_high) {
+ pr_debug("Outstanding high boundary reached (%d)- stopping queue\n",
+ ecm_ipa_ctx->outstanding_high);
+ netif_stop_queue(net);
+ status = -NETDEV_TX_BUSY;
+ goto out;
+ }
+
+ ret = ipa_tx_dp(IPA_TO_USB_CLIENT, skb, NULL);
+ if (ret) {
+ ECM_IPA_ERROR("ipa transmit failed (%d)\n", ret);
+ goto fail_tx_packet;
+ }
+
+ atomic_inc(&ecm_ipa_ctx->outstanding_pkts);
+ net->stats.tx_packets++;
+ net->stats.tx_bytes += skb->len;
+ status = NETDEV_TX_OK;
+ goto out;
+
+fail_tx_packet:
+out:
+ resource_release(ecm_ipa_ctx);
+resource_busy:
+ return status;
+}
+
+/**
+ * ecm_ipa_packet_receive_notify() - Rx notify
+ *
+ * @priv: ecm driver context
+ * @evt: event type
+ * @data: data provided with event
+ *
+ * IPA will pass a packet to the Linux network stack with skb->data pointing
+ * to Ethernet packet frame.
+ */
+static void ecm_ipa_packet_receive_notify(void *priv,
+ enum ipa_dp_evt_type evt,
+ unsigned long data)
+{
+ struct sk_buff *skb = (struct sk_buff *)data;
+ struct ecm_ipa_dev *ecm_ipa_ctx = priv;
+ int result;
+
+ if (unlikely(ecm_ipa_ctx->state != ECM_IPA_CONNECTED_AND_UP)) {
+ ECM_IPA_ERROR("Missing pipe connected and/or iface up\n");
+ return;
+ }
+
+ if (evt != IPA_RECEIVE) {
+ ECM_IPA_ERROR("A none IPA_RECEIVE event in ecm_ipa_receive\n");
+ return;
+ }
+
+ skb->dev = ecm_ipa_ctx->net;
+ skb->protocol = eth_type_trans(skb, ecm_ipa_ctx->net);
+ if (rx_filter(skb)) {
+ pr_debug("packet got filtered out on Rx path\n");
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
+ result = netif_rx(skb);
+ if (result)
+ ECM_IPA_ERROR("fail on netif_rx\n");
+ ecm_ipa_ctx->net->stats.rx_packets++;
+ ecm_ipa_ctx->net->stats.rx_bytes += skb->len;
+
+ return;
+}
+
+/** ecm_ipa_stop() - called when network device transitions to the down
+ * state.
+ * @net: the network device being stopped.
+ *
+ * This API is used by Linux network stack to notify the network driver that
+ * its state was changed to "down"
+ * The driver will stop the "send" queue and change its internal
+ * state to "Connected".
+ */
+static int ecm_ipa_stop(struct net_device *net)
+{
+ struct ecm_ipa_dev *ecm_ipa_ctx = netdev_priv(net);
+ int next_state;
+
+ ECM_IPA_LOG_ENTRY();
+
+ next_state = ecm_ipa_next_state(ecm_ipa_ctx->state, ECM_IPA_STOP);
+ if (next_state == ECM_IPA_INVALID) {
+ ECM_IPA_ERROR("can't do network interface down without up\n");
+ return -EPERM;
+ }
+ ecm_ipa_ctx->state = next_state;
+ ECM_IPA_STATE_DEBUG(ecm_ipa_ctx);
+
+ netif_stop_queue(net);
+ pr_debug("network device stopped\n");
+
+ ECM_IPA_LOG_EXIT();
+ return 0;
+}
+
+/** ecm_ipa_disconnect() - called when the USB cable is unplugged.
+ * @priv: same value that was set by ecm_ipa_init(), this
+ * parameter holds the network device pointer.
+ *
+ * Once the USB cable is unplugged the USB driver will notify the network
+ * interface driver.
+ * The internal driver state will returned to its initialized state and
+ * Linux network stack will be informed for carrier off and the send queue
+ * will be stopped.
+ */
+int ecm_ipa_disconnect(void *priv)
+{
+ struct ecm_ipa_dev *ecm_ipa_ctx = priv;
+ int next_state;
+
+ ECM_IPA_LOG_ENTRY();
+ NULL_CHECK(ecm_ipa_ctx);
+ pr_debug("priv=0x%p\n", priv);
+
+ next_state = ecm_ipa_next_state(ecm_ipa_ctx->state, ECM_IPA_DISCONNECT);
+ if (next_state == ECM_IPA_INVALID) {
+ ECM_IPA_ERROR("can't disconnect before connect\n");
+ return -EPERM;
+ }
+ ecm_ipa_ctx->state = next_state;
+ ECM_IPA_STATE_DEBUG(ecm_ipa_ctx);
+
+ netif_carrier_off(ecm_ipa_ctx->net);
+ pr_debug("carrier_off notifcation was sent\n");
+
+ netif_stop_queue(ecm_ipa_ctx->net);
+ pr_debug("queue stopped\n");
+
+ ECM_IPA_LOG_EXIT();
+
+ return 0;
+}
+EXPORT_SYMBOL(ecm_ipa_disconnect);
+
+
+/**
+ * ecm_ipa_cleanup() - unregister the network interface driver and free
+ * internal data structs.
+ * @priv: same value that was set by ecm_ipa_init(), this
+ * parameter holds the network device pointer.
+ *
+ * This function shall be called once the network interface is not
+ * needed anymore, e.g: when the USB composition does not support ECM.
+ * This function shall be called after the pipes were disconnected.
+ * Detailed description:
+ * - delete the driver dependency defined for IPA resource manager and
+ * destroy the producer resource.
+ * - remove the debugfs entries
+ * - deregister the network interface from Linux network stack
+ * - free all internal data structs
+ */
+void ecm_ipa_cleanup(void *priv)
+{
+ struct ecm_ipa_dev *ecm_ipa_ctx = priv;
+ int next_state;
+
+ ECM_IPA_LOG_ENTRY();
+
+ pr_debug("priv=0x%p\n", priv);
+
+ if (!ecm_ipa_ctx) {
+ ECM_IPA_ERROR("ecm_ipa_ctx NULL pointer\n");
+ return;
+ }
+
+ next_state = ecm_ipa_next_state(ecm_ipa_ctx->state, ECM_IPA_CLEANUP);
+ if (next_state == ECM_IPA_INVALID) {
+ ECM_IPA_ERROR("can't clean driver without cable disconnect\n");
+ return;
+ }
+ ecm_ipa_ctx->state = next_state;
+ ECM_IPA_STATE_DEBUG(ecm_ipa_ctx);
+
+ ecm_ipa_destory_rm_resource(ecm_ipa_ctx);
+ ecm_ipa_debugfs_destroy(ecm_ipa_ctx);
+
+ unregister_netdev(ecm_ipa_ctx->net);
+ free_netdev(ecm_ipa_ctx->net);
+
+ pr_debug("cleanup done\n");
+ ECM_IPA_LOG_EXIT();
+
+ return ;
+}
+EXPORT_SYMBOL(ecm_ipa_cleanup);
+
+/**
* ecm_ipa_rules_cfg() - set header insertion and register Tx/Rx properties
* Headers will be commited to HW
- * @dev: main driver context parameters
+ * @ecm_ipa_ctx: main driver context parameters
* @dst_mac: destination MAC address
* @src_mac: source MAC address
*
* Returns negative errno, or zero on success
*/
-static int ecm_ipa_rules_cfg(struct ecm_ipa_dev *dev,
+static int ecm_ipa_rules_cfg(struct ecm_ipa_dev *ecm_ipa_ctx,
const void *dst_mac, const void *src_mac)
{
struct ipa_ioc_add_hdr *hdrs;
@@ -295,8 +730,8 @@
result = ipv6_hdr->status;
goto out_free_mem;
}
- dev->eth_ipv4_hdr_hdl = ipv4_hdr->hdr_hdl;
- dev->eth_ipv6_hdr_hdl = ipv6_hdr->hdr_hdl;
+ ecm_ipa_ctx->eth_ipv4_hdr_hdl = ipv4_hdr->hdr_hdl;
+ ecm_ipa_ctx->eth_ipv6_hdr_hdl = ipv6_hdr->hdr_hdl;
ECM_IPA_LOG_EXIT();
out_free_mem:
kfree(hdrs);
@@ -304,7 +739,14 @@
return result;
}
-static void ecm_ipa_rules_destroy(struct ecm_ipa_dev *dev)
+/**
+ * ecm_ipa_rules_destroy() - remove the IPA core configuration done for
+ * the driver data path.
+ * @ecm_ipa_ctx: the driver context
+ *
+ * Revert the work done on ecm_ipa_rules_cfg.
+ */
+static void ecm_ipa_rules_destroy(struct ecm_ipa_dev *ecm_ipa_ctx)
{
struct ipa_ioc_del_hdr *del_hdr;
struct ipa_hdr_del *ipv4;
@@ -317,9 +759,9 @@
del_hdr->commit = 1;
del_hdr->num_hdls = 2;
ipv4 = &del_hdr->hdl[0];
- ipv4->hdl = dev->eth_ipv4_hdr_hdl;
+ ipv4->hdl = ecm_ipa_ctx->eth_ipv4_hdr_hdl;
ipv6 = &del_hdr->hdl[1];
- ipv6->hdl = dev->eth_ipv6_hdr_hdl;
+ ipv6->hdl = ecm_ipa_ctx->eth_ipv6_hdr_hdl;
result = ipa_del_hdr(del_hdr);
if (result || ipv4->status || ipv6->status)
ECM_IPA_ERROR("ipa_del_hdr failed");
@@ -408,133 +850,30 @@
*
* Returns negative errno, or zero on success
*/
-int ecm_ipa_configure(u8 host_ethaddr[], u8 device_ethaddr[],
- void *priv)
-{
- struct ecm_ipa_dev *dev = priv;
- struct net_device *net;
- int result;
- ECM_IPA_LOG_ENTRY();
- NULL_CHECK(host_ethaddr);
- NULL_CHECK(host_ethaddr);
- NULL_CHECK(dev);
- net = dev->net;
- NULL_CHECK(net);
- pr_debug("host_ethaddr=%pM device_ethaddr=%pM\n",
- host_ethaddr, device_ethaddr);
- result = ecm_ipa_create_rm_resource(dev);
- if (result) {
- ECM_IPA_ERROR("fail on RM create\n");
- return -EINVAL;
- }
- pr_debug("RM resource was created\n");
- netif_carrier_off(dev->net);
- result = ecm_ipa_set_device_ethernet_addr(net->dev_addr,
- device_ethaddr);
- if (result) {
- ECM_IPA_ERROR("set device MAC failed\n");
- goto fail_set_device_ethernet;
- }
- result = ecm_ipa_rules_cfg(dev, host_ethaddr, device_ethaddr);
- if (result) {
- ECM_IPA_ERROR("fail on ipa rules set\n");
- goto fail_set_device_ethernet;
- }
- pr_debug("Ethernet header insertion was set\n");
- result = ecm_ipa_register_properties();
- if (result) {
- ECM_IPA_ERROR("fail on properties set\n");
- goto fail_register_tx;
- }
- pr_debug("ECM 2 Tx and 2 Rx properties were registered\n");
- result = register_netdev(net);
- if (result) {
- ECM_IPA_ERROR("register_netdev failed: %d\n", result);
- goto fail_register_netdev;
- }
- pr_debug("register_netdev succeeded\n");
- ECM_IPA_LOG_EXIT();
- return 0;
-fail_register_netdev:
- ecm_ipa_deregister_properties();
-fail_register_tx:
-fail_set_device_ethernet:
- ecm_ipa_rules_destroy(dev);
- ecm_ipa_destory_rm_resource(dev);
- free_netdev(net);
- return result;
-}
-EXPORT_SYMBOL(ecm_ipa_configure);
-
-int ecm_ipa_connect(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl,
- void *priv)
-{
- struct ecm_ipa_dev *dev = priv;
- ECM_IPA_LOG_ENTRY();
- NULL_CHECK(priv);
- pr_debug("usb_to_ipa_hdl = %d, ipa_to_usb_hdl = %d\n",
- usb_to_ipa_hdl, ipa_to_usb_hdl);
- if (!usb_to_ipa_hdl || usb_to_ipa_hdl >= IPA_CLIENT_MAX) {
- ECM_IPA_ERROR("usb_to_ipa_hdl(%d) is not a valid ipa handle\n",
- usb_to_ipa_hdl);
- return -EINVAL;
- }
- if (!ipa_to_usb_hdl || ipa_to_usb_hdl >= IPA_CLIENT_MAX) {
- ECM_IPA_ERROR("ipa_to_usb_hdl(%d) is not a valid ipa handle\n",
- ipa_to_usb_hdl);
- return -EINVAL;
- }
- dev->ipa_to_usb_hdl = ipa_to_usb_hdl;
- dev->usb_to_ipa_hdl = usb_to_ipa_hdl;
- ecm_ipa_ep_registers_cfg(usb_to_ipa_hdl, ipa_to_usb_hdl);
- netif_carrier_on(dev->net);
- if (!netif_carrier_ok(dev->net)) {
- ECM_IPA_ERROR("netif_carrier_ok error\n");
- return -EBUSY;
- }
- ECM_IPA_LOG_EXIT();
- return 0;
-}
-EXPORT_SYMBOL(ecm_ipa_connect);
-
-int ecm_ipa_disconnect(void *priv)
-{
- struct ecm_ipa_dev *dev = priv;
- ECM_IPA_LOG_ENTRY();
- NULL_CHECK(dev);
- netif_carrier_off(dev->net);
- ECM_IPA_LOG_EXIT();
- return 0;
-}
-EXPORT_SYMBOL(ecm_ipa_disconnect);
static void ecm_ipa_rm_notify(void *user_data, enum ipa_rm_event event,
unsigned long data)
{
- struct ecm_ipa_dev *dev = user_data;
+ struct ecm_ipa_dev *ecm_ipa_ctx = user_data;
ECM_IPA_LOG_ENTRY();
if (event == IPA_RM_RESOURCE_GRANTED &&
- netif_queue_stopped(dev->net)) {
+ netif_queue_stopped(ecm_ipa_ctx->net)) {
pr_debug("Resource Granted - waking queue\n");
- netif_wake_queue(dev->net);
+ netif_wake_queue(ecm_ipa_ctx->net);
} else {
pr_debug("Resource released\n");
}
ECM_IPA_LOG_EXIT();
}
-static int ecm_ipa_create_rm_resource(struct ecm_ipa_dev *dev)
+static int ecm_ipa_create_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx)
{
struct ipa_rm_create_params create_params = {0};
int result;
ECM_IPA_LOG_ENTRY();
- if (!dev->rm_enable) {
- pr_debug("RM feature not used\n");
- return 0;
- }
create_params.name = IPA_RM_RESOURCE_STD_ECM_PROD;
- create_params.reg_params.user_data = dev;
+ create_params.reg_params.user_data = ecm_ipa_ctx;
create_params.reg_params.notify_cb = ecm_ipa_rm_notify;
result = ipa_rm_create_resource(&create_params);
if (result) {
@@ -566,86 +905,44 @@
return result;
}
-static void ecm_ipa_destory_rm_resource(struct ecm_ipa_dev *dev)
+static void ecm_ipa_destory_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx)
{
+ int result;
+
ECM_IPA_LOG_ENTRY();
- if (!dev->rm_enable)
- return;
+
ipa_rm_delete_dependency(IPA_RM_RESOURCE_STD_ECM_PROD,
IPA_RM_RESOURCE_USB_CONS);
ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_STD_ECM_PROD);
+ result = ipa_rm_delete_resource(IPA_RM_RESOURCE_STD_ECM_PROD);
+ if (result)
+ ECM_IPA_ERROR("resource deletion failed\n");
ECM_IPA_LOG_EXIT();
}
static bool rx_filter(struct sk_buff *skb)
{
- struct ecm_ipa_dev *dev = netdev_priv(skb->dev);
- return !dev->rx_enable;
+ struct ecm_ipa_dev *ecm_ipa_ctx = netdev_priv(skb->dev);
+ return !ecm_ipa_ctx->rx_enable;
}
static bool tx_filter(struct sk_buff *skb)
{
- struct ecm_ipa_dev *dev = netdev_priv(skb->dev);
- return !dev->tx_enable;
+ struct ecm_ipa_dev *ecm_ipa_ctx = netdev_priv(skb->dev);
+ return !ecm_ipa_ctx->tx_enable;
}
-static bool rm_enabled(struct ecm_ipa_dev *dev)
+static bool rm_enabled(struct ecm_ipa_dev *ecm_ipa_ctx)
{
- return dev->rm_enable;
+ return ecm_ipa_ctx->rm_enable;
}
-static int ecm_ipa_open(struct net_device *net)
-{
- ECM_IPA_LOG_ENTRY();
- netif_start_queue(net);
- ECM_IPA_LOG_EXIT();
- return 0;
-}
-
-static int ecm_ipa_stop(struct net_device *net)
-{
- ECM_IPA_LOG_ENTRY();
- pr_debug("stopping net device\n");
- netif_stop_queue(net);
- ECM_IPA_LOG_EXIT();
- return 0;
-}
-
-/**
- * ecm_ipa_cleanup() - destroys all
- * ecm information
- * @priv: main driver context parameters
- *
- */
-void ecm_ipa_cleanup(void *priv)
-{
- struct ecm_ipa_dev *dev = priv;
- ECM_IPA_LOG_ENTRY();
- if (!dev) {
- ECM_IPA_ERROR("dev NULL pointer\n");
- return;
- }
-
- ecm_ipa_destory_rm_resource(dev);
- ecm_ipa_debugfs_destroy(dev);
-
- if (!dev->net) {
- unregister_netdev(dev->net);
- free_netdev(dev->net);
- }
- pr_debug("cleanup done\n");
- ecm_ipa_ctx = NULL;
- ECM_IPA_LOG_EXIT();
- return ;
-}
-EXPORT_SYMBOL(ecm_ipa_cleanup);
-
-static int resource_request(struct ecm_ipa_dev *dev)
+static int resource_request(struct ecm_ipa_dev *ecm_ipa_ctx)
{
int result = 0;
- if (!rm_enabled(dev))
+ if (!rm_enabled(ecm_ipa_ctx))
goto out;
result = ipa_rm_inactivity_timer_request_resource(
IPA_RM_RESOURCE_STD_ECM_PROD);
@@ -653,9 +950,9 @@
return result;
}
-static void resource_release(struct ecm_ipa_dev *dev)
+static void resource_release(struct ecm_ipa_dev *ecm_ipa_ctx)
{
- if (!rm_enabled(dev))
+ if (!rm_enabled(ecm_ipa_ctx))
goto out;
ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_STD_ECM_PROD);
out:
@@ -663,111 +960,6 @@
}
/**
- * ecm_ipa_start_xmit() - send data from APPs to USB core via IPA core
- * @skb: packet received from Linux stack
- * @net: the network device being used to send this packet
- *
- * Several conditions needed in order to send the packet to IPA:
- * - we are in a valid state were the queue is not stopped
- * - Filter Tx switch is turned off
- * - The resources required for actual Tx are all up
- *
- */
-static netdev_tx_t ecm_ipa_start_xmit(struct sk_buff *skb,
- struct net_device *net)
-{
- int ret;
- netdev_tx_t status = NETDEV_TX_BUSY;
- struct ecm_ipa_dev *dev = netdev_priv(net);
-
- if (unlikely(netif_queue_stopped(net))) {
- ECM_IPA_ERROR("interface queue is stopped\n");
- goto out;
- }
-
- if (unlikely(tx_filter(skb))) {
- dev_kfree_skb_any(skb);
- pr_debug("packet got filtered out on Tx path\n");
- status = NETDEV_TX_OK;
- goto out;
- }
- ret = resource_request(dev);
- if (ret) {
- pr_debug("Waiting to resource\n");
- netif_stop_queue(net);
- goto resource_busy;
- }
-
- pr_debug("Before sending packet the outstanding packets counter is %d\n",
- atomic_read(&dev->outstanding_pkts));
-
- if (atomic_read(&dev->outstanding_pkts) >= dev->outstanding_high) {
- pr_debug("Outstanding high boundary reached (%d)- stopping queue\n",
- dev->outstanding_high);
- netif_stop_queue(net);
- status = -NETDEV_TX_BUSY;
- goto out;
- }
-
- ret = ipa_tx_dp(IPA_TO_USB_CLIENT, skb, NULL);
- if (ret) {
- ECM_IPA_ERROR("ipa transmit failed (%d)\n", ret);
- goto fail_tx_packet;
- }
-
- atomic_inc(&dev->outstanding_pkts);
- net->stats.tx_packets++;
- net->stats.tx_bytes += skb->len;
- status = NETDEV_TX_OK;
- goto out;
-
-fail_tx_packet:
-out:
- resource_release(dev);
-resource_busy:
- return status;
-}
-
-/**
- * ecm_ipa_packet_receive_notify() - Rx notify
- *
- * @priv: ecm driver context
- * @evt: event type
- * @data: data provided with event
- *
- * IPA will pass a packet with skb->data pointing to Ethernet packet frame
- */
-void ecm_ipa_packet_receive_notify(void *priv,
- enum ipa_dp_evt_type evt,
- unsigned long data)
-{
- struct sk_buff *skb = (struct sk_buff *)data;
- struct ecm_ipa_dev *dev = priv;
- int result;
-
- if (evt != IPA_RECEIVE) {
- ECM_IPA_ERROR("A none IPA_RECEIVE event in ecm_ipa_receive\n");
- return;
- }
-
- skb->dev = dev->net;
- skb->protocol = eth_type_trans(skb, dev->net);
- if (rx_filter(skb)) {
- pr_debug("packet got filtered out on Rx path\n");
- dev_kfree_skb_any(skb);
- return;
- }
-
- result = netif_rx(skb);
- if (result)
- ECM_IPA_ERROR("fail on netif_rx\n");
- dev->net->stats.rx_packets++;
- dev->net->stats.rx_bytes += skb->len;
-
- return;
-}
-
-/**
* ecm_ipa_tx_complete_notify() - Rx notify
*
* @priv: ecm driver context
@@ -777,58 +969,39 @@
* Check that the packet is the one we sent and release it
* This function will be called in defered context in IPA wq.
*/
-void ecm_ipa_tx_complete_notify(void *priv,
+static void ecm_ipa_tx_complete_notify(void *priv,
enum ipa_dp_evt_type evt,
unsigned long data)
{
struct sk_buff *skb = (struct sk_buff *)data;
- struct ecm_ipa_dev *dev = priv;
+ struct ecm_ipa_dev *ecm_ipa_ctx = priv;
- if (!dev) {
- ECM_IPA_ERROR("dev is NULL pointer\n");
+ if (!ecm_ipa_ctx) {
+ ECM_IPA_ERROR("ecm_ipa_ctx is NULL pointer\n");
return;
}
if (evt != IPA_WRITE_DONE) {
ECM_IPA_ERROR("unsupported event on Tx callback\n");
return;
}
- atomic_dec(&dev->outstanding_pkts);
- if (netif_queue_stopped(dev->net) &&
- atomic_read(&dev->outstanding_pkts) < (dev->outstanding_low)) {
+ atomic_dec(&ecm_ipa_ctx->outstanding_pkts);
+ if (netif_queue_stopped(ecm_ipa_ctx->net) &&
+ atomic_read(&ecm_ipa_ctx->outstanding_pkts) <
+ (ecm_ipa_ctx->outstanding_low)) {
pr_debug("Outstanding low boundary reached (%d) - waking up queue\n",
- dev->outstanding_low);
- netif_wake_queue(dev->net);
+ ecm_ipa_ctx->outstanding_low);
+ netif_wake_queue(ecm_ipa_ctx->net);
}
- pr_debug("After Tx-complete the outstanding packets counter is %d\n",
- atomic_read(&dev->outstanding_pkts));
dev_kfree_skb_any(skb);
return;
}
-static int ecm_ipa_debugfs_tx_open(struct inode *inode, struct file *file)
+static int ecm_ipa_debugfs_atomic_open(struct inode *inode, struct file *file)
{
- struct ecm_ipa_dev *dev = inode->i_private;
+ struct ecm_ipa_dev *ecm_ipa_ctx = inode->i_private;
ECM_IPA_LOG_ENTRY();
- file->private_data = &(dev->tx_enable);
- ECM_IPA_LOG_ENTRY();
- return 0;
-}
-
-static int ecm_ipa_debugfs_rx_open(struct inode *inode, struct file *file)
-{
- struct ecm_ipa_dev *dev = inode->i_private;
- ECM_IPA_LOG_ENTRY();
- file->private_data = &(dev->rx_enable);
- ECM_IPA_LOG_EXIT();
- return 0;
-}
-
-static int ecm_ipa_debugfs_rm_open(struct inode *inode, struct file *file)
-{
- struct ecm_ipa_dev *dev = inode->i_private;
- ECM_IPA_LOG_ENTRY();
- file->private_data = &(dev->rm_enable);
+ file->private_data = &(ecm_ipa_ctx->outstanding_pkts);
ECM_IPA_LOG_EXIT();
return 0;
}
@@ -836,25 +1009,25 @@
static ssize_t ecm_ipa_debugfs_enable_write_dma(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
- struct ecm_ipa_dev *dev = file->private_data;
+ struct ecm_ipa_dev *ecm_ipa_ctx = file->private_data;
int result;
ECM_IPA_LOG_ENTRY();
- file->private_data = &dev->dma_enable;
+ file->private_data = &ecm_ipa_ctx->dma_enable;
result = ecm_ipa_debugfs_enable_write(file, buf, count, ppos);
- if (dev->dma_enable)
- ecm_ipa_ep_registers_dma_cfg(dev->usb_to_ipa_hdl);
+ if (ecm_ipa_ctx->dma_enable)
+ ecm_ipa_ep_registers_dma_cfg(ecm_ipa_ctx->usb_to_ipa_hdl);
else
- ecm_ipa_ep_registers_cfg(dev->usb_to_ipa_hdl,
- dev->usb_to_ipa_hdl);
+ ecm_ipa_ep_registers_cfg(ecm_ipa_ctx->usb_to_ipa_hdl,
+ ecm_ipa_ctx->usb_to_ipa_hdl);
ECM_IPA_LOG_EXIT();
return result;
}
static int ecm_ipa_debugfs_dma_open(struct inode *inode, struct file *file)
{
- struct ecm_ipa_dev *dev = inode->i_private;
+ struct ecm_ipa_dev *ecm_ipa_ctx = inode->i_private;
ECM_IPA_LOG_ENTRY();
- file->private_data = dev;
+ file->private_data = ecm_ipa_ctx;
ECM_IPA_LOG_EXIT();
return 0;
}
@@ -904,87 +1077,93 @@
return size;
}
-static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *dev)
+static ssize_t ecm_ipa_debugfs_atomic_read(struct file *file,
+ char __user *ubuf, size_t count, loff_t *ppos)
{
- const mode_t flags = S_IRUGO | S_IWUGO;
+ int nbytes;
+ u8 atomic_str[DEBUGFS_TEMP_BUF_SIZE] = {0};
+ atomic_t *atomic_var = file->private_data;
+ nbytes = scnprintf(atomic_str, sizeof(atomic_str), "%d\n",
+ atomic_read(atomic_var));
+ return simple_read_from_buffer(ubuf, count, ppos, atomic_str, nbytes);
+}
- int ret = -EINVAL;
+
+static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx)
+{
+ const mode_t flags_read_write = S_IRUGO | S_IWUGO;
+ const mode_t flags_read_only = S_IRUGO;
+ struct dentry *file;
+
ECM_IPA_LOG_ENTRY();
- if (!dev)
- return -EINVAL;
- dev->folder = debugfs_create_dir("ecm_ipa", NULL);
- if (!dev->folder) {
- ECM_IPA_ERROR("could not create debugfs folder entry\n");
- ret = -EFAULT;
- goto fail_folder;
- }
- dev->tx_file = debugfs_create_file("tx_enable", flags, dev->folder, dev,
- &ecm_ipa_debugfs_tx_ops);
- if (!dev->tx_file) {
- ECM_IPA_ERROR("could not create debugfs tx file\n");
- ret = -EFAULT;
- goto fail_file;
- }
- dev->rx_file = debugfs_create_file("rx_enable", flags, dev->folder, dev,
- &ecm_ipa_debugfs_rx_ops);
- if (!dev->rx_file) {
- ECM_IPA_ERROR("could not create debugfs rx file\n");
- ret = -EFAULT;
- goto fail_file;
- }
- dev->rm_file = debugfs_create_file("rm_enable", flags, dev->folder, dev,
- &ecm_ipa_debugfs_rm_ops);
- if (!dev->rm_file) {
- ECM_IPA_ERROR("could not create debugfs rm file\n");
- ret = -EFAULT;
- goto fail_file;
- }
- dev->dma_file = debugfs_create_file("dma_enable", flags, dev->folder,
- dev, &ecm_ipa_debugfs_dma_ops);
- if (!dev->dma_file) {
- ECM_IPA_ERROR("could not create debugfs dma file\n");
- ret = -EFAULT;
- goto fail_file;
- }
- dev->outstanding_high_file = debugfs_create_u8("outstanding_high",
- flags, dev->folder, &dev->outstanding_high);
- if (!dev->outstanding_high_file) {
- ECM_IPA_ERROR("could not create outstanding_high file\n");
- ret = -EFAULT;
+ if (!ecm_ipa_ctx)
+ return -EINVAL;
+
+ ecm_ipa_ctx->directory = debugfs_create_dir("ecm_ipa", NULL);
+ if (!ecm_ipa_ctx->directory) {
+ ECM_IPA_ERROR("could not create debugfs directory entry\n");
+ goto fail_directory;
+ }
+ file = debugfs_create_bool("tx_enable", flags_read_write,
+ ecm_ipa_ctx->directory, &ecm_ipa_ctx->tx_enable);
+ if (!file) {
+ ECM_IPA_ERROR("could not create debugfs tx file\n");
goto fail_file;
}
- dev->outstanding_low_file = debugfs_create_u8("outstanding_low",
- flags, dev->folder, &dev->outstanding_low);
- if (!dev->outstanding_low_file) {
+ file = debugfs_create_bool("rx_enable", flags_read_write,
+ ecm_ipa_ctx->directory, &ecm_ipa_ctx->rx_enable);
+ if (!file) {
+ ECM_IPA_ERROR("could not create debugfs rx file\n");
+ goto fail_file;
+ }
+ file = debugfs_create_bool("rm_enable", flags_read_write,
+ ecm_ipa_ctx->directory, &ecm_ipa_ctx->rm_enable);
+ if (!file) {
+ ECM_IPA_ERROR("could not create debugfs rm file\n");
+ goto fail_file;
+ }
+ file = debugfs_create_u8("outstanding_high", flags_read_write,
+ ecm_ipa_ctx->directory, &ecm_ipa_ctx->outstanding_high);
+ if (!file) {
+ ECM_IPA_ERROR("could not create outstanding_high file\n");
+ goto fail_file;
+ }
+ file = debugfs_create_u8("outstanding_low", flags_read_write,
+ ecm_ipa_ctx->directory, &ecm_ipa_ctx->outstanding_low);
+ if (!file) {
ECM_IPA_ERROR("could not create outstanding_low file\n");
- ret = -EFAULT;
+ goto fail_file;
+ }
+ file = debugfs_create_file("dma_enable", flags_read_write,
+ ecm_ipa_ctx->directory,
+ ecm_ipa_ctx, &ecm_ipa_debugfs_dma_ops);
+ if (!file) {
+ ECM_IPA_ERROR("could not create debugfs dma file\n");
+ goto fail_file;
+ }
+ file = debugfs_create_file("outstanding", flags_read_only,
+ ecm_ipa_ctx->directory,
+ ecm_ipa_ctx, &ecm_ipa_debugfs_atomic_ops);
+ if (!file) {
+ ECM_IPA_ERROR("could not create outstanding file\n");
goto fail_file;
}
ECM_IPA_LOG_EXIT();
+
return 0;
fail_file:
- debugfs_remove_recursive(dev->folder);
-fail_folder:
- return ret;
+ debugfs_remove_recursive(ecm_ipa_ctx->directory);
+fail_directory:
+ return -EFAULT;
}
-static void ecm_ipa_debugfs_destroy(struct ecm_ipa_dev *dev)
+static void ecm_ipa_debugfs_destroy(struct ecm_ipa_dev *ecm_ipa_ctx)
{
- debugfs_remove_recursive(dev->folder);
+ debugfs_remove_recursive(ecm_ipa_ctx->directory);
}
-static void eth_get_drvinfo(struct net_device *net,
- struct ethtool_drvinfo *drv_info)
-{
- ECM_IPA_LOG_ENTRY();
- strlcpy(drv_info->driver, DRIVER_NAME, sizeof(drv_info->driver));
- strlcpy(drv_info->version, DRIVER_VERSION, sizeof(drv_info->version));
- ECM_IPA_LOG_EXIT();
-}
-
-
/**
* ecm_ipa_ep_cfg() - configure the USB endpoints for ECM
*
@@ -1000,7 +1179,7 @@
* - No aggregation
* - Add Ethernet header
*/
-int ecm_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl)
+static int ecm_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl)
{
int result = 0;
struct ipa_ep_cfg usb_to_ipa_ep_cfg;
@@ -1042,7 +1221,7 @@
* which is needed for cores that does not support blocks logic
* Note that client handles are the actual pipe index
*/
-int ecm_ipa_ep_registers_dma_cfg(u32 usb_to_ipa_hdl)
+static int ecm_ipa_ep_registers_dma_cfg(u32 usb_to_ipa_hdl)
{
int result = 0;
struct ipa_ep_cfg_mode cfg_mode;
@@ -1078,7 +1257,8 @@
*
* Returns 0 for success, negative otherwise
*/
-int ecm_ipa_set_device_ethernet_addr(u8 *dev_ethaddr, u8 device_ethaddr[])
+static int ecm_ipa_set_device_ethernet_addr(u8 *dev_ethaddr,
+ u8 device_ethaddr[])
{
if (!is_valid_ether_addr(device_ethaddr))
return -EINVAL;
@@ -1087,6 +1267,89 @@
return 0;
}
+/** ecm_ipa_next_state - return the next state of the driver
+ * @current_state: the current state of the driver
+ * @operation: an enum which represent the operation being made on the driver
+ * by its API.
+ *
+ * This function implements the driver internal state machine.
+ * Its decisions are based on the driver current state and the operation
+ * being made.
+ * In case the operation is invalid this state machine will return
+ * the value ECM_IPA_INVALID to inform the caller for a forbidden sequence.
+ */
+static enum ecm_ipa_state ecm_ipa_next_state(enum ecm_ipa_state current_state,
+ enum ecm_ipa_operation operation)
+{
+ int next_state = ECM_IPA_INVALID;
+
+ switch (current_state) {
+ case ECM_IPA_UNLOADED:
+ if (operation == ECM_IPA_INITIALIZE)
+ next_state = ECM_IPA_INITIALIZED;
+ break;
+ case ECM_IPA_INITIALIZED:
+ if (operation == ECM_IPA_CONNECT)
+ next_state = ECM_IPA_CONNECTED;
+ else if (operation == ECM_IPA_OPEN)
+ next_state = ECM_IPA_UP;
+ else if (operation == ECM_IPA_CLEANUP)
+ next_state = ECM_IPA_UNLOADED;
+ break;
+ case ECM_IPA_CONNECTED:
+ if (operation == ECM_IPA_DISCONNECT)
+ next_state = ECM_IPA_INITIALIZED;
+ else if (operation == ECM_IPA_OPEN)
+ next_state = ECM_IPA_CONNECTED_AND_UP;
+ break;
+ case ECM_IPA_UP:
+ if (operation == ECM_IPA_STOP)
+ next_state = ECM_IPA_INITIALIZED;
+ else if (operation == ECM_IPA_CONNECT)
+ next_state = ECM_IPA_CONNECTED_AND_UP;
+ break;
+ case ECM_IPA_CONNECTED_AND_UP:
+ if (operation == ECM_IPA_STOP)
+ next_state = ECM_IPA_CONNECTED;
+ else if (operation == ECM_IPA_DISCONNECT)
+ next_state = ECM_IPA_UP;
+ break;
+ default:
+ ECM_IPA_ERROR("State is not supported\n");
+ break;
+ }
+
+ pr_debug("state transition ( %s -> %s )- %s\n",
+ ecm_ipa_state_string(current_state),
+ ecm_ipa_state_string(next_state) ,
+ next_state == ECM_IPA_INVALID ?
+ "Forbidden" : "Allowed");
+
+ return next_state;
+}
+
+/**
+ * ecm_ipa_state_string - return the state string representation
+ * @state: enum which describe the state
+ */
+static const char *ecm_ipa_state_string(enum ecm_ipa_state state)
+{
+ switch (state) {
+ case ECM_IPA_UNLOADED:
+ return "ECM_IPA_UNLOADED";
+ case ECM_IPA_INITIALIZED:
+ return "ECM_IPA_INITIALIZED";
+ case ECM_IPA_CONNECTED:
+ return "ECM_IPA_CONNECTED";
+ case ECM_IPA_UP:
+ return "ECM_IPA_UP";
+ case ECM_IPA_CONNECTED_AND_UP:
+ return "ECM_IPA_CONNECTED_AND_UP";
+ default:
+ return "Not supported";
+ }
+}
+
/**
* ecm_ipa_init_module() - module initialization
*
diff --git a/drivers/net/ethernet/msm/msm_rmnet_smux.c b/drivers/net/ethernet/msm/msm_rmnet_smux.c
index 5fe724e..e2bd82d 100644
--- a/drivers/net/ethernet/msm/msm_rmnet_smux.c
+++ b/drivers/net/ethernet/msm/msm_rmnet_smux.c
@@ -55,7 +55,7 @@
#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
/* Configure device instances */
-#define RMNET_SMUX_DEVICE_COUNT (1)
+#define RMNET_SMUX_DEVICE_COUNT (2)
/* allow larger frames */
#define RMNET_DATA_LEN 2000
diff --git a/drivers/net/ethernet/msm/msm_rmnet_wwan.c b/drivers/net/ethernet/msm/msm_rmnet_wwan.c
index f90ee3d..98bdccc 100644
--- a/drivers/net/ethernet/msm/msm_rmnet_wwan.c
+++ b/drivers/net/ethernet/msm/msm_rmnet_wwan.c
@@ -547,7 +547,7 @@
static void wwan_tx_timeout(struct net_device *dev)
{
- pr_warning("[%s] wwan_tx_timeout()\n", dev->name);
+ pr_warning("[%s] wwan_tx_timeout(), data stall in UL\n", dev->name);
}
/**
diff --git a/drivers/net/wireless/wcnss/wcnss_prealloc.c b/drivers/net/wireless/wcnss/wcnss_prealloc.c
index 7d10657..c450bcb 100644
--- a/drivers/net/wireless/wcnss/wcnss_prealloc.c
+++ b/drivers/net/wireless/wcnss/wcnss_prealloc.c
@@ -13,8 +13,9 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/wcnss_wlan.h>
+#include <linux/spinlock.h>
-static DEFINE_MUTEX(alloc_lock);
+static DEFINE_SPINLOCK(alloc_lock);
struct wcnss_prealloc {
int occupied;
@@ -57,15 +58,18 @@
{
int i = 0;
- for (i = 0; i < ARRAY_SIZE(wcnss_allocs); i++)
+ for (i = 0; i < ARRAY_SIZE(wcnss_allocs); i++) {
kfree(wcnss_allocs[i].ptr);
+ wcnss_allocs[i].ptr = NULL;
+ }
}
void *wcnss_prealloc_get(unsigned int size)
{
int i = 0;
+ unsigned long flags;
- mutex_lock(&alloc_lock);
+ spin_lock_irqsave(&alloc_lock, flags);
for (i = 0; i < ARRAY_SIZE(wcnss_allocs); i++) {
if (wcnss_allocs[i].occupied)
continue;
@@ -73,12 +77,13 @@
if (wcnss_allocs[i].size > size) {
/* we found the slot */
wcnss_allocs[i].occupied = 1;
- mutex_unlock(&alloc_lock);
+ spin_unlock_irqrestore(&alloc_lock, flags);
return wcnss_allocs[i].ptr;
}
}
- pr_err("wcnss: %s: prealloc not available\n", __func__);
- mutex_unlock(&alloc_lock);
+ spin_unlock_irqrestore(&alloc_lock, flags);
+ pr_err("wcnss: %s: prealloc not available for size: %d\n",
+ __func__, size);
return NULL;
}
@@ -87,16 +92,17 @@
int wcnss_prealloc_put(void *ptr)
{
int i = 0;
+ unsigned long flags;
- mutex_lock(&alloc_lock);
+ spin_lock_irqsave(&alloc_lock, flags);
for (i = 0; i < ARRAY_SIZE(wcnss_allocs); i++) {
if (wcnss_allocs[i].ptr == ptr) {
wcnss_allocs[i].occupied = 0;
- mutex_unlock(&alloc_lock);
+ spin_unlock_irqrestore(&alloc_lock, flags);
return 1;
}
}
- mutex_unlock(&alloc_lock);
+ spin_unlock_irqrestore(&alloc_lock, flags);
return 0;
}
diff --git a/drivers/net/wireless/wcnss/wcnss_vreg.c b/drivers/net/wireless/wcnss/wcnss_vreg.c
index 7e6cd4f..c02daa4 100644
--- a/drivers/net/wireless/wcnss/wcnss_vreg.c
+++ b/drivers/net/wireless/wcnss/wcnss_vreg.c
@@ -31,6 +31,7 @@
static LIST_HEAD(power_on_lock_list);
static DEFINE_MUTEX(list_lock);
static DEFINE_SEMAPHORE(wcnss_power_on_lock);
+static int auto_detect;
#define MSM_RIVA_PHYS 0x03204000
#define MSM_PRONTO_PHYS 0xfb21b000
@@ -42,11 +43,17 @@
#define PRONTO_SPARE_OFFSET 0x1088
#define NVBIN_DLND_BIT BIT(25)
+#define PRONTO_IRIS_REG_READ_OFFSET 0x1134
+#define PRONTO_IRIS_REG_CHIP_ID 0x04
+
#define WCNSS_PMU_CFG_IRIS_XO_CFG BIT(3)
#define WCNSS_PMU_CFG_IRIS_XO_EN BIT(4)
#define WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP BIT(5)
#define WCNSS_PMU_CFG_IRIS_XO_CFG_STS BIT(6) /* 1: in progress, 0: done */
+#define WCNSS_PMU_CFG_IRIS_XO_READ BIT(9)
+#define WCNSS_PMU_CFG_IRIS_XO_READ_STS BIT(10)
+
#define WCNSS_PMU_CFG_IRIS_XO_MODE 0x6
#define WCNSS_PMU_CFG_IRIS_XO_MODE_48 (3 << 1)
@@ -56,6 +63,8 @@
#define VREG_OPTIMUM_MODE_MASK 0x0004
#define VREG_ENABLE_MASK 0x0008
+#define WCNSS_INVALID_IRIS_REG 0xbaadbaad
+
struct vregs_info {
const char * const name;
int state;
@@ -110,10 +119,38 @@
struct list_head list;
};
+enum {
+ WCNSS_XO_48MHZ = 1,
+ WCNSS_XO_19MHZ,
+ WCNSS_XO_INVALID,
+};
+
+enum {
+ IRIS_3660, /* also 3660A and 3680 */
+ IRIS_3620
+};
+
+
+int xo_auto_detect(u32 reg)
+{
+ reg >>= 30;
+
+ switch (reg) {
+ case IRIS_3660:
+ return WCNSS_XO_48MHZ;
+
+ case IRIS_3620:
+ return WCNSS_XO_19MHZ;
+
+ default:
+ return WCNSS_XO_INVALID;
+ }
+}
static int configure_iris_xo(struct device *dev, bool use_48mhz_xo, int on)
{
u32 reg = 0;
+ u32 iris_reg = WCNSS_INVALID_IRIS_REG;
int rc = 0;
int size = 0;
int pmu_offset = 0;
@@ -121,6 +158,7 @@
unsigned long wcnss_phys_addr;
void __iomem *pmu_conf_reg;
void __iomem *spare_reg;
+ void __iomem *iris_read_reg;
struct clk *clk;
struct clk *clk_rf = NULL;
@@ -136,14 +174,6 @@
return PTR_ERR(clk);
}
- if (!use_48mhz_xo) {
- clk_rf = clk_get(dev, "rf_clk");
- if (IS_ERR(clk_rf)) {
- pr_err("Couldn't get rf_clk\n");
- clk_put(clk);
- return PTR_ERR(clk_rf);
- }
- }
} else {
wcnss_phys_addr = MSM_RIVA_PHYS;
pmu_offset = RIVA_PMU_OFFSET;
@@ -172,16 +202,13 @@
}
/* NV bit is set to indicate that platform driver is capable
- * of doing NV download. SSR should not set NV bit; during
- * SSR NV bin is downloaded by WLAN driver.
+ * of doing NV download.
*/
- if (!wcnss_cold_boot_done()) {
- pr_debug("wcnss: Indicate NV bin download\n");
- spare_reg = msm_wcnss_base + spare_offset;
- reg = readl_relaxed(spare_reg);
- reg |= NVBIN_DLND_BIT;
- writel_relaxed(reg, spare_reg);
- }
+ pr_debug("wcnss: Indicate NV bin download\n");
+ spare_reg = msm_wcnss_base + spare_offset;
+ reg = readl_relaxed(spare_reg);
+ reg |= NVBIN_DLND_BIT;
+ writel_relaxed(reg, spare_reg);
pmu_conf_reg = msm_wcnss_base + pmu_offset;
writel_relaxed(0, pmu_conf_reg);
@@ -190,10 +217,44 @@
WCNSS_PMU_CFG_IRIS_XO_EN;
writel_relaxed(reg, pmu_conf_reg);
+ if (wcnss_xo_auto_detect_enabled()) {
+ iris_read_reg = msm_wcnss_base +
+ PRONTO_IRIS_REG_READ_OFFSET;
+ iris_reg = readl_relaxed(iris_read_reg);
+ }
+
+ if (iris_reg != WCNSS_INVALID_IRIS_REG) {
+ iris_reg &= 0xffff;
+ iris_reg |= PRONTO_IRIS_REG_CHIP_ID;
+ writel_relaxed(iris_reg, iris_read_reg);
+
+ /* Iris read */
+ reg = readl_relaxed(pmu_conf_reg);
+ reg |= WCNSS_PMU_CFG_IRIS_XO_READ;
+ writel_relaxed(reg, pmu_conf_reg);
+
+ /* Wait for PMU_CFG.iris_reg_read_sts */
+ while (readl_relaxed(pmu_conf_reg) &
+ WCNSS_PMU_CFG_IRIS_XO_READ_STS)
+ cpu_relax();
+
+ iris_reg = readl_relaxed(iris_read_reg);
+ auto_detect = xo_auto_detect(iris_reg);
+
+ /* Reset iris read bit */
+ reg &= ~WCNSS_PMU_CFG_IRIS_XO_READ;
+
+ } else if (wcnss_xo_auto_detect_enabled())
+ /* Default to 48 MHZ */
+ auto_detect = WCNSS_XO_48MHZ;
+ else
+ auto_detect = WCNSS_XO_INVALID;
+
/* Clear XO_MODE[b2:b1] bits. Clear implies 19.2 MHz TCXO */
reg &= ~(WCNSS_PMU_CFG_IRIS_XO_MODE);
- if (use_48mhz_xo)
+ if ((use_48mhz_xo && auto_detect == WCNSS_XO_INVALID)
+ || auto_detect == WCNSS_XO_48MHZ)
reg |= WCNSS_PMU_CFG_IRIS_XO_MODE_48;
writel_relaxed(reg, pmu_conf_reg);
@@ -213,30 +274,41 @@
writel_relaxed(reg, pmu_conf_reg);
clk_disable_unprepare(clk);
- if (!use_48mhz_xo) {
+ if ((!use_48mhz_xo && auto_detect == WCNSS_XO_INVALID)
+ || auto_detect == WCNSS_XO_19MHZ) {
+
+ clk_rf = clk_get(dev, "rf_clk");
+ if (IS_ERR(clk_rf)) {
+ pr_err("Couldn't get rf_clk\n");
+ goto fail;
+ }
+
rc = clk_prepare_enable(clk_rf);
if (rc) {
pr_err("clk_rf enable failed\n");
goto fail;
}
}
- } else if (clk_rf != NULL && !use_48mhz_xo)
- clk_disable_unprepare(clk_rf);
+
+ } else if ((!use_48mhz_xo && auto_detect == WCNSS_XO_INVALID)
+ || auto_detect == WCNSS_XO_19MHZ) {
+ clk_rf = clk_get(dev, "rf_clk");
+ if (IS_ERR(clk_rf)) {
+ pr_err("Couldn't get rf_clk\n");
+ goto fail;
+ }
+ clk_disable_unprepare(clk_rf);
+ }
+
/* Add some delay for XO to settle */
msleep(20);
+fail:
clk_put(clk);
- if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
- if (!use_48mhz_xo)
- clk_put(clk_rf);
- }
-
- return rc;
-fail:
if (clk_rf != NULL)
clk_put(clk_rf);
- clk_put(clk);
+
return rc;
}
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index 1a175c9..0b4f0fe 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -28,6 +28,10 @@
#include <linux/of_gpio.h>
#include <linux/clk.h>
#include <linux/ratelimit.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/pm8xxx/misc.h>
#include <mach/msm_smd.h>
#include <mach/msm_iomap.h>
@@ -48,6 +52,14 @@
module_param(has_48mhz_xo, int, S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(has_48mhz_xo, "Is an external 48 MHz XO present");
+static int has_calibrated_data = WCNSS_CONFIG_UNSPECIFIED;
+module_param(has_calibrated_data, int, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(has_calibrated_data, "whether calibrated data file available");
+
+static int has_autodetect_xo = WCNSS_CONFIG_UNSPECIFIED;
+module_param(has_autodetect_xo, int, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(has_autodetect_xo, "Perform auto detect to configure IRIS XO");
+
static int do_not_cancel_vote = WCNSS_CONFIG_UNSPECIFIED;
module_param(do_not_cancel_vote, int, S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(do_not_cancel_vote, "Do not cancel votes for wcnss");
@@ -67,6 +79,15 @@
#define CCU_RIVA_LAST_ADDR1_OFFSET 0x108
#define CCU_RIVA_LAST_ADDR2_OFFSET 0x10c
+#define PRONTO_PMU_SPARE_OFFSET 0x1088
+
+#define PRONTO_PMU_GDSCR_OFFSET 0x0024
+#define PRONTO_PMU_GDSCR_SW_COLLAPSE BIT(0)
+#define PRONTO_PMU_GDSCR_HW_CTRL BIT(1)
+
+#define PRONTO_PMU_CBCR_OFFSET 0x0008
+#define PRONTO_PMU_CBCR_CLK_EN BIT(0)
+
#define MSM_PRONTO_A2XB_BASE 0xfb100400
#define A2XB_CFG_OFFSET 0x00
#define A2XB_INT_SRC_OFFSET 0x0c
@@ -91,21 +112,30 @@
#define CCU_PRONTO_LAST_ADDR1_OFFSET 0x10
#define CCU_PRONTO_LAST_ADDR2_OFFSET 0x14
+#define WCNSS_DEF_WLAN_RX_BUFF_COUNT 1024
+
#define WCNSS_CTRL_CHANNEL "WCNSS_CTRL"
-#define WCNSS_MAX_FRAME_SIZE 500
+#define WCNSS_MAX_FRAME_SIZE (4*1024)
#define WCNSS_VERSION_LEN 30
/* message types */
#define WCNSS_CTRL_MSG_START 0x01000000
-#define WCNSS_VERSION_REQ (WCNSS_CTRL_MSG_START + 0)
-#define WCNSS_VERSION_RSP (WCNSS_CTRL_MSG_START + 1)
-#define WCNSS_NVBIN_DNLD_REQ (WCNSS_CTRL_MSG_START + 2)
-#define WCNSS_NVBIN_DNLD_RSP (WCNSS_CTRL_MSG_START + 3)
+#define WCNSS_VERSION_REQ (WCNSS_CTRL_MSG_START + 0)
+#define WCNSS_VERSION_RSP (WCNSS_CTRL_MSG_START + 1)
+#define WCNSS_NVBIN_DNLD_REQ (WCNSS_CTRL_MSG_START + 2)
+#define WCNSS_NVBIN_DNLD_RSP (WCNSS_CTRL_MSG_START + 3)
+#define WCNSS_CALDATA_UPLD_REQ (WCNSS_CTRL_MSG_START + 4)
+#define WCNSS_CALDATA_UPLD_RSP (WCNSS_CTRL_MSG_START + 5)
+#define WCNSS_CALDATA_DNLD_REQ (WCNSS_CTRL_MSG_START + 6)
+#define WCNSS_CALDATA_DNLD_RSP (WCNSS_CTRL_MSG_START + 7)
#define VALID_VERSION(version) \
((strncmp(version, "INVALID", WCNSS_VERSION_LEN)) ? 1 : 0)
+#define FW_CALDATA_CAPABLE() \
+ ((penv->fw_major >= 1) && (penv->fw_minor >= 5) ? 1 : 0)
+
struct smd_msg_hdr {
unsigned int msg_type;
unsigned int msg_len;
@@ -119,6 +149,21 @@
unsigned char revision;
};
+struct wcnss_pmic_dump {
+ char reg_name[10];
+ u16 reg_addr;
+};
+
+static struct wcnss_pmic_dump wcnss_pmic_reg_dump[] = {
+ {"S2", 0x1D8},
+ {"L4", 0xB4},
+ {"L10", 0xC0},
+ {"LVS2", 0x62},
+ {"S4", 0x1E8},
+ {"LVS7", 0x06C},
+ {"LVS1", 0x060},
+};
+
#define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
/*
@@ -126,6 +171,13 @@
* header, so NV fragment size as next multiple of 1Kb is 3Kb.
*/
#define NV_FRAGMENT_SIZE 3072
+#define MAX_CALIBRATED_DATA_SIZE (64*1024)
+#define LAST_FRAGMENT (1 << 0)
+#define MESSAGE_TO_FOLLOW (1 << 1)
+#define CAN_RECEIVE_CALDATA (1 << 15)
+#define WCNSS_RESP_SUCCESS 1
+#define WCNSS_RESP_FAIL 0
+
/* Macro to find the total number fragments of the NV bin Image */
#define TOTALFRAGMENTS(x) (((x % NV_FRAGMENT_SIZE) == 0) ? \
@@ -144,11 +196,14 @@
unsigned short frag_number;
/*
- * When set to 1 it indicates that no more fragments will
- * be sent. Receiver shall send back response message after
- * the last fragment.
+ * bit 0: When set to 1 it indicates that no more fragments will
+ * be sent.
+ * bit 1: When set, a new message will be followed by this message
+ * bit 2- bit 14: Reserved
+ * bit 15: when set, it indicates that the sender is capable of
+ * receiving Calibrated data.
*/
- unsigned short is_last_fragment;
+ unsigned short msg_flags;
/* NV Image size (number of bytes) */
unsigned int nvbin_buffer_size;
@@ -160,6 +215,7 @@
*/
};
+
struct nvbin_dnld_req_msg {
/*
* Note: The length specified in nvbin_dnld_req_msg messages
@@ -170,6 +226,39 @@
struct nvbin_dnld_req_params dnld_req_params;
};
+struct cal_data_params {
+
+ /* The total size of the calibrated data, including all the
+ * fragments.
+ */
+ unsigned int total_size;
+ unsigned short frag_number;
+ /*
+ * bit 0: When set to 1 it indicates that no more fragments will
+ * be sent.
+ * bit 1: When set, a new message will be followed by this message
+ * bit 2- bit 15: Reserved
+ */
+ unsigned short msg_flags;
+ /*
+ * fragment size
+ */
+ unsigned int frag_size;
+ /*
+ * Following the frag_size, frag_size of fragmented
+ * data will be followed.
+ */
+};
+
+struct cal_data_msg {
+ /*
+ * The length specified in cal_data_msg should be
+ * hdr.msg_len = sizeof(cal_data_msg) + frag_size
+ */
+ struct smd_msg_hdr hdr;
+ struct cal_data_params cal_params;
+};
+
static struct {
struct platform_device *pdev;
void *pil;
@@ -180,9 +269,11 @@
const struct dev_pm_ops *pm_ops;
int triggered;
int smd_channel_ready;
- int cold_boot_done;
+ u32 wlan_rx_buff_count;
smd_channel_t *smd_ch;
unsigned char wcnss_version[WCNSS_VERSION_LEN];
+ unsigned char fw_major;
+ unsigned char fw_minor;
unsigned int serial_number;
int thermal_mitigation;
enum wcnss_hw_type wcnss_hw_type;
@@ -198,6 +289,20 @@
void __iomem *pronto_a2xb_base;
void __iomem *pronto_ccpu_base;
void __iomem *fiq_reg;
+ int ssr_boot;
+ int nv_downloaded;
+ unsigned char *fw_cal_data;
+ unsigned char *user_cal_data;
+ int fw_cal_rcvd;
+ int fw_cal_exp_frag;
+ int fw_cal_available;
+ int user_cal_read;
+ int user_cal_available;
+ int user_cal_rcvd;
+ int user_cal_exp_size;
+ int device_opened;
+ struct mutex dev_lock;
+ wait_queue_head_t read_wait;
} *penv = NULL;
static ssize_t wcnss_serial_number_show(struct device *dev,
@@ -269,6 +374,25 @@
static DEVICE_ATTR(wcnss_version, S_IRUSR,
wcnss_version_show, NULL);
+void wcnss_riva_dump_pmic_regs(void)
+{
+ int i, rc;
+ u8 val;
+
+ for (i = 0; i < ARRAY_SIZE(wcnss_pmic_reg_dump); i++) {
+ val = 0;
+ rc = pm8xxx_read_register(wcnss_pmic_reg_dump[i].reg_addr,
+ &val);
+ if (rc)
+ pr_err("PMIC READ: Failed to read addr = %d\n",
+ wcnss_pmic_reg_dump[i].reg_addr);
+ else
+ pr_info_ratelimited("PMIC READ: %s addr = %x, value = %x\n",
+ wcnss_pmic_reg_dump[i].reg_name,
+ wcnss_pmic_reg_dump[i].reg_addr, val);
+ }
+}
+
/* wcnss_reset_intr() is invoked when host drivers fails to
* communicate with WCNSS over SMD; so logging these registers
* helps to know WCNSS failure reason
@@ -293,6 +417,7 @@
ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR2_OFFSET;
reg = readl_relaxed(ccu_reg);
pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
+ wcnss_riva_dump_pmic_regs();
}
EXPORT_SYMBOL(wcnss_riva_log_debug_regs);
@@ -303,6 +428,28 @@
void __iomem *reg_addr, *tst_addr, *tst_ctrl_addr;
u32 reg = 0;
+
+ reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SPARE_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_info_ratelimited("%s: PRONTO_PMU_SPARE %08x\n", __func__, reg);
+
+ reg_addr = penv->msm_wcnss_base + PRONTO_PMU_GDSCR_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ reg >>= 31;
+
+ if (!reg) {
+ pr_info_ratelimited("%s: Cannot log, Pronto common SS is power collapsed\n",
+ __func__);
+ return;
+ }
+ reg &= ~(PRONTO_PMU_GDSCR_SW_COLLAPSE | PRONTO_PMU_GDSCR_HW_CTRL);
+ writel_relaxed(reg, reg_addr);
+
+ reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CBCR_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ reg |= PRONTO_PMU_CBCR_CLK_EN;
+ writel_relaxed(reg, reg_addr);
+
reg_addr = penv->pronto_a2xb_base + A2XB_CFG_OFFSET;
reg = readl_relaxed(reg_addr);
pr_info_ratelimited("%s: A2XB_CFG_OFFSET %08x\n", __func__, reg);
@@ -459,21 +606,26 @@
switch (event) {
case SMD_EVENT_DATA:
len = smd_read_avail(penv->smd_ch);
- if (len < 0)
+ if (len < 0) {
pr_err("wcnss: failed to read from smd %d\n", len);
+ return;
+ }
schedule_work(&penv->wcnssctrl_rx_work);
break;
case SMD_EVENT_OPEN:
pr_debug("wcnss: opening WCNSS SMD channel :%s",
WCNSS_CTRL_CHANNEL);
- if (!VALID_VERSION(penv->wcnss_version))
- schedule_work(&penv->wcnssctrl_version_work);
+ schedule_work(&penv->wcnssctrl_version_work);
+
break;
case SMD_EVENT_CLOSE:
pr_debug("wcnss: closing WCNSS SMD channel :%s",
WCNSS_CTRL_CHANNEL);
+ /* This SMD is closed only during SSR */
+ penv->ssr_boot = true;
+ penv->nv_downloaded = 0;
break;
default:
@@ -554,7 +706,7 @@
static int __devinit
wcnss_wlan_ctrl_probe(struct platform_device *pdev)
{
- if (!penv)
+ if (!penv || !penv->triggered)
return -ENODEV;
penv->smd_channel_ready = 1;
@@ -609,7 +761,7 @@
{
int ret = 0;
- if (!penv)
+ if (!penv || !penv->triggered)
return -ENODEV;
ret = smd_named_open_on_edge(WCNSS_CTRL_CHANNEL, SMD_APPS_WCNSS,
@@ -658,6 +810,15 @@
}
EXPORT_SYMBOL(wcnss_get_wlan_config);
+int wcnss_device_ready(void)
+{
+ if (penv && penv->pdev && penv->nv_downloaded)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(wcnss_device_ready);
+
+
struct resource *wcnss_wlan_get_memory_map(struct device *dev)
{
if (penv && dev && (dev == &penv->pdev->dev) && penv->smd_channel_ready)
@@ -750,6 +911,11 @@
module_param_call(enable_wcnss_suspend_notify, enable_wcnss_suspend_notify_set,
param_get_int, &enable_wcnss_suspend_notify, S_IRUGO | S_IWUSR);
+int wcnss_xo_auto_detect_enabled(void)
+{
+ return (has_autodetect_xo == 1 ? 1 : 0);
+}
+
void wcnss_suspend_notify(void)
{
@@ -837,14 +1003,23 @@
}
EXPORT_SYMBOL(wcnss_hardware_type);
-int wcnss_cold_boot_done(void)
+int fw_cal_data_available(void)
{
if (penv)
- return penv->cold_boot_done;
+ return penv->fw_cal_available;
else
return -ENODEV;
}
-EXPORT_SYMBOL(wcnss_cold_boot_done);
+
+u32 wcnss_get_wlan_rx_buff_count(void)
+{
+ if (penv)
+ return penv->wlan_rx_buff_count;
+ else
+ return WCNSS_DEF_WLAN_RX_BUFF_COUNT;
+
+}
+EXPORT_SYMBOL(wcnss_get_wlan_rx_buff_count);
static int wcnss_smd_tx(void *data, int len)
@@ -864,14 +1039,147 @@
return ret;
}
+static unsigned char wcnss_fw_status(void)
+{
+ int len = 0;
+ int rc = 0;
+
+ unsigned char fw_status = 0xFF;
+
+ len = smd_read_avail(penv->smd_ch);
+ if (len < 1) {
+ pr_err("%s: invalid firmware status", __func__);
+ return fw_status;
+ }
+
+ rc = smd_read(penv->smd_ch, &fw_status, 1);
+ if (rc < 0) {
+ pr_err("%s: incomplete data read from smd\n", __func__);
+ return fw_status;
+ }
+ return fw_status;
+}
+
+static void wcnss_send_cal_rsp(unsigned char fw_status)
+{
+ struct smd_msg_hdr *rsphdr;
+ unsigned char *msg = NULL;
+ int rc;
+
+ msg = kmalloc((sizeof(struct smd_msg_hdr) + 1), GFP_KERNEL);
+ if (NULL == msg) {
+ pr_err("wcnss: %s: failed to get memory\n", __func__);
+ return;
+ }
+
+ rsphdr = (struct smd_msg_hdr *)msg;
+ rsphdr->msg_type = WCNSS_CALDATA_UPLD_RSP;
+ rsphdr->msg_len = sizeof(struct smd_msg_hdr) + 1;
+ memcpy(msg+sizeof(struct smd_msg_hdr), &fw_status, 1);
+
+ rc = wcnss_smd_tx(msg, rsphdr->msg_len);
+ if (rc < 0)
+ pr_err("wcnss: smd tx failed\n");
+
+ kfree(msg);
+}
+
+/* Collect calibrated data from WCNSS */
+void extract_cal_data(int len)
+{
+ int rc;
+ struct cal_data_params calhdr;
+ unsigned char fw_status = WCNSS_RESP_FAIL;
+
+ if (len < sizeof(struct cal_data_params)) {
+ pr_err("wcnss: incomplete cal header length\n");
+ return;
+ }
+
+ rc = smd_read(penv->smd_ch, (unsigned char *)&calhdr,
+ sizeof(struct cal_data_params));
+ if (rc < sizeof(struct cal_data_params)) {
+ pr_err("wcnss: incomplete cal header read from smd\n");
+ return;
+ }
+
+ if (penv->fw_cal_exp_frag != calhdr.frag_number) {
+ pr_err("wcnss: Invalid frgament");
+ goto exit;
+ }
+
+ if (calhdr.frag_size > WCNSS_MAX_FRAME_SIZE) {
+ pr_err("wcnss: Invalid fragment size");
+ goto exit;
+ }
+
+ if (0 == calhdr.frag_number) {
+ if (calhdr.total_size > MAX_CALIBRATED_DATA_SIZE) {
+ pr_err("wcnss: Invalid cal data size %d",
+ calhdr.total_size);
+ goto exit;
+ }
+ kfree(penv->fw_cal_data);
+ penv->fw_cal_rcvd = 0;
+ penv->fw_cal_data = kmalloc(calhdr.total_size,
+ GFP_KERNEL);
+ if (penv->fw_cal_data == NULL) {
+ smd_read(penv->smd_ch, NULL, calhdr.frag_size);
+ goto exit;
+ }
+ }
+
+ mutex_lock(&penv->dev_lock);
+ if (penv->fw_cal_rcvd + calhdr.frag_size >
+ MAX_CALIBRATED_DATA_SIZE) {
+ pr_err("calibrated data size is more than expected %d",
+ penv->fw_cal_rcvd + calhdr.frag_size);
+ penv->fw_cal_exp_frag = 0;
+ penv->fw_cal_rcvd = 0;
+ smd_read(penv->smd_ch, NULL, calhdr.frag_size);
+ goto unlock_exit;
+ }
+
+ rc = smd_read(penv->smd_ch, penv->fw_cal_data + penv->fw_cal_rcvd,
+ calhdr.frag_size);
+ if (rc < calhdr.frag_size)
+ goto unlock_exit;
+
+ penv->fw_cal_exp_frag++;
+ penv->fw_cal_rcvd += calhdr.frag_size;
+
+ if (calhdr.msg_flags & LAST_FRAGMENT) {
+ penv->fw_cal_exp_frag = 0;
+ penv->fw_cal_available = true;
+ pr_info("wcnss: cal data collection completed\n");
+ }
+ mutex_unlock(&penv->dev_lock);
+ wake_up(&penv->read_wait);
+
+ if (penv->fw_cal_available) {
+ fw_status = WCNSS_RESP_SUCCESS;
+ wcnss_send_cal_rsp(fw_status);
+ }
+ return;
+
+unlock_exit:
+ mutex_unlock(&penv->dev_lock);
+
+exit:
+ wcnss_send_cal_rsp(fw_status);
+ return;
+}
+
+
static void wcnssctrl_rx_handler(struct work_struct *worker)
{
int len = 0;
int rc = 0;
- unsigned char buf[WCNSS_MAX_FRAME_SIZE];
+ unsigned char buf[sizeof(struct wcnss_version)];
struct smd_msg_hdr *phdr;
struct wcnss_version *pversion;
int hw_type;
+ unsigned char fw_status = 0;
len = smd_read_avail(penv->smd_ch);
if (len > WCNSS_MAX_FRAME_SIZE) {
@@ -882,23 +1190,33 @@
if (len <= 0)
return;
- rc = smd_read(penv->smd_ch, buf, len);
- if (rc < len) {
- pr_err("wcnss: incomplete data read from smd\n");
+ rc = smd_read(penv->smd_ch, buf, sizeof(struct smd_msg_hdr));
+ if (rc < sizeof(struct smd_msg_hdr)) {
+ pr_err("wcnss: incomplete header read from smd\n");
return;
}
+ len -= sizeof(struct smd_msg_hdr);
phdr = (struct smd_msg_hdr *)buf;
switch (phdr->msg_type) {
case WCNSS_VERSION_RSP:
- pversion = (struct wcnss_version *)buf;
- if (len != sizeof(struct wcnss_version)) {
+ if (len != sizeof(struct wcnss_version)
+ - sizeof(struct smd_msg_hdr)) {
pr_err("wcnss: invalid version data from wcnss %d\n",
- len);
+ len);
return;
}
+ rc = smd_read(penv->smd_ch, buf+sizeof(struct smd_msg_hdr),
+ len);
+ if (rc < len) {
+ pr_err("wcnss: incomplete data read from smd\n");
+ return;
+ }
+ pversion = (struct wcnss_version *)buf;
+ penv->fw_major = pversion->major;
+ penv->fw_minor = pversion->minor;
snprintf(penv->wcnss_version, WCNSS_VERSION_LEN,
"%02x%02x%02x%02x", pversion->major, pversion->minor,
pversion->version, pversion->revision);
@@ -930,7 +1248,22 @@
break;
case WCNSS_NVBIN_DNLD_RSP:
- pr_info("wcnss: received WCNSS_NVBIN_DNLD_RSP from ccpu\n");
+ penv->nv_downloaded = true;
+ fw_status = wcnss_fw_status();
+ pr_debug("wcnss: received WCNSS_NVBIN_DNLD_RSP from ccpu %u\n",
+ fw_status);
+ break;
+
+ case WCNSS_CALDATA_DNLD_RSP:
+ penv->nv_downloaded = true;
+ fw_status = wcnss_fw_status();
+ pr_debug("wcnss: received WCNSS_CALDATA_DNLD_RSP from ccpu %u\n",
+ fw_status);
+ break;
+
+ case WCNSS_CALDATA_UPLD_REQ:
+ penv->fw_cal_available = 0;
+ extract_cal_data(len);
break;
default:
@@ -953,7 +1286,8 @@
return;
}
-static void wcnss_nvbin_dnld_req(struct work_struct *worker)
+
+static void wcnss_nvbin_dnld(void)
{
int ret = 0;
struct nvbin_dnld_req_msg *dnld_req_msg;
@@ -970,8 +1304,8 @@
ret = request_firmware(&nv, NVBIN_FILE, dev);
if (ret || !nv || !nv->data || !nv->size) {
- pr_err("wcnss: wcnss_nvbin_dnld_req: request_firmware failed for %s\n",
- NVBIN_FILE);
+ pr_err("wcnss: %s: request_firmware failed for %s\n",
+ __func__, NVBIN_FILE);
return;
}
@@ -992,13 +1326,14 @@
NV_FRAGMENT_SIZE), GFP_KERNEL);
if (NULL == outbuffer) {
- pr_err("wcnss: wcnss_nvbin_dnld_req: failed to get buffer\n");
+ pr_err("wcnss: %s: failed to get buffer\n", __func__);
goto err_free_nv;
}
dnld_req_msg = (struct nvbin_dnld_req_msg *)outbuffer;
dnld_req_msg->hdr.msg_type = WCNSS_NVBIN_DNLD_REQ;
+ dnld_req_msg->dnld_req_params.msg_flags = 0;
for (count = 0; count < total_fragments; count++) {
dnld_req_msg->dnld_req_params.frag_number = count;
@@ -1009,10 +1344,14 @@
if (!cur_frag_size)
cur_frag_size = NV_FRAGMENT_SIZE;
- dnld_req_msg->dnld_req_params.is_last_fragment = 1;
+ dnld_req_msg->dnld_req_params.msg_flags |=
+ LAST_FRAGMENT;
+ dnld_req_msg->dnld_req_params.msg_flags |=
+ CAN_RECEIVE_CALDATA;
} else {
cur_frag_size = NV_FRAGMENT_SIZE;
- dnld_req_msg->dnld_req_params.is_last_fragment = 0;
+ dnld_req_msg->dnld_req_params.msg_flags &=
+ ~LAST_FRAGMENT;
}
dnld_req_msg->dnld_req_params.nvbin_buffer_size =
@@ -1030,7 +1369,8 @@
retry_count = 0;
while ((ret == -ENOSPC) && (retry_count <= 3)) {
- pr_debug("wcnss: wcnss_nvbin_dnld_req: smd tx failed, ENOSPC\n");
+ pr_debug("wcnss: %s: smd tx failed, ENOSPC\n",
+ __func__);
pr_debug("fragment: %d, len: %d, TotFragments: %d, retry_count: %d\n",
count, dnld_req_msg->hdr.msg_len,
total_fragments, retry_count);
@@ -1043,7 +1383,7 @@
}
if (ret < 0) {
- pr_err("wcnss: wcnss_nvbin_dnld_req: smd tx failed\n");
+ pr_err("wcnss: %s: smd tx failed\n", __func__);
pr_err("fragment %d, len: %d, TotFragments: %d, retry_count: %d\n",
count, dnld_req_msg->hdr.msg_len,
total_fragments, retry_count);
@@ -1062,6 +1402,138 @@
return;
}
+
+static void wcnss_caldata_dnld(const void *cal_data,
+ unsigned int cal_data_size, bool msg_to_follow)
+{
+ int ret = 0;
+ struct cal_data_msg *cal_msg;
+ unsigned short total_fragments = 0;
+ unsigned short count = 0;
+ unsigned short retry_count = 0;
+ unsigned short cur_frag_size = 0;
+ unsigned char *outbuffer = NULL;
+
+ total_fragments = TOTALFRAGMENTS(cal_data_size);
+
+ outbuffer = kmalloc((sizeof(struct cal_data_msg) +
+ NV_FRAGMENT_SIZE), GFP_KERNEL);
+
+ if (NULL == outbuffer) {
+ pr_err("wcnss: %s: failed to get buffer\n", __func__);
+ return;
+ }
+
+ cal_msg = (struct cal_data_msg *)outbuffer;
+
+ cal_msg->hdr.msg_type = WCNSS_CALDATA_DNLD_REQ;
+ cal_msg->cal_params.msg_flags = 0;
+
+ for (count = 0; count < total_fragments; count++) {
+ cal_msg->cal_params.frag_number = count;
+
+ if (count == (total_fragments - 1)) {
+ cur_frag_size = cal_data_size % NV_FRAGMENT_SIZE;
+ if (!cur_frag_size)
+ cur_frag_size = NV_FRAGMENT_SIZE;
+
+ cal_msg->cal_params.msg_flags
+ |= LAST_FRAGMENT;
+ if (msg_to_follow)
+ cal_msg->cal_params.msg_flags |=
+ MESSAGE_TO_FOLLOW;
+ } else {
+ cur_frag_size = NV_FRAGMENT_SIZE;
+ cal_msg->cal_params.msg_flags &=
+ ~LAST_FRAGMENT;
+ }
+
+ cal_msg->cal_params.total_size = cal_data_size;
+ cal_msg->cal_params.frag_size =
+ cur_frag_size;
+
+ cal_msg->hdr.msg_len =
+ sizeof(struct cal_data_msg) + cur_frag_size;
+
+ memcpy((outbuffer + sizeof(struct cal_data_msg)),
+ (cal_data + count * NV_FRAGMENT_SIZE),
+ cur_frag_size);
+
+ ret = wcnss_smd_tx(outbuffer, cal_msg->hdr.msg_len);
+
+ retry_count = 0;
+ while ((ret == -ENOSPC) && (retry_count <= 3)) {
+ pr_debug("wcnss: %s: smd tx failed, ENOSPC\n",
+ __func__);
+ pr_debug("fragment: %d, len: %d, TotFragments: %d, retry_count: %d\n",
+ count, cal_msg->hdr.msg_len,
+ total_fragments, retry_count);
+
+ /* wait and try again */
+ msleep(20);
+ retry_count++;
+ ret = wcnss_smd_tx(outbuffer,
+ cal_msg->hdr.msg_len);
+ }
+
+ if (ret < 0) {
+ pr_err("wcnss: %s: smd tx failed\n", __func__);
+ pr_err("fragment %d, len: %d, TotFragments: %d, retry_count: %d\n",
+ count, cal_msg->hdr.msg_len,
+ total_fragments, retry_count);
+ goto err_dnld;
+ }
+ }
+
+
+err_dnld:
+ /* free buffer */
+ kfree(outbuffer);
+
+ return;
+}
+
+
+static void wcnss_nvbin_dnld_main(struct work_struct *worker)
+{
+ int retry = 0;
+
+ if (!FW_CALDATA_CAPABLE())
+ goto nv_download;
+
+ if (!penv->fw_cal_available && WCNSS_CONFIG_UNSPECIFIED
+ != has_calibrated_data && !penv->user_cal_available) {
+ while (!penv->user_cal_available && retry++ < 5)
+ msleep(500);
+ }
+
+ /* only cal data is sent during ssr (if available) */
+ if (penv->fw_cal_available && penv->ssr_boot) {
+ pr_info_ratelimited("wcnss: cal download during SSR, using fw cal");
+ wcnss_caldata_dnld(penv->fw_cal_data, penv->fw_cal_rcvd, false);
+ return;
+
+ } else if (penv->user_cal_available && penv->ssr_boot) {
+ pr_info_ratelimited("wcnss: cal download during SSR, using user cal");
+ wcnss_caldata_dnld(penv->user_cal_data,
+ penv->user_cal_rcvd, false);
+ return;
+
+ } else if (penv->user_cal_available) {
+ pr_info_ratelimited("wcnss: cal download during cold boot, using user cal");
+ wcnss_caldata_dnld(penv->user_cal_data,
+ penv->user_cal_rcvd, true);
+ }
+
+nv_download:
+ pr_info_ratelimited("wcnss: NV download");
+ wcnss_nvbin_dnld();
+
+ return;
+}
+
+
+
static int
wcnss_trigger_config(struct platform_device *pdev)
{
@@ -1071,7 +1543,12 @@
int size = 0;
struct resource *res;
int has_pronto_hw = of_property_read_bool(pdev->dev.of_node,
- "qcom,has_pronto_hw");
+ "qcom,has-pronto-hw");
+
+ if (of_property_read_u32(pdev->dev.of_node,
+ "qcom,wlan-rx-buff-count", &penv->wlan_rx_buff_count)) {
+ penv->wlan_rx_buff_count = WCNSS_DEF_WLAN_RX_BUFF_COUNT;
+ }
/* make sure we are only triggered once */
if (penv->triggered)
@@ -1083,7 +1560,7 @@
if (WCNSS_CONFIG_UNSPECIFIED == has_48mhz_xo) {
if (has_pronto_hw) {
has_48mhz_xo = of_property_read_bool(pdev->dev.of_node,
- "qcom,has_48mhz_xo");
+ "qcom,has-48mhz-xo");
} else {
has_48mhz_xo = pdata->has_48mhz_xo;
}
@@ -1091,6 +1568,11 @@
penv->wcnss_hw_type = (has_pronto_hw) ? WCNSS_PRONTO_HW : WCNSS_RIVA_HW;
penv->wlan_config.use_48mhz_xo = has_48mhz_xo;
+ if (WCNSS_CONFIG_UNSPECIFIED == has_autodetect_xo && has_pronto_hw) {
+ has_autodetect_xo = of_property_read_bool(pdev->dev.of_node,
+ "qcom,has-autodetect-xo");
+ }
+
penv->thermal_mitigation = 0;
strlcpy(penv->wcnss_version, "INVALID", WCNSS_VERSION_LEN);
@@ -1122,15 +1604,6 @@
goto fail_power;
}
- /* trigger initialization of the WCNSS */
- penv->pil = subsystem_get(WCNSS_PIL_DEVICE);
- if (IS_ERR(penv->pil)) {
- dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
- ret = PTR_ERR(penv->pil);
- penv->pil = NULL;
- goto fail_pil;
- }
-
/* allocate resources */
penv->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"wcnss_mmio");
@@ -1146,7 +1619,7 @@
}
INIT_WORK(&penv->wcnssctrl_rx_work, wcnssctrl_rx_handler);
INIT_WORK(&penv->wcnssctrl_version_work, wcnss_send_version_req);
- INIT_WORK(&penv->wcnssctrl_nvbin_dnld_work, wcnss_nvbin_dnld_req);
+ INIT_WORK(&penv->wcnssctrl_nvbin_dnld_work, wcnss_nvbin_dnld_main);
wake_lock_init(&penv->wcnss_wake_lock, WAKE_LOCK_SUSPEND, "wcnss");
@@ -1162,7 +1635,7 @@
if (!penv->msm_wcnss_base) {
ret = -ENOMEM;
pr_err("%s: ioremap wcnss physical failed\n", __func__);
- goto fail_wake;
+ goto fail_ioremap;
}
if (wcnss_hardware_type() == WCNSS_RIVA_HW) {
@@ -1170,20 +1643,20 @@
if (!penv->riva_ccu_base) {
ret = -ENOMEM;
pr_err("%s: ioremap wcnss physical failed\n", __func__);
- goto fail_ioremap;
+ goto fail_ioremap2;
}
} else {
penv->pronto_a2xb_base = ioremap(MSM_PRONTO_A2XB_BASE, SZ_512);
if (!penv->pronto_a2xb_base) {
ret = -ENOMEM;
pr_err("%s: ioremap wcnss physical failed\n", __func__);
- goto fail_ioremap;
+ goto fail_ioremap2;
}
penv->pronto_ccpu_base = ioremap(MSM_PRONTO_CCPU_BASE, SZ_512);
if (!penv->pronto_ccpu_base) {
ret = -ENOMEM;
pr_err("%s: ioremap wcnss physical failed\n", __func__);
- goto fail_ioremap2;
+ goto fail_ioremap3;
}
/* for reset FIQ */
res = platform_get_resource_byname(penv->pdev,
@@ -1191,32 +1664,45 @@
if (!res) {
dev_err(&pdev->dev, "insufficient irq mem resources\n");
ret = -ENOENT;
- goto fail_ioremap3;
+ goto fail_ioremap4;
}
penv->fiq_reg = ioremap_nocache(res->start, resource_size(res));
if (!penv->fiq_reg) {
pr_err("wcnss: %s: ioremap_nocache() failed fiq_reg addr:%pr\n",
__func__, &res->start);
ret = -ENOMEM;
- goto fail_ioremap3;
+ goto fail_ioremap4;
}
}
- penv->cold_boot_done = 1;
+
+ /* trigger initialization of the WCNSS */
+ penv->pil = subsystem_get(WCNSS_PIL_DEVICE);
+ if (IS_ERR(penv->pil)) {
+ dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
+ ret = PTR_ERR(penv->pil);
+ penv->pil = NULL;
+ goto fail_pil;
+ }
return 0;
+fail_pil:
+ if (penv->riva_ccu_base)
+ iounmap(penv->riva_ccu_base);
+ if (penv->fiq_reg)
+ iounmap(penv->fiq_reg);
+fail_ioremap4:
+ if (penv->pronto_ccpu_base)
+ iounmap(penv->pronto_ccpu_base);
fail_ioremap3:
- iounmap(penv->pronto_ccpu_base);
+ if (penv->pronto_a2xb_base)
+ iounmap(penv->pronto_a2xb_base);
fail_ioremap2:
- iounmap(penv->pronto_a2xb_base);
+ if (penv->msm_wcnss_base)
+ iounmap(penv->msm_wcnss_base);
fail_ioremap:
- iounmap(penv->msm_wcnss_base);
-fail_wake:
wake_lock_destroy(&penv->wcnss_wake_lock);
fail_res:
- if (penv->pil)
- subsystem_put(penv->pil);
-fail_pil:
wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
WCNSS_WLAN_SWITCH_OFF);
fail_power:
@@ -1229,20 +1715,131 @@
return ret;
}
-#ifndef MODULE
static int wcnss_node_open(struct inode *inode, struct file *file)
{
struct platform_device *pdev;
- pr_info(DEVICE " triggered by userspace\n");
+ if (!penv)
+ return -EFAULT;
- pdev = penv->pdev;
- return wcnss_trigger_config(pdev);
+ /* first open is only to trigger WCNSS platform driver */
+ if (!penv->triggered) {
+ pr_info(DEVICE " triggered by userspace\n");
+ pdev = penv->pdev;
+ return wcnss_trigger_config(pdev);
+
+ } else if (penv->device_opened) {
+ pr_info(DEVICE " already opened\n");
+ return -EBUSY;
+ }
+
+ mutex_lock(&penv->dev_lock);
+ penv->user_cal_rcvd = 0;
+ penv->user_cal_read = 0;
+ penv->user_cal_available = false;
+ penv->user_cal_data = NULL;
+ penv->device_opened = 1;
+ mutex_unlock(&penv->dev_lock);
+
+ return 0;
}
+static ssize_t wcnss_wlan_read(struct file *fp, char __user
+ *buffer, size_t count, loff_t *position)
+{
+ int rc = 0;
+
+ if (!penv || !penv->device_opened)
+ return -EFAULT;
+
+ rc = wait_event_interruptible(penv->read_wait, penv->fw_cal_rcvd
+ > penv->user_cal_read || penv->fw_cal_available);
+
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&penv->dev_lock);
+
+ if (penv->fw_cal_available && penv->fw_cal_rcvd
+ == penv->user_cal_read) {
+ rc = 0;
+ goto exit;
+ }
+
+ if (count > penv->fw_cal_rcvd - penv->user_cal_read)
+ count = penv->fw_cal_rcvd - penv->user_cal_read;
+
+ rc = copy_to_user(buffer, penv->fw_cal_data +
+ penv->user_cal_read, count);
+ if (rc == 0) {
+ penv->user_cal_read += count;
+ rc = count;
+ }
+
+exit:
+ mutex_unlock(&penv->dev_lock);
+ return rc;
+}
+
+/* first (valid) write to this device should be 4 bytes cal file size */
+static ssize_t wcnss_wlan_write(struct file *fp, const char __user
+ *user_buffer, size_t count, loff_t *position)
+{
+ int rc = 0;
+ int size = 0;
+
+ if (!penv || !penv->device_opened || penv->user_cal_available)
+ return -EFAULT;
+
+ if (penv->user_cal_rcvd == 0 && count >= 4
+ && !penv->user_cal_data) {
+ rc = copy_from_user((void *)&size, user_buffer, 4);
+ if (size > MAX_CALIBRATED_DATA_SIZE) {
+ pr_err(DEVICE " invalid size to write %d\n", size);
+ return -EFAULT;
+ }
+
+ rc += count;
+ count -= 4;
+ penv->user_cal_exp_size = size;
+ penv->user_cal_data = kmalloc(size, GFP_KERNEL);
+ if (penv->user_cal_data == NULL) {
+ pr_err(DEVICE " no memory to write\n");
+ return -ENOMEM;
+ }
+ if (0 == count)
+ goto exit;
+
+ } else if (penv->user_cal_rcvd == 0 && count < 4)
+ return -EFAULT;
+
+ if (MAX_CALIBRATED_DATA_SIZE < count + penv->user_cal_rcvd) {
+ pr_err(DEVICE " invalid size to write %d\n", count +
+ penv->user_cal_rcvd);
+ rc = -ENOMEM;
+ goto exit;
+ }
+ rc = copy_from_user((void *)penv->user_cal_data +
+ penv->user_cal_rcvd, user_buffer, count);
+ if (0 == rc) {
+ penv->user_cal_rcvd += count;
+ rc += count;
+ }
+ if (penv->user_cal_rcvd == penv->user_cal_exp_size) {
+ penv->user_cal_available = true;
+ pr_info_ratelimited("wcnss: user cal written");
+ }
+
+exit:
+ return rc;
+}
+
+
static const struct file_operations wcnss_node_fops = {
.owner = THIS_MODULE,
.open = wcnss_node_open,
+ .read = wcnss_wlan_read,
+ .write = wcnss_wlan_write,
};
static struct miscdevice wcnss_misc = {
@@ -1250,8 +1847,6 @@
.name = DEVICE,
.fops = &wcnss_node_fops,
};
-#endif /* ifndef MODULE */
-
static int __devinit
wcnss_wlan_probe(struct platform_device *pdev)
@@ -1279,18 +1874,8 @@
return -ENOENT;
}
-
-#ifdef MODULE
-
- /* Since we were built as a module, we are running because
- * the module was loaded, therefore we assume userspace
- * applications are available to service PIL, so we can
- * trigger the WCNSS configuration now
- */
- pr_info(DEVICE " probed in MODULE mode\n");
- return wcnss_trigger_config(pdev);
-
-#else
+ mutex_init(&penv->dev_lock);
+ init_waitqueue_head(&penv->read_wait);
/* Since we were built into the kernel we'll be called as part
* of kernel initialization. We don't know if userspace
@@ -1303,7 +1888,6 @@
pr_info(DEVICE " probed in built-in mode\n");
return misc_register(&wcnss_misc);
-#endif
}
static int __devexit
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index fe151b5..acd42ae3 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -107,4 +107,10 @@
help
OpenFirmware SLIMBUS accessors
+config OF_CORESIGHT
+ def_bool y
+ depends on CORESIGHT
+ help
+ OpenFirmware CoreSight accessors
+
endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index c3a31c8..61a99f2 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -15,3 +15,4 @@
obj-$(CONFIG_OF_SPMI) += of_spmi.o
obj-$(CONFIG_OF_MTD) += of_mtd.o
obj-$(CONFIG_OF_SLIMBUS) += of_slimbus.o
+obj-$(CONFIG_OF_CORESIGHT) += of_coresight.o
diff --git a/drivers/coresight/of_coresight.c b/drivers/of/of_coresight.c
similarity index 98%
rename from drivers/coresight/of_coresight.c
rename to drivers/of/of_coresight.c
index 1eccd09..8b8c0d62 100644
--- a/drivers/coresight/of_coresight.c
+++ b/drivers/of/of_coresight.c
@@ -97,7 +97,7 @@
"coresight-default-sink");
return pdata;
}
-EXPORT_SYMBOL_GPL(of_get_coresight_platform_data);
+EXPORT_SYMBOL(of_get_coresight_platform_data);
struct coresight_cti_data *of_get_coresight_cti_data(
struct device *dev, struct device_node *node)
diff --git a/drivers/of/of_slimbus.c b/drivers/of/of_slimbus.c
index 9692185..234a5eb 100644
--- a/drivers/of/of_slimbus.c
+++ b/drivers/of/of_slimbus.c
@@ -22,6 +22,7 @@
{
struct device_node *node;
struct slim_boardinfo *binfo = NULL;
+ struct slim_boardinfo *temp;
int n = 0;
int ret = 0;
@@ -58,14 +59,16 @@
}
memcpy(slim->e_addr, prop->value, 6);
- binfo = krealloc(binfo, (n + 1) * sizeof(struct slim_boardinfo),
+ temp = krealloc(binfo, (n + 1) * sizeof(struct slim_boardinfo),
GFP_KERNEL);
- if (!binfo) {
+ if (!temp) {
dev_err(&ctrl->dev, "out of memory");
kfree(name);
kfree(slim);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto of_slim_err;
}
+ binfo = temp;
slim->dev.of_node = of_node_get(node);
slim->name = (const char *)name;
@@ -73,13 +76,15 @@
binfo[n].slim_slave = slim;
n++;
}
- return slim_register_board_info(binfo, n);
+ ret = slim_register_board_info(binfo, n);
+ if (!ret)
+ goto of_slim_ret;
of_slim_err:
- n--;
- while (n >= 0) {
+ while (n-- > 0) {
kfree(binfo[n].slim_slave->name);
kfree(binfo[n].slim_slave);
}
+of_slim_ret:
kfree(binfo);
return ret;
}
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 0970505..5f0ba94 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -214,7 +214,7 @@
#if defined(CONFIG_MICROBLAZE)
dev->archdata.dma_mask = 0xffffffffUL;
#endif
- dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ dev->dev.coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8);
dev->dev.bus = &platform_bus_type;
dev->dev.platform_data = platform_data;
diff --git a/drivers/platform/msm/ipa/Makefile b/drivers/platform/msm/ipa/Makefile
index b7eca61..2b6ce75 100644
--- a/drivers/platform/msm/ipa/Makefile
+++ b/drivers/platform/msm/ipa/Makefile
@@ -1,4 +1,4 @@
obj-$(CONFIG_IPA) += ipat.o
ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o \
- ipa_utils.o ipa_nat.o rmnet_bridge.o a2_service.o ipa_bridge.o ipa_intf.o teth_bridge.o \
+ ipa_utils.o ipa_nat.o a2_service.o ipa_bridge.o ipa_intf.o teth_bridge.o \
ipa_rm.o ipa_rm_dependency_graph.o ipa_rm_peers_list.o ipa_rm_resource.o ipa_rm_inactivity_timer.o
diff --git a/drivers/platform/msm/ipa/a2_service.c b/drivers/platform/msm/ipa/a2_service.c
index 4b5f0a2..8e79185 100644
--- a/drivers/platform/msm/ipa/a2_service.c
+++ b/drivers/platform/msm/ipa/a2_service.c
@@ -86,15 +86,19 @@
struct list_head bam_tx_pool;
spinlock_t bam_tx_pool_spinlock;
struct workqueue_struct *a2_mux_tx_workqueue;
+ struct workqueue_struct *a2_mux_rx_workqueue;
int a2_mux_initialized;
bool bam_is_connected;
+ bool bam_connect_in_progress;
int a2_mux_send_power_vote_on_init_once;
int a2_mux_sw_bridge_is_connected;
+ bool a2_mux_dl_wakeup;
u32 a2_device_handle;
struct mutex wakeup_lock;
struct completion ul_wakeup_ack_completion;
struct completion bam_connection_completion;
struct completion request_resource_completion;
+ struct completion dl_wakeup_completion;
rwlock_t ul_wakeup_lock;
int wait_for_ack;
struct wake_lock bam_wakelock;
@@ -205,6 +209,7 @@
smsm_change_state(SMSM_APPS_STATE,
clear_bit & SMSM_A2_POWER_CONTROL_ACK,
~clear_bit & SMSM_A2_POWER_CONTROL_ACK);
+ IPA_STATS_INC_CNT(ipa_ctx->stats.a2_power_apps_acks);
clear_bit = ~clear_bit;
}
@@ -216,10 +221,13 @@
if (a2_mux_ctx->bam_dmux_uplink_vote == vote)
IPADBG("%s: warning - duplicate power vote\n", __func__);
a2_mux_ctx->bam_dmux_uplink_vote = vote;
- if (vote)
+ if (vote) {
smsm_change_state(SMSM_APPS_STATE, 0, SMSM_A2_POWER_CONTROL);
- else
+ IPA_STATS_INC_CNT(ipa_ctx->stats.a2_power_on_reqs_out);
+ } else {
smsm_change_state(SMSM_APPS_STATE, SMSM_A2_POWER_CONTROL, 0);
+ IPA_STATS_INC_CNT(ipa_ctx->stats.a2_power_off_reqs_out);
+ }
}
static inline void ul_powerdown(void)
@@ -233,7 +241,6 @@
INIT_COMPLETION(a2_mux_ctx->ul_wakeup_ack_completion);
power_vote(0);
}
- a2_mux_ctx->bam_is_connected = false;
}
static void ul_wakeup(void)
@@ -241,7 +248,8 @@
int ret;
mutex_lock(&a2_mux_ctx->wakeup_lock);
- if (a2_mux_ctx->bam_is_connected) {
+ if (a2_mux_ctx->bam_is_connected &&
+ !a2_mux_ctx->bam_connect_in_progress) {
IPADBG("%s Already awake\n", __func__);
mutex_unlock(&a2_mux_ctx->wakeup_lock);
return;
@@ -255,7 +263,6 @@
grab_wakelock();
else
a2_mux_ctx->a2_pc_disabled_wakelock_skipped = 1;
- a2_mux_ctx->bam_is_connected = true;
mutex_unlock(&a2_mux_ctx->wakeup_lock);
return;
}
@@ -271,7 +278,8 @@
A2_MUX_COMPLETION_TIMEOUT);
a2_mux_ctx->wait_for_ack = 0;
if (unlikely(ret == 0)) {
- IPADBG("%s timeout previous ack\n", __func__);
+ IPAERR("%s previous ack from modem timed out\n",
+ __func__);
goto bail;
}
}
@@ -281,7 +289,7 @@
ret = wait_for_completion_timeout(&a2_mux_ctx->ul_wakeup_ack_completion,
A2_MUX_COMPLETION_TIMEOUT);
if (unlikely(ret == 0)) {
- IPADBG("%s timeout wakeup ack\n", __func__);
+ IPAERR("%s wakup ack from modem timed out\n", __func__);
goto bail;
}
INIT_COMPLETION(a2_mux_ctx->bam_connection_completion);
@@ -290,11 +298,10 @@
&a2_mux_ctx->bam_connection_completion,
A2_MUX_COMPLETION_TIMEOUT);
if (unlikely(ret == 0)) {
- IPADBG("%s timeout power on\n", __func__);
+ IPAERR("%s modem power on timed out\n", __func__);
goto bail;
}
}
- a2_mux_ctx->bam_is_connected = true;
IPADBG("%s complete\n", __func__);
mutex_unlock(&a2_mux_ctx->wakeup_lock);
return;
@@ -363,26 +370,95 @@
dev_kfree_skb_any(skb);
}
+static bool msm_bam_dmux_kickoff_ul_power_down(void)
+
+{
+ bool is_connected;
+
+ write_lock(&a2_mux_ctx->ul_wakeup_lock);
+ if (a2_mux_ctx->bam_connect_in_progress) {
+ a2_mux_ctx->bam_is_connected = false;
+ is_connected = true;
+ } else {
+ is_connected = a2_mux_ctx->bam_is_connected;
+ a2_mux_ctx->bam_is_connected = false;
+ if (is_connected) {
+ a2_mux_ctx->bam_connect_in_progress = true;
+ queue_work(a2_mux_ctx->a2_mux_tx_workqueue,
+ &a2_mux_ctx->kickoff_ul_power_down);
+ }
+ }
+ write_unlock(&a2_mux_ctx->ul_wakeup_lock);
+ return is_connected;
+}
+
+static bool msm_bam_dmux_kickoff_ul_wakeup(void)
+{
+ bool is_connected;
+
+ write_lock(&a2_mux_ctx->ul_wakeup_lock);
+ if (a2_mux_ctx->bam_connect_in_progress) {
+ a2_mux_ctx->bam_is_connected = true;
+ is_connected = false;
+ } else {
+ is_connected = a2_mux_ctx->bam_is_connected;
+ a2_mux_ctx->bam_is_connected = true;
+ if (!is_connected) {
+ a2_mux_ctx->bam_connect_in_progress = true;
+ queue_work(a2_mux_ctx->a2_mux_tx_workqueue,
+ &a2_mux_ctx->kickoff_ul_wakeup);
+ }
+ }
+ write_unlock(&a2_mux_ctx->ul_wakeup_lock);
+ return is_connected;
+}
+
static void kickoff_ul_power_down_func(struct work_struct *work)
{
- unsigned long flags;
+ bool is_connected;
- write_lock_irqsave(&a2_mux_ctx->ul_wakeup_lock, flags);
- if (a2_mux_ctx->bam_is_connected) {
- IPADBG("%s: UL active - forcing powerdown\n", __func__);
- ul_powerdown();
- }
- write_unlock_irqrestore(&a2_mux_ctx->ul_wakeup_lock, flags);
- ipa_rm_notify_completion(IPA_RM_RESOURCE_RELEASED,
- IPA_RM_RESOURCE_A2_CONS);
+ IPADBG("%s: UL active - forcing powerdown\n", __func__);
+ ul_powerdown();
+ write_lock(&a2_mux_ctx->ul_wakeup_lock);
+ is_connected = a2_mux_ctx->bam_is_connected;
+ a2_mux_ctx->bam_is_connected = false;
+ a2_mux_ctx->bam_connect_in_progress = false;
+ write_unlock(&a2_mux_ctx->ul_wakeup_lock);
+ if (is_connected)
+ msm_bam_dmux_kickoff_ul_wakeup();
+ else
+ ipa_rm_notify_completion(IPA_RM_RESOURCE_RELEASED,
+ IPA_RM_RESOURCE_A2_CONS);
}
static void kickoff_ul_wakeup_func(struct work_struct *work)
{
- if (!a2_mux_ctx->bam_is_connected)
- ul_wakeup();
- ipa_rm_notify_completion(IPA_RM_RESOURCE_GRANTED,
- IPA_RM_RESOURCE_A2_CONS);
+ bool is_connected;
+ int ret;
+
+ ul_wakeup();
+ write_lock(&a2_mux_ctx->ul_wakeup_lock);
+ is_connected = a2_mux_ctx->bam_is_connected;
+ a2_mux_ctx->bam_is_connected = true;
+ a2_mux_ctx->bam_connect_in_progress = false;
+ write_unlock(&a2_mux_ctx->ul_wakeup_lock);
+ if (is_connected)
+ ipa_rm_notify_completion(IPA_RM_RESOURCE_GRANTED,
+ IPA_RM_RESOURCE_A2_CONS);
+ INIT_COMPLETION(a2_mux_ctx->dl_wakeup_completion);
+ if (!a2_mux_ctx->a2_mux_dl_wakeup) {
+ ret = wait_for_completion_timeout(
+ &a2_mux_ctx->dl_wakeup_completion,
+ A2_MUX_COMPLETION_TIMEOUT);
+ if (unlikely(ret == 0)) {
+ IPAERR("%s timeout waiting for A2 PROD granted\n",
+ __func__);
+ BUG();
+ return;
+ }
+ }
+ if (!is_connected)
+ msm_bam_dmux_kickoff_ul_power_down();
}
static void kickoff_ul_request_resource_func(struct work_struct *work)
@@ -401,40 +477,15 @@
&a2_mux_ctx->request_resource_completion,
A2_MUX_COMPLETION_TIMEOUT);
if (unlikely(ret == 0)) {
- IPADBG("%s timeout request A2 PROD resource\n",
- __func__);
+ IPAERR("%s timeout waiting for A2 PROD granted\n",
+ __func__);
BUG();
return;
}
}
toggle_apps_ack();
-}
-
-static bool msm_bam_dmux_kickoff_ul_wakeup(void)
-{
- bool is_connected;
-
- read_lock(&a2_mux_ctx->ul_wakeup_lock);
- is_connected = a2_mux_ctx->bam_is_connected;
- read_unlock(&a2_mux_ctx->ul_wakeup_lock);
- if (!is_connected)
- queue_work(a2_mux_ctx->a2_mux_tx_workqueue,
- &a2_mux_ctx->kickoff_ul_wakeup);
- return is_connected;
-}
-
-static bool msm_bam_dmux_kickoff_ul_power_down(void)
-
-{
- bool is_connected;
-
- read_lock(&a2_mux_ctx->ul_wakeup_lock);
- is_connected = a2_mux_ctx->bam_is_connected;
- read_unlock(&a2_mux_ctx->ul_wakeup_lock);
- if (is_connected)
- queue_work(a2_mux_ctx->a2_mux_tx_workqueue,
- &a2_mux_ctx->kickoff_ul_power_down);
- return is_connected;
+ a2_mux_ctx->a2_mux_dl_wakeup = true;
+ complete_all(&a2_mux_ctx->dl_wakeup_completion);
}
static void ipa_embedded_notify(void *priv,
@@ -487,10 +538,8 @@
__func__);
return -EFAULT;
}
- ret = sps_device_reset(a2_mux_ctx->a2_device_handle);
- if (ret)
- IPAERR("%s: device reset failed ret = %d\n",
- __func__, ret);
+ if (sps_ctrl_bam_dma_clk(true))
+ WARN_ON(1);
memset(&connect_params, 0, sizeof(struct ipa_sys_connect_params));
connect_params.client = IPA_CLIENT_A2_TETHERED_CONS;
connect_params.notify = ipa_tethered_notify;
@@ -561,6 +610,8 @@
ipa_bridge_teardown(IPA_BRIDGE_DIR_UL, IPA_BRIDGE_TYPE_TETHERED,
a2_mux_ctx->tethered_prod);
bridge_tethered_ul_failed:
+ if (sps_ctrl_bam_dma_clk(false))
+ WARN_ON(1);
return ret;
}
@@ -602,16 +653,13 @@
__func__, ret);
return ret;
}
- ret = sps_device_reset(a2_mux_ctx->a2_device_handle);
- if (ret) {
- IPAERR("%s: device reset failed ret = %d\n",
- __func__, ret);
- return ret;
- }
+ if (sps_ctrl_bam_dma_clk(false))
+ WARN_ON(1);
verify_tx_queue_is_empty(__func__);
(void) ipa_rm_release_resource(IPA_RM_RESOURCE_A2_PROD);
if (a2_mux_ctx->disconnect_ack)
toggle_apps_ack();
+ a2_mux_ctx->a2_mux_dl_wakeup = false;
a2_mux_ctx->a2_mux_sw_bridge_is_connected = 0;
complete_all(&a2_mux_ctx->bam_connection_completion);
return 0;
@@ -634,12 +682,14 @@
last_processed_state = new_state & SMSM_A2_POWER_CONTROL;
if (new_state & SMSM_A2_POWER_CONTROL) {
IPADBG("%s: MODEM PWR CTRL 1\n", __func__);
+ IPA_STATS_INC_CNT(ipa_ctx->stats.a2_power_on_reqs_in);
grab_wakelock();
(void) connect_to_bam();
- queue_work(a2_mux_ctx->a2_mux_tx_workqueue,
+ queue_work(a2_mux_ctx->a2_mux_rx_workqueue,
&a2_mux_ctx->kickoff_ul_request_resource);
} else if (!(new_state & SMSM_A2_POWER_CONTROL)) {
IPADBG("%s: MODEM PWR CTRL 0\n", __func__);
+ IPA_STATS_INC_CNT(ipa_ctx->stats.a2_power_off_reqs_in);
(void) disconnect_to_bam();
release_wakelock();
} else {
@@ -653,6 +703,7 @@
{
IPADBG("%s: 0x%08x -> 0x%08x\n", __func__, old_state,
new_state);
+ IPA_STATS_INC_CNT(ipa_ctx->stats.a2_power_modem_acks);
complete_all(&a2_mux_ctx->ul_wakeup_ack_completion);
}
@@ -929,7 +980,8 @@
}
spin_unlock_irqrestore(&a2_mux_ctx->bam_ch[id].lock, flags);
read_lock(&a2_mux_ctx->ul_wakeup_lock);
- is_connected = a2_mux_ctx->bam_is_connected;
+ is_connected = a2_mux_ctx->bam_is_connected &&
+ !a2_mux_ctx->bam_connect_in_progress;
read_unlock(&a2_mux_ctx->ul_wakeup_lock);
if (!is_connected)
return -ENODEV;
@@ -1233,7 +1285,8 @@
a2_mux_ctx->bam_ch[lcid].use_wm = 0;
spin_unlock_irqrestore(&a2_mux_ctx->bam_ch[lcid].lock, flags);
read_lock(&a2_mux_ctx->ul_wakeup_lock);
- is_connected = a2_mux_ctx->bam_is_connected;
+ is_connected = a2_mux_ctx->bam_is_connected &&
+ !a2_mux_ctx->bam_connect_in_progress;
read_unlock(&a2_mux_ctx->ul_wakeup_lock);
if (!is_connected)
return -ENODEV;
@@ -1302,7 +1355,8 @@
if (!a2_mux_ctx->a2_mux_initialized)
return -ENODEV;
read_lock(&a2_mux_ctx->ul_wakeup_lock);
- is_connected = a2_mux_ctx->bam_is_connected;
+ is_connected = a2_mux_ctx->bam_is_connected &&
+ !a2_mux_ctx->bam_connect_in_progress;
read_unlock(&a2_mux_ctx->ul_wakeup_lock);
if (!is_connected && !bam_ch_is_in_reset(lcid))
return -ENODEV;
@@ -1438,6 +1492,7 @@
init_completion(&a2_mux_ctx->ul_wakeup_ack_completion);
init_completion(&a2_mux_ctx->bam_connection_completion);
init_completion(&a2_mux_ctx->request_resource_completion);
+ init_completion(&a2_mux_ctx->dl_wakeup_completion);
wake_lock_init(&a2_mux_ctx->bam_wakelock,
WAKE_LOCK_SUSPEND, "a2_mux_wakelock");
a2_mux_ctx->a2_mux_initialized = 1;
@@ -1449,6 +1504,13 @@
__func__);
return -ENOMEM;
}
+ a2_mux_ctx->a2_mux_rx_workqueue =
+ create_singlethread_workqueue("a2_mux_rx");
+ if (!a2_mux_ctx->a2_mux_rx_workqueue) {
+ IPAERR("%s: a2_mux_rx_workqueue alloc failed\n",
+ __func__);
+ return -ENOMEM;
+ }
return 0;
}
@@ -1492,6 +1554,7 @@
a2_props.options = SPS_BAM_OPT_IRQ_WAKEUP;
a2_props.num_pipes = A2_NUM_PIPES;
a2_props.summing_threshold = A2_SUMMING_THRESHOLD;
+ a2_props.manage = SPS_BAM_MGR_DEVICE_REMOTE;
/* need to free on tear down */
rc = sps_register_bam_device(&a2_props, &h);
if (rc < 0) {
@@ -1573,5 +1636,7 @@
NULL);
if (a2_mux_ctx->a2_mux_tx_workqueue)
destroy_workqueue(a2_mux_ctx->a2_mux_tx_workqueue);
+ if (a2_mux_ctx->a2_mux_rx_workqueue)
+ destroy_workqueue(a2_mux_ctx->a2_mux_rx_workqueue);
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa.c b/drivers/platform/msm/ipa/ipa.c
index 16f722c..1cbd514 100644
--- a/drivers/platform/msm/ipa/ipa.c
+++ b/drivers/platform/msm/ipa/ipa.c
@@ -73,6 +73,18 @@
.ab = 0,
.ib = 0,
},
+ {
+ .src = MSM_BUS_MASTER_BAM_DMA,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+ {
+ .src = MSM_BUS_MASTER_BAM_DMA,
+ .dst = MSM_BUS_SLAVE_OCIMEM,
+ .ab = 0,
+ .ib = 0,
+ },
};
static struct msm_bus_vectors ipa_max_perf_vectors[] = {
@@ -82,6 +94,18 @@
.ab = 50000000,
.ib = 960000000,
},
+ {
+ .src = MSM_BUS_MASTER_BAM_DMA,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 50000000,
+ .ib = 960000000,
+ },
+ {
+ .src = MSM_BUS_MASTER_BAM_DMA,
+ .dst = MSM_BUS_SLAVE_OCIMEM,
+ .ab = 50000000,
+ .ib = 960000000,
+ },
};
static struct msm_bus_paths ipa_usecases[] = {
@@ -172,6 +196,8 @@
if (_IOC_NR(cmd) >= IPA_IOCTL_MAX)
return -ENOTTY;
+ ipa_inc_client_enable_clks();
+
switch (cmd) {
case IPA_IOC_ALLOC_NAT_MEM:
if (copy_from_user((u8 *)&nat_mem, (u8 *)arg,
@@ -614,10 +640,13 @@
rm_depend.depends_on_name);
break;
default: /* redundant, as cmd was checked against MAXNR */
+ ipa_dec_client_disable_clks();
return -ENOTTY;
}
kfree(param);
+ ipa_dec_client_disable_clks();
+
return retval;
}
@@ -747,66 +776,6 @@
return ret;
}
-static int ipa_handle_tx_poll_for_pipe(struct ipa_sys_context *sys,
- bool process_all)
-{
- struct ipa_tx_pkt_wrapper *tx_pkt, *t;
- struct sps_iovec iov;
- unsigned long irq_flags;
- int ret;
- int cnt = 0;
-
- do {
- iov.addr = 0;
- ret = sps_get_iovec(sys->ep->ep_hdl, &iov);
- if (ret) {
- pr_err("%s: sps_get_iovec failed %d\n", __func__, ret);
- break;
- }
- if (!iov.addr)
- break;
- spin_lock_irqsave(&sys->spinlock, irq_flags);
- tx_pkt = list_first_entry(&sys->head_desc_list,
- struct ipa_tx_pkt_wrapper, link);
- spin_unlock_irqrestore(&sys->spinlock, irq_flags);
-
- switch (tx_pkt->cnt) {
- case 1:
- ipa_wq_write_done(&tx_pkt->work);
- ++cnt;
- break;
- case 0xFFFF:
- /* reached end of set */
- spin_lock_irqsave(&sys->spinlock, irq_flags);
- list_for_each_entry_safe(tx_pkt, t,
- &sys->wait_desc_list, link) {
- list_del(&tx_pkt->link);
- list_add(&tx_pkt->link, &sys->head_desc_list);
- }
- tx_pkt =
- list_first_entry(&sys->head_desc_list,
- struct ipa_tx_pkt_wrapper, link);
- spin_unlock_irqrestore(&sys->spinlock, irq_flags);
- ipa_wq_write_done(&tx_pkt->work);
- ++cnt;
- break;
- default:
- /* keep looping till reach the end of the set */
- spin_lock_irqsave(&sys->spinlock,
- irq_flags);
- list_del(&tx_pkt->link);
- list_add_tail(&tx_pkt->link,
- &sys->wait_desc_list);
- spin_unlock_irqrestore(&sys->spinlock,
- irq_flags);
- ++cnt;
- break;
- }
- } while (process_all);
-
- return cnt;
-}
-
static void ipa_poll_function(struct work_struct *work)
{
int ret;
@@ -826,13 +795,15 @@
/* check all the system pipes for tx comp and rx avail */
if (ipa_ctx->sys[IPA_A5_LAN_WAN_IN].ep->valid)
- cnt |= ipa_handle_rx_core(false, true);
+ cnt |= ipa_handle_rx_core(
+ &ipa_ctx->sys[IPA_A5_LAN_WAN_IN],
+ false, true);
for (i = 0; i < num_tx_pipes; i++)
if (ipa_ctx->sys[tx_pipes[i]].ep->valid)
- cnt |= ipa_handle_tx_poll_for_pipe(
+ cnt |= ipa_handle_tx_core(
&ipa_ctx->sys[tx_pipes[i]],
- false);
+ false, true);
} while (cnt);
/* re-post the poll work */
@@ -901,7 +872,7 @@
/* LAN-WAN OUT (A5->IPA) */
memset(&sys_in, 0, sizeof(struct ipa_sys_connect_params));
sys_in.client = IPA_CLIENT_A5_LAN_WAN_PROD;
- sys_in.desc_fifo_sz = IPA_SYS_DESC_FIFO_SZ;
+ sys_in.desc_fifo_sz = IPA_SYS_TX_DATA_DESC_FIFO_SZ;
sys_in.ipa_ep_cfg.mode.mode = IPA_BASIC;
sys_in.ipa_ep_cfg.mode.dst = IPA_CLIENT_A5_LAN_WAN_CONS;
if (ipa_setup_sys_pipe(&sys_in, &ipa_ctx->clnt_hdl_data_out)) {
@@ -1123,7 +1094,7 @@
u32 producer_hdl = 0;
u32 consumer_hdl = 0;
- rmnet_bridge_get_client_handles(&producer_hdl, &consumer_hdl);
+ teth_bridge_get_client_handles(&producer_hdl, &consumer_hdl);
/* configure aggregation on producer */
memset(&agg_params, 0, sizeof(struct ipa_ep_cfg_aggr));
@@ -1633,10 +1604,6 @@
IPADBG("polling_mode=%u delay_ms=%u\n", polling_mode, polling_delay_ms);
ipa_ctx->polling_mode = polling_mode;
- if (ipa_ctx->polling_mode)
- atomic_set(&ipa_ctx->curr_polling_state, 1);
- else
- atomic_set(&ipa_ctx->curr_polling_state, 0);
IPADBG("hdr_lcl=%u ip4_rt=%u ip6_rt=%u ip4_flt=%u ip6_flt=%u\n",
hdr_tbl_lcl, ip4_rt_tbl_lcl, ip6_rt_tbl_lcl, ip4_flt_tbl_lcl,
ip6_flt_tbl_lcl);
@@ -1712,6 +1679,7 @@
bam_props.num_pipes = IPA_NUM_PIPES;
bam_props.summing_threshold = IPA_SUMMING_THRESHOLD;
bam_props.event_threshold = IPA_EVENT_THRESHOLD;
+ bam_props.options |= SPS_BAM_NO_LOCAL_CLK_GATING;
result = sps_register_bam_device(&bam_props, &ipa_ctx->bam_handle);
if (result) {
@@ -1857,7 +1825,10 @@
ipa_ctx->sys[i].ep = &ipa_ctx->ep[i];
else
ipa_ctx->sys[i].ep = &ipa_ctx->ep[WLAN_AMPDU_TX_EP];
- INIT_LIST_HEAD(&ipa_ctx->sys[i].wait_desc_list);
+ if (ipa_ctx->polling_mode)
+ atomic_set(&ipa_ctx->sys[i].curr_polling_state, 1);
+ else
+ atomic_set(&ipa_ctx->sys[i].curr_polling_state, 0);
}
ipa_ctx->rx_wq = create_singlethread_workqueue("ipa rx wq");
@@ -1867,7 +1838,8 @@
goto fail_rx_wq;
}
- ipa_ctx->tx_wq = create_singlethread_workqueue("ipa tx wq");
+ ipa_ctx->tx_wq = alloc_workqueue("ipa tx wq", WQ_MEM_RECLAIM |
+ WQ_CPU_INTENSIVE, 2);
if (!ipa_ctx->tx_wq) {
IPAERR(":fail to create tx wq\n");
result = -ENOMEM;
@@ -1887,7 +1859,7 @@
if (result) {
IPAERR("ipa bridge init err.\n");
result = -ENODEV;
- goto fail_bridge_init;
+ goto fail_a5_pipes;
}
/* setup the A5-IPA pipes */
@@ -2008,8 +1980,6 @@
ipa_cleanup_rx();
ipa_teardown_a5_pipes();
fail_a5_pipes:
- ipa_bridge_cleanup();
-fail_bridge_init:
destroy_workqueue(ipa_ctx->tx_wq);
fail_tx_wq:
destroy_workqueue(ipa_ctx->rx_wq);
diff --git a/drivers/platform/msm/ipa/ipa_bridge.c b/drivers/platform/msm/ipa/ipa_bridge.c
index 83b7175..83c4db0 100644
--- a/drivers/platform/msm/ipa/ipa_bridge.c
+++ b/drivers/platform/msm/ipa/ipa_bridge.c
@@ -12,10 +12,56 @@
#include <linux/delay.h>
#include <linux/ratelimit.h>
+#include <mach/msm_smem.h>
#include "ipa_i.h"
-#define A2_EMBEDDED_PIPE_TX 4
-#define A2_EMBEDDED_PIPE_RX 5
+/*
+ * EP0 (teth)
+ * A2_BAM(1)->(12)DMA_BAM->DMA_BAM(13)->(6)IPA_BAM->IPA_BAM(10)->USB_BAM(0)
+ * A2_BAM(0)<-(15)DMA_BAM<-DMA_BAM(14)<-(7)IPA_BAM<-IPA_BAM(11)<-USB_BAM(1)
+ *
+ * EP2 (emb)
+ * A2_BAM(5)->(16)DMA_BAM->DMA_BAM(17)->(8)IPA_BAM->
+ * A2_BAM(4)<-(19)DMA_BAM<-DMA_BAM(18)<-(9)IPA_BAM<-
+ */
+
+#define A2_TETHERED_PIPE_UL 0
+#define DMA_A2_TETHERED_PIPE_UL 15
+#define DMA_IPA_TETHERED_PIPE_UL 14
+#define A2_TETHERED_PIPE_DL 1
+#define DMA_A2_TETHERED_PIPE_DL 12
+#define DMA_IPA_TETHERED_PIPE_DL 13
+
+#define A2_EMBEDDED_PIPE_UL 4
+#define DMA_A2_EMBEDDED_PIPE_UL 19
+#define DMA_IPA_EMBEDDED_PIPE_UL 18
+#define A2_EMBEDDED_PIPE_DL 5
+#define DMA_A2_EMBEDDED_PIPE_DL 16
+#define DMA_IPA_EMBEDDED_PIPE_DL 17
+
+#define IPA_SMEM_PIPE_MEM_SZ 32768
+
+#define IPA_UL_DATA_FIFO_SZ 0xc00
+#define IPA_UL_DESC_FIFO_SZ 0x530
+#define IPA_DL_DATA_FIFO_SZ 0x2400
+#define IPA_DL_DESC_FIFO_SZ 0x8a0
+#define IPA_DL_EMB_DATA_FIFO_SZ 0x1800
+#define IPA_DL_EMB_DESC_FIFO_SZ 0x4e8
+
+#define IPA_SMEM_UL_DATA_FIFO_OFST 0
+#define IPA_SMEM_UL_DESC_FIFO_OFST 0xc00
+#define IPA_SMEM_DL_DATA_FIFO_OFST 0x1130
+#define IPA_SMEM_DL_DESC_FIFO_OFST 0x3530
+#define IPA_SMEM_UL_EMB_DATA_FIFO_OFST 0x3dd0
+#define IPA_SMEM_UL_EMB_DESC_FIFO_OFST 0x49d0
+
+#define IPA_OCIMEM_DL_A2_DATA_FIFO_OFST 0
+#define IPA_OCIMEM_DL_A2_DESC_FIFO_OFST (IPA_OCIMEM_DL_A2_DATA_FIFO_OFST + \
+ IPA_DL_EMB_DATA_FIFO_SZ)
+#define IPA_OCIMEM_DL_IPA_DATA_FIFO_OFST (IPA_OCIMEM_DL_A2_DESC_FIFO_OFST + \
+ IPA_DL_EMB_DESC_FIFO_SZ)
+#define IPA_OCIMEM_DL_IPA_DESC_FIFO_OFST (IPA_OCIMEM_DL_IPA_DATA_FIFO_OFST + \
+ IPA_DL_EMB_DATA_FIFO_SZ)
enum ipa_pipe_type {
IPA_DL_FROM_A2,
@@ -25,678 +71,415 @@
IPA_PIPE_TYPE_MAX
};
-static int polling_min_sleep[IPA_BRIDGE_DIR_MAX] = { 950, 950 };
-static int polling_max_sleep[IPA_BRIDGE_DIR_MAX] = { 1050, 1050 };
-static int polling_inactivity[IPA_BRIDGE_DIR_MAX] = { 4, 4 };
-
-struct ipa_pkt_info {
- void *buffer;
- dma_addr_t dma_address;
- uint32_t len;
- struct list_head link;
-};
-
struct ipa_bridge_pipe_context {
- struct list_head head_desc_list;
struct sps_pipe *pipe;
- struct sps_connect connection;
- struct sps_mem_buffer desc_mem_buf;
- struct sps_register_event register_event;
- struct list_head free_desc_list;
+ bool ipa_facing;
bool valid;
};
struct ipa_bridge_context {
struct ipa_bridge_pipe_context pipe[IPA_PIPE_TYPE_MAX];
- struct workqueue_struct *ul_wq;
- struct workqueue_struct *dl_wq;
- struct work_struct ul_work;
- struct work_struct dl_work;
enum ipa_bridge_type type;
};
static struct ipa_bridge_context bridge[IPA_BRIDGE_TYPE_MAX];
-static void ipa_do_bridge_work(enum ipa_bridge_dir dir,
- struct ipa_bridge_context *ctx);
-
-static void ul_work_func(struct work_struct *work)
+static void ipa_get_dma_pipe_num(enum ipa_bridge_dir dir,
+ enum ipa_bridge_type type, int *a2, int *ipa)
{
- struct ipa_bridge_context *ctx = container_of(work,
- struct ipa_bridge_context, ul_work);
- ipa_do_bridge_work(IPA_BRIDGE_DIR_UL, ctx);
+ if (type == IPA_BRIDGE_TYPE_TETHERED) {
+ if (dir == IPA_BRIDGE_DIR_UL) {
+ *a2 = DMA_A2_TETHERED_PIPE_UL;
+ *ipa = DMA_IPA_TETHERED_PIPE_UL;
+ } else {
+ *a2 = DMA_A2_TETHERED_PIPE_DL;
+ *ipa = DMA_IPA_TETHERED_PIPE_DL;
+ }
+ } else {
+ if (dir == IPA_BRIDGE_DIR_UL) {
+ *a2 = DMA_A2_EMBEDDED_PIPE_UL;
+ *ipa = DMA_IPA_EMBEDDED_PIPE_UL;
+ } else {
+ *a2 = DMA_A2_EMBEDDED_PIPE_DL;
+ *ipa = DMA_IPA_EMBEDDED_PIPE_DL;
+ }
+ }
}
-static void dl_work_func(struct work_struct *work)
+static int ipa_get_desc_fifo_sz(enum ipa_bridge_dir dir,
+ enum ipa_bridge_type type)
{
- struct ipa_bridge_context *ctx = container_of(work,
- struct ipa_bridge_context, dl_work);
- ipa_do_bridge_work(IPA_BRIDGE_DIR_DL, ctx);
+ int sz;
+
+ if (type == IPA_BRIDGE_TYPE_TETHERED) {
+ if (dir == IPA_BRIDGE_DIR_UL)
+ sz = IPA_UL_DESC_FIFO_SZ;
+ else
+ sz = IPA_DL_DESC_FIFO_SZ;
+ } else {
+ if (dir == IPA_BRIDGE_DIR_UL)
+ sz = IPA_UL_DESC_FIFO_SZ;
+ else
+ sz = IPA_DL_EMB_DESC_FIFO_SZ;
+ }
+
+ return sz;
}
-static int ipa_switch_to_intr_mode(enum ipa_bridge_dir dir,
- struct ipa_bridge_context *ctx)
+static int ipa_get_data_fifo_sz(enum ipa_bridge_dir dir,
+ enum ipa_bridge_type type)
+{
+ int sz;
+
+ if (type == IPA_BRIDGE_TYPE_TETHERED) {
+ if (dir == IPA_BRIDGE_DIR_UL)
+ sz = IPA_UL_DATA_FIFO_SZ;
+ else
+ sz = IPA_DL_DATA_FIFO_SZ;
+ } else {
+ if (dir == IPA_BRIDGE_DIR_UL)
+ sz = IPA_UL_DATA_FIFO_SZ;
+ else
+ sz = IPA_DL_EMB_DATA_FIFO_SZ;
+ }
+
+ return sz;
+}
+
+static int ipa_get_a2_pipe_num(enum ipa_bridge_dir dir,
+ enum ipa_bridge_type type)
+{
+ int ep;
+
+ if (type == IPA_BRIDGE_TYPE_TETHERED) {
+ if (dir == IPA_BRIDGE_DIR_UL)
+ ep = A2_TETHERED_PIPE_UL;
+ else
+ ep = A2_TETHERED_PIPE_DL;
+ } else {
+ if (dir == IPA_BRIDGE_DIR_UL)
+ ep = A2_EMBEDDED_PIPE_UL;
+ else
+ ep = A2_EMBEDDED_PIPE_DL;
+ }
+
+ return ep;
+}
+
+int ipa_setup_ipa_dma_fifos(enum ipa_bridge_dir dir,
+ enum ipa_bridge_type type,
+ struct sps_mem_buffer *desc,
+ struct sps_mem_buffer *data)
{
int ret;
- struct ipa_bridge_pipe_context *sys = &ctx->pipe[2 * dir];
- ret = sps_get_config(sys->pipe, &sys->connection);
+ ret = sps_setup_bam2bam_fifo(data,
+ IPA_OCIMEM_DL_IPA_DATA_FIFO_OFST,
+ ipa_get_data_fifo_sz(dir, type), 1);
if (ret) {
- IPAERR("sps_get_config() failed %d type=%d dir=%d\n",
- ret, ctx->type, dir);
- goto fail;
+ IPAERR("DAFIFO setup fail %d dir %d type %d\n",
+ ret, dir, type);
+ return ret;
}
- sys->register_event.options = SPS_O_EOT;
- ret = sps_register_event(sys->pipe, &sys->register_event);
+
+ ret = sps_setup_bam2bam_fifo(desc,
+ IPA_OCIMEM_DL_IPA_DESC_FIFO_OFST,
+ ipa_get_desc_fifo_sz(dir, type), 1);
if (ret) {
- IPAERR("sps_register_event() failed %d type=%d dir=%d\n",
- ret, ctx->type, dir);
- goto fail;
- }
- sys->connection.options =
- SPS_O_AUTO_ENABLE | SPS_O_ACK_TRANSFERS | SPS_O_EOT;
- ret = sps_set_config(sys->pipe, &sys->connection);
- if (ret) {
- IPAERR("sps_set_config() failed %d type=%d dir=%d\n",
- ret, ctx->type, dir);
- goto fail;
- }
- ret = 0;
-fail:
- return ret;
-}
-
-static int ipa_switch_to_poll_mode(enum ipa_bridge_dir dir,
- enum ipa_bridge_type type)
-{
- int ret;
- struct ipa_bridge_pipe_context *sys = &bridge[type].pipe[2 * dir];
-
- ret = sps_get_config(sys->pipe, &sys->connection);
- if (ret) {
- IPAERR("sps_get_config() failed %d type=%d dir=%d\n",
- ret, type, dir);
- goto fail;
- }
- sys->connection.options =
- SPS_O_AUTO_ENABLE | SPS_O_ACK_TRANSFERS | SPS_O_POLL;
- ret = sps_set_config(sys->pipe, &sys->connection);
- if (ret) {
- IPAERR("sps_set_config() failed %d type=%d dir=%d\n",
- ret, type, dir);
- goto fail;
- }
- ret = 0;
-fail:
- return ret;
-}
-
-static int queue_rx_single(enum ipa_bridge_dir dir, enum ipa_bridge_type type)
-{
- struct ipa_bridge_pipe_context *sys_rx = &bridge[type].pipe[2 * dir];
- struct ipa_pkt_info *info;
- int ret;
-
- info = kmalloc(sizeof(struct ipa_pkt_info), GFP_KERNEL);
- if (!info) {
- IPAERR("unable to alloc rx_pkt_info type=%d dir=%d\n",
- type, dir);
- goto fail_pkt;
+ IPAERR("DEFIFO setup fail %d dir %d type %d\n",
+ ret, dir, type);
+ return ret;
}
- info->buffer = kmalloc(IPA_RX_SKB_SIZE, GFP_KERNEL | GFP_DMA);
- if (!info->buffer) {
- IPAERR("unable to alloc rx_pkt_buffer type=%d dir=%d\n",
- type, dir);
- goto fail_buffer;
- }
+ IPADBG("dir=%d type=%d Dpa=%x Dsz=%u Dva=%p dpa=%x dsz=%u dva=%p\n",
+ dir, type, data->phys_base, data->size, data->base,
+ desc->phys_base, desc->size, desc->base);
- info->dma_address = dma_map_single(NULL, info->buffer, IPA_RX_SKB_SIZE,
- DMA_BIDIRECTIONAL);
- if (info->dma_address == 0 || info->dma_address == ~0) {
- IPAERR("dma_map_single failure %p for %p type=%d dir=%d\n",
- (void *)info->dma_address, info->buffer,
- type, dir);
- goto fail_dma;
- }
-
- list_add_tail(&info->link, &sys_rx->head_desc_list);
- ret = sps_transfer_one(sys_rx->pipe, info->dma_address,
- IPA_RX_SKB_SIZE, info,
- SPS_IOVEC_FLAG_INT);
- if (ret) {
- list_del(&info->link);
- dma_unmap_single(NULL, info->dma_address, IPA_RX_SKB_SIZE,
- DMA_BIDIRECTIONAL);
- IPAERR("sps_transfer_one failed %d type=%d dir=%d\n", ret,
- type, dir);
- goto fail_dma;
- }
return 0;
-
-fail_dma:
- kfree(info->buffer);
-fail_buffer:
- kfree(info);
-fail_pkt:
- IPAERR("failed type=%d dir=%d\n", type, dir);
- return -ENOMEM;
}
-static int ipa_reclaim_tx(struct ipa_bridge_pipe_context *sys_tx, bool all)
+int ipa_setup_a2_dma_fifos(enum ipa_bridge_dir dir,
+ enum ipa_bridge_type type,
+ struct sps_mem_buffer *desc,
+ struct sps_mem_buffer *data)
{
- struct sps_iovec iov;
- struct ipa_pkt_info *tx_pkt;
- int cnt = 0;
int ret;
- do {
- iov.addr = 0;
- ret = sps_get_iovec(sys_tx->pipe, &iov);
- if (ret || iov.addr == 0) {
- break;
+ if (type == IPA_BRIDGE_TYPE_TETHERED) {
+ if (dir == IPA_BRIDGE_DIR_UL) {
+ desc->base = ipa_ctx->smem_pipe_mem +
+ IPA_SMEM_UL_DESC_FIFO_OFST;
+ desc->phys_base = smem_virt_to_phys(desc->base);
+ desc->size = ipa_get_desc_fifo_sz(dir, type);
+ data->base = ipa_ctx->smem_pipe_mem +
+ IPA_SMEM_UL_DATA_FIFO_OFST;
+ data->phys_base = smem_virt_to_phys(data->base);
+ data->size = ipa_get_data_fifo_sz(dir, type);
} else {
- tx_pkt = list_first_entry(&sys_tx->head_desc_list,
- struct ipa_pkt_info,
- link);
- list_move_tail(&tx_pkt->link,
- &sys_tx->free_desc_list);
- cnt++;
+ desc->base = ipa_ctx->smem_pipe_mem +
+ IPA_SMEM_DL_DESC_FIFO_OFST;
+ desc->phys_base = smem_virt_to_phys(desc->base);
+ desc->size = ipa_get_desc_fifo_sz(dir, type);
+ data->base = ipa_ctx->smem_pipe_mem +
+ IPA_SMEM_DL_DATA_FIFO_OFST;
+ data->phys_base = smem_virt_to_phys(data->base);
+ data->size = ipa_get_data_fifo_sz(dir, type);
}
- } while (all);
-
- return cnt;
-}
-
-static void ipa_do_bridge_work(enum ipa_bridge_dir dir,
- struct ipa_bridge_context *ctx)
-{
- struct ipa_bridge_pipe_context *sys_rx = &ctx->pipe[2 * dir];
- struct ipa_bridge_pipe_context *sys_tx = &ctx->pipe[2 * dir + 1];
- struct ipa_pkt_info *tx_pkt;
- struct ipa_pkt_info *rx_pkt;
- struct ipa_pkt_info *tmp_pkt;
- struct sps_iovec iov;
- int ret;
- int inactive_cycles = 0;
-
- while (1) {
- ++inactive_cycles;
-
- if (ipa_reclaim_tx(sys_tx, false))
- inactive_cycles = 0;
-
- iov.addr = 0;
- ret = sps_get_iovec(sys_rx->pipe, &iov);
- if (ret || iov.addr == 0) {
- /* no-op */
+ } else {
+ if (dir == IPA_BRIDGE_DIR_UL) {
+ desc->base = ipa_ctx->smem_pipe_mem +
+ IPA_SMEM_UL_EMB_DESC_FIFO_OFST;
+ desc->phys_base = smem_virt_to_phys(desc->base);
+ desc->size = ipa_get_desc_fifo_sz(dir, type);
+ data->base = ipa_ctx->smem_pipe_mem +
+ IPA_SMEM_UL_EMB_DATA_FIFO_OFST;
+ data->phys_base = smem_virt_to_phys(data->base);
+ data->size = ipa_get_data_fifo_sz(dir, type);
} else {
- inactive_cycles = 0;
-
- rx_pkt = list_first_entry(&sys_rx->head_desc_list,
- struct ipa_pkt_info,
- link);
- list_del(&rx_pkt->link);
- rx_pkt->len = iov.size;
-
-retry_alloc_tx:
- if (list_empty(&sys_tx->free_desc_list)) {
- tmp_pkt = kmalloc(sizeof(struct ipa_pkt_info),
- GFP_KERNEL);
- if (!tmp_pkt) {
- pr_debug_ratelimited("%s: unable to alloc tx_pkt_info type=%d dir=%d\n",
- __func__, ctx->type, dir);
- usleep_range(polling_min_sleep[dir],
- polling_max_sleep[dir]);
- goto retry_alloc_tx;
- }
-
- tmp_pkt->buffer = kmalloc(IPA_RX_SKB_SIZE,
- GFP_KERNEL | GFP_DMA);
- if (!tmp_pkt->buffer) {
- pr_debug_ratelimited("%s: unable to alloc tx_pkt_buffer type=%d dir=%d\n",
- __func__, ctx->type, dir);
- kfree(tmp_pkt);
- usleep_range(polling_min_sleep[dir],
- polling_max_sleep[dir]);
- goto retry_alloc_tx;
- }
-
- tmp_pkt->dma_address = dma_map_single(NULL,
- tmp_pkt->buffer,
- IPA_RX_SKB_SIZE,
- DMA_BIDIRECTIONAL);
- if (tmp_pkt->dma_address == 0 ||
- tmp_pkt->dma_address == ~0) {
- pr_debug_ratelimited("%s: dma_map_single failure %p for %p type=%d dir=%d\n",
- __func__,
- (void *)tmp_pkt->dma_address,
- tmp_pkt->buffer, ctx->type, dir);
- }
-
- list_add_tail(&tmp_pkt->link,
- &sys_tx->free_desc_list);
- }
-
- tx_pkt = list_first_entry(&sys_tx->free_desc_list,
- struct ipa_pkt_info,
- link);
- list_del(&tx_pkt->link);
-
-retry_add_rx:
- list_add_tail(&tx_pkt->link,
- &sys_rx->head_desc_list);
- ret = sps_transfer_one(sys_rx->pipe,
- tx_pkt->dma_address,
- IPA_RX_SKB_SIZE,
- tx_pkt,
- SPS_IOVEC_FLAG_INT);
+ ret = sps_setup_bam2bam_fifo(data,
+ IPA_OCIMEM_DL_A2_DATA_FIFO_OFST,
+ ipa_get_data_fifo_sz(dir, type), 1);
if (ret) {
- list_del(&tx_pkt->link);
- pr_debug_ratelimited("%s: sps_transfer_one failed %d type=%d dir=%d\n",
- __func__, ret, ctx->type, dir);
- usleep_range(polling_min_sleep[dir],
- polling_max_sleep[dir]);
- goto retry_add_rx;
+ IPAERR("DAFIFO setup fail %d dir %d type %d\n",
+ ret, dir, type);
+ return ret;
}
-retry_add_tx:
- list_add_tail(&rx_pkt->link,
- &sys_tx->head_desc_list);
- ret = sps_transfer_one(sys_tx->pipe,
- rx_pkt->dma_address,
- iov.size,
- rx_pkt,
- SPS_IOVEC_FLAG_INT |
- SPS_IOVEC_FLAG_EOT);
+ ret = sps_setup_bam2bam_fifo(desc,
+ IPA_OCIMEM_DL_A2_DESC_FIFO_OFST,
+ ipa_get_desc_fifo_sz(dir, type), 1);
if (ret) {
- pr_debug_ratelimited("%s: fail to add to TX type=%d dir=%d\n",
- __func__, ctx->type, dir);
- list_del(&rx_pkt->link);
- ipa_reclaim_tx(sys_tx, true);
- usleep_range(polling_min_sleep[dir],
- polling_max_sleep[dir]);
- goto retry_add_tx;
+ IPAERR("DEFIFO setup fail %d dir %d type %d\n",
+ ret, dir, type);
+ return ret;
}
- IPA_STATS_INC_BRIDGE_CNT(ctx->type, dir,
- ipa_ctx->stats.bridged_pkts);
- }
-
- if (inactive_cycles >= polling_inactivity[dir]) {
- ipa_switch_to_intr_mode(dir, ctx);
- break;
}
}
+
+ IPADBG("dir=%d type=%d Dpa=%x Dsz=%u Dva=%p dpa=%x dsz=%u dva=%p\n",
+ dir, type, data->phys_base, data->size, data->base,
+ desc->phys_base, desc->size, desc->base);
+
+ return 0;
}
-static void ipa_sps_irq_rx_notify(struct sps_event_notify *notify)
-{
- enum ipa_bridge_type type = (enum ipa_bridge_type) notify->user;
-
- switch (notify->event_id) {
- case SPS_EVENT_EOT:
- ipa_switch_to_poll_mode(IPA_BRIDGE_DIR_UL, type);
- queue_work(bridge[type].ul_wq, &bridge[type].ul_work);
- break;
- default:
- IPAERR("recieved unexpected event id %d type %d\n",
- notify->event_id, type);
- }
-}
-
-static int setup_bridge_to_ipa(enum ipa_bridge_dir dir,
+static int setup_dma_bam_bridge(enum ipa_bridge_dir dir,
enum ipa_bridge_type type,
struct ipa_sys_connect_params *props,
u32 *clnt_hdl)
{
- struct ipa_bridge_pipe_context *sys;
- dma_addr_t dma_addr;
- enum ipa_pipe_type pipe_type;
- int ipa_ep_idx;
- int ret;
- int i;
-
- ipa_ep_idx = ipa_get_ep_mapping(ipa_ctx->mode, props->client);
- if (ipa_ep_idx == -1) {
- IPAERR("Invalid client=%d mode=%d type=%d dir=%d\n",
- props->client, ipa_ctx->mode, type, dir);
- ret = -EINVAL;
- goto alloc_endpoint_failed;
- }
-
- if (ipa_ctx->ep[ipa_ep_idx].valid) {
- IPAERR("EP %d already allocated type=%d dir=%d\n", ipa_ep_idx,
- type, dir);
- ret = -EINVAL;
- goto alloc_endpoint_failed;
- }
-
- pipe_type = (dir == IPA_BRIDGE_DIR_DL) ? IPA_DL_TO_IPA :
- IPA_UL_FROM_IPA;
-
- sys = &bridge[type].pipe[pipe_type];
- sys->pipe = sps_alloc_endpoint();
- if (sys->pipe == NULL) {
- IPAERR("alloc endpoint failed type=%d dir=%d\n", type, dir);
- ret = -ENOMEM;
- goto alloc_endpoint_failed;
- }
- ret = sps_get_config(sys->pipe, &sys->connection);
- if (ret) {
- IPAERR("get config failed %d type=%d dir=%d\n", ret, type, dir);
- ret = -EINVAL;
- goto get_config_failed;
- }
-
- if (dir == IPA_BRIDGE_DIR_DL) {
- sys->connection.source = SPS_DEV_HANDLE_MEM;
- sys->connection.src_pipe_index = ipa_ctx->a5_pipe_index++;
- sys->connection.destination = ipa_ctx->bam_handle;
- sys->connection.dest_pipe_index = ipa_ep_idx;
- sys->connection.mode = SPS_MODE_DEST;
- sys->connection.options =
- SPS_O_AUTO_ENABLE | SPS_O_ACK_TRANSFERS | SPS_O_POLL;
- } else {
- sys->connection.source = ipa_ctx->bam_handle;
- sys->connection.src_pipe_index = ipa_ep_idx;
- sys->connection.destination = SPS_DEV_HANDLE_MEM;
- sys->connection.dest_pipe_index = ipa_ctx->a5_pipe_index++;
- sys->connection.mode = SPS_MODE_SRC;
- sys->connection.options = SPS_O_AUTO_ENABLE | SPS_O_EOT |
- SPS_O_ACK_TRANSFERS | SPS_O_NO_DISABLE;
- }
-
- sys->desc_mem_buf.size = props->desc_fifo_sz;
- sys->desc_mem_buf.base = dma_alloc_coherent(NULL,
- sys->desc_mem_buf.size,
- &dma_addr,
- 0);
- if (sys->desc_mem_buf.base == NULL) {
- IPAERR("memory alloc failed type=%d dir=%d\n", type, dir);
- ret = -ENOMEM;
- goto get_config_failed;
- }
- sys->desc_mem_buf.phys_base = dma_addr;
- memset(sys->desc_mem_buf.base, 0x0, sys->desc_mem_buf.size);
- sys->connection.desc = sys->desc_mem_buf;
- sys->connection.event_thresh = IPA_EVENT_THRESHOLD;
-
- ret = sps_connect(sys->pipe, &sys->connection);
- if (ret < 0) {
- IPAERR("connect error %d type=%d dir=%d\n", ret, type, dir);
- goto connect_failed;
- }
-
- INIT_LIST_HEAD(&sys->head_desc_list);
- INIT_LIST_HEAD(&sys->free_desc_list);
-
- memset(&ipa_ctx->ep[ipa_ep_idx], 0,
- sizeof(struct ipa_ep_context));
-
- ipa_ctx->ep[ipa_ep_idx].valid = 1;
- ipa_ctx->ep[ipa_ep_idx].client_notify = props->notify;
- ipa_ctx->ep[ipa_ep_idx].priv = props->priv;
-
- ret = ipa_cfg_ep(ipa_ep_idx, &props->ipa_ep_cfg);
- if (ret < 0) {
- IPAERR("ep cfg set error %d type=%d dir=%d\n", ret, type, dir);
- ipa_ctx->ep[ipa_ep_idx].valid = 0;
- goto event_reg_failed;
- }
-
- if (dir == IPA_BRIDGE_DIR_UL) {
- sys->register_event.options = SPS_O_EOT;
- sys->register_event.mode = SPS_TRIGGER_CALLBACK;
- sys->register_event.xfer_done = NULL;
- sys->register_event.callback = ipa_sps_irq_rx_notify;
- sys->register_event.user = (void *)type;
- ret = sps_register_event(sys->pipe, &sys->register_event);
- if (ret < 0) {
- IPAERR("register event error %d type=%d dir=%d\n", ret,
- type, dir);
- goto event_reg_failed;
- }
-
- for (i = 0; i < IPA_RX_POOL_CEIL; i++) {
- ret = queue_rx_single(dir, type);
- if (ret < 0)
- IPAERR("queue fail dir=%d type=%d iter=%d\n",
- dir, type, i);
- }
- }
-
- *clnt_hdl = ipa_ep_idx;
- sys->valid = true;
-
- return 0;
-
-event_reg_failed:
- sps_disconnect(sys->pipe);
-connect_failed:
- dma_free_coherent(NULL,
- sys->desc_mem_buf.size,
- sys->desc_mem_buf.base,
- sys->desc_mem_buf.phys_base);
-get_config_failed:
- sps_free_endpoint(sys->pipe);
-alloc_endpoint_failed:
- return ret;
-}
-
-static void bam_mux_rx_notify(struct sps_event_notify *notify)
-{
- enum ipa_bridge_type type = (enum ipa_bridge_type) notify->user;
-
- switch (notify->event_id) {
- case SPS_EVENT_EOT:
- ipa_switch_to_poll_mode(IPA_BRIDGE_DIR_DL, type);
- queue_work(bridge[type].dl_wq, &bridge[type].dl_work);
- break;
- default:
- IPAERR("recieved unexpected event id %d type %d\n",
- notify->event_id, type);
- }
-}
-
-static int setup_bridge_to_a2(enum ipa_bridge_dir dir,
- enum ipa_bridge_type type,
- u32 desc_fifo_sz)
-{
- struct ipa_bridge_pipe_context *sys;
- struct a2_mux_pipe_connection pipe_conn = { 0 };
- dma_addr_t dma_addr;
- u32 a2_handle;
+ struct ipa_connect_params ipa_in_params;
+ struct ipa_sps_params sps_out_params;
+ int dma_a2_pipe;
+ int dma_ipa_pipe;
+ struct sps_pipe *pipe;
+ struct sps_pipe *pipe_a2;
+ struct sps_connect _connection;
+ struct sps_connect *connection = &_connection;
+ struct a2_mux_pipe_connection pipe_conn = {0};
enum a2_mux_pipe_direction pipe_dir;
- enum ipa_pipe_type pipe_type;
+ u32 dma_hdl = sps_dma_get_bam_handle();
+ u32 a2_hdl;
u32 pa;
int ret;
- int i;
+
+ memset(&ipa_in_params, 0, sizeof(ipa_in_params));
+ memset(&sps_out_params, 0, sizeof(sps_out_params));
pipe_dir = (dir == IPA_BRIDGE_DIR_UL) ? IPA_TO_A2 : A2_TO_IPA;
ret = ipa_get_a2_mux_pipe_info(pipe_dir, &pipe_conn);
if (ret) {
- IPAERR("ipa_get_a2_mux_pipe_info failed type=%d dir=%d\n",
- type, dir);
- ret = -EINVAL;
- goto alloc_endpoint_failed;
+ IPAERR("ipa_get_a2_mux_pipe_info failed dir=%d type=%d\n",
+ dir, type);
+ goto fail_get_a2_prop;
}
pa = (dir == IPA_BRIDGE_DIR_UL) ? pipe_conn.dst_phy_addr :
pipe_conn.src_phy_addr;
- ret = sps_phy2h(pa, &a2_handle);
+ ret = sps_phy2h(pa, &a2_hdl);
if (ret) {
- IPAERR("sps_phy2h failed (A2 BAM) %d type=%d dir=%d\n",
- ret, type, dir);
- ret = -EINVAL;
- goto alloc_endpoint_failed;
+ IPAERR("sps_phy2h failed (A2 BAM) %d dir=%d type=%d\n",
+ ret, dir, type);
+ goto fail_get_a2_prop;
}
- pipe_type = (dir == IPA_BRIDGE_DIR_UL) ? IPA_UL_TO_A2 : IPA_DL_FROM_A2;
+ ipa_get_dma_pipe_num(dir, type, &dma_a2_pipe, &dma_ipa_pipe);
- sys = &bridge[type].pipe[pipe_type];
- sys->pipe = sps_alloc_endpoint();
- if (sys->pipe == NULL) {
- IPAERR("alloc endpoint failed type=%d dir=%d\n", type, dir);
+ ipa_in_params.ipa_ep_cfg = props->ipa_ep_cfg;
+ ipa_in_params.client = props->client;
+ ipa_in_params.client_bam_hdl = dma_hdl;
+ ipa_in_params.client_ep_idx = dma_ipa_pipe;
+ ipa_in_params.priv = props->priv;
+ ipa_in_params.notify = props->notify;
+ ipa_in_params.desc_fifo_sz = ipa_get_desc_fifo_sz(dir, type);
+ ipa_in_params.data_fifo_sz = ipa_get_data_fifo_sz(dir, type);
+
+ if (type == IPA_BRIDGE_TYPE_EMBEDDED && dir == IPA_BRIDGE_DIR_DL) {
+ if (ipa_setup_ipa_dma_fifos(dir, type, &ipa_in_params.desc,
+ &ipa_in_params.data)) {
+ IPAERR("fail to setup IPA-DMA FIFOs dir=%d type=%d\n",
+ dir, type);
+ goto fail_get_a2_prop;
+ }
+ }
+
+ if (ipa_connect(&ipa_in_params, &sps_out_params, clnt_hdl)) {
+ IPAERR("ipa connect failed dir=%d type=%d\n", dir, type);
+ goto fail_get_a2_prop;
+ }
+
+ pipe = sps_alloc_endpoint();
+ if (pipe == NULL) {
+ IPAERR("sps_alloc_endpoint failed dir=%d type=%d\n", dir, type);
ret = -ENOMEM;
- goto alloc_endpoint_failed;
+ goto fail_sps_alloc;
}
- ret = sps_get_config(sys->pipe, &sys->connection);
+
+ memset(&_connection, 0, sizeof(_connection));
+ ret = sps_get_config(pipe, connection);
if (ret) {
- IPAERR("get config failed %d type=%d dir=%d\n", ret, type, dir);
- ret = -EINVAL;
- goto get_config_failed;
+ IPAERR("sps_get_config failed %d dir=%d type=%d\n", ret, dir,
+ type);
+ goto fail_sps_get_config;
}
- if (dir == IPA_BRIDGE_DIR_UL) {
- sys->connection.source = SPS_DEV_HANDLE_MEM;
- sys->connection.src_pipe_index = ipa_ctx->a5_pipe_index++;
- sys->connection.destination = a2_handle;
- if (type == IPA_BRIDGE_TYPE_TETHERED)
- sys->connection.dest_pipe_index =
- pipe_conn.dst_pipe_index;
- else
- sys->connection.dest_pipe_index = A2_EMBEDDED_PIPE_TX;
- sys->connection.mode = SPS_MODE_DEST;
- sys->connection.options =
- SPS_O_AUTO_ENABLE | SPS_O_ACK_TRANSFERS | SPS_O_POLL;
- } else {
- sys->connection.source = a2_handle;
- if (type == IPA_BRIDGE_TYPE_TETHERED)
- sys->connection.src_pipe_index =
- pipe_conn.src_pipe_index;
- else
- sys->connection.src_pipe_index = A2_EMBEDDED_PIPE_RX;
- sys->connection.destination = SPS_DEV_HANDLE_MEM;
- sys->connection.dest_pipe_index = ipa_ctx->a5_pipe_index++;
- sys->connection.mode = SPS_MODE_SRC;
- sys->connection.options = SPS_O_AUTO_ENABLE | SPS_O_EOT |
- SPS_O_ACK_TRANSFERS;
- }
-
- sys->desc_mem_buf.size = desc_fifo_sz;
- sys->desc_mem_buf.base = dma_alloc_coherent(NULL,
- sys->desc_mem_buf.size,
- &dma_addr,
- 0);
- if (sys->desc_mem_buf.base == NULL) {
- IPAERR("memory alloc failed type=%d dir=%d\n", type, dir);
- ret = -ENOMEM;
- goto get_config_failed;
- }
- sys->desc_mem_buf.phys_base = dma_addr;
- memset(sys->desc_mem_buf.base, 0x0, sys->desc_mem_buf.size);
- sys->connection.desc = sys->desc_mem_buf;
- sys->connection.event_thresh = IPA_EVENT_THRESHOLD;
-
- ret = sps_connect(sys->pipe, &sys->connection);
- if (ret < 0) {
- IPAERR("connect error %d type=%d dir=%d\n", ret, type, dir);
- ret = -EINVAL;
- goto connect_failed;
- }
-
- INIT_LIST_HEAD(&sys->head_desc_list);
- INIT_LIST_HEAD(&sys->free_desc_list);
-
if (dir == IPA_BRIDGE_DIR_DL) {
- sys->register_event.options = SPS_O_EOT;
- sys->register_event.mode = SPS_TRIGGER_CALLBACK;
- sys->register_event.xfer_done = NULL;
- sys->register_event.callback = bam_mux_rx_notify;
- sys->register_event.user = (void *)type;
- ret = sps_register_event(sys->pipe, &sys->register_event);
- if (ret < 0) {
- IPAERR("register event error %d type=%d dir=%d\n",
- ret, type, dir);
- ret = -EINVAL;
- goto event_reg_failed;
- }
-
- for (i = 0; i < IPA_RX_POOL_CEIL; i++) {
- ret = queue_rx_single(dir, type);
- if (ret < 0)
- IPAERR("queue fail dir=%d type=%d iter=%d\n",
- dir, type, i);
- }
+ connection->mode = SPS_MODE_SRC;
+ connection->source = dma_hdl;
+ connection->destination = sps_out_params.ipa_bam_hdl;
+ connection->src_pipe_index = dma_ipa_pipe;
+ connection->dest_pipe_index = sps_out_params.ipa_ep_idx;
+ } else {
+ connection->mode = SPS_MODE_DEST;
+ connection->source = sps_out_params.ipa_bam_hdl;
+ connection->destination = dma_hdl;
+ connection->src_pipe_index = sps_out_params.ipa_ep_idx;
+ connection->dest_pipe_index = dma_ipa_pipe;
}
- sys->valid = true;
+ connection->event_thresh = IPA_EVENT_THRESHOLD;
+ connection->data = sps_out_params.data;
+ connection->desc = sps_out_params.desc;
+ connection->options = SPS_O_AUTO_ENABLE;
+
+ ret = sps_connect(pipe, connection);
+ if (ret) {
+ IPAERR("sps_connect failed %d dir=%d type=%d\n", ret, dir,
+ type);
+ goto fail_sps_get_config;
+ }
+
+ if (dir == IPA_BRIDGE_DIR_DL) {
+ bridge[type].pipe[IPA_DL_TO_IPA].pipe = pipe;
+ bridge[type].pipe[IPA_DL_TO_IPA].ipa_facing = true;
+ bridge[type].pipe[IPA_DL_TO_IPA].valid = true;
+ } else {
+ bridge[type].pipe[IPA_UL_FROM_IPA].pipe = pipe;
+ bridge[type].pipe[IPA_UL_FROM_IPA].ipa_facing = true;
+ bridge[type].pipe[IPA_UL_FROM_IPA].valid = true;
+ }
+
+ IPADBG("dir=%d type=%d (ipa) src(0x%x:%u)->dst(0x%x:%u)\n", dir, type,
+ connection->source, connection->src_pipe_index,
+ connection->destination, connection->dest_pipe_index);
+
+ pipe_a2 = sps_alloc_endpoint();
+ if (pipe_a2 == NULL) {
+ IPAERR("sps_alloc_endpoint failed2 dir=%d type=%d\n", dir,
+ type);
+ ret = -ENOMEM;
+ goto fail_sps_alloc_a2;
+ }
+
+ memset(&_connection, 0, sizeof(_connection));
+ ret = sps_get_config(pipe_a2, connection);
+ if (ret) {
+ IPAERR("sps_get_config failed2 %d dir=%d type=%d\n", ret, dir,
+ type);
+ goto fail_sps_get_config_a2;
+ }
+
+ if (dir == IPA_BRIDGE_DIR_DL) {
+ connection->mode = SPS_MODE_DEST;
+ connection->source = a2_hdl;
+ connection->destination = dma_hdl;
+ connection->src_pipe_index = ipa_get_a2_pipe_num(dir, type);
+ connection->dest_pipe_index = dma_a2_pipe;
+ } else {
+ connection->mode = SPS_MODE_SRC;
+ connection->source = dma_hdl;
+ connection->destination = a2_hdl;
+ connection->src_pipe_index = dma_a2_pipe;
+ connection->dest_pipe_index = ipa_get_a2_pipe_num(dir, type);
+ }
+
+ connection->event_thresh = IPA_EVENT_THRESHOLD;
+
+ if (ipa_setup_a2_dma_fifos(dir, type, &connection->desc,
+ &connection->data)) {
+ IPAERR("fail to setup A2-DMA FIFOs dir=%d type=%d\n",
+ dir, type);
+ goto fail_sps_get_config_a2;
+ }
+
+ connection->options = SPS_O_AUTO_ENABLE;
+
+ ret = sps_connect(pipe_a2, connection);
+ if (ret) {
+ IPAERR("sps_connect failed2 %d dir=%d type=%d\n", ret, dir,
+ type);
+ goto fail_sps_get_config_a2;
+ }
+
+ if (dir == IPA_BRIDGE_DIR_DL) {
+ bridge[type].pipe[IPA_DL_FROM_A2].pipe = pipe_a2;
+ bridge[type].pipe[IPA_DL_FROM_A2].valid = true;
+ } else {
+ bridge[type].pipe[IPA_UL_TO_A2].pipe = pipe_a2;
+ bridge[type].pipe[IPA_UL_TO_A2].valid = true;
+ }
+
+ IPADBG("dir=%d type=%d (a2) src(0x%x:%u)->dst(0x%x:%u)\n", dir, type,
+ connection->source, connection->src_pipe_index,
+ connection->destination, connection->dest_pipe_index);
return 0;
-event_reg_failed:
- sps_disconnect(sys->pipe);
-connect_failed:
- dma_free_coherent(NULL,
- sys->desc_mem_buf.size,
- sys->desc_mem_buf.base,
- sys->desc_mem_buf.phys_base);
-get_config_failed:
- sps_free_endpoint(sys->pipe);
-alloc_endpoint_failed:
+fail_sps_get_config_a2:
+ sps_free_endpoint(pipe_a2);
+fail_sps_alloc_a2:
+ sps_disconnect(pipe);
+fail_sps_get_config:
+ sps_free_endpoint(pipe);
+fail_sps_alloc:
+ ipa_disconnect(*clnt_hdl);
+fail_get_a2_prop:
return ret;
}
/**
- * ipa_bridge_init() - create workqueues and work items serving SW bridges
+ * ipa_bridge_init()
*
* Return codes: 0: success, -ENOMEM: failure
*/
int ipa_bridge_init(void)
{
- int ret;
int i;
- bridge[IPA_BRIDGE_TYPE_TETHERED].ul_wq =
- create_singlethread_workqueue("ipa_ul_teth");
- if (!bridge[IPA_BRIDGE_TYPE_TETHERED].ul_wq) {
- IPAERR("ipa ul teth wq alloc failed\n");
- ret = -ENOMEM;
- goto fail_ul_teth;
+ ipa_ctx->smem_pipe_mem = smem_alloc(SMEM_BAM_PIPE_MEMORY,
+ IPA_SMEM_PIPE_MEM_SZ);
+ if (!ipa_ctx->smem_pipe_mem) {
+ IPAERR("smem alloc failed\n");
+ return -ENOMEM;
}
+ IPADBG("smem_pipe_mem = %p\n", ipa_ctx->smem_pipe_mem);
- bridge[IPA_BRIDGE_TYPE_TETHERED].dl_wq =
- create_singlethread_workqueue("ipa_dl_teth");
- if (!bridge[IPA_BRIDGE_TYPE_TETHERED].dl_wq) {
- IPAERR("ipa dl teth wq alloc failed\n");
- ret = -ENOMEM;
- goto fail_dl_teth;
- }
-
- bridge[IPA_BRIDGE_TYPE_EMBEDDED].ul_wq =
- create_singlethread_workqueue("ipa_ul_emb");
- if (!bridge[IPA_BRIDGE_TYPE_EMBEDDED].ul_wq) {
- IPAERR("ipa ul emb wq alloc failed\n");
- ret = -ENOMEM;
- goto fail_ul_emb;
- }
-
- bridge[IPA_BRIDGE_TYPE_EMBEDDED].dl_wq =
- create_singlethread_workqueue("ipa_dl_emb");
- if (!bridge[IPA_BRIDGE_TYPE_EMBEDDED].dl_wq) {
- IPAERR("ipa dl emb wq alloc failed\n");
- ret = -ENOMEM;
- goto fail_dl_emb;
- }
-
- for (i = 0; i < IPA_BRIDGE_TYPE_MAX; i++) {
- INIT_WORK(&bridge[i].ul_work, ul_work_func);
- INIT_WORK(&bridge[i].dl_work, dl_work_func);
+ for (i = 0; i < IPA_BRIDGE_TYPE_MAX; i++)
bridge[i].type = i;
- }
return 0;
-
-fail_dl_emb:
- destroy_workqueue(bridge[IPA_BRIDGE_TYPE_EMBEDDED].ul_wq);
-fail_ul_emb:
- destroy_workqueue(bridge[IPA_BRIDGE_TYPE_TETHERED].dl_wq);
-fail_dl_teth:
- destroy_workqueue(bridge[IPA_BRIDGE_TYPE_TETHERED].ul_wq);
-fail_ul_teth:
- return ret;
}
/**
@@ -720,7 +503,7 @@
if (props == NULL || clnt_hdl == NULL ||
type >= IPA_BRIDGE_TYPE_MAX || dir >= IPA_BRIDGE_DIR_MAX ||
- props->client >= IPA_CLIENT_MAX || props->desc_fifo_sz == 0) {
+ props->client >= IPA_CLIENT_MAX) {
IPAERR("Bad param props=%p clnt_hdl=%p type=%d dir=%d\n",
props, clnt_hdl, type, dir);
return -EINVAL;
@@ -728,52 +511,21 @@
ipa_inc_client_enable_clks();
- if (setup_bridge_to_ipa(dir, type, props, clnt_hdl)) {
+ if (setup_dma_bam_bridge(dir, type, props, clnt_hdl)) {
IPAERR("fail to setup SYS pipe to IPA dir=%d type=%d\n",
dir, type);
ret = -EINVAL;
goto bail_ipa;
}
- if (setup_bridge_to_a2(dir, type, props->desc_fifo_sz)) {
- IPAERR("fail to setup SYS pipe to A2 dir=%d type=%d\n",
- dir, type);
- ret = -EINVAL;
- goto bail_a2;
- }
-
-
return 0;
-bail_a2:
- ipa_bridge_teardown(dir, type, *clnt_hdl);
bail_ipa:
ipa_dec_client_disable_clks();
return ret;
}
EXPORT_SYMBOL(ipa_bridge_setup);
-static void ipa_bridge_free_pkt(struct ipa_pkt_info *pkt)
-{
- list_del(&pkt->link);
- dma_unmap_single(NULL, pkt->dma_address, IPA_RX_SKB_SIZE,
- DMA_BIDIRECTIONAL);
- kfree(pkt->buffer);
- kfree(pkt);
-}
-
-static void ipa_bridge_free_resources(struct ipa_bridge_pipe_context *pipe)
-{
- struct ipa_pkt_info *pkt;
- struct ipa_pkt_info *n;
-
- list_for_each_entry_safe(pkt, n, &pipe->head_desc_list, link)
- ipa_bridge_free_pkt(pkt);
-
- list_for_each_entry_safe(pkt, n, &pipe->free_desc_list, link)
- ipa_bridge_free_pkt(pkt);
-}
-
/**
* ipa_bridge_teardown() - teardown SW bridge leg
* @dir: downlink or uplink (from air interface perspective)
@@ -808,12 +560,10 @@
for (; lo <= hi; lo++) {
sys = &bridge[type].pipe[lo];
if (sys->valid) {
+ if (sys->ipa_facing)
+ ipa_disconnect(clnt_hdl);
sps_disconnect(sys->pipe);
- dma_free_coherent(NULL, sys->desc_mem_buf.size,
- sys->desc_mem_buf.base,
- sys->desc_mem_buf.phys_base);
sps_free_endpoint(sys->pipe);
- ipa_bridge_free_resources(sys);
sys->valid = false;
}
}
@@ -825,19 +575,3 @@
return 0;
}
EXPORT_SYMBOL(ipa_bridge_teardown);
-
-/**
- * ipa_bridge_cleanup() - destroy workqueues serving the SW bridges
- *
- * Return codes:
- * None
- */
-void ipa_bridge_cleanup(void)
-{
- int i;
-
- for (i = 0; i < IPA_BRIDGE_TYPE_MAX; i++) {
- destroy_workqueue(bridge[i].dl_wq);
- destroy_workqueue(bridge[i].ul_wq);
- }
-}
diff --git a/drivers/platform/msm/ipa/ipa_client.c b/drivers/platform/msm/ipa/ipa_client.c
index 6033510..5c343e8 100644
--- a/drivers/platform/msm/ipa/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_client.c
@@ -9,26 +9,28 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-
#include <linux/delay.h>
#include "ipa_i.h"
-#define IPA_HOLB_TMR_VAL 0xff
+/*
+ * These values were determined empirically and shows good E2E bi-
+ * directional throughputs
+ */
+#define IPA_A2_HOLB_TMR_EN 0x1
+#define IPA_A2_HOLB_TMR_DEFAULT_VAL 0x1ff
+#define IPA_WLAN_HOLB_TMR_EN 0x1
+#define IPA_WLAN_HOLB_TMR_DEFAULT_VAL 0x7f
static void ipa_enable_data_path(u32 clnt_hdl)
{
- struct ipa_ep_context *ep = &ipa_ctx->ep[clnt_hdl];
-
if (ipa_ctx->ipa_hw_mode == IPA_HW_MODE_VIRTUAL) {
/* IPA_HW_MODE_VIRTUAL lacks support for TAG IC & EP suspend */
return;
}
- if (ipa_ctx->ipa_hw_type == IPA_HW_v1_1 && ep->suspended) {
+ if (ipa_ctx->ipa_hw_type == IPA_HW_v1_1)
ipa_write_reg(ipa_ctx->mmio,
IPA_ENDP_INIT_CTRL_n_OFST(clnt_hdl), 0);
- ep->suspended = false;
- }
}
static int ipa_disable_data_path(u32 clnt_hdl)
@@ -52,7 +54,7 @@
goto fail_alloc;
}
- if (ipa_ctx->ipa_hw_type == IPA_HW_v1_1 && !ep->suspended) {
+ if (ipa_ctx->ipa_hw_type == IPA_HW_v1_1) {
ipa_write_reg(ipa_ctx->mmio,
IPA_ENDP_INIT_CTRL_n_OFST(clnt_hdl), 1);
@@ -87,7 +89,6 @@
ep->cfg.aggr.aggr_en == IPA_ENABLE_AGGR &&
ep->cfg.aggr.aggr_time_limit)
msleep(ep->cfg.aggr.aggr_time_limit);
- ep->suspended = true;
}
return 0;
@@ -182,6 +183,26 @@
return 0;
}
+static void ipa_program_holb(struct ipa_ep_context *ep, int ipa_ep_idx)
+{
+ struct ipa_ep_cfg_holb holb;
+
+ if (IPA_CLIENT_IS_PROD(ep->client))
+ return;
+
+ switch (ep->client) {
+ case IPA_CLIENT_A2_TETHERED_CONS:
+ case IPA_CLIENT_A2_EMBEDDED_CONS:
+ holb.en = IPA_A2_HOLB_TMR_EN;
+ holb.tmr_val = IPA_A2_HOLB_TMR_DEFAULT_VAL;
+ break;
+ default:
+ return;
+ }
+
+ ipa_cfg_ep_holb(ipa_ep_idx, &holb);
+}
+
/**
* ipa_connect() - low-level IPA client connect
* @in: [in] input parameters from client
@@ -297,19 +318,7 @@
memcpy(&sps->desc, &ep->connect.desc, sizeof(struct sps_mem_buffer));
memcpy(&sps->data, &ep->connect.data, sizeof(struct sps_mem_buffer));
- if (in->client == IPA_CLIENT_HSIC1_CONS ||
- in->client == IPA_CLIENT_HSIC2_CONS ||
- in->client == IPA_CLIENT_HSIC3_CONS ||
- in->client == IPA_CLIENT_HSIC4_CONS) {
- IPADBG("disable holb for ep=%d tmr=%d\n", ipa_ep_idx,
- IPA_HOLB_TMR_VAL);
- ipa_write_reg(ipa_ctx->mmio,
- IPA_ENDP_INIT_HOL_BLOCK_EN_n_OFST(ipa_ep_idx),
- 0x1);
- ipa_write_reg(ipa_ctx->mmio,
- IPA_ENDP_INIT_HOL_BLOCK_TIMER_n_OFST(ipa_ep_idx),
- IPA_HOLB_TMR_VAL);
- }
+ ipa_program_holb(ep, ipa_ep_idx);
IPADBG("client %d (ep: %d) connected\n", in->client, ipa_ep_idx);
@@ -369,6 +378,11 @@
ep = &ipa_ctx->ep[clnt_hdl];
+ if (ep->suspended) {
+ ipa_inc_client_enable_clks();
+ ep->suspended = false;
+ }
+
result = ipa_disable_data_path(clnt_hdl);
if (result) {
IPAERR("disable data path failed res=%d clnt=%d.\n", result,
@@ -424,55 +438,79 @@
EXPORT_SYMBOL(ipa_disconnect);
/**
- * ipa_connection_suspend() - suspend B2B connection to/from IPA
+ * ipa_resume() - low-level IPA client resume
* @clnt_hdl: [in] opaque client handle assigned by IPA to client
*
- * Should be called by the driver of the peripheral that wants to suspend
- * its BAM-BAM connection to/from IPA in BAM-BAM mode. The pipe is not
- * disconnected and must later be resumed before data transfer can begin
+ * Should be called by the driver of the peripheral that wants to resume IPA
+ * connection. Resume IPA connection results in turning on IPA clocks in
+ * case they were off as a result of suspend.
+ * this api can be called only if a call to ipa_suspend() was
+ * made.
*
* Returns: 0 on success, negative on failure
*
* Note: Should not be called from atomic context
*/
-int ipa_connection_suspend(u32 clnt_hdl)
+int ipa_resume(u32 clnt_hdl)
{
- int result;
+ struct ipa_ep_context *ep;
if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0) {
- IPAERR("bad parm.\n");
- return -EINVAL;
- }
- result = ipa_disable_data_path(clnt_hdl);
- if (result)
- IPAERR("disable data path failed res=%d clnt=%d.\n", result,
- clnt_hdl);
-
- return result;
-}
-EXPORT_SYMBOL(ipa_connection_suspend);
-
-/**
- * ipa_connection_resume() - resume B2B connection to/from IPA
- * @clnt_hdl: [in] opaque client handle assigned by IPA to client
- *
- * Should be called by the driver of the peripheral that wants to resume
- * its previously suspended BAM-BAM connection to/from IPA in BAM-BAM mode.
- *
- * Returns: 0 on success, negative on failure
- *
- * Note: Should not be called from atomic context
- */
-int ipa_connection_resume(u32 clnt_hdl)
-{
- if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0) {
- IPAERR("bad parm.\n");
+ IPAERR("bad parm. clnt_hdl %d\n", clnt_hdl);
return -EINVAL;
}
- ipa_enable_data_path(clnt_hdl);
+ ep = &ipa_ctx->ep[clnt_hdl];
+
+ if (!ep->suspended) {
+ IPAERR("EP not suspended. clnt_hdl %d\n", clnt_hdl);
+ return -EPERM;
+ }
+
+ ipa_inc_client_enable_clks();
+ ep->suspended = false;
return 0;
}
-EXPORT_SYMBOL(ipa_connection_resume);
+EXPORT_SYMBOL(ipa_resume);
+/**
+* ipa_suspend() - low-level IPA client suspend
+* @clnt_hdl: [in] opaque client handle assigned by IPA to client
+*
+* Should be called by the driver of the peripheral that wants to suspend IPA
+* connection. Suspend IPA connection results in turning off IPA clocks in
+* case that there is no active clients using IPA. Pipes remains connected in
+* case of suspend.
+*
+* Returns: 0 on success, negative on failure
+*
+* Note: Should not be called from atomic context
+*/
+int ipa_suspend(u32 clnt_hdl)
+{
+ struct ipa_ep_context *ep;
+
+ if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0) {
+ IPAERR("bad parm. clnt_hdl %d\n", clnt_hdl);
+ return -EINVAL;
+ }
+
+ ep = &ipa_ctx->ep[clnt_hdl];
+
+ if (ep->suspended) {
+ IPAERR("EP already suspended. clnt_hdl %d\n", clnt_hdl);
+ return -EPERM;
+ }
+
+ if (IPA_CLIENT_IS_CONS(ep->client) &&
+ ep->cfg.aggr.aggr_en == IPA_ENABLE_AGGR &&
+ ep->cfg.aggr.aggr_time_limit)
+ msleep(ep->cfg.aggr.aggr_time_limit);
+
+ ipa_dec_client_disable_clks();
+ ep->suspended = true;
+
+ return 0;
+}
+EXPORT_SYMBOL(ipa_suspend);
diff --git a/drivers/platform/msm/ipa/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_debugfs.c
index bc5aa6f..aaf5cc0 100644
--- a/drivers/platform/msm/ipa/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_debugfs.c
@@ -15,7 +15,7 @@
#include <linux/debugfs.h>
#include <linux/stringify.h>
#include "ipa_i.h"
-
+#include "ipa_rm_i.h"
#define IPA_MAX_MSG_LEN 4096
#define IPA_DBG_CNTR_ON 127265
@@ -94,6 +94,7 @@
static struct dentry *dent;
static struct dentry *dfile_gen_reg;
static struct dentry *dfile_ep_reg;
+static struct dentry *dfile_ep_holb;
static struct dentry *dfile_hdr;
static struct dentry *dfile_ip4_rt;
static struct dentry *dfile_ip6_rt;
@@ -102,6 +103,8 @@
static struct dentry *dfile_stats;
static struct dentry *dfile_dbg_cnt;
static struct dentry *dfile_msg;
+static struct dentry *dfile_ip4_nat;
+static struct dentry *dfile_rm_stats;
static char dbg_buff[IPA_MAX_MSG_LEN];
static s8 ep_reg_idx;
@@ -144,6 +147,53 @@
return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
}
+static ssize_t ipa_write_ep_holb(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct ipa_ep_cfg_holb holb;
+ u32 en;
+ u32 tmr_val;
+ u32 ep_idx;
+ unsigned long missing;
+ char *sptr, *token;
+
+ if (sizeof(dbg_buff) < count + 1)
+ return -EFAULT;
+
+ missing = copy_from_user(dbg_buff, buf, count);
+ if (missing)
+ return -EFAULT;
+
+ dbg_buff[count] = '\0';
+
+ sptr = dbg_buff;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &ep_idx))
+ return -EINVAL;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &en))
+ return -EINVAL;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &tmr_val))
+ return -EINVAL;
+
+ holb.en = en;
+ holb.tmr_val = tmr_val;
+
+ ipa_cfg_ep_holb(ep_idx, &holb);
+
+ return count;
+}
+
static ssize_t ipa_write_ep_reg(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
@@ -554,17 +604,31 @@
"rx=%u\n"
"rx_repl_repost=%u\n"
"x_intr_repost=%u\n"
+ "x_intr_repost_tx=%u\n"
"rx_q_len=%u\n"
"act_clnt=%u\n"
- "con_clnt_bmap=0x%x\n",
+ "con_clnt_bmap=0x%x\n"
+ "a2_power_on_reqs_in=%u\n"
+ "a2_power_on_reqs_out=%u\n"
+ "a2_power_off_reqs_in=%u\n"
+ "a2_power_off_reqs_out=%u\n"
+ "a2_power_modem_acks=%u\n"
+ "a2_power_apps_acks=%u\n",
ipa_ctx->stats.tx_sw_pkts,
ipa_ctx->stats.tx_hw_pkts,
ipa_ctx->stats.rx_pkts,
ipa_ctx->stats.rx_repl_repost,
ipa_ctx->stats.x_intr_repost,
+ ipa_ctx->stats.x_intr_repost_tx,
ipa_ctx->stats.rx_q_len,
ipa_ctx->ipa_active_clients,
- connect);
+ connect,
+ ipa_ctx->stats.a2_power_on_reqs_in,
+ ipa_ctx->stats.a2_power_on_reqs_out,
+ ipa_ctx->stats.a2_power_off_reqs_in,
+ ipa_ctx->stats.a2_power_off_reqs_out,
+ ipa_ctx->stats.a2_power_modem_acks,
+ ipa_ctx->stats.a2_power_apps_acks);
cnt += nbytes;
for (i = 0; i < MAX_NUM_EXCP; i++) {
@@ -654,6 +718,252 @@
return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
}
+static ssize_t ipa_read_nat4(struct file *file,
+ char __user *ubuf, size_t count,
+ loff_t *ppos) {
+
+#define ENTRY_U32_FIELDS 8
+#define NAT_ENTRY_ENABLE 0x8000
+#define NAT_ENTRY_RST_FIN_BIT 0x4000
+#define BASE_TABLE 0
+#define EXPANSION_TABLE 1
+
+ u32 *base_tbl, *indx_tbl;
+ u32 tbl_size, *tmp;
+ u32 value, i, j, rule_id;
+ u16 enable, tbl_entry, flag;
+ int nbytes, cnt;
+
+ cnt = 0;
+ value = ipa_ctx->nat_mem.public_ip_addr;
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "Table IP Address:%d.%d.%d.%d\n",
+ ((value & 0xFF000000) >> 24),
+ ((value & 0x00FF0000) >> 16),
+ ((value & 0x0000FF00) >> 8),
+ ((value & 0x000000FF)));
+ cnt += nbytes;
+
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "Table Size:%d\n",
+ ipa_ctx->nat_mem.size_base_tables);
+ cnt += nbytes;
+
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "Expansion Table Size:%d\n",
+ ipa_ctx->nat_mem.size_expansion_tables);
+ cnt += nbytes;
+
+ if (!ipa_ctx->nat_mem.is_sys_mem) {
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "Not supported for local(shared) memory\n");
+ cnt += nbytes;
+
+ return simple_read_from_buffer(ubuf, count,
+ ppos, dbg_buff, cnt);
+ }
+
+
+ /* Print Base tables */
+ rule_id = 0;
+ for (j = 0; j < 2; j++) {
+ if (j == BASE_TABLE) {
+ tbl_size = ipa_ctx->nat_mem.size_base_tables;
+ base_tbl = (u32 *)ipa_ctx->nat_mem.ipv4_rules_addr;
+
+ nbytes = scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN,
+ "\nBase Table:\n");
+ cnt += nbytes;
+ } else {
+ tbl_size = ipa_ctx->nat_mem.size_expansion_tables;
+ base_tbl =
+ (u32 *)ipa_ctx->nat_mem.ipv4_expansion_rules_addr;
+
+ nbytes = scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN,
+ "\nExpansion Base Table:\n");
+ cnt += nbytes;
+ }
+
+ if (base_tbl != NULL) {
+ for (i = 0; i <= tbl_size; i++, rule_id++) {
+ tmp = base_tbl;
+ value = tmp[4];
+ enable = ((value & 0xFFFF0000) >> 16);
+
+ if (enable & NAT_ENTRY_ENABLE) {
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "Rule:%d ",
+ rule_id);
+ cnt += nbytes;
+
+ value = *tmp;
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "Private_IP:%d.%d.%d.%d ",
+ ((value & 0xFF000000) >> 24),
+ ((value & 0x00FF0000) >> 16),
+ ((value & 0x0000FF00) >> 8),
+ ((value & 0x000000FF)));
+ cnt += nbytes;
+ tmp++;
+
+ value = *tmp;
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "Target_IP:%d.%d.%d.%d ",
+ ((value & 0xFF000000) >> 24),
+ ((value & 0x00FF0000) >> 16),
+ ((value & 0x0000FF00) >> 8),
+ ((value & 0x000000FF)));
+ cnt += nbytes;
+ tmp++;
+
+ value = *tmp;
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "Next_Index:%d Public_Port:%d ",
+ (value & 0x0000FFFF),
+ ((value & 0xFFFF0000) >> 16));
+ cnt += nbytes;
+ tmp++;
+
+ value = *tmp;
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "Private_Port:%d Target_Port:%d ",
+ (value & 0x0000FFFF),
+ ((value & 0xFFFF0000) >> 16));
+ cnt += nbytes;
+ tmp++;
+
+ value = *tmp;
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "IP-CKSM-delta:0x%x ",
+ (value & 0x0000FFFF));
+ cnt += nbytes;
+
+ flag = ((value & 0xFFFF0000) >> 16);
+ if (flag & NAT_ENTRY_RST_FIN_BIT) {
+ nbytes =
+ scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "IP_CKSM_delta:0x%x Flags:%s ",
+ (value & 0x0000FFFF),
+ "Direct_To_A5");
+ cnt += nbytes;
+ } else {
+ nbytes =
+ scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "IP_CKSM_delta:0x%x Flags:%s ",
+ (value & 0x0000FFFF),
+ "Fwd_to_route");
+ cnt += nbytes;
+ }
+ tmp++;
+
+ value = *tmp;
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "Time_stamp:0x%x Proto:%d ",
+ (value & 0x00FFFFFF),
+ ((value & 0xFF000000) >> 27));
+ cnt += nbytes;
+ tmp++;
+
+ value = *tmp;
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "Prev_Index:%d Indx_tbl_entry:%d ",
+ (value & 0x0000FFFF),
+ ((value & 0xFFFF0000) >> 16));
+ cnt += nbytes;
+ tmp++;
+
+ value = *tmp;
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "TCP_UDP_cksum_delta:0x%x\n",
+ ((value & 0xFFFF0000) >> 16));
+ cnt += nbytes;
+ }
+
+ base_tbl += ENTRY_U32_FIELDS;
+
+ }
+ }
+ }
+
+ /* Print Index tables */
+ rule_id = 0;
+ for (j = 0; j < 2; j++) {
+ if (j == BASE_TABLE) {
+ tbl_size = ipa_ctx->nat_mem.size_base_tables;
+ indx_tbl = (u32 *)ipa_ctx->nat_mem.index_table_addr;
+
+ nbytes = scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN,
+ "\nIndex Table:\n");
+ cnt += nbytes;
+ } else {
+ tbl_size = ipa_ctx->nat_mem.size_expansion_tables;
+ indx_tbl =
+ (u32 *)ipa_ctx->nat_mem.index_table_expansion_addr;
+
+ nbytes = scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN,
+ "\nExpansion Index Table:\n");
+ cnt += nbytes;
+ }
+
+ if (indx_tbl != NULL) {
+ for (i = 0; i <= tbl_size; i++, rule_id++) {
+ tmp = indx_tbl;
+ value = *tmp;
+ tbl_entry = (value & 0x0000FFFF);
+
+ if (tbl_entry) {
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "Rule:%d ",
+ rule_id);
+ cnt += nbytes;
+
+ value = *tmp;
+ nbytes = scnprintf(dbg_buff + cnt,
+ IPA_MAX_MSG_LEN,
+ "Table_Entry:%d Next_Index:%d\n",
+ tbl_entry,
+ ((value & 0xFFFF0000) >> 16));
+ cnt += nbytes;
+ }
+
+ indx_tbl++;
+ }
+ }
+ }
+
+ return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
+}
+
+static ssize_t ipa_rm_read_stats(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ int result, nbytes, cnt = 0;
+ result = ipa_rm_stat(dbg_buff, IPA_MAX_MSG_LEN);
+ if (result < 0) {
+ nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
+ "Error in printing RM stat %d\n", result);
+ cnt += nbytes;
+ } else
+ cnt += result;
+ return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
+}
+
const struct file_operations ipa_gen_reg_ops = {
.read = ipa_read_gen_reg,
};
@@ -663,6 +973,10 @@
.write = ipa_write_ep_reg,
};
+const struct file_operations ipa_ep_holb_ops = {
+ .write = ipa_write_ep_holb,
+};
+
const struct file_operations ipa_hdr_ops = {
.read = ipa_read_hdr,
};
@@ -690,11 +1004,20 @@
.write = ipa_write_dbg_cnt,
};
+const struct file_operations ipa_nat4_ops = {
+ .read = ipa_read_nat4,
+};
+
+const struct file_operations ipa_rm_stats = {
+ .read = ipa_rm_read_stats,
+};
+
void ipa_debugfs_init(void)
{
const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
const mode_t read_write_mode = S_IRUSR | S_IRGRP | S_IROTH |
S_IWUSR | S_IWGRP | S_IWOTH;
+ const mode_t write_only_mode = S_IWUSR | S_IWGRP | S_IWOTH;
dent = debugfs_create_dir("ipa", 0);
if (IS_ERR(dent)) {
@@ -716,6 +1039,13 @@
goto fail;
}
+ dfile_ep_holb = debugfs_create_file("holb", write_only_mode, dent,
+ 0, &ipa_ep_holb_ops);
+ if (!dfile_ep_holb || IS_ERR(dfile_ep_holb)) {
+ IPAERR("fail to create file for debug_fs dfile_ep_hol_en\n");
+ goto fail;
+ }
+
dfile_hdr = debugfs_create_file("hdr", read_only_mode, dent, 0,
&ipa_hdr_ops);
if (!dfile_hdr || IS_ERR(dfile_hdr)) {
@@ -772,6 +1102,20 @@
goto fail;
}
+ dfile_ip4_nat = debugfs_create_file("ip4_nat", read_only_mode, dent,
+ 0, &ipa_nat4_ops);
+ if (!dfile_ip4_nat || IS_ERR(dfile_ip4_nat)) {
+ IPAERR("fail to create file for debug_fs ip4 nat\n");
+ goto fail;
+ }
+
+ dfile_rm_stats = debugfs_create_file("rm_stats", read_only_mode,
+ dent, 0,
+ &ipa_rm_stats);
+ if (!dfile_rm_stats || IS_ERR(dfile_rm_stats)) {
+ IPAERR("fail to create file for debug_fs rm_stats\n");
+ goto fail;
+ }
return;
fail:
diff --git a/drivers/platform/msm/ipa/ipa_dp.c b/drivers/platform/msm/ipa/ipa_dp.c
index 228c77fe..38a1a9c 100644
--- a/drivers/platform/msm/ipa/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_dp.c
@@ -17,20 +17,21 @@
#include <linux/netdevice.h>
#include "ipa_i.h"
+
#define list_next_entry(pos, member) \
list_entry(pos->member.next, typeof(*pos), member)
#define IPA_LAST_DESC_CNT 0xFFFF
-#define POLLING_INACTIVITY 40
-#define POLLING_MIN_SLEEP 950
-#define POLLING_MAX_SLEEP 1050
+#define POLLING_INACTIVITY_RX 40
+#define POLLING_MIN_SLEEP_RX 950
+#define POLLING_MAX_SLEEP_RX 1050
+#define POLLING_INACTIVITY_TX 40
+#define POLLING_MIN_SLEEP_TX 400
+#define POLLING_MAX_SLEEP_TX 500
static void replenish_rx_work_func(struct work_struct *work);
static struct delayed_work replenish_rx_work;
-static void switch_to_intr_work_func(struct work_struct *work);
-static struct delayed_work switch_to_intr_work;
static void ipa_wq_handle_rx(struct work_struct *work);
static DECLARE_WORK(rx_work, ipa_wq_handle_rx);
-
/**
* ipa_write_done() - this function will be (eventually) called when a Tx
* operation is complete
@@ -47,57 +48,166 @@
void ipa_wq_write_done(struct work_struct *work)
{
struct ipa_tx_pkt_wrapper *tx_pkt;
- struct ipa_tx_pkt_wrapper *next_pkt;
struct ipa_tx_pkt_wrapper *tx_pkt_expected;
unsigned long irq_flags;
- struct ipa_mem_buffer mult = { 0 };
- int i;
- u32 cnt;
tx_pkt = container_of(work, struct ipa_tx_pkt_wrapper, work);
- cnt = tx_pkt->cnt;
- IPADBG("cnt=%d\n", cnt);
- if (unlikely(cnt == 0))
+ if (unlikely(tx_pkt == NULL))
WARN_ON(1);
+ WARN_ON(tx_pkt->cnt != 1);
- if (cnt > 1 && cnt != IPA_LAST_DESC_CNT)
- mult = tx_pkt->mult;
+ spin_lock_irqsave(&tx_pkt->sys->spinlock, irq_flags);
+ tx_pkt_expected = list_first_entry(&tx_pkt->sys->head_desc_list,
+ struct ipa_tx_pkt_wrapper,
+ link);
+ if (unlikely(tx_pkt != tx_pkt_expected)) {
+ spin_unlock_irqrestore(&tx_pkt->sys->spinlock,
+ irq_flags);
+ WARN_ON(1);
+ }
+ list_del(&tx_pkt->link);
+ spin_unlock_irqrestore(&tx_pkt->sys->spinlock, irq_flags);
+ if (unlikely(ipa_ctx->ipa_hw_type == IPA_HW_v1_0)) {
+ dma_pool_free(ipa_ctx->dma_pool,
+ tx_pkt->bounce,
+ tx_pkt->mem.phys_base);
+ } else {
+ dma_unmap_single(NULL, tx_pkt->mem.phys_base,
+ tx_pkt->mem.size,
+ DMA_TO_DEVICE);
+ }
- for (i = 0; i < cnt; i++) {
- if (unlikely(tx_pkt == NULL))
- WARN_ON(1);
- spin_lock_irqsave(&tx_pkt->sys->spinlock, irq_flags);
- tx_pkt_expected = list_first_entry(&tx_pkt->sys->head_desc_list,
- struct ipa_tx_pkt_wrapper,
- link);
- if (unlikely(tx_pkt != tx_pkt_expected)) {
- spin_unlock_irqrestore(&tx_pkt->sys->spinlock,
- irq_flags);
- WARN_ON(1);
+ if (tx_pkt->callback)
+ tx_pkt->callback(tx_pkt->user1, tx_pkt->user2);
+
+ kmem_cache_free(ipa_ctx->tx_pkt_wrapper_cache, tx_pkt);
+}
+
+int ipa_handle_tx_core(struct ipa_sys_context *sys, bool process_all,
+ bool in_poll_state)
+{
+ struct ipa_tx_pkt_wrapper *tx_pkt;
+ struct sps_iovec iov;
+ int ret;
+ int cnt = 0;
+ unsigned long irq_flags;
+
+ while ((in_poll_state ? atomic_read(&sys->curr_polling_state) :
+ !atomic_read(&sys->curr_polling_state))) {
+ if (cnt && !process_all)
+ break;
+ ret = sps_get_iovec(sys->ep->ep_hdl, &iov);
+ if (ret) {
+ IPAERR("sps_get_iovec failed %d\n", ret);
+ break;
}
- next_pkt = list_next_entry(tx_pkt, link);
+
+ if (iov.addr == 0)
+ break;
+
+ if (unlikely(list_empty(&sys->head_desc_list)))
+ continue;
+
+ spin_lock_irqsave(&sys->spinlock, irq_flags);
+ tx_pkt = list_first_entry(&sys->head_desc_list,
+ struct ipa_tx_pkt_wrapper, link);
+
+ sys->len--;
list_del(&tx_pkt->link);
- spin_unlock_irqrestore(&tx_pkt->sys->spinlock, irq_flags);
- if (unlikely(ipa_ctx->ipa_hw_type == IPA_HW_v1_0)) {
+ spin_unlock_irqrestore(&sys->spinlock, irq_flags);
+
+ IPADBG("--curr_cnt=%d\n", sys->len);
+
+ if (unlikely(ipa_ctx->ipa_hw_type == IPA_HW_v1_0))
dma_pool_free(ipa_ctx->dma_pool,
tx_pkt->bounce,
tx_pkt->mem.phys_base);
- } else {
+ else
dma_unmap_single(NULL, tx_pkt->mem.phys_base,
tx_pkt->mem.size,
DMA_TO_DEVICE);
- }
if (tx_pkt->callback)
tx_pkt->callback(tx_pkt->user1, tx_pkt->user2);
+ if (tx_pkt->cnt > 1 && tx_pkt->cnt != IPA_LAST_DESC_CNT)
+ dma_pool_free(ipa_ctx->dma_pool, tx_pkt->mult.base,
+ tx_pkt->mult.phys_base);
+
kmem_cache_free(ipa_ctx->tx_pkt_wrapper_cache, tx_pkt);
- tx_pkt = next_pkt;
+ cnt++;
+ };
+
+ return cnt;
+}
+
+/**
+ * ipa_tx_switch_to_intr_mode() - Operate the Tx data path in interrupt mode
+ */
+static void ipa_tx_switch_to_intr_mode(struct ipa_sys_context *sys)
+{
+ int ret;
+
+ if (!atomic_read(&sys->curr_polling_state)) {
+ IPAERR("already in intr mode\n");
+ goto fail;
}
- if (mult.phys_base)
- dma_pool_free(ipa_ctx->dma_pool, mult.base, mult.phys_base);
+ ret = sps_get_config(sys->ep->ep_hdl, &sys->ep->connect);
+ if (ret) {
+ IPAERR("sps_get_config() failed %d\n", ret);
+ goto fail;
+ }
+ sys->event.options = SPS_O_EOT;
+ ret = sps_register_event(sys->ep->ep_hdl, &sys->event);
+ if (ret) {
+ IPAERR("sps_register_event() failed %d\n", ret);
+ goto fail;
+ }
+ sys->ep->connect.options =
+ SPS_O_AUTO_ENABLE | SPS_O_ACK_TRANSFERS | SPS_O_EOT;
+ ret = sps_set_config(sys->ep->ep_hdl, &sys->ep->connect);
+ if (ret) {
+ IPAERR("sps_set_config() failed %d\n", ret);
+ goto fail;
+ }
+ atomic_set(&sys->curr_polling_state, 0);
+ ipa_handle_tx_core(sys, true, false);
+ return;
+
+fail:
+ IPA_STATS_INC_CNT(ipa_ctx->stats.x_intr_repost_tx);
+ schedule_delayed_work(&sys->switch_to_intr_work, msecs_to_jiffies(1));
+ return;
+}
+
+static void ipa_handle_tx(struct ipa_sys_context *sys)
+{
+ int inactive_cycles = 0;
+ int cnt;
+
+ ipa_inc_client_enable_clks();
+ do {
+ cnt = ipa_handle_tx_core(sys, true, true);
+ if (cnt == 0) {
+ inactive_cycles++;
+ usleep_range(POLLING_MIN_SLEEP_TX,
+ POLLING_MAX_SLEEP_TX);
+ } else {
+ inactive_cycles = 0;
+ }
+ } while (inactive_cycles <= POLLING_INACTIVITY_TX);
+
+ ipa_tx_switch_to_intr_mode(sys);
+ ipa_dec_client_disable_clks();
+}
+
+static void ipa_wq_handle_tx(struct work_struct *work)
+{
+ struct ipa_tx_pkt_wrapper *tx_pkt;
+ tx_pkt = container_of(work, struct ipa_tx_pkt_wrapper, work);
+ ipa_handle_tx(tx_pkt->sys);
}
/**
@@ -122,7 +232,7 @@
struct ipa_tx_pkt_wrapper *tx_pkt;
unsigned long irq_flags;
int result;
- u16 sps_flags = SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_INT;
+ u16 sps_flags = SPS_IOVEC_FLAG_EOT;
dma_addr_t dma_address;
u16 len;
u32 mem_flag = GFP_ATOMIC;
@@ -165,7 +275,6 @@
}
INIT_LIST_HEAD(&tx_pkt->link);
- INIT_WORK(&tx_pkt->work, ipa_wq_write_done);
tx_pkt->type = desc->type;
tx_pkt->cnt = 1; /* only 1 desc in this "set" */
@@ -187,10 +296,15 @@
IPADBG("sending cmd=%d pyld_len=%d sps_flags=%x\n",
desc->opcode, desc->len, sps_flags);
IPA_DUMP_BUFF(desc->pyld, dma_address, desc->len);
+ INIT_WORK(&tx_pkt->work, ipa_wq_write_done);
} else {
len = desc->len;
+ INIT_WORK(&tx_pkt->work, ipa_wq_handle_tx);
}
+ if (unlikely(ipa_ctx->polling_mode))
+ INIT_WORK(&tx_pkt->work, ipa_wq_handle_tx);
+
spin_lock_irqsave(&sys->spinlock, irq_flags);
list_add_tail(&tx_pkt->link, &sys->head_desc_list);
result = sps_transfer_one(sys->ep->ep_hdl, dma_address, len, tx_pkt,
@@ -286,8 +400,8 @@
tx_pkt->mult.base = transfer.iovec;
tx_pkt->mult.size = size;
tx_pkt->cnt = num_desc;
- INIT_WORK(&tx_pkt->work, ipa_wq_write_done);
}
+ INIT_WORK(&tx_pkt->work, ipa_wq_handle_tx);
iovec = &transfer.iovec[i];
iovec->flags = 0;
@@ -355,8 +469,7 @@
}
if (i == (num_desc - 1)) {
- iovec->flags |= (SPS_IOVEC_FLAG_EOT |
- SPS_IOVEC_FLAG_INT);
+ iovec->flags |= SPS_IOVEC_FLAG_EOT;
/* "mark" the last desc */
tx_pkt->cnt = IPA_LAST_DESC_CNT;
}
@@ -475,6 +588,49 @@
/**
* ipa_sps_irq_tx_notify() - Callback function which will be called by
+ * the SPS driver to start a Tx poll operation.
+ * Called in an interrupt context.
+ * @notify: SPS driver supplied notification struct
+ *
+ * This function defer the work for this event to the tx workqueue.
+ */
+static void ipa_sps_irq_tx_notify(struct sps_event_notify *notify)
+{
+ struct ipa_sys_context *sys = &ipa_ctx->sys[IPA_A5_LAN_WAN_OUT];
+ struct ipa_tx_pkt_wrapper *tx_pkt;
+ int ret;
+
+ IPADBG("event %d notified\n", notify->event_id);
+
+ switch (notify->event_id) {
+ case SPS_EVENT_EOT:
+ tx_pkt = notify->data.transfer.user;
+ if (!atomic_read(&sys->curr_polling_state)) {
+ ret = sps_get_config(sys->ep->ep_hdl,
+ &sys->ep->connect);
+ if (ret) {
+ IPAERR("sps_get_config() failed %d\n", ret);
+ break;
+ }
+ sys->ep->connect.options = SPS_O_AUTO_ENABLE |
+ SPS_O_ACK_TRANSFERS | SPS_O_POLL;
+ ret = sps_set_config(sys->ep->ep_hdl,
+ &sys->ep->connect);
+ if (ret) {
+ IPAERR("sps_set_config() failed %d\n", ret);
+ break;
+ }
+ atomic_set(&sys->curr_polling_state, 1);
+ queue_work(ipa_ctx->tx_wq, &tx_pkt->work);
+ }
+ break;
+ default:
+ IPAERR("recieved unexpected event id %d\n", notify->event_id);
+ }
+}
+
+/**
+ * ipa_sps_irq_tx_no_aggr_notify() - Callback function which will be called by
* the SPS driver after a Tx operation is complete.
* Called in an interrupt context.
* @notify: SPS driver supplied notification struct
@@ -482,7 +638,7 @@
* This function defer the work for this event to the tx workqueue.
* This event will be later handled by ipa_write_done.
*/
-static void ipa_sps_irq_tx_notify(struct sps_event_notify *notify)
+static void ipa_sps_irq_tx_no_aggr_notify(struct sps_event_notify *notify)
{
struct ipa_tx_pkt_wrapper *tx_pkt;
@@ -512,7 +668,8 @@
* - Call the endpoints notify function, passing the skb in the parameters
* - Replenish the rx cache
*/
-int ipa_handle_rx_core(bool process_all, bool in_poll_state)
+int ipa_handle_rx_core(struct ipa_sys_context *sys, bool process_all,
+ bool in_poll_state)
{
struct ipa_a5_mux_hdr *mux_hdr;
struct ipa_rx_pkt_wrapper *rx_pkt;
@@ -521,15 +678,14 @@
unsigned int pull_len;
unsigned int padding;
int ret;
- struct ipa_sys_context *sys = &ipa_ctx->sys[IPA_A5_LAN_WAN_IN];
struct ipa_ep_context *ep;
int cnt = 0;
struct completion *compl;
struct ipa_tree_node *node;
unsigned int src_pipe;
- while ((in_poll_state ? atomic_read(&ipa_ctx->curr_polling_state) :
- !atomic_read(&ipa_ctx->curr_polling_state))) {
+ while ((in_poll_state ? atomic_read(&sys->curr_polling_state) :
+ !atomic_read(&sys->curr_polling_state))) {
if (cnt && !process_all)
break;
@@ -654,19 +810,15 @@
/**
* ipa_rx_switch_to_intr_mode() - Operate the Rx data path in interrupt mode
*/
-static void ipa_rx_switch_to_intr_mode(void)
+static void ipa_rx_switch_to_intr_mode(struct ipa_sys_context *sys)
{
int ret;
- struct ipa_sys_context *sys;
- IPADBG("Enter");
- if (!atomic_read(&ipa_ctx->curr_polling_state)) {
+ if (!atomic_read(&sys->curr_polling_state)) {
IPAERR("already in intr mode\n");
goto fail;
}
- sys = &ipa_ctx->sys[IPA_A5_LAN_WAN_IN];
-
ret = sps_get_config(sys->ep->ep_hdl, &sys->ep->connect);
if (ret) {
IPAERR("sps_get_config() failed %d\n", ret);
@@ -685,15 +837,16 @@
IPAERR("sps_set_config() failed %d\n", ret);
goto fail;
}
- atomic_set(&ipa_ctx->curr_polling_state, 0);
- ipa_handle_rx_core(true, false);
+ atomic_set(&sys->curr_polling_state, 0);
+ ipa_handle_rx_core(sys, true, false);
return;
fail:
IPA_STATS_INC_CNT(ipa_ctx->stats.x_intr_repost);
- schedule_delayed_work(&switch_to_intr_work, msecs_to_jiffies(1));
+ schedule_delayed_work(&sys->switch_to_intr_work, msecs_to_jiffies(1));
}
+
/**
* ipa_rx_notify() - Callback function which is called by the SPS driver when a
* a packet is received
@@ -709,29 +862,29 @@
*/
static void ipa_sps_irq_rx_notify(struct sps_event_notify *notify)
{
- struct ipa_ep_context *ep;
+ struct ipa_sys_context *sys = &ipa_ctx->sys[IPA_A5_LAN_WAN_IN];
int ret;
IPADBG("event %d notified\n", notify->event_id);
switch (notify->event_id) {
case SPS_EVENT_EOT:
- if (!atomic_read(&ipa_ctx->curr_polling_state)) {
- ep = ipa_ctx->sys[IPA_A5_LAN_WAN_IN].ep;
-
- ret = sps_get_config(ep->ep_hdl, &ep->connect);
+ if (!atomic_read(&sys->curr_polling_state)) {
+ ret = sps_get_config(sys->ep->ep_hdl,
+ &sys->ep->connect);
if (ret) {
IPAERR("sps_get_config() failed %d\n", ret);
break;
}
- ep->connect.options = SPS_O_AUTO_ENABLE |
+ sys->ep->connect.options = SPS_O_AUTO_ENABLE |
SPS_O_ACK_TRANSFERS | SPS_O_POLL;
- ret = sps_set_config(ep->ep_hdl, &ep->connect);
+ ret = sps_set_config(sys->ep->ep_hdl,
+ &sys->ep->connect);
if (ret) {
IPAERR("sps_set_config() failed %d\n", ret);
break;
}
- atomic_set(&ipa_ctx->curr_polling_state, 1);
+ atomic_set(&sys->curr_polling_state, 1);
queue_work(ipa_ctx->rx_wq, &rx_work);
}
break;
@@ -740,6 +893,53 @@
}
}
+static void switch_to_intr_tx_work_func(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct ipa_sys_context *sys;
+ dwork = container_of(work, struct delayed_work, work);
+ sys = container_of(dwork, struct ipa_sys_context, switch_to_intr_work);
+ ipa_handle_tx(sys);
+}
+
+/**
+ * ipa_handle_rx() - handle packet reception. This function is executed in the
+ * context of a work queue.
+ * @work: work struct needed by the work queue
+ *
+ * ipa_handle_rx_core() is run in polling mode. After all packets has been
+ * received, the driver switches back to interrupt mode.
+ */
+static void ipa_handle_rx(struct ipa_sys_context *sys)
+{
+ int inactive_cycles = 0;
+ int cnt;
+
+ ipa_inc_client_enable_clks();
+ do {
+ cnt = ipa_handle_rx_core(sys, true, true);
+ if (cnt == 0) {
+ inactive_cycles++;
+ usleep_range(POLLING_MIN_SLEEP_RX,
+ POLLING_MAX_SLEEP_RX);
+ } else {
+ inactive_cycles = 0;
+ }
+ } while (inactive_cycles <= POLLING_INACTIVITY_RX);
+
+ ipa_rx_switch_to_intr_mode(sys);
+ ipa_dec_client_disable_clks();
+}
+
+static void switch_to_intr_rx_work_func(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct ipa_sys_context *sys;
+ dwork = container_of(work, struct delayed_work, work);
+ sys = container_of(dwork, struct ipa_sys_context, switch_to_intr_work);
+ ipa_handle_rx(sys);
+}
+
/**
* ipa_setup_sys_pipe() - Setup an IPA end-point in system-BAM mode and perform
* IPA EP configuration
@@ -817,11 +1017,8 @@
ipa_ctx->ep[ipa_ep_idx].connect.dest_pipe_index =
ipa_ctx->a5_pipe_index++;
ipa_ctx->ep[ipa_ep_idx].connect.src_pipe_index = ipa_ep_idx;
- ipa_ctx->ep[ipa_ep_idx].connect.options =
- SPS_O_AUTO_ENABLE | SPS_O_EOT | SPS_O_ACK_TRANSFERS |
+ ipa_ctx->ep[ipa_ep_idx].connect.options = SPS_O_ACK_TRANSFERS |
SPS_O_NO_DISABLE;
- if (ipa_ctx->polling_mode)
- ipa_ctx->ep[ipa_ep_idx].connect.options |= SPS_O_POLL;
} else {
ipa_ctx->ep[ipa_ep_idx].connect.mode = SPS_MODE_DEST;
ipa_ctx->ep[ipa_ep_idx].connect.source = SPS_DEV_HANDLE_MEM;
@@ -830,13 +1027,16 @@
ipa_ctx->ep[ipa_ep_idx].connect.src_pipe_index =
ipa_ctx->a5_pipe_index++;
ipa_ctx->ep[ipa_ep_idx].connect.dest_pipe_index = ipa_ep_idx;
- ipa_ctx->ep[ipa_ep_idx].connect.options =
- SPS_O_AUTO_ENABLE | SPS_O_EOT;
- if (ipa_ctx->polling_mode)
+ if (sys_in->client == IPA_CLIENT_A5_LAN_WAN_PROD)
ipa_ctx->ep[ipa_ep_idx].connect.options |=
- SPS_O_ACK_TRANSFERS | SPS_O_POLL;
+ SPS_O_ACK_TRANSFERS;
}
+ ipa_ctx->ep[ipa_ep_idx].connect.options |= (SPS_O_AUTO_ENABLE |
+ SPS_O_EOT);
+ if (ipa_ctx->polling_mode)
+ ipa_ctx->ep[ipa_ep_idx].connect.options |= SPS_O_POLL;
+
ipa_ctx->ep[ipa_ep_idx].connect.desc.size = sys_in->desc_fifo_sz;
ipa_ctx->ep[ipa_ep_idx].connect.desc.base =
dma_alloc_coherent(NULL, ipa_ctx->ep[ipa_ep_idx].connect.desc.size,
@@ -858,14 +1058,18 @@
switch (ipa_ep_idx) {
case 1:
- /* fall through */
+ sys_idx = ipa_ep_idx;
+ break;
case 2:
- /* fall through */
+ sys_idx = ipa_ep_idx;
+ INIT_DELAYED_WORK(&ipa_ctx->sys[sys_idx].switch_to_intr_work,
+ switch_to_intr_tx_work_func);
+ break;
case 3:
sys_idx = ipa_ep_idx;
INIT_DELAYED_WORK(&replenish_rx_work, replenish_rx_work_func);
- INIT_DELAYED_WORK(&switch_to_intr_work,
- switch_to_intr_work_func);
+ INIT_DELAYED_WORK(&ipa_ctx->sys[sys_idx].switch_to_intr_work,
+ switch_to_intr_rx_work_func);
break;
case WLAN_AMPDU_TX_EP:
sys_idx = IPA_A5_WLAN_AMPDU_OUT;
@@ -886,7 +1090,10 @@
ipa_ctx->sys[sys_idx].event.callback =
IPA_CLIENT_IS_CONS(sys_in->client) ?
ipa_sps_irq_rx_notify :
- ipa_sps_irq_tx_notify;
+ (sys_in->client ==
+ IPA_CLIENT_A5_LAN_WAN_PROD ?
+ ipa_sps_irq_tx_notify :
+ ipa_sps_irq_tx_no_aggr_notify);
result = sps_register_event(ipa_ctx->ep[ipa_ep_idx].ep_hdl,
&ipa_ctx->sys[sys_idx].event);
if (result < 0) {
@@ -1078,37 +1285,9 @@
}
EXPORT_SYMBOL(ipa_tx_dp);
-static void ipa_handle_rx(void)
-{
- int inactive_cycles = 0;
- int cnt;
-
- ipa_inc_client_enable_clks();
- do {
- cnt = ipa_handle_rx_core(true, true);
- if (cnt == 0) {
- inactive_cycles++;
- usleep_range(POLLING_MIN_SLEEP, POLLING_MAX_SLEEP);
- } else {
- inactive_cycles = 0;
- }
- } while (inactive_cycles <= POLLING_INACTIVITY);
-
- ipa_rx_switch_to_intr_mode();
- ipa_dec_client_disable_clks();
-}
-
-/**
- * ipa_handle_rx() - handle packet reception. This function is executed in the
- * context of a work queue.
- * @work: work struct needed by the work queue
- *
- * ipa_handle_rx_core() is run in polling mode. After all packets has been
- * received, the driver switches back to interrupt mode.
- */
static void ipa_wq_handle_rx(struct work_struct *work)
{
- ipa_handle_rx();
+ ipa_handle_rx(&ipa_ctx->sys[IPA_A5_LAN_WAN_IN]);
}
/**
@@ -1166,7 +1345,7 @@
ret = sps_transfer_one(sys->ep->ep_hdl, rx_pkt->dma_address,
IPA_RX_SKB_SIZE, rx_pkt,
- SPS_IOVEC_FLAG_INT);
+ 0);
if (ret) {
IPAERR("sps_transfer_one failed %d\n", ret);
@@ -1202,11 +1381,6 @@
ipa_replenish_rx_cache();
}
-static void switch_to_intr_work_func(struct work_struct *work)
-{
- ipa_handle_rx();
-}
-
/**
* ipa_cleanup_rx() - release RX queue resources
*
diff --git a/drivers/platform/msm/ipa/ipa_flt.c b/drivers/platform/msm/ipa/ipa_flt.c
index b63b939..edb9fb1 100644
--- a/drivers/platform/msm/ipa/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_flt.c
@@ -368,6 +368,7 @@
return 0;
proc_err:
dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
+ mem->base = NULL;
error:
return -EPERM;
@@ -456,7 +457,7 @@
if (mem->size > avail) {
IPAERR("tbl too big, needed %d avail %d\n", mem->size, avail);
- goto fail_hw_tbl_gen;
+ goto fail_send_cmd;
}
if (ip == IPA_IP_v4) {
diff --git a/drivers/platform/msm/ipa/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_hdr.c
index 7d0bc24..9618da2 100644
--- a/drivers/platform/msm/ipa/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_hdr.c
@@ -89,7 +89,7 @@
if (ipa_ctx->hdr_tbl_lcl && mem->size > IPA_RAM_HDR_SIZE) {
IPAERR("tbl too big, needed %d avail %d\n", mem->size,
IPA_RAM_HDR_SIZE);
- goto fail_hw_tbl_gen;
+ goto fail_send_cmd;
}
cmd->hdr_table_addr = mem->phys_base;
@@ -126,7 +126,7 @@
return 0;
fail_send_cmd:
- if (mem->phys_base)
+ if (mem->base)
dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
fail_hw_tbl_gen:
kfree(cmd);
diff --git a/drivers/platform/msm/ipa/ipa_i.h b/drivers/platform/msm/ipa/ipa_i.h
index a03ba16..8ad0b5a 100644
--- a/drivers/platform/msm/ipa/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_i.h
@@ -30,7 +30,8 @@
#define IPA_COOKIE 0xfacefeed
#define IPA_NUM_PIPES 0x14
-#define IPA_SYS_DESC_FIFO_SZ (0x800)
+#define IPA_SYS_DESC_FIFO_SZ 0x800
+#define IPA_SYS_TX_DATA_DESC_FIFO_SZ 0x1000
#ifdef IPA_DEBUG
#define IPADBG(fmt, args...) \
@@ -40,10 +41,11 @@
#define IPADBG(fmt, args...)
#endif
-#define WLAN_AMPDU_TX_EP (15)
-#define WLAN_PROD_TX_EP (19)
-#define MAX_NUM_EXCP (8)
-#define MAX_NUM_IMM_CMD (17)
+#define WLAN_AMPDU_TX_EP 15
+#define WLAN_PROD_TX_EP 19
+
+#define MAX_NUM_EXCP 8
+#define MAX_NUM_IMM_CMD 17
#define IPA_STATS
@@ -117,7 +119,6 @@
#define IPA_DFLT_HDR_NAME "ipa_excp_hdr"
#define IPA_INVALID_L4_PROTOCOL 0xFF
-
#define IPA_CLIENT_IS_PROD(x) (x >= IPA_CLIENT_PROD && x < IPA_CLIENT_CONS)
#define IPA_CLIENT_IS_CONS(x) (x >= IPA_CLIENT_CONS && x < IPA_CLIENT_MAX)
#define IPA_SETFIELD(val, shift, mask) (((val) << (shift)) & (mask))
@@ -348,6 +349,7 @@
enum ipa_client_type client;
struct sps_pipe *ep_hdl;
struct ipa_ep_cfg cfg;
+ struct ipa_ep_cfg_holb holb;
u32 dst_pipe_index;
u32 rt_tbl_idx;
struct sps_connect connect;
@@ -370,7 +372,6 @@
* @spinlock: protects the list and its size
* @event: used to request CALLBACK mode from SPS driver
* @ep: IPA EP context
- * @wait_desc_list: used to hold completed Tx packets
*
* IPA context specific to the system-bam pipes a.k.a LAN IN/OUT and WAN
*/
@@ -380,7 +381,8 @@
spinlock_t spinlock;
struct sps_register_event event;
struct ipa_ep_context *ep;
- struct list_head wait_desc_list;
+ atomic_t curr_polling_state;
+ struct delayed_work switch_to_intr_work;
};
/**
@@ -477,6 +479,14 @@
* @is_sys_mem: flag indicating if NAT memory is sys memory
* @is_dev_init: flag indicating if NAT device is initialized
* @lock: NAT memory mutex
+ * @nat_base_address: nat table virutal address
+ * @ipv4_rules_addr: base nat table address
+ * @ipv4_expansion_rules_addr: expansion table address
+ * @index_table_addr: index table address
+ * @index_table_expansion_addr: index expansion table address
+ * @size_base_tables: base table size
+ * @size_expansion_tables: expansion table size
+ * @public_ip_addr: ip address of nat table
*/
struct ipa_nat_mem {
struct class *class;
@@ -490,6 +500,14 @@
bool is_sys_mem;
bool is_dev_init;
struct mutex lock;
+ void *nat_base_address;
+ char *ipv4_rules_addr;
+ char *ipv4_expansion_rules_addr;
+ char *index_table_addr;
+ char *index_table_expansion_addr;
+ u32 size_base_tables;
+ u32 size_expansion_tables;
+ u32 public_ip_addr;
};
/**
@@ -528,9 +546,16 @@
u32 bridged_pkts[IPA_BRIDGE_TYPE_MAX][IPA_BRIDGE_DIR_MAX];
u32 rx_repl_repost;
u32 x_intr_repost;
+ u32 x_intr_repost_tx;
u32 rx_q_len;
u32 msg_w[IPA_EVENT_MAX];
u32 msg_r[IPA_EVENT_MAX];
+ u32 a2_power_on_reqs_in;
+ u32 a2_power_on_reqs_out;
+ u32 a2_power_off_reqs_in;
+ u32 a2_power_off_reqs_out;
+ u32 a2_power_modem_acks;
+ u32 a2_power_apps_acks;
};
/**
@@ -633,7 +658,6 @@
uint aggregation_type;
uint aggregation_byte_limit;
uint aggregation_time_limit;
- atomic_t curr_polling_state;
struct delayed_work poll_work;
bool hdr_tbl_lcl;
struct ipa_mem_buffer hdr_mem;
@@ -659,6 +683,7 @@
enum ipa_hw_mode ipa_hw_mode;
/* featurize if memory footprint becomes a concern */
struct ipa_stats stats;
+ void *smem_pipe_mem;
};
/**
@@ -743,7 +768,7 @@
struct a2_mux_pipe_connection *pipe_connect);
int ipa_get_a2_mux_bam_info(u32 *a2_bam_mem_base, u32 *a2_bam_mem_size,
u32 *a2_bam_irq);
-void rmnet_bridge_get_client_handles(u32 *producer_handle,
+void teth_bridge_get_client_handles(u32 *producer_handle,
u32 *consumer_handle);
int ipa_send_one(struct ipa_sys_context *sys, struct ipa_desc *desc,
bool in_atomic);
@@ -788,7 +813,10 @@
void ipa_cleanup_rx(void);
int ipa_cfg_filter(u32 disable);
void ipa_wq_write_done(struct work_struct *work);
-int ipa_handle_rx_core(bool process_all, bool in_poll_state);
+int ipa_handle_rx_core(struct ipa_sys_context *sys, bool process_all,
+ bool in_poll_state);
+int ipa_handle_tx_core(struct ipa_sys_context *sys, bool process_all,
+ bool in_poll_state);
int ipa_pipe_mem_init(u32 start_ofst, u32 size);
int ipa_pipe_mem_alloc(u32 *ofst, u32 size);
int ipa_pipe_mem_free(u32 ofst, u32 size);
diff --git a/drivers/platform/msm/ipa/ipa_nat.c b/drivers/platform/msm/ipa/ipa_nat.c
index befa2cf..e2c344f 100644
--- a/drivers/platform/msm/ipa/ipa_nat.c
+++ b/drivers/platform/msm/ipa/ipa_nat.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -75,6 +75,7 @@
IPAERR("unable to map memory. Err:%d\n", result);
goto bail;
}
+ ipa_ctx->nat_mem.nat_base_address = nat_ctx->vaddr;
} else {
IPADBG("Mapping shared(local) memory\n");
IPADBG("map sz=0x%lx\n", vsize);
@@ -88,7 +89,7 @@
result = -EAGAIN;
goto bail;
}
-
+ ipa_ctx->nat_mem.nat_base_address = (void *)vma->vm_start;
}
nat_ctx->is_mapped = true;
vma->vm_ops = &ipa_nat_remap_vm_ops;
@@ -299,6 +300,35 @@
goto free_cmd;
}
+ ipa_ctx->nat_mem.public_ip_addr = init->ip_addr;
+ IPADBG("Table ip address:0x%x", ipa_ctx->nat_mem.public_ip_addr);
+
+ ipa_ctx->nat_mem.ipv4_rules_addr =
+ (char *)ipa_ctx->nat_mem.nat_base_address + init->ipv4_rules_offset;
+ IPADBG("ipv4_rules_addr: 0x%p\n",
+ ipa_ctx->nat_mem.ipv4_rules_addr);
+
+ ipa_ctx->nat_mem.ipv4_expansion_rules_addr =
+ (char *)ipa_ctx->nat_mem.nat_base_address + init->expn_rules_offset;
+ IPADBG("ipv4_expansion_rules_addr: 0x%p\n",
+ ipa_ctx->nat_mem.ipv4_expansion_rules_addr);
+
+ ipa_ctx->nat_mem.index_table_addr =
+ (char *)ipa_ctx->nat_mem.nat_base_address + init->index_offset;
+ IPADBG("index_table_addr: 0x%p\n",
+ ipa_ctx->nat_mem.index_table_addr);
+
+ ipa_ctx->nat_mem.index_table_expansion_addr =
+ (char *)ipa_ctx->nat_mem.nat_base_address + init->index_expn_offset;
+ IPADBG("index_table_expansion_addr: 0x%p\n",
+ ipa_ctx->nat_mem.index_table_expansion_addr);
+
+ IPADBG("size_base_tables: %d\n", init->table_entries);
+ ipa_ctx->nat_mem.size_base_tables = init->table_entries;
+
+ IPADBG("size_expansion_tables: %d\n", init->expn_table_entries);
+ ipa_ctx->nat_mem.size_expansion_tables = init->expn_table_entries;
+
IPADBG("return\n");
result = 0;
free_cmd:
diff --git a/drivers/platform/msm/ipa/ipa_rm.c b/drivers/platform/msm/ipa/ipa_rm.c
index 1fdd300..e057c5a 100644
--- a/drivers/platform/msm/ipa/ipa_rm.c
+++ b/drivers/platform/msm/ipa/ipa_rm.c
@@ -21,6 +21,7 @@
struct ipa_rm_context_type {
struct ipa_rm_dep_graph *dep_graph;
struct workqueue_struct *ipa_rm_wq;
+ rwlock_t lock;
};
static struct ipa_rm_context_type *ipa_rm_ctx;
@@ -41,10 +42,9 @@
struct ipa_rm_resource *resource;
int result;
- if (!create_params) {
- result = -EINVAL;
- goto bail;
- }
+ if (!create_params)
+ return -EINVAL;
+ write_lock(&ipa_rm_ctx->lock);
if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
create_params->name,
&resource) == 0) {
@@ -59,11 +59,51 @@
if (result)
ipa_rm_resource_delete(resource);
bail:
+ write_unlock(&ipa_rm_ctx->lock);
return result;
}
EXPORT_SYMBOL(ipa_rm_create_resource);
/**
+ * ipa_rm_delete_resource() - delete resource
+ * @resource_name: name of resource to be deleted
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * This function is called by IPA RM client to delete client's resources.
+ *
+ */
+int ipa_rm_delete_resource(enum ipa_rm_resource_name resource_name)
+{
+ struct ipa_rm_resource *resource;
+ int result;
+
+ IPADBG("IPA RM ::ipa_rm_delete_resource num[%d] ENTER\n",
+ resource_name);
+ write_lock(&ipa_rm_ctx->lock);
+ if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
+ resource_name,
+ &resource) != 0) {
+ IPADBG("ipa_rm_delete_resource param are bad********\n");
+ result = -EINVAL;
+ goto bail;
+ }
+ result = ipa_rm_resource_delete(resource);
+ if (result) {
+ IPADBG("error in ipa_rm_resource_delete\n");
+ goto bail;
+ }
+ result = ipa_rm_dep_graph_remove(ipa_rm_ctx->dep_graph,
+ resource_name);
+ IPADBG("IPA RM ::ipa_rm_delete_resource [%d] SUCCESS\n",
+ resource_name);
+bail:
+ write_unlock(&ipa_rm_ctx->lock);
+ return result;
+}
+EXPORT_SYMBOL(ipa_rm_delete_resource);
+
+/**
* ipa_rm_add_dependency() - create dependency
* between 2 resources
* @resource_name: name of dependent resource
@@ -77,13 +117,19 @@
int ipa_rm_add_dependency(enum ipa_rm_resource_name resource_name,
enum ipa_rm_resource_name depends_on_name)
{
- return ipa_rm_dep_graph_add_dependency(
- ipa_rm_ctx->dep_graph,
- resource_name,
- depends_on_name);
+ int result;
+
+ read_lock(&ipa_rm_ctx->lock);
+ result = ipa_rm_dep_graph_add_dependency(
+ ipa_rm_ctx->dep_graph,
+ resource_name,
+ depends_on_name);
+ read_unlock(&ipa_rm_ctx->lock);
+ return result;
}
EXPORT_SYMBOL(ipa_rm_add_dependency);
+
/**
* ipa_rm_delete_dependency() - create dependency
* between 2 resources
@@ -98,10 +144,14 @@
int ipa_rm_delete_dependency(enum ipa_rm_resource_name resource_name,
enum ipa_rm_resource_name depends_on_name)
{
- return ipa_rm_dep_graph_delete_dependency(
- ipa_rm_ctx->dep_graph,
- resource_name,
- depends_on_name);
+ int result;
+ read_lock(&ipa_rm_ctx->lock);
+ result = ipa_rm_dep_graph_delete_dependency(
+ ipa_rm_ctx->dep_graph,
+ resource_name,
+ depends_on_name);
+ read_unlock(&ipa_rm_ctx->lock);
+ return result;
}
EXPORT_SYMBOL(ipa_rm_delete_dependency);
@@ -120,10 +170,9 @@
int result;
IPADBG("IPA RM ::ipa_rm_request_resource ENTER\n");
- if (!IPA_RM_RESORCE_IS_PROD(resource_name)) {
- result = -EINVAL;
- goto bail;
- }
+ if (!IPA_RM_RESORCE_IS_PROD(resource_name))
+ return -EINVAL;
+ read_lock(&ipa_rm_ctx->lock);
if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
resource_name,
&resource) != 0) {
@@ -136,6 +185,7 @@
bail:
IPADBG("IPA RM ::ipa_rm_request_resource EXIT [%d]\n", result);
+ read_unlock(&ipa_rm_ctx->lock);
return result;
}
EXPORT_SYMBOL(ipa_rm_request_resource);
@@ -155,10 +205,9 @@
int result;
IPADBG("IPA RM ::ipa_rm_release_resource ENTER\n");
- if (!IPA_RM_RESORCE_IS_PROD(resource_name)) {
- result = -EINVAL;
- goto bail;
- }
+ if (!IPA_RM_RESORCE_IS_PROD(resource_name))
+ return -EINVAL;
+ read_lock(&ipa_rm_ctx->lock);
if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
resource_name,
&resource) != 0) {
@@ -170,6 +219,7 @@
bail:
IPADBG("IPA RM ::ipa_rm_release_resource EXIT [%d]\n", result);
+ read_unlock(&ipa_rm_ctx->lock);
return result;
}
EXPORT_SYMBOL(ipa_rm_release_resource);
@@ -189,10 +239,10 @@
{
int result;
struct ipa_rm_resource *resource;
- if (!IPA_RM_RESORCE_IS_PROD(resource_name)) {
- result = -EINVAL;
- goto bail;
- }
+
+ if (!IPA_RM_RESORCE_IS_PROD(resource_name))
+ return -EINVAL;
+ read_lock(&ipa_rm_ctx->lock);
if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
resource_name,
&resource) != 0) {
@@ -201,8 +251,10 @@
}
result = ipa_rm_resource_producer_register(
(struct ipa_rm_resource_prod *)resource,
- reg_params);
+ reg_params,
+ true);
bail:
+ read_unlock(&ipa_rm_ctx->lock);
return result;
}
EXPORT_SYMBOL(ipa_rm_register);
@@ -222,10 +274,10 @@
{
int result;
struct ipa_rm_resource *resource;
- if (!IPA_RM_RESORCE_IS_PROD(resource_name)) {
- result = -EINVAL;
- goto bail;
- }
+
+ if (!IPA_RM_RESORCE_IS_PROD(resource_name))
+ return -EINVAL;
+ read_lock(&ipa_rm_ctx->lock);
if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
resource_name,
&resource) != 0) {
@@ -236,6 +288,7 @@
(struct ipa_rm_resource_prod *)resource,
reg_params);
bail:
+ read_unlock(&ipa_rm_ctx->lock);
return result;
}
EXPORT_SYMBOL(ipa_rm_deregister);
@@ -260,7 +313,8 @@
}
ipa_rm_wq_send_cmd(IPA_RM_WQ_RESOURCE_CB,
resource_name,
- event);
+ event,
+ false);
result = 0;
bail:
return result;
@@ -278,25 +332,33 @@
case IPA_RM_WQ_NOTIFY_PROD:
if (!IPA_RM_RESORCE_IS_PROD(ipa_rm_work->resource_name))
return;
+ read_lock(&ipa_rm_ctx->lock);
if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
ipa_rm_work->resource_name,
- &resource) != 0)
+ &resource) != 0){
+ read_unlock(&ipa_rm_ctx->lock);
return;
+ }
ipa_rm_resource_producer_notify_clients(
(struct ipa_rm_resource_prod *)resource,
- ipa_rm_work->event);
-
+ ipa_rm_work->event,
+ ipa_rm_work->notify_registered_only);
+ read_unlock(&ipa_rm_ctx->lock);
break;
case IPA_RM_WQ_NOTIFY_CONS:
break;
case IPA_RM_WQ_RESOURCE_CB:
+ read_lock(&ipa_rm_ctx->lock);
if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
ipa_rm_work->resource_name,
- &resource) != 0)
+ &resource) != 0){
+ read_unlock(&ipa_rm_ctx->lock);
return;
+ }
ipa_rm_resource_consumer_handle_cb(
(struct ipa_rm_resource_cons *)resource,
ipa_rm_work->event);
+ read_unlock(&ipa_rm_ctx->lock);
break;
default:
break;
@@ -309,12 +371,15 @@
* ipa_rm_wq_send_cmd() - send a command for deferred work
* @wq_cmd: command that should be executed
* @resource_name: resource on which command should be executed
+ * @notify_registered_only: notify only clients registered by
+ * ipa_rm_register()
*
* Returns: 0 on success, negative otherwise
*/
int ipa_rm_wq_send_cmd(enum ipa_rm_wq_cmd wq_cmd,
enum ipa_rm_resource_name resource_name,
- enum ipa_rm_event event)
+ enum ipa_rm_event event,
+ bool notify_registered_only)
{
int result = -ENOMEM;
struct ipa_rm_wq_work_type *work = kzalloc(sizeof(*work), GFP_KERNEL);
@@ -323,6 +388,7 @@
work->wq_cmd = wq_cmd;
work->resource_name = resource_name;
work->event = event;
+ work->notify_registered_only = notify_registered_only;
result = queue_work(ipa_rm_ctx->ipa_rm_wq,
(struct work_struct *)work);
}
@@ -351,6 +417,7 @@
result = ipa_rm_dep_graph_create(&(ipa_rm_ctx->dep_graph));
if (result)
goto graph_alloc_fail;
+ rwlock_init(&ipa_rm_ctx->lock);
IPADBG("IPA RM ipa_rm_initialize SUCCESS\n");
return 0;
@@ -363,6 +430,46 @@
}
/**
+ * ipa_rm_stat() - print RM stat
+ * @buf: [in] The user buff used to print
+ * @size: [in] The size of buf
+ * Returns: number of bytes used on success, negative on failure
+ *
+ * This function is called by ipa_debugfs in order to receive
+ * a full picture of the current state of the RM
+ */
+
+int ipa_rm_stat(char *buf, int size)
+{
+ int i, cnt = 0, result = EINVAL;
+ struct ipa_rm_resource *resource = NULL;
+
+ if (!buf || size < 0)
+ goto bail;
+
+ read_lock(&ipa_rm_ctx->lock);
+ for (i = 0; i < IPA_RM_RESOURCE_PROD_MAX; ++i) {
+ result = ipa_rm_dep_graph_get_resource(
+ ipa_rm_ctx->dep_graph,
+ i,
+ &resource);
+ if (!result) {
+ result = ipa_rm_resource_producer_print_stat(
+ resource, buf + cnt,
+ size-cnt);
+ if (result < 0)
+ goto bail;
+ cnt += result;
+ }
+ }
+ result = cnt;
+
+bail:
+ read_unlock(&ipa_rm_ctx->lock);
+ return result;
+}
+
+/**
* ipa_rm_exit() - free all IPA RM resources
*/
void ipa_rm_exit(void)
diff --git a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c
index 6afab42..8144a42 100644
--- a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c
+++ b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c
@@ -39,7 +39,6 @@
result = -ENOMEM;
goto bail;
}
- rwlock_init(&((*dep_graph)->lock));
bail:
return result;
}
@@ -55,12 +54,10 @@
int resource_index;
if (!graph)
return;
- write_lock(&graph->lock);
for (resource_index = 0;
resource_index < IPA_RM_RESOURCE_MAX;
resource_index++)
kfree(graph->resource_table[resource_index]);
- write_unlock(&graph->lock);
memset(graph->resource_table, 0, sizeof(graph->resource_table));
}
@@ -88,9 +85,7 @@
result = -EINVAL;
goto bail;
}
- read_lock(&graph->lock);
*resource = graph->resource_table[resource_index];
- read_unlock(&graph->lock);
if (!*resource) {
result = -EINVAL;
goto bail;
@@ -112,6 +107,7 @@
{
int result = 0;
int resource_index;
+
if (!graph || !resource) {
result = -EINVAL;
goto bail;
@@ -121,14 +117,29 @@
result = -EINVAL;
goto bail;
}
- write_lock(&graph->lock);
graph->resource_table[resource_index] = resource;
- write_unlock(&graph->lock);
bail:
return result;
}
/**
+ * ipa_rm_dep_graph_remove() - removes resource from graph
+ * @graph: [in] dependency graph
+ * @resource: [in] resource to add
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_rm_dep_graph_remove(struct ipa_rm_dep_graph *graph,
+ enum ipa_rm_resource_name resource_name)
+{
+ if (!graph)
+ return -EINVAL;
+
+ graph->resource_table[resource_name] = NULL;
+ return 0;
+}
+
+/**
* ipa_rm_dep_graph_add_dependency() - adds dependency between
* two nodes in graph
* @graph: [in] dependency graph
diff --git a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.h b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.h
index 19d9461..4126819 100644
--- a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.h
+++ b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.h
@@ -19,7 +19,6 @@
struct ipa_rm_dep_graph {
struct ipa_rm_resource *resource_table[IPA_RM_RESOURCE_MAX];
- rwlock_t lock;
};
int ipa_rm_dep_graph_get_resource(
@@ -34,6 +33,9 @@
int ipa_rm_dep_graph_add(struct ipa_rm_dep_graph *graph,
struct ipa_rm_resource *resource);
+int ipa_rm_dep_graph_remove(struct ipa_rm_dep_graph *graph,
+ enum ipa_rm_resource_name resource_name);
+
int ipa_rm_dep_graph_add_dependency(struct ipa_rm_dep_graph *graph,
enum ipa_rm_resource_name resource_name,
enum ipa_rm_resource_name depends_on_name);
diff --git a/drivers/platform/msm/ipa/ipa_rm_i.h b/drivers/platform/msm/ipa/ipa_rm_i.h
index 141a442..b853e2b 100644
--- a/drivers/platform/msm/ipa/ipa_rm_i.h
+++ b/drivers/platform/msm/ipa/ipa_rm_i.h
@@ -45,20 +45,26 @@
* should be done
* @dep_graph: data structure to search for resource if exists
* @event: event to notify
+ * @notify_registered_only: notify only clients registered by
+ * ipa_rm_register()
*/
struct ipa_rm_wq_work_type {
struct work_struct work;
enum ipa_rm_wq_cmd wq_cmd;
enum ipa_rm_resource_name resource_name;
enum ipa_rm_event event;
+ bool notify_registered_only;
};
int ipa_rm_wq_send_cmd(enum ipa_rm_wq_cmd wq_cmd,
enum ipa_rm_resource_name resource_name,
- enum ipa_rm_event event);
+ enum ipa_rm_event event,
+ bool notify_registered_only);
int ipa_rm_initialize(void);
+int ipa_rm_stat(char *buf, int size);
+
void ipa_rm_exit(void);
#endif /* _IPA_RM_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_rm_peers_list.c b/drivers/platform/msm/ipa/ipa_rm_peers_list.c
index 55f8239..4beb2b8 100644
--- a/drivers/platform/msm/ipa/ipa_rm_peers_list.c
+++ b/drivers/platform/msm/ipa/ipa_rm_peers_list.c
@@ -167,12 +167,12 @@
bool ipa_rm_peers_list_has_last_peer(
struct ipa_rm_peers_list *peers_list)
{
- bool result = true;
+ bool result = false;
if (!peers_list)
goto bail;
read_lock(&peers_list->peers_lock);
if (peers_list->peers_count == 1)
- result = false;
+ result = true;
read_unlock(&peers_list->peers_lock);
bail:
return result;
diff --git a/drivers/platform/msm/ipa/ipa_rm_resource.c b/drivers/platform/msm/ipa/ipa_rm_resource.c
index 3615952..7142dc7 100644
--- a/drivers/platform/msm/ipa/ipa_rm_resource.c
+++ b/drivers/platform/msm/ipa/ipa_rm_resource.c
@@ -80,8 +80,8 @@
int result = 0;
int driver_result;
unsigned long flags;
- IPADBG("IPA RM ::ipa_rm_resource_consumer_request %d ENTER\n",
- consumer->resource.name);
+ IPADBG("IPA RM ::%s name %d ENTER\n",
+ __func__, consumer->resource.name);
spin_lock_irqsave(&consumer->resource.state_lock, flags);
switch (consumer->resource.state) {
case IPA_RM_RELEASED:
@@ -115,8 +115,7 @@
consumer->usage_count++;
bail:
spin_unlock_irqrestore(&consumer->resource.state_lock, flags);
- IPADBG("IPA RM ::ipa_rm_resource_consumer_request %d EXIT %d\n",
- consumer->resource.name, result);
+ IPADBG("IPA RM ::ipa_rm_resource_consumer_request EXIT [%d]\n", result);
return result;
}
@@ -127,8 +126,8 @@
int driver_result;
unsigned long flags;
enum ipa_rm_resource_state save_state;
- IPADBG("IPA RM ::ipa_rm_resource_consumer_release %d ENTER\n",
- consumer->resource.name);
+ IPADBG("IPA RM ::%s name %d ENTER\n",
+ __func__, consumer->resource.name);
spin_lock_irqsave(&consumer->resource.state_lock, flags);
switch (consumer->resource.state) {
case IPA_RM_RELEASED:
@@ -163,8 +162,7 @@
}
bail:
spin_unlock_irqrestore(&consumer->resource.state_lock, flags);
- IPADBG("IPA RM ::ipa_rm_resource_consumer_release %d EXIT %d\n",
- consumer->resource.name, result);
+ IPADBG("IPA RM ::ipa_rm_resource_consumer_release EXIT [%d]\n", result);
return result;
}
@@ -173,10 +171,13 @@
* all registered clients of given producer
* @producer: producer
* @event: event to notify
+ * @notify_registered_only: notify only clients registered by
+ * ipa_rm_register()
*/
void ipa_rm_resource_producer_notify_clients(
struct ipa_rm_resource_prod *producer,
- enum ipa_rm_event event)
+ enum ipa_rm_event event,
+ bool notify_registered_only)
{
struct ipa_rm_notification_info *reg_info, *reg_info_cloned;
struct list_head *pos, *q;
@@ -186,6 +187,8 @@
reg_info = list_entry(pos,
struct ipa_rm_notification_info,
link);
+ if (notify_registered_only && !reg_info->explicit)
+ continue;
reg_info_cloned = kzalloc(sizeof(*reg_info_cloned), GFP_ATOMIC);
if (!reg_info_cloned)
goto clone_list_failed;
@@ -226,7 +229,8 @@
rwlock_init(&(*producer)->event_listeners_lock);
INIT_LIST_HEAD(&((*producer)->event_listeners));
result = ipa_rm_resource_producer_register(*producer,
- &(create_params->reg_params));
+ &(create_params->reg_params),
+ false);
if (result)
goto register_fail;
(*resource) = (struct ipa_rm_resource *) (*producer);
@@ -332,25 +336,68 @@
* ipa_rm_resource_delete() - deletes resource
* @resource: [in] resource
* for resource initialization with IPA RM
+ *
+ * Returns: 0 on success, negative on failure
*/
-void ipa_rm_resource_delete(struct ipa_rm_resource *resource)
+int ipa_rm_resource_delete(struct ipa_rm_resource *resource)
{
- if (!resource)
- return;
- if (resource->peers_list)
- ipa_rm_peers_list_delete(resource->peers_list);
+ struct ipa_rm_resource *consumer, *producer;
+ int peers_index, result = 0, list_size;
+
+ IPADBG("ipa_rm_resource_delete ENTER with resource %d\n",
+ resource->name);
+ if (!resource) {
+ IPADBG("ipa_rm_resource_delete ENTER with invalid param\n");
+ return -EINVAL;
+ }
if (resource->type == IPA_RM_PRODUCER) {
+ if (resource->peers_list) {
+ list_size = ipa_rm_peers_list_get_size(
+ resource->peers_list);
+ for (peers_index = 0;
+ peers_index < list_size;
+ peers_index++) {
+ consumer = ipa_rm_peers_list_get_resource(
+ peers_index,
+ resource->peers_list);
+ if (consumer)
+ ipa_rm_resource_delete_dependency(
+ resource,
+ consumer);
+ }
+ ipa_rm_peers_list_delete(resource->peers_list);
+ }
ipa_rm_resource_producer_delete(
(struct ipa_rm_resource_prod *) resource);
kfree((struct ipa_rm_resource_prod *) resource);
- } else
+ } else if (resource->type == IPA_RM_CONSUMER) {
+ if (resource->peers_list) {
+ list_size = ipa_rm_peers_list_get_size(
+ resource->peers_list);
+ for (peers_index = 0;
+ peers_index < list_size;
+ peers_index++){
+ producer = ipa_rm_peers_list_get_resource(
+ peers_index,
+ resource->peers_list);
+ if (producer)
+ ipa_rm_resource_delete_dependency(
+ producer,
+ resource);
+ }
+ ipa_rm_peers_list_delete(resource->peers_list);
+ }
kfree((struct ipa_rm_resource_cons *) resource);
+ }
+ IPADBG("ipa_rm_resource_delete SUCCESS\n");
+ return result;
}
/**
* ipa_rm_resource_register() - register resource
* @resource: [in] resource
* @reg_params: [in] registration parameters
+ * @explicit: [in] registered explicitly by ipa_rm_register()
*
* Returns: 0 on success, negative on failure
*
@@ -358,7 +405,8 @@
*
*/
int ipa_rm_resource_producer_register(struct ipa_rm_resource_prod *producer,
- struct ipa_rm_register_params *reg_params)
+ struct ipa_rm_register_params *reg_params,
+ bool explicit)
{
int result = 0;
struct ipa_rm_notification_info *reg_info;
@@ -367,6 +415,8 @@
result = -EPERM;
goto bail;
}
+ IPADBG("IPA RM: %s name %d ENTER\n",
+ __func__, producer->resource.name);
read_lock(&producer->event_listeners_lock);
list_for_each(pos, &(producer->event_listeners)) {
reg_info = list_entry(pos,
@@ -388,6 +438,7 @@
}
reg_info->reg_params.user_data = reg_params->user_data;
reg_info->reg_params.notify_cb = reg_params->notify_cb;
+ reg_info->explicit = explicit;
INIT_LIST_HEAD(®_info->link);
write_lock(&producer->event_listeners_lock);
list_add(®_info->link, &producer->event_listeners);
@@ -474,10 +525,11 @@
consumer_result = ipa_rm_resource_consumer_request(
(struct ipa_rm_resource_cons *)depends_on);
spin_lock_irqsave(&resource->state_lock, flags);
- if (consumer_result != -EINPROGRESS)
+ if (consumer_result != -EINPROGRESS) {
resource->state = prev_state;
((struct ipa_rm_resource_prod *)
resource)->pending_request--;
+ }
result = consumer_result;
break;
}
@@ -509,8 +561,15 @@
{
int result = 0;
unsigned long flags;
+ unsigned long consumer_flags;
+ bool state_changed = false;
+ bool release_consumer = false;
if (!resource || !depends_on)
return -EINVAL;
+ IPADBG("IPA RM: %s from %d to %d ENTER\n",
+ __func__,
+ resource->name,
+ depends_on->name);
if (!ipa_rm_peers_list_check_dependency(resource->peers_list,
resource->name,
depends_on->peers_list,
@@ -519,38 +578,64 @@
spin_lock_irqsave(&resource->state_lock, flags);
switch (resource->state) {
case IPA_RM_RELEASED:
+ break;
case IPA_RM_GRANTED:
+ release_consumer = true;
break;
case IPA_RM_RELEASE_IN_PROGRESS:
if (((struct ipa_rm_resource_prod *)
- resource)->pending_release > 0)
- ((struct ipa_rm_resource_prod *)
+ resource)->pending_release > 0)
+ ((struct ipa_rm_resource_prod *)
resource)->pending_release--;
+ spin_lock_irqsave(&depends_on->state_lock, consumer_flags);
+ if (depends_on->state == IPA_RM_RELEASE_IN_PROGRESS &&
+ ((struct ipa_rm_resource_prod *)
+ resource)->pending_release == 0) {
+ resource->state = IPA_RM_RELEASED;
+ state_changed = true;
+ }
+ spin_unlock_irqrestore(&depends_on->state_lock, consumer_flags);
break;
case IPA_RM_REQUEST_IN_PROGRESS:
+ release_consumer = true;
if (((struct ipa_rm_resource_prod *)
- resource)->pending_request > 0)
- ((struct ipa_rm_resource_prod *)
+ resource)->pending_request > 0)
+ ((struct ipa_rm_resource_prod *)
resource)->pending_request--;
+ spin_lock_irqsave(&depends_on->state_lock, consumer_flags);
+ if (depends_on->state == IPA_RM_REQUEST_IN_PROGRESS &&
+ ((struct ipa_rm_resource_prod *)
+ resource)->pending_request == 0) {
+ resource->state = IPA_RM_GRANTED;
+ state_changed = true;
+ }
+ spin_unlock_irqrestore(&depends_on->state_lock, consumer_flags);
break;
default:
result = -EINVAL;
spin_unlock_irqrestore(&resource->state_lock, flags);
goto bail;
}
- spin_unlock_irqrestore(&resource->state_lock, flags);
- (void) ipa_rm_resource_consumer_release(
- (struct ipa_rm_resource_cons *)depends_on);
- if (ipa_rm_peers_list_has_last_peer(resource->peers_list)) {
+ if (state_changed &&
+ ipa_rm_peers_list_has_last_peer(resource->peers_list)) {
(void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
resource->name,
- IPA_RM_RESOURCE_RELEASED);
+ resource->state,
+ false);
result = -EINPROGRESS;
}
+ spin_unlock_irqrestore(&resource->state_lock, flags);
ipa_rm_peers_list_remove_peer(resource->peers_list,
depends_on->name);
ipa_rm_peers_list_remove_peer(depends_on->peers_list,
resource->name);
+ if (release_consumer)
+ (void) ipa_rm_resource_consumer_release(
+ (struct ipa_rm_resource_cons *)depends_on);
+ IPADBG("IPA RM: %s from %d to %d SUCCESS\n",
+ __func__,
+ resource->name,
+ depends_on->name);
bail:
return result;
}
@@ -568,13 +653,17 @@
unsigned long flags;
struct ipa_rm_resource *consumer;
int consumer_result;
- IPADBG("IPA RM ::ipa_rm_resource_producer_request %d ENTER\n",
+ IPADBG("IPA RM ::ipa_rm_resource_producer_request [%d] ENTER\n",
producer->resource.name);
if (ipa_rm_peers_list_is_empty(producer->resource.peers_list)) {
spin_lock_irqsave(&producer->resource.state_lock, flags);
producer->resource.state = IPA_RM_GRANTED;
- spin_unlock_irqrestore(&producer->resource.state_lock, flags);
- return 0;
+ (void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
+ producer->resource.name,
+ IPA_RM_RESOURCE_GRANTED,
+ true);
+ result = 0;
+ goto unlock_and_bail;
}
spin_lock_irqsave(&producer->resource.state_lock, flags);
IPADBG("IPA RM ::ipa_rm_resource_producer_request state [%d]\n",
@@ -625,15 +714,17 @@
}
}
spin_lock_irqsave(&producer->resource.state_lock, flags);
- if (producer->pending_request == 0)
+ if (producer->pending_request == 0) {
producer->resource.state = IPA_RM_GRANTED;
- spin_unlock_irqrestore(&producer->resource.state_lock, flags);
- return result;
+ (void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
+ producer->resource.name,
+ IPA_RM_RESOURCE_GRANTED,
+ true);
+ }
unlock_and_bail:
spin_unlock_irqrestore(&producer->resource.state_lock, flags);
bail:
- IPADBG("IPA RM ::ipa_rm_resource_producer_request %d EXIT %d\n",
- producer->resource.name, result);
+ IPADBG("IPA RM ::ipa_rm_resource_producer_request EXIT[%d]\n", result);
return result;
}
@@ -651,11 +742,16 @@
unsigned long flags;
struct ipa_rm_resource *consumer;
int consumer_result;
- IPADBG("IPA RM ::ipa_rm_resource_producer_release %d ENTER\n",
- producer->resource.name);
+ IPADBG("IPA RM: %s name %d ENTER\n",
+ __func__,
+ producer->resource.name);
if (ipa_rm_peers_list_is_empty(producer->resource.peers_list)) {
spin_lock_irqsave(&producer->resource.state_lock, flags);
producer->resource.state = IPA_RM_RELEASED;
+ (void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
+ producer->resource.name,
+ IPA_RM_RESOURCE_RELEASED,
+ true);
spin_unlock_irqrestore(&producer->resource.state_lock, flags);
return 0;
}
@@ -702,14 +798,16 @@
}
}
spin_lock_irqsave(&producer->resource.state_lock, flags);
- if (producer->pending_release == 0)
+ if (producer->pending_release == 0) {
producer->resource.state = IPA_RM_RELEASED;
- spin_unlock_irqrestore(&producer->resource.state_lock, flags);
- return result;
+ (void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
+ producer->resource.name,
+ IPA_RM_RESOURCE_RELEASED,
+ true);
+ }
bail:
spin_unlock_irqrestore(&producer->resource.state_lock, flags);
- IPADBG("IPA RM ::ipa_rm_resource_producer_release %d EXIT %d\n",
- producer->resource.name, result);
+ IPADBG("IPA RM ::ipa_rm_resource_producer_release EXIT[%d]\n", result);
return result;
}
@@ -732,7 +830,8 @@
&producer->resource.state_lock, flags);
ipa_rm_resource_producer_notify_clients(
producer,
- IPA_RM_RESOURCE_GRANTED);
+ IPA_RM_RESOURCE_GRANTED,
+ false);
goto bail;
}
}
@@ -749,7 +848,8 @@
&producer->resource.state_lock, flags);
ipa_rm_resource_producer_notify_clients(
producer,
- IPA_RM_RESOURCE_RELEASED);
+ IPA_RM_RESOURCE_RELEASED,
+ false);
goto bail;
}
}
@@ -814,3 +914,197 @@
spin_unlock_irqrestore(&consumer->resource.state_lock, flags);
return;
}
+
+/*
+ * ipa_rm_resource_producer_print_stat() - print the
+ * resource status and all his dependencies
+ *
+ * @resource: [in] Resource resource
+ * @buff: [in] The buf used to print
+ * @size: [in] Buf size
+ *
+ * Returns: number of bytes used on success, negative on failure
+ */
+
+int ipa_rm_resource_producer_print_stat(
+ struct ipa_rm_resource *resource,
+ char *buf,
+ int size){
+
+ int i, nbytes, cnt = 0;
+ unsigned long flags;
+ struct ipa_rm_resource *consumer;
+
+ if (!buf || size < 0)
+ return -EINVAL;
+ switch (resource->name) {
+ case IPA_RM_RESOURCE_BRIDGE_PROD:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "BRIDGE_PROD[");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RESOURCE_A2_PROD:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "A2_PROD[");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RESOURCE_USB_PROD:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "USB_PROD[");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RESOURCE_HSIC_PROD:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "HSIC_PROD[");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RESOURCE_STD_ECM_PROD:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "STD_ECM_PROD[");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RESOURCE_WWAN_0_PROD:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "WWAN_0_PROD[");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RESOURCE_WWAN_1_PROD:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "WWAN_1_PROD[");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RESOURCE_WWAN_2_PROD:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "WWAN_2_PROD[");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RESOURCE_WWAN_3_PROD:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "WWAN_3_PROD[");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RESOURCE_WWAN_4_PROD:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "WWAN_4_PROD[");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RESOURCE_WWAN_5_PROD:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "WWAN_5_PROD[");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RESOURCE_WWAN_6_PROD:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "WWAN_6_PROD[");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RESOURCE_WWAN_7_PROD:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "WWAN_7_PROD[");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RESOURCE_WLAN_PROD:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "WLAN_PROD[");
+ cnt += nbytes;
+ break;
+ default:
+ return -EPERM;
+ }
+ spin_lock_irqsave(&resource->state_lock, flags);
+ switch (resource->state) {
+ case IPA_RM_RELEASED:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "Released] -> ");
+ cnt += nbytes;
+ break;
+ case IPA_RM_REQUEST_IN_PROGRESS:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "Request In Progress] -> ");
+ cnt += nbytes;
+ break;
+ case IPA_RM_GRANTED:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "Granted] -> ");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RELEASE_IN_PROGRESS:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "Release In Progress] -> ");
+ cnt += nbytes;
+ break;
+ default:
+ spin_unlock_irqrestore(
+ &resource->state_lock,
+ flags);
+ return -EPERM;
+ }
+ spin_unlock_irqrestore(
+ &resource->state_lock,
+ flags);
+ for (i = 0; i < resource->peers_list->max_peers; ++i) {
+ consumer =
+ ipa_rm_peers_list_get_resource(
+ i,
+ resource->peers_list);
+ if (consumer) {
+ switch (consumer->name) {
+ case IPA_RM_RESOURCE_A2_CONS:
+ nbytes = scnprintf(buf + cnt,
+ size - cnt,
+ " A2_CONS[");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RESOURCE_USB_CONS:
+ nbytes = scnprintf(buf + cnt,
+ size - cnt,
+ " USB_CONS[");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RESOURCE_HSIC_CONS:
+ nbytes = scnprintf(buf + cnt,
+ size - cnt,
+ " HSIC_CONS[");
+ cnt += nbytes;
+ break;
+ default:
+ return -EPERM;
+ }
+ spin_lock_irqsave(&consumer->state_lock, flags);
+ switch (consumer->state) {
+ case IPA_RM_RELEASED:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "Released], ");
+ cnt += nbytes;
+ break;
+ case IPA_RM_REQUEST_IN_PROGRESS:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "Request In Progress], ");
+ cnt += nbytes;
+ break;
+ case IPA_RM_GRANTED:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "Granted], ");
+ cnt += nbytes;
+ break;
+ case IPA_RM_RELEASE_IN_PROGRESS:
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "Release In Progress], ");
+ cnt += nbytes;
+ break;
+ default:
+ spin_unlock_irqrestore(
+ &consumer->state_lock,
+ flags);
+ return -EPERM;
+ }
+ spin_unlock_irqrestore(
+ &consumer->state_lock,
+ flags);
+ }
+ }
+ nbytes = scnprintf(buf + cnt, size - cnt,
+ "\n");
+ cnt += nbytes;
+ return cnt;
+}
diff --git a/drivers/platform/msm/ipa/ipa_rm_resource.h b/drivers/platform/msm/ipa/ipa_rm_resource.h
index b9c2e91..3fe474b 100644
--- a/drivers/platform/msm/ipa/ipa_rm_resource.h
+++ b/drivers/platform/msm/ipa/ipa_rm_resource.h
@@ -39,10 +39,12 @@
* struct ipa_rm_notification_info - notification information
* of IPA RM client
* @reg_params: registration parameters
+ * @explicit: registered explicitly by ipa_rm_register()
* @link: link to the list of all registered clients information
*/
struct ipa_rm_notification_info {
struct ipa_rm_register_params reg_params;
+ bool explicit;
struct list_head link;
};
@@ -99,10 +101,11 @@
struct ipa_rm_create_params *create_params,
struct ipa_rm_resource **resource);
-void ipa_rm_resource_delete(struct ipa_rm_resource *resource);
+int ipa_rm_resource_delete(struct ipa_rm_resource *resource);
int ipa_rm_resource_producer_register(struct ipa_rm_resource_prod *producer,
- struct ipa_rm_register_params *reg_params);
+ struct ipa_rm_register_params *reg_params,
+ bool explicit);
int ipa_rm_resource_producer_deregister(struct ipa_rm_resource_prod *producer,
struct ipa_rm_register_params *reg_params);
@@ -122,6 +125,12 @@
void ipa_rm_resource_producer_notify_clients(
struct ipa_rm_resource_prod *producer,
- enum ipa_rm_event event);
+ enum ipa_rm_event event,
+ bool notify_registered_only);
+
+int ipa_rm_resource_producer_print_stat(
+ struct ipa_rm_resource *resource,
+ char *buf,
+ int size);
#endif /* _IPA_RM_RESOURCE_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_rt.c b/drivers/platform/msm/ipa/ipa_rt.c
index fc5f668..6430c07 100644
--- a/drivers/platform/msm/ipa/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_rt.c
@@ -305,6 +305,7 @@
rt_tbl_mem.base, rt_tbl_mem.phys_base);
proc_err:
dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
+ mem->base = NULL;
error:
return -EPERM;
}
@@ -378,7 +379,7 @@
if (mem->size > avail) {
IPAERR("tbl too big, needed %d avail %d\n", mem->size, avail);
- goto fail_hw_tbl_gen;
+ goto fail_send_cmd;
}
if (ip == IPA_IP_v4) {
@@ -413,7 +414,7 @@
return 0;
fail_send_cmd:
- if (mem->phys_base)
+ if (mem->base)
dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
fail_hw_tbl_gen:
kfree(cmd);
diff --git a/drivers/platform/msm/ipa/ipa_utils.c b/drivers/platform/msm/ipa/ipa_utils.c
index 264de0d..23de300 100644
--- a/drivers/platform/msm/ipa/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_utils.c
@@ -910,6 +910,73 @@
EXPORT_SYMBOL(ipa_cfg_ep_route);
/**
+ * ipa_cfg_ep_holb() - IPA end-point holb configuration
+ *
+ * If an IPA producer pipe is full, IPA HW by default will block
+ * indefinitely till space opens up. During this time no packets
+ * including those from unrelated pipes will be processed. Enabling
+ * HOLB means IPA HW will be allowed to drop packets as/when needed
+ * and indefinite blocking is avoided.
+ *
+ * @clnt_hdl: [in] opaque client handle assigned by IPA to client
+ * @ipa_ep_cfg: [in] IPA end-point configuration params
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_cfg_ep_holb(u32 clnt_hdl, const struct ipa_ep_cfg_holb *ipa_ep_cfg)
+{
+ if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0 ||
+ ipa_ep_cfg == NULL || ipa_ep_cfg->tmr_val > 511 ||
+ ipa_ep_cfg->en > 1) {
+ IPAERR("bad parm.\n");
+ return -EINVAL;
+ }
+
+ if (IPA_CLIENT_IS_PROD(ipa_ctx->ep[clnt_hdl].client)) {
+ IPAERR("HOLB does not apply to IPA in EP %d\n", clnt_hdl);
+ return -EINVAL;
+ }
+
+ if (ipa_ctx->ipa_hw_type == IPA_HW_v1_0) {
+ IPAERR("per EP HOLB not supported\n");
+ return -EPERM;
+ } else {
+ ipa_ctx->ep[clnt_hdl].holb = *ipa_ep_cfg;
+ ipa_write_reg(ipa_ctx->mmio,
+ IPA_ENDP_INIT_HOL_BLOCK_EN_n_OFST(clnt_hdl),
+ ipa_ep_cfg->en);
+ ipa_write_reg(ipa_ctx->mmio,
+ IPA_ENDP_INIT_HOL_BLOCK_TIMER_n_OFST(clnt_hdl),
+ ipa_ep_cfg->tmr_val);
+ IPAERR("cfg holb %u ep=%d tmr=%d\n", ipa_ep_cfg->en, clnt_hdl,
+ ipa_ep_cfg->tmr_val);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ipa_cfg_ep_holb);
+
+/**
+ * ipa_cfg_ep_holb_by_client() - IPA end-point holb configuration
+ *
+ * Wrapper function for ipa_cfg_ep_holb() with client name instead of
+ * client handle. This function is used for clients that does not have
+ * client handle.
+ *
+ * @client: [in] client name
+ * @ipa_ep_cfg: [in] IPA end-point configuration params
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_cfg_ep_holb_by_client(enum ipa_client_type client,
+ const struct ipa_ep_cfg_holb *ipa_ep_cfg)
+{
+ return ipa_cfg_ep_holb(ipa_get_ep_mapping(ipa_ctx->mode, client),
+ ipa_ep_cfg);
+}
+EXPORT_SYMBOL(ipa_cfg_ep_holb_by_client);
+
+/**
* ipa_dump_buff_internal() - dumps buffer for debug purposes
* @base: buffer base address
* @phy_base: buffer physical base address
@@ -1263,4 +1330,3 @@
else
return 0;
}
-
diff --git a/drivers/platform/msm/ipa/rmnet_bridge.c b/drivers/platform/msm/ipa/rmnet_bridge.c
deleted file mode 100644
index 696b363..0000000
--- a/drivers/platform/msm/ipa/rmnet_bridge.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/export.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <mach/bam_dmux.h>
-#include <mach/ipa.h>
-#include <mach/sps.h>
-
-static struct rmnet_bridge_cb_type {
- u32 producer_handle;
- u32 consumer_handle;
- u32 ipa_producer_handle;
- u32 ipa_consumer_handle;
- bool is_connected;
-} rmnet_bridge_cb;
-
-/**
-* rmnet_bridge_init() - Initialize RmNet bridge module
-*
-* Return codes:
-* 0: success
-*/
-int rmnet_bridge_init(void)
-{
- memset(&rmnet_bridge_cb, 0, sizeof(struct rmnet_bridge_cb_type));
-
- return 0;
-}
-EXPORT_SYMBOL(rmnet_bridge_init);
-
-/**
-* rmnet_bridge_disconnect() - Disconnect RmNet bridge module
-*
-* Return codes:
-* 0: success
-* -EINVAL: invalid parameters
-*/
-int rmnet_bridge_disconnect(void)
-{
- int ret = 0;
- if (false == rmnet_bridge_cb.is_connected) {
- pr_err("%s: trying to disconnect already disconnected RmNet bridge\n",
- __func__);
- goto bail;
- }
-
- rmnet_bridge_cb.is_connected = false;
-
- ret = ipa_bridge_teardown(IPA_BRIDGE_DIR_DL, IPA_BRIDGE_TYPE_TETHERED,
- rmnet_bridge_cb.ipa_consumer_handle);
- ret = ipa_bridge_teardown(IPA_BRIDGE_DIR_UL, IPA_BRIDGE_TYPE_TETHERED,
- rmnet_bridge_cb.ipa_producer_handle);
-bail:
- return ret;
-}
-EXPORT_SYMBOL(rmnet_bridge_disconnect);
-
-/**
-* rmnet_bridge_connect() - Connect RmNet bridge module
-* @producer_hdl: IPA producer handle
-* @consumer_hdl: IPA consumer handle
-* @wwan_logical_channel_id: WWAN logical channel ID
-*
-* Return codes:
-* 0: success
-* -EINVAL: invalid parameters
-*/
-int rmnet_bridge_connect(u32 producer_hdl,
- u32 consumer_hdl,
- int wwan_logical_channel_id)
-{
- struct ipa_sys_connect_params props;
- int ret = 0;
-
- if (true == rmnet_bridge_cb.is_connected) {
- ret = 0;
- pr_err("%s: trying to connect already connected RmNet bridge\n",
- __func__);
- goto bail;
- }
-
- rmnet_bridge_cb.consumer_handle = consumer_hdl;
- rmnet_bridge_cb.producer_handle = producer_hdl;
- rmnet_bridge_cb.is_connected = true;
-
- memset(&props, 0, sizeof(props));
- props.ipa_ep_cfg.mode.mode = IPA_DMA;
- props.ipa_ep_cfg.mode.dst = IPA_CLIENT_USB_CONS;
- props.client = IPA_CLIENT_A2_TETHERED_PROD;
- props.desc_fifo_sz = 0x800;
- /* setup notification callback if needed */
-
- ret = ipa_bridge_setup(IPA_BRIDGE_DIR_DL, IPA_BRIDGE_TYPE_TETHERED,
- &props, &rmnet_bridge_cb.ipa_consumer_handle);
- if (ret) {
- pr_err("%s: IPA DL bridge setup failure\n", __func__);
- goto bail_dl;
- }
-
- memset(&props, 0, sizeof(props));
- props.client = IPA_CLIENT_A2_TETHERED_CONS;
- props.desc_fifo_sz = 0x800;
- /* setup notification callback if needed */
-
- ret = ipa_bridge_setup(IPA_BRIDGE_DIR_UL, IPA_BRIDGE_TYPE_TETHERED,
- &props, &rmnet_bridge_cb.ipa_producer_handle);
- if (ret) {
- pr_err("%s: IPA UL bridge setup failure\n", __func__);
- goto bail_ul;
- }
- return 0;
-bail_ul:
- ipa_bridge_teardown(IPA_BRIDGE_DIR_DL, IPA_BRIDGE_TYPE_TETHERED,
- rmnet_bridge_cb.ipa_consumer_handle);
-bail_dl:
- rmnet_bridge_cb.is_connected = false;
-bail:
- return ret;
-}
-EXPORT_SYMBOL(rmnet_bridge_connect);
-
-void rmnet_bridge_get_client_handles(u32 *producer_handle,
- u32 *consumer_handle)
-{
- if (producer_handle == NULL || consumer_handle == NULL)
- return;
-
- *producer_handle = rmnet_bridge_cb.producer_handle;
- *consumer_handle = rmnet_bridge_cb.consumer_handle;
-}
diff --git a/drivers/platform/msm/ipa/teth_bridge.c b/drivers/platform/msm/ipa/teth_bridge.c
index 774c0e6..93f2366 100644
--- a/drivers/platform/msm/ipa/teth_bridge.c
+++ b/drivers/platform/msm/ipa/teth_bridge.c
@@ -60,6 +60,22 @@
#define TETH_MTU_BYTE 1500
+#define TETH_INACTIVITY_TIME_MSEC (1000)
+
+#define TETH_WORKQUEUE_NAME "tethering_bridge_wq"
+
+#define TETH_TOTAL_HDR_ENTRIES 6
+#define TETH_TOTAL_RT_ENTRIES_IP 3
+#define TETH_TOTAL_FLT_ENTRIES_IP 2
+#define TETH_IP_FAMILIES 2
+
+/**
+ * struct mac_addresses_type - store host PC and device MAC addresses
+ * @host_pc_mac_addr: MAC address of the host PC
+ * @host_pc_mac_addr_known: is the MAC address of the host PC known ?
+ * @device_mac_addr: MAC address of the device
+ * @device_mac_addr_known: is the MAC address of the device known ?
+ */
struct mac_addresses_type {
u8 host_pc_mac_addr[ETH_ALEN];
bool host_pc_mac_addr_known;
@@ -67,11 +83,61 @@
bool device_mac_addr_known;
};
+/**
+ * struct stats - driver statistics, viewable using debugfs
+ * @a2_to_usb_num_sw_tx_packets: number of packets bridged from A2 to USB using
+ * the SW bridge
+ * @usb_to_a2_num_sw_tx_packets: number of packets bridged from USB to A2 using
+ * the SW bridge
+ * @num_sw_tx_packets_during_resource_wakeup: number of packets bridged during a
+ * resource wakeup period, there is a special treatment for these kind of
+ * packets
+ */
struct stats {
u64 a2_to_usb_num_sw_tx_packets;
u64 usb_to_a2_num_sw_tx_packets;
+ u64 num_sw_tx_packets_during_resource_wakeup;
};
+/**
+ * struct teth_bridge_ctx - Tethering bridge driver context information
+ * @class: kernel class pointer
+ * @dev_num: kernel device number
+ * @dev: kernel device struct pointer
+ * @cdev: kernel character device struct
+ * @usb_ipa_pipe_hdl: USB to IPA pipe handle
+ * @ipa_usb_pipe_hdl: IPA to USB pipe handle
+ * @a2_ipa_pipe_hdl: A2 to IPA pipe handle
+ * @ipa_a2_pipe_hdl: IPA to A2 pipe handle
+ * @is_connected: is the tethered bridge connected ?
+ * @link_protocol: IP / Ethernet
+ * @mac_addresses: Struct which holds host pc and device MAC addresses, relevant
+ * in ethernet mode only
+ * @is_hw_bridge_complete: is HW bridge setup ?
+ * @aggr_params: aggregation parmeters
+ * @aggr_params_known: are the aggregation parameters known ?
+ * @tethering_mode: Rmnet / MBIM
+ * @is_bridge_prod_up: completion object signaled when the bridge producer
+ * finished its resource request procedure
+ * @is_bridge_prod_down: completion object signaled when the bridge producer
+ * finished its resource release procedure
+ * @comp_hw_bridge_work: used for setting up the HW bridge using a workqueue
+ * @comp_hw_bridge_in_progress: true when the HW bridge setup is in progress
+ * @aggr_caps: aggregation capabilities
+ * @stats: statistics, how many packets were transmitted using the SW bridge
+ * @teth_wq: dedicated workqueue, used for setting up the HW bridge and for
+ * sending packets using the SW bridge when the system is waking up from power
+ * collapse
+ * @a2_ipa_hdr_len: A2 to IPA header length, used for configuring the A2
+ * endpoint for header removal
+ * @ipa_a2_hdr_len: IPA to A2 header length, used for configuring the A2
+ * endpoint for header removal
+ * @hdr_del: array to store the headers handles in order to delete them later
+ * @routing_del: array of routing rules handles, one array for IPv4 and one for
+ * IPv6
+ * @filtering_del: array of routing rules handles, one array for IPv4 and one
+ * for IPv6
+ */
struct teth_bridge_ctx {
struct class *class;
dev_t dev_num;
@@ -94,15 +160,46 @@
bool comp_hw_bridge_in_progress;
struct teth_aggr_capabilities *aggr_caps;
struct stats stats;
+ struct workqueue_struct *teth_wq;
+ u16 a2_ipa_hdr_len;
+ u16 ipa_a2_hdr_len;
+ struct ipa_ioc_del_hdr *hdr_del;
+ struct ipa_ioc_del_rt_rule *routing_del[TETH_IP_FAMILIES];
+ struct ipa_ioc_del_flt_rule *filtering_del[TETH_IP_FAMILIES];
+};
+static struct teth_bridge_ctx *teth_ctx;
+
+enum teth_packet_direction {
+ TETH_USB_TO_A2,
+ TETH_A2_TO_USB,
};
-static struct teth_bridge_ctx *teth_ctx;
+/**
+ * struct teth_work - wrapper for an skb which is sent using a workqueue
+ * @work: used by the workqueue
+ * @skb: pointer to the skb to be sent
+ * @dir: direction of send, A2 to USB or USB to A2
+ */
+struct teth_work {
+ struct work_struct work;
+ struct sk_buff *skb;
+ enum teth_packet_direction dir;
+};
#ifdef CONFIG_DEBUG_FS
#define TETH_MAX_MSG_LEN 512
static char dbg_buff[TETH_MAX_MSG_LEN];
#endif
+/**
+ * add_eth_hdrs() - add Ethernet headers to IPA
+ * @hdr_name_ipv4: header name for IPv4
+ * @hdr_name_ipv6: header name for IPv6
+ * @src_mac_addr: source MAC address
+ * @dst_mac_addr: destination MAC address
+ *
+ * This function is called only when link protocol is Ethernet
+ */
static int add_eth_hdrs(char *hdr_name_ipv4, char *hdr_name_ipv6,
u8 *src_mac_addr, u8 *dst_mac_addr)
{
@@ -110,6 +207,7 @@
struct ipa_ioc_add_hdr *hdrs;
struct ethhdr hdr_ipv4;
struct ethhdr hdr_ipv6;
+ int idx1;
TETH_DBG_FUNC_ENTRY();
memcpy(hdr_ipv4.h_source, src_mac_addr, ETH_ALEN);
@@ -144,6 +242,13 @@
res = ipa_add_hdr(hdrs);
if (res || hdrs->hdr[0].status || hdrs->hdr[1].status)
TETH_ERR("Header insertion failed\n");
+
+ /* Save the headers handles in order to delete them later */
+ for (idx1 = 0; idx1 < hdrs->num_hdrs; idx1++) {
+ int idx2 = teth_ctx->hdr_del->num_hdls++;
+ teth_ctx->hdr_del->hdl[idx2].hdl = hdrs->hdr[idx1].hdr_hdl;
+ }
+
kfree(hdrs);
TETH_DBG_FUNC_EXIT();
@@ -169,6 +274,7 @@
}
hdr_cfg.hdr_len = a2_ipa_hdr_len;
+ teth_ctx->a2_ipa_hdr_len = a2_ipa_hdr_len;
res = ipa_cfg_ep_hdr(teth_ctx->a2_ipa_pipe_hdl, &hdr_cfg);
if (res) {
TETH_ERR("Header removal config for A2->IPA pipe failed\n");
@@ -184,6 +290,7 @@
}
hdr_cfg.hdr_len = ipa_a2_hdr_len;
+ teth_ctx->ipa_a2_hdr_len = ipa_a2_hdr_len;
res = ipa_cfg_ep_hdr(teth_ctx->ipa_a2_pipe_hdl, &hdr_cfg);
if (res) {
TETH_ERR("Header insertion config for IPA->A2 pipe failed\n");
@@ -200,6 +307,7 @@
int res;
struct ipa_ioc_add_hdr *mbim_hdr;
u8 mbim_stream_id = 0;
+ int idx;
TETH_DBG_FUNC_ENTRY();
mbim_hdr = kzalloc(sizeof(struct ipa_ioc_add_hdr) +
@@ -223,12 +331,23 @@
} else {
TETH_DBG("Added MBIM stream ID header\n");
}
+
+ /* Save the header handle in order to delete it later */
+ idx = teth_ctx->hdr_del->num_hdls++;
+ teth_ctx->hdr_del->hdl[idx].hdl = mbim_hdr->hdr[0].hdr_hdl;
+
kfree(mbim_hdr);
TETH_DBG_FUNC_EXIT();
return res;
}
+/**
+ * configure_ipa_header_block() - adds headers and configures endpoint registers
+ *
+ * - For IP link protocol and MBIM aggregation, configure MBIM header
+ * - For Ethernet link protocol, configure Ethernet headers
+ */
static int configure_ipa_header_block(void)
{
int res;
@@ -299,6 +418,7 @@
struct ipa_ioc_add_rt_rule *rt_rule;
struct ipa_ioc_get_hdr hdr_info;
int res;
+ int idx;
TETH_DBG_FUNC_ENTRY();
/* Get the header handle */
@@ -325,6 +445,12 @@
res = ipa_add_rt_rule(rt_rule);
if (res || rt_rule->rules[0].status)
TETH_ERR("Failed adding routing rule\n");
+
+ /* Save the routing rule handle in order to delete it later */
+ idx = teth_ctx->routing_del[ip_address_family]->num_hdls++;
+ teth_ctx->routing_del[ip_address_family]->hdl[idx].hdl =
+ rt_rule->rules[0].rt_rule_hdl;
+
kfree(rt_rule);
TETH_DBG_FUNC_EXIT();
@@ -365,6 +491,14 @@
return res;
}
+/**
+ * configure_ipa_routing_block() - Configure the IPA routing block
+ *
+ * This function configures IPA for:
+ * - Route all packets from USB to A2
+ * - Route all packets from A2 to USB
+ * - Use the correct headers in Ethernet or MBIM cases
+ */
static int configure_ipa_routing_block(void)
{
int res;
@@ -432,6 +566,7 @@
struct ipa_ioc_add_flt_rule *flt_tbl;
struct ipa_ioc_get_rt_tbl rt_tbl_info;
int res;
+ int idx;
TETH_DBG_FUNC_ENTRY();
/* Get the needed routing table handle */
@@ -462,6 +597,12 @@
res = ipa_add_flt_rule(flt_tbl);
if (res || flt_tbl->rules[0].status)
TETH_ERR("Failed adding filtering table\n");
+
+ /* Save the filtering rule handle in order to delete it later */
+ idx = teth_ctx->filtering_del[ip_address_family]->num_hdls++;
+ teth_ctx->filtering_del[ip_address_family]->hdl[idx].hdl =
+ flt_tbl->rules[0].flt_rule_hdl;
+
kfree(flt_tbl);
TETH_DBG_FUNC_EXIT();
@@ -493,6 +634,13 @@
return res;
}
+/**
+ * configure_ipa_filtering_block() - Configures IPA filtering block
+ *
+ * This function configures IPA for:
+ * - Filter all traffic coming from USB to A2 pointing routing table
+ * - Filter all traffic coming from A2 to USB pointing routing table
+ */
static int configure_ipa_filtering_block(void)
{
int res;
@@ -568,7 +716,6 @@
u32 pipe_hdl)
{
struct ipa_ep_cfg_aggr agg_params;
- struct ipa_ep_cfg_hdr hdr_params;
int res;
TETH_DBG_FUNC_ENTRY();
@@ -585,18 +732,7 @@
TETH_ERR("ipa_cfg_ep_aggr() failed\n");
goto bail;
}
-
- if (!client_is_prod) {
- memset(&hdr_params, 0, sizeof(hdr_params));
- hdr_params.hdr_len = 1;
- res = ipa_cfg_ep_hdr(pipe_hdl, &hdr_params);
- if (res) {
- TETH_ERR("ipa_cfg_ep_hdr() failed\n");
- goto bail;
- }
- }
TETH_DBG_FUNC_EXIT();
-
bail:
return res;
}
@@ -621,12 +757,30 @@
}
}
+/**
+ * teth_set_aggregation() - set aggregation parameters to IPA
+ *
+ * The parameters to this function are passed in the context variable ipa_ctx.
+ */
static int teth_set_aggregation(void)
{
int res;
char aggr_prot_str[20];
TETH_DBG_FUNC_ENTRY();
+ if (!teth_ctx->aggr_params_known) {
+ TETH_ERR("Aggregation parameters unknown.\n");
+ return -EINVAL;
+ }
+
+ if ((teth_ctx->usb_ipa_pipe_hdl == 0) ||
+ (teth_ctx->ipa_usb_pipe_hdl == 0))
+ return 0;
+ /*
+ * Returning 0 in case pipe handles are 0 becuase aggregation
+ * params will be set later
+ */
+
if (teth_ctx->aggr_params.ul.aggr_prot == TETH_AGGR_PROTOCOL_MBIM ||
teth_ctx->aggr_params.dl.aggr_prot == TETH_AGGR_PROTOCOL_MBIM) {
res = ipa_set_aggr_mode(IPA_MBIM);
@@ -672,6 +826,35 @@
return res;
}
+/**
+ * teth_request_resource() - wrapper function to
+ * ipa_rm_inactivity_timer_request_resource()
+ *
+ * - initialize the is_bridge_prod_up completion object
+ * - request the resource
+ * - error handling
+ */
+static int teth_request_resource(void)
+{
+ int res;
+
+ INIT_COMPLETION(teth_ctx->is_bridge_prod_up);
+ res = ipa_rm_inactivity_timer_request_resource(
+ IPA_RM_RESOURCE_BRIDGE_PROD);
+ if (res < 0) {
+ if (res == -EINPROGRESS)
+ wait_for_completion(&teth_ctx->is_bridge_prod_up);
+ else
+ return res;
+ }
+
+ return 0;
+}
+
+/**
+ * complete_hw_bridge() - setup the HW bridge from USB to A2 and back through
+ * IPA
+ */
static void complete_hw_bridge(struct work_struct *work)
{
int res;
@@ -682,6 +865,12 @@
"ETHERNET" :
"IP");
+ res = teth_request_resource();
+ if (res) {
+ TETH_ERR("request_resource() failed.\n");
+ goto bail;
+ }
+
res = teth_set_aggregation();
if (res) {
TETH_ERR("Failed setting aggregation params\n");
@@ -719,6 +908,7 @@
teth_ctx->is_hw_bridge_complete = true;
bail:
teth_ctx->comp_hw_bridge_in_progress = false;
+ ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
TETH_DBG_FUNC_EXIT();
return;
@@ -733,6 +923,20 @@
mac_addr[4], mac_addr[5]);
}
+/**
+ * check_to_complete_hw_bridge() - can HW bridge be set up ?
+ * @param skb: pointer to socket buffer
+ * @param my_mac_addr: pointer to write 'my' extracted MAC address to
+ * @param my_mac_addr_known: pointer to update whether 'my' extracted MAC
+ * address is known
+ * @param peer_mac_addr_known: pointer to update whether the 'peer' extracted
+ * MAC address is known
+ *
+ * This function is used by both A2 and USB callback functions, therefore the
+ * meaning of 'my' and 'peer' changes according to the context.
+ * Extracts MAC address from the packet in Ethernet link protocol,
+ * Sets up the HW bridge in case all conditions are met.
+ */
static void check_to_complete_hw_bridge(struct sk_buff *skb,
u8 *my_mac_addr,
bool *my_mac_addr_known,
@@ -758,10 +962,114 @@
(teth_ctx->aggr_params_known)) {
INIT_WORK(&teth_ctx->comp_hw_bridge_work, complete_hw_bridge);
teth_ctx->comp_hw_bridge_in_progress = true;
- schedule_work(&teth_ctx->comp_hw_bridge_work);
+ queue_work(teth_ctx->teth_wq, &teth_ctx->comp_hw_bridge_work);
}
}
+/**
+ * teth_send_skb_work() - workqueue function for sending a packet
+ */
+static void teth_send_skb_work(struct work_struct *work)
+{
+ struct teth_work *work_data =
+ container_of(work, struct teth_work, work);
+ int res;
+
+ res = teth_request_resource();
+ if (res) {
+ TETH_ERR("Packet send failure, dropping packet !\n");
+ goto bail;
+ }
+
+ switch (work_data->dir) {
+ case TETH_USB_TO_A2:
+ res = a2_mux_write(A2_MUX_TETHERED_0, work_data->skb);
+ if (res) {
+ TETH_ERR("Packet send failure, dropping packet !\n");
+ goto bail;
+ }
+ teth_ctx->stats.usb_to_a2_num_sw_tx_packets++;
+ break;
+
+ case TETH_A2_TO_USB:
+ res = ipa_tx_dp(IPA_CLIENT_USB_CONS, work_data->skb, NULL);
+ if (res) {
+ TETH_ERR("Packet send failure, dropping packet !\n");
+ goto bail;
+ }
+ teth_ctx->stats.a2_to_usb_num_sw_tx_packets++;
+ break;
+
+ default:
+ TETH_ERR("Unsupported direction to send !\n");
+ WARN_ON(1);
+ }
+ ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
+ kfree(work_data);
+ teth_ctx->stats.num_sw_tx_packets_during_resource_wakeup++;
+
+ return;
+bail:
+ ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
+ dev_kfree_skb(work_data->skb);
+ kfree(work_data);
+}
+
+/**
+ * defer_skb_send() - defer sending an skb using the SW bridge to a workqueue
+ * @param skb: pointer to the socket buffer
+ * @param dir: direction of send
+ *
+ * In case where during a packet send, the A2 or USB needs to wake up from power
+ * collapse, defer the send and return the context to IPA driver. This is
+ * important since IPA driver has a single threaded Rx path.
+ */
+static void defer_skb_send(struct sk_buff *skb, enum teth_packet_direction dir)
+{
+ struct teth_work *work = kmalloc(sizeof(struct teth_work), GFP_KERNEL);
+
+ if (!work) {
+ TETH_ERR("No mem, dropping packet\n");
+ dev_kfree_skb(skb);
+ ipa_rm_inactivity_timer_release_resource
+ (IPA_RM_RESOURCE_BRIDGE_PROD);
+ return;
+ }
+
+ /*
+ * Since IPA uses a single Rx thread, we don't
+ * want to wait for completion here
+ */
+ INIT_WORK(&work->work, teth_send_skb_work);
+ work->dir = dir;
+ work->skb = skb;
+ queue_work(teth_ctx->teth_wq, &work->work);
+}
+
+/**
+ * usb_notify_cb() - callback function for sending packets from USB to A2
+ * @param priv: private data
+ * @param evt: event - RECEIVE or WRITE_DONE
+ * @param data: pointer to skb to be sent
+ *
+ * This callback function is installed by the IPA driver, it is invoked in 2
+ * cases:
+ * 1. When a packet comes from the USB pipe and is routed to A5 (SW bridging)
+ * 2. After a packet has been bridged from USB to A2 and its skb should be freed
+ *
+ * Invocation: sps driver --> IPA driver --> bridge driver
+ *
+ * In the event of IPA_RECEIVE:
+ * - Checks whether the HW bridge can be set up..
+ * - Requests the BRIDGE_PROD resource so that A2 and USB are not in power
+ * collapse. In case where the resource is waking up, defer the send operation
+ * to a workqueue in order to not block the IPA driver single threaded Rx path.
+ * - Sends the packets to A2 using a2_service driver API.
+ * - Releases the BRIDGE_PROD resource.
+ *
+ * In the event of IPA_WRITE_DONE:
+ * - Frees the skb memory
+ */
static void usb_notify_cb(void *priv,
enum ipa_dp_evt_type evt,
unsigned long data)
@@ -778,13 +1086,36 @@
&teth_ctx->mac_addresses.host_pc_mac_addr_known,
&teth_ctx->mac_addresses.device_mac_addr_known);
- /* Send the packet to A2, using a2_service driver API */
- teth_ctx->stats.usb_to_a2_num_sw_tx_packets++;
+ /*
+ * Request the BRIDGE_PROD resource, send the packet and release
+ * the resource
+ */
+ res = ipa_rm_inactivity_timer_request_resource(
+ IPA_RM_RESOURCE_BRIDGE_PROD);
+ if (res < 0) {
+ if (res == -EINPROGRESS) {
+ /* The resource is waking up */
+ defer_skb_send(skb, TETH_USB_TO_A2);
+ } else {
+ TETH_ERR(
+ "Packet send failure, dropping packet !\n");
+ dev_kfree_skb(skb);
+ }
+ ipa_rm_inactivity_timer_release_resource(
+ IPA_RM_RESOURCE_BRIDGE_PROD);
+ return;
+ }
res = a2_mux_write(A2_MUX_TETHERED_0, skb);
if (res) {
TETH_ERR("Packet send failure, dropping packet !\n");
dev_kfree_skb(skb);
+ ipa_rm_inactivity_timer_release_resource(
+ IPA_RM_RESOURCE_BRIDGE_PROD);
+ return;
}
+ teth_ctx->stats.usb_to_a2_num_sw_tx_packets++;
+ ipa_rm_inactivity_timer_release_resource(
+ IPA_RM_RESOURCE_BRIDGE_PROD);
break;
case IPA_WRITE_DONE:
@@ -799,6 +1130,30 @@
return;
}
+/**
+ * a2_notify_cb() - callback function for sending packets from A2 to USB
+ * @param user_data: private data
+ * @param event: event - RECEIVE or WRITE_DONE
+ * @param data: pointer to skb to be sent
+ *
+ * This callback function is installed by the IPA driver, it is invoked in 2
+ * cases:
+ * 1. When a packet comes from the A2 pipe and is routed to A5 (SW bridging)
+ * 2. After a packet has been bridged from A2 to USB and its skb should be freed
+ *
+ * Invocation: sps driver --> IPA driver --> a2_service driver --> bridge driver
+ *
+ * In the event of A2_MUX_RECEIVE:
+ * - Checks whether the HW bridge can be set up..
+ * - Requests the BRIDGE_PROD resource so that A2 and USB are not in power
+ * collapse. In case where the resource is waking up, defer the send operation
+ * to a workqueue in order to not block the IPA driver single threaded Rx path.
+ * - Sends the packets to USB using IPA drivers ipa_tx_dp() API.
+ * - Releases the BRIDGE_PROD resource.
+ *
+ * In the event of A2_MUX_WRITE_DONE:
+ * - Frees the skb memory
+ */
static void a2_notify_cb(void *user_data,
enum a2_mux_event_type event,
unsigned long data)
@@ -816,13 +1171,37 @@
&teth_ctx->
mac_addresses.host_pc_mac_addr_known);
- /* Send the packet to USB */
- teth_ctx->stats.a2_to_usb_num_sw_tx_packets++;
+ /*
+ * Request the BRIDGE_PROD resource, send the packet and release
+ * the resource
+ */
+ res = ipa_rm_inactivity_timer_request_resource(
+ IPA_RM_RESOURCE_BRIDGE_PROD);
+ if (res < 0) {
+ if (res == -EINPROGRESS) {
+ /* The resource is waking up */
+ defer_skb_send(skb, TETH_A2_TO_USB);
+ } else {
+ TETH_ERR(
+ "Packet send failure, dropping packet !\n");
+ dev_kfree_skb(skb);
+ }
+ ipa_rm_inactivity_timer_release_resource(
+ IPA_RM_RESOURCE_BRIDGE_PROD);
+ return;
+ }
+
res = ipa_tx_dp(IPA_CLIENT_USB_CONS, skb, NULL);
if (res) {
TETH_ERR("Packet send failure, dropping packet !\n");
dev_kfree_skb(skb);
+ ipa_rm_inactivity_timer_release_resource(
+ IPA_RM_RESOURCE_BRIDGE_PROD);
+ return;
}
+ teth_ctx->stats.a2_to_usb_num_sw_tx_packets++;
+ ipa_rm_inactivity_timer_release_resource(
+ IPA_RM_RESOURCE_BRIDGE_PROD);
break;
case A2_MUX_WRITE_DONE:
@@ -837,6 +1216,15 @@
return;
}
+/**
+ * bridge_prod_notify_cb() - IPA Resource Manager callback function
+ * @param notify_cb_data: private data
+ * @param event: RESOURCE_GRANTED / RESOURCE_RELEASED
+ * @param data: not used in this case
+ *
+ * This callback function is called by IPA resource manager to notify the
+ * BRIDGE_PROD entity of events like RESOURCE_GRANTED and RESOURCE_RELEASED.
+ */
static void bridge_prod_notify_cb(void *notify_cb_data,
enum ipa_rm_event event,
unsigned long data)
@@ -859,12 +1247,61 @@
return;
}
+static void a2_prod_notify_cb(void *notify_cb_data,
+ enum ipa_rm_event event,
+ unsigned long data)
+{
+ int res;
+ struct ipa_ep_cfg ipa_ep_cfg;
+
+ switch (event) {
+ case IPA_RM_RESOURCE_GRANTED:
+ res = a2_mux_get_tethered_client_handles(
+ A2_MUX_TETHERED_0,
+ &teth_ctx->ipa_a2_pipe_hdl,
+ &teth_ctx->a2_ipa_pipe_hdl);
+ if (res) {
+ TETH_ERR(
+ "a2_mux_get_tethered_client_handles() failed, res = %d\n",
+ res);
+ return;
+ }
+
+ /* Reset the various endpoints configuration */
+ memset(&ipa_ep_cfg, 0, sizeof(ipa_ep_cfg));
+ ipa_ep_cfg.hdr.hdr_len = teth_ctx->ipa_a2_hdr_len;
+ ipa_cfg_ep(teth_ctx->ipa_a2_pipe_hdl, &ipa_ep_cfg);
+
+ memset(&ipa_ep_cfg, 0, sizeof(ipa_ep_cfg));
+ ipa_ep_cfg.hdr.hdr_len = teth_ctx->a2_ipa_hdr_len;
+ ipa_cfg_ep(teth_ctx->a2_ipa_pipe_hdl, &ipa_ep_cfg);
+ break;
+
+ case IPA_RM_RESOURCE_RELEASED:
+ break;
+
+ default:
+ TETH_ERR("Unsupported notification!\n");
+ WARN_ON(1);
+ break;
+ }
+
+ return;
+}
+
/**
* teth_bridge_init() - Initialize the Tethering bridge driver
-* @usb_notify_cb_ptr: Callback function which should be used
-* by the caller. Output parameter.
-* @private_data_ptr: Data for the callback function. Should
-* be used by the caller. Output parameter.
+* @usb_notify_cb_ptr: Callback function which should be used by the caller.
+* Output parameter.
+* @private_data_ptr: Data for the callback function. Should be used by the
+* caller. Output parameter.
+*
+* USB driver gets a pointer to a callback function (usb_notify_cb) and an
+* associated data. USB driver installs this callback function in the call to
+* ipa_connect().
+*
+* Builds IPA resource manager dependency graph.
+*
* Return codes: 0: success,
* -EINVAL - Bad parameter
* Other negative value - Failure
@@ -872,6 +1309,7 @@
int teth_bridge_init(ipa_notify_cb *usb_notify_cb_ptr, void **private_data_ptr)
{
int res = 0;
+ struct ipa_rm_register_params a2_prod_reg_params;
TETH_DBG_FUNC_ENTRY();
if (usb_notify_cb_ptr == NULL) {
@@ -886,34 +1324,48 @@
/* Build IPA Resource manager dependency graph */
res = ipa_rm_add_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
IPA_RM_RESOURCE_USB_CONS);
- if (res && res != -EEXIST) {
+ if (res && res != -EINPROGRESS) {
TETH_ERR("ipa_rm_add_dependency() failed\n");
goto bail;
}
res = ipa_rm_add_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
IPA_RM_RESOURCE_A2_CONS);
- if (res && res != -EEXIST) {
+ if (res && res != -EINPROGRESS) {
TETH_ERR("ipa_rm_add_dependency() failed\n");
goto fail_add_dependency_1;
}
res = ipa_rm_add_dependency(IPA_RM_RESOURCE_USB_PROD,
IPA_RM_RESOURCE_A2_CONS);
- if (res && res != -EEXIST) {
+ if (res && res != -EINPROGRESS) {
TETH_ERR("ipa_rm_add_dependency() failed\n");
goto fail_add_dependency_2;
}
res = ipa_rm_add_dependency(IPA_RM_RESOURCE_A2_PROD,
IPA_RM_RESOURCE_USB_CONS);
- if (res && res != -EEXIST) {
+ if (res && res != -EINPROGRESS) {
TETH_ERR("ipa_rm_add_dependency() failed\n");
goto fail_add_dependency_3;
}
+ /* Register for A2_PROD resource notifications */
+ a2_prod_reg_params.user_data = NULL;
+ a2_prod_reg_params.notify_cb = a2_prod_notify_cb;
+ res = ipa_rm_register(IPA_RM_RESOURCE_A2_PROD, &a2_prod_reg_params);
+ if (res) {
+ TETH_ERR("ipa_rm_register() failed\n");
+ goto fail_add_dependency_4;
+ }
+
+ /* Return 0 as EINPROGRESS is a valid return value at this point */
+ res = 0;
goto bail;
+fail_add_dependency_4:
+ ipa_rm_delete_dependency(IPA_RM_RESOURCE_A2_PROD,
+ IPA_RM_RESOURCE_USB_CONS);
fail_add_dependency_3:
ipa_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD,
IPA_RM_RESOURCE_A2_CONS);
@@ -930,46 +1382,155 @@
EXPORT_SYMBOL(teth_bridge_init);
/**
+ * initialize_context() - Initialize the ipa_ctx struct
+ */
+static void initialize_context(void)
+{
+ TETH_DBG_FUNC_ENTRY();
+ /* Initialize context variables */
+ teth_ctx->usb_ipa_pipe_hdl = 0;
+ teth_ctx->ipa_a2_pipe_hdl = 0;
+ teth_ctx->a2_ipa_pipe_hdl = 0;
+ teth_ctx->ipa_usb_pipe_hdl = 0;
+ teth_ctx->is_connected = false;
+
+ /* The default link protocol is Ethernet */
+ teth_ctx->link_protocol = TETH_LINK_PROTOCOL_ETHERNET;
+
+ memset(&teth_ctx->mac_addresses, 0, sizeof(teth_ctx->mac_addresses));
+ teth_ctx->is_hw_bridge_complete = false;
+ memset(&teth_ctx->aggr_params, 0, sizeof(teth_ctx->aggr_params));
+ teth_ctx->aggr_params_known = false;
+ teth_ctx->tethering_mode = 0;
+ INIT_COMPLETION(teth_ctx->is_bridge_prod_up);
+ INIT_COMPLETION(teth_ctx->is_bridge_prod_down);
+ teth_ctx->comp_hw_bridge_in_progress = false;
+ memset(&teth_ctx->stats, 0, sizeof(teth_ctx->stats));
+ teth_ctx->a2_ipa_hdr_len = 0;
+ teth_ctx->ipa_a2_hdr_len = 0;
+ memset(teth_ctx->hdr_del,
+ 0,
+ sizeof(struct ipa_ioc_del_hdr) + TETH_TOTAL_HDR_ENTRIES *
+ sizeof(struct ipa_hdr_del));
+ memset(teth_ctx->routing_del[IPA_IP_v4],
+ 0,
+ sizeof(struct ipa_ioc_del_rt_rule) +
+ TETH_TOTAL_RT_ENTRIES_IP * sizeof(struct ipa_rt_rule_del));
+ teth_ctx->routing_del[IPA_IP_v4]->ip = IPA_IP_v4;
+ memset(teth_ctx->routing_del[IPA_IP_v6],
+ 0,
+ sizeof(struct ipa_ioc_del_rt_rule) +
+ TETH_TOTAL_RT_ENTRIES_IP * sizeof(struct ipa_rt_rule_del));
+ teth_ctx->routing_del[IPA_IP_v6]->ip = IPA_IP_v6;
+ memset(teth_ctx->filtering_del[IPA_IP_v4],
+ 0,
+ sizeof(struct ipa_ioc_del_flt_rule) +
+ TETH_TOTAL_FLT_ENTRIES_IP * sizeof(struct ipa_flt_rule_del));
+ teth_ctx->filtering_del[IPA_IP_v4]->ip = IPA_IP_v4;
+ memset(teth_ctx->filtering_del[IPA_IP_v6],
+ 0,
+ sizeof(struct ipa_ioc_del_flt_rule) +
+ TETH_TOTAL_FLT_ENTRIES_IP * sizeof(struct ipa_flt_rule_del));
+ teth_ctx->filtering_del[IPA_IP_v6]->ip = IPA_IP_v6;
+
+ TETH_DBG_FUNC_EXIT();
+}
+
+/**
* teth_bridge_disconnect() - Disconnect tethering bridge module
-*
-* Return codes: 0: success
-* -EPERM: Operation not permitted as the bridge is already
-* disconnected
*/
int teth_bridge_disconnect(void)
{
- int res = -EPERM;
+ int res;
+ struct ipa_rm_register_params a2_prod_reg_params;
TETH_DBG_FUNC_ENTRY();
if (!teth_ctx->is_connected) {
TETH_ERR(
- "Trying to disconnect an already disconnected bridge\n");
+ "Trying to disconnect an already disconnected bridge\n");
goto bail;
}
- teth_ctx->is_connected = false;
-
- res = ipa_rm_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
- if (res == -EINPROGRESS)
- wait_for_completion(&teth_ctx->is_bridge_prod_down);
-
- /* Initialize statistics */
- memset(&teth_ctx->stats, 0, sizeof(teth_ctx->stats));
-
- /* Delete IPA Resource manager dependency graph */
+ /*
+ * Delete part of IPA resource manager dependency graph. Only the
+ * BRIDGE_PROD <-> A2 dependency remains intact
+ */
res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
IPA_RM_RESOURCE_USB_CONS);
- res |= ipa_rm_delete_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
- IPA_RM_RESOURCE_A2_CONS);
- res |= ipa_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD,
- IPA_RM_RESOURCE_A2_CONS);
- res |= ipa_rm_delete_dependency(IPA_RM_RESOURCE_A2_PROD,
- IPA_RM_RESOURCE_USB_CONS);
+ if ((res != 0) && (res != -EINPROGRESS))
+ TETH_ERR(
+ "Failed deleting ipa_rm dependency BRIDGE_PROD <-> USB_CONS\n");
+ res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD,
+ IPA_RM_RESOURCE_A2_CONS);
+ if ((res != 0) && (res != -EINPROGRESS))
+ TETH_ERR(
+ "Failed deleting ipa_rm dependency USB_PROD <-> A2_CONS\n");
+ res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_A2_PROD,
+ IPA_RM_RESOURCE_USB_CONS);
+ if ((res != 0) && (res != -EINPROGRESS))
+ TETH_ERR(
+ "Failed deleting ipa_rm dependency A2_PROD <-> USB_CONS\n");
+
+ /* Request the BRIDGE_PROD resource, A2 and IPA should power up */
+ res = teth_request_resource();
+ if (res) {
+ TETH_ERR("request_resource() failed.\n");
+ goto bail;
+ }
+
+ /* Close the channel to A2 */
+ if (a2_mux_close_channel(A2_MUX_TETHERED_0))
+ TETH_ERR("a2_mux_close_channel() failed\n");
+
+ /* Teardown the IPA HW bridge */
+ if (teth_ctx->is_hw_bridge_complete) {
+ /* Delete header entries */
+ if (ipa_del_hdr(teth_ctx->hdr_del))
+ TETH_ERR("ipa_del_hdr() failed\n");
+
+ /* Delete installed routing rules */
+ if (ipa_del_rt_rule(teth_ctx->routing_del[IPA_IP_v4]))
+ TETH_ERR("ipa_del_rt_rule() failed\n");
+ if (ipa_del_rt_rule(teth_ctx->routing_del[IPA_IP_v6]))
+ TETH_ERR("ipa_del_rt_rule() failed\n");
+
+ /* Delete installed filtering rules */
+ if (ipa_del_flt_rule(teth_ctx->filtering_del[IPA_IP_v4]))
+ TETH_ERR("ipa_del_flt_rule() failed\n");
+ if (ipa_del_flt_rule(teth_ctx->filtering_del[IPA_IP_v6]))
+ TETH_ERR("ipa_del_flt_rule() failed\n");
+
+ /*
+ * Commit all the data to HW, including header, routing and
+ * filtering blocks, IPv4 and IPv6
+ */
+ if (ipa_commit_hdr())
+ TETH_ERR("Failed committing headers\n");
+ }
+
+ initialize_context();
+
+ ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
+
+ /* Delete the last ipa_rm dependency - BRIDGE_PROD <-> A2 */
+ res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
+ IPA_RM_RESOURCE_A2_CONS);
+ if ((res != 0) && (res != -EINPROGRESS))
+ TETH_ERR(
+ "Failed deleting ipa_rm dependency BRIDGE_PROD <-> A2_CONS\n");
+
+ /* Deregister from A2_PROD notifications */
+ a2_prod_reg_params.user_data = NULL;
+ a2_prod_reg_params.notify_cb = a2_prod_notify_cb;
+ res = ipa_rm_deregister(IPA_RM_RESOURCE_A2_PROD, &a2_prod_reg_params);
if (res)
- TETH_ERR("Failed deleting ipa_rm dependency.\n");
+ TETH_ERR("Failed deregistering from A2_prod notifications.\n");
+
+ teth_ctx->is_connected = false;
bail:
TETH_DBG_FUNC_EXIT();
- return res;
+
+ return 0;
}
EXPORT_SYMBOL(teth_bridge_disconnect);
@@ -1003,12 +1564,10 @@
teth_ctx->usb_ipa_pipe_hdl = connect_params->usb_ipa_pipe_hdl;
teth_ctx->tethering_mode = connect_params->tethering_mode;
- res = ipa_rm_request_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
- if (res < 0) {
- if (res == -EINPROGRESS)
- wait_for_completion(&teth_ctx->is_bridge_prod_up);
- else
- goto bail;
+ res = teth_request_resource();
+ if (res) {
+ TETH_ERR("request_resource() failed.\n");
+ goto bail;
}
res = a2_mux_open_channel(A2_MUX_TETHERED_0,
@@ -1039,10 +1598,28 @@
if (teth_ctx->tethering_mode == TETH_TETHERING_MODE_MBIM)
teth_ctx->link_protocol = TETH_LINK_PROTOCOL_IP;
- TETH_DBG_FUNC_EXIT();
+
+ if (teth_ctx->aggr_params_known) {
+ res = teth_set_aggregation();
+ if (res) {
+ TETH_ERR("Failed setting aggregation params\n");
+ goto bail;
+ }
+ }
+
+ /* In case of IP link protocol, complete HW bridge */
+ if ((teth_ctx->link_protocol == TETH_LINK_PROTOCOL_IP) &&
+ (!teth_ctx->comp_hw_bridge_in_progress) &&
+ (teth_ctx->aggr_params_known) &&
+ (!teth_ctx->is_hw_bridge_complete)) {
+ INIT_WORK(&teth_ctx->comp_hw_bridge_work, complete_hw_bridge);
+ teth_ctx->comp_hw_bridge_in_progress = true;
+ queue_work(teth_ctx->teth_wq, &teth_ctx->comp_hw_bridge_work);
+ }
bail:
- if (res)
- ipa_rm_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
+ ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
+ TETH_DBG_FUNC_EXIT();
+
return res;
}
EXPORT_SYMBOL(teth_bridge_connect);
@@ -1057,6 +1634,9 @@
TETH_AGGR_MAX_AGGR_PACKET_SIZE_DEFAULT;
}
+/**
+ * teth_set_bridge_mode() - set the link protocol (IP / Ethernet)
+ */
static void teth_set_bridge_mode(enum teth_link_protocol_type link_protocol)
{
teth_ctx->link_protocol = link_protocol;
@@ -1064,10 +1644,19 @@
memset(&teth_ctx->mac_addresses, 0, sizeof(teth_ctx->mac_addresses));
}
+/**
+ * teth_bridge_set_aggr_params() - kernel API to set aggregation parameters
+ * @param aggr_params: aggregation parmeters for uplink and downlink
+ *
+ * Besides setting the aggregation parameters, the function enforces max tranfer
+ * size which is less then 8K and also forbids Ethernet link protocol with MBIM
+ * aggregation which is not supported by HW.
+ */
int teth_bridge_set_aggr_params(struct teth_aggr_params *aggr_params)
{
int res;
+ TETH_DBG_FUNC_ENTRY();
if (!aggr_params) {
TETH_ERR("Invalid parameter\n");
return -EINVAL;
@@ -1086,6 +1675,20 @@
aggr_params->ul.max_transfer_size_byte =
TETH_AGGR_MAX_AGGR_PACKET_SIZE_DEFAULT;
+ /* Ethernet link protocol and MBIM aggregation is not supported */
+ if (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET &&
+ (aggr_params->dl.aggr_prot == TETH_AGGR_PROTOCOL_MBIM ||
+ aggr_params->ul.aggr_prot == TETH_AGGR_PROTOCOL_MBIM)) {
+ TETH_ERR("Ethernet with MBIM is not supported.\n");
+ return -EINVAL;
+ }
+
+ res = teth_request_resource();
+ if (res) {
+ TETH_ERR("request_resource() failed.\n");
+ return res;
+ }
+
memcpy(&teth_ctx->aggr_params,
aggr_params,
sizeof(struct teth_aggr_params));
@@ -1097,6 +1700,9 @@
if (res)
TETH_ERR("Failed setting aggregation params\n");
+ ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
+ TETH_DBG_FUNC_EXIT();
+
return res;
}
EXPORT_SYMBOL(teth_bridge_set_aggr_params);
@@ -1135,6 +1741,19 @@
}
res = teth_bridge_set_aggr_params(&aggr_params);
+ if (res)
+ break;
+
+ /* In case of IP link protocol, complete HW bridge */
+ if ((teth_ctx->link_protocol == TETH_LINK_PROTOCOL_IP) &&
+ (!teth_ctx->comp_hw_bridge_in_progress) &&
+ (!teth_ctx->is_hw_bridge_complete)) {
+ INIT_WORK(&teth_ctx->comp_hw_bridge_work,
+ complete_hw_bridge);
+ teth_ctx->comp_hw_bridge_in_progress = true;
+ queue_work(teth_ctx->teth_wq,
+ &teth_ctx->comp_hw_bridge_work);
+ }
break;
case TETH_BRIDGE_IOC_GET_AGGR_PARAMS:
@@ -1190,7 +1809,11 @@
return res;
}
-static void set_aggr_capabilities(void)
+/**
+ * set_aggr_capabilities() - allocates and fills the aggregation capabilities
+ * struct
+ */
+static int set_aggr_capabilities(void)
{
u16 NUM_PROTOCOLS = 2;
@@ -1198,9 +1821,9 @@
NUM_PROTOCOLS *
sizeof(struct teth_aggr_params_link),
GFP_KERNEL);
- if (teth_ctx->aggr_caps == NULL) {
+ if (!teth_ctx->aggr_caps) {
TETH_ERR("Memory alloc failed for aggregation capabilities.\n");
- return;
+ return -ENOMEM;
}
teth_ctx->aggr_caps->num_protocols = NUM_PROTOCOLS;
@@ -1210,8 +1833,15 @@
teth_ctx->aggr_caps->prot_caps[1].aggr_prot = TETH_AGGR_PROTOCOL_TLP;
set_aggr_default_params(&teth_ctx->aggr_caps->prot_caps[1]);
+
+ return 0;
}
+/**
+* teth_bridge_get_client_handles() - Get USB <--> IPA pipe handles
+* @producer_handle: USB --> IPA pipe handle
+* @consumer_handle: IPA --> USB pipe handle
+*/
void teth_bridge_get_client_handles(u32 *producer_handle,
u32 *consumer_handle)
{
@@ -1379,6 +2009,11 @@
TETH_MAX_MSG_LEN - nbytes,
"A2 to USB SW Tx packets: %lld\n",
teth_ctx->stats.a2_to_usb_num_sw_tx_packets);
+ nbytes += scnprintf(
+ &dbg_buff[nbytes],
+ TETH_MAX_MSG_LEN - nbytes,
+ "SW Tx packets sent during resource wakeup: %lld\n",
+ teth_ctx->stats.num_sw_tx_packets_during_resource_wakeup);
return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
}
@@ -1505,7 +2140,59 @@
return -ENOMEM;
}
- set_aggr_capabilities();
+ res = set_aggr_capabilities();
+ if (res) {
+ TETH_ERR("kzalloc err.\n");
+ goto fail_alloc_aggr_caps;
+ }
+
+ res = -ENOMEM;
+ teth_ctx->hdr_del = kzalloc(sizeof(struct ipa_ioc_del_hdr) +
+ TETH_TOTAL_HDR_ENTRIES *
+ sizeof(struct ipa_hdr_del),
+ GFP_KERNEL);
+ if (!teth_ctx->hdr_del) {
+ TETH_ERR("kzalloc err.\n");
+ goto fail_alloc_hdr_del;
+ }
+
+ teth_ctx->routing_del[IPA_IP_v4] =
+ kzalloc(sizeof(struct ipa_ioc_del_rt_rule) +
+ TETH_TOTAL_RT_ENTRIES_IP *
+ sizeof(struct ipa_rt_rule_del),
+ GFP_KERNEL);
+ if (!teth_ctx->routing_del[IPA_IP_v4]) {
+ TETH_ERR("kzalloc err.\n");
+ goto fail_alloc_routing_del_ipv4;
+ }
+ teth_ctx->routing_del[IPA_IP_v6] =
+ kzalloc(sizeof(struct ipa_ioc_del_rt_rule) +
+ TETH_TOTAL_RT_ENTRIES_IP *
+ sizeof(struct ipa_rt_rule_del),
+ GFP_KERNEL);
+ if (!teth_ctx->routing_del[IPA_IP_v6]) {
+ TETH_ERR("kzalloc err.\n");
+ goto fail_alloc_routing_del_ipv6;
+ }
+
+ teth_ctx->filtering_del[IPA_IP_v4] =
+ kzalloc(sizeof(struct ipa_ioc_del_flt_rule) +
+ TETH_TOTAL_FLT_ENTRIES_IP *
+ sizeof(struct ipa_flt_rule_del),
+ GFP_KERNEL);
+ if (!teth_ctx->filtering_del[IPA_IP_v4]) {
+ TETH_ERR("kzalloc err.\n");
+ goto fail_alloc_filtering_del_ipv4;
+ }
+ teth_ctx->filtering_del[IPA_IP_v6] =
+ kzalloc(sizeof(struct ipa_ioc_del_flt_rule) +
+ TETH_TOTAL_FLT_ENTRIES_IP *
+ sizeof(struct ipa_flt_rule_del),
+ GFP_KERNEL);
+ if (!teth_ctx->filtering_del[IPA_IP_v6]) {
+ TETH_ERR("kzalloc err.\n");
+ goto fail_alloc_filtering_del_ipv6;
+ }
teth_ctx->class = class_create(THIS_MODULE, TETH_BRIDGE_DRV_NAME);
@@ -1536,8 +2223,6 @@
goto fail_cdev_add;
}
- teth_ctx->comp_hw_bridge_in_progress = false;
-
teth_debugfs_init();
/* Create BRIDGE_PROD entity in IPA Resource Manager */
@@ -1552,9 +2237,21 @@
init_completion(&teth_ctx->is_bridge_prod_up);
init_completion(&teth_ctx->is_bridge_prod_down);
- /* The default link protocol is Ethernet */
- teth_ctx->link_protocol = TETH_LINK_PROTOCOL_ETHERNET;
+ res = ipa_rm_inactivity_timer_init(IPA_RM_RESOURCE_BRIDGE_PROD,
+ TETH_INACTIVITY_TIME_MSEC);
+ if (res) {
+ TETH_ERR("ipa_rm_inactivity_timer_init() failed, res=%d\n",
+ res);
+ goto fail_cdev_add;
+ }
+ teth_ctx->teth_wq = create_workqueue(TETH_WORKQUEUE_NAME);
+ if (!teth_ctx->teth_wq) {
+ TETH_ERR("workqueue creation failed\n");
+ goto fail_cdev_add;
+ }
+
+ initialize_context();
TETH_DBG("Tethering bridge driver init OK\n");
return 0;
@@ -1563,7 +2260,18 @@
fail_device_create:
unregister_chrdev_region(teth_ctx->dev_num, 1);
fail_alloc_chrdev_region:
+ kfree(teth_ctx->filtering_del[IPA_IP_v6]);
+fail_alloc_filtering_del_ipv6:
+ kfree(teth_ctx->filtering_del[IPA_IP_v4]);
+fail_alloc_filtering_del_ipv4:
+ kfree(teth_ctx->routing_del[IPA_IP_v6]);
+fail_alloc_routing_del_ipv6:
+ kfree(teth_ctx->routing_del[IPA_IP_v4]);
+fail_alloc_routing_del_ipv4:
+ kfree(teth_ctx->hdr_del);
+fail_alloc_hdr_del:
kfree(teth_ctx->aggr_caps);
+fail_alloc_aggr_caps:
kfree(teth_ctx);
teth_ctx = NULL;
diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c
index 1907adc..d378838 100644
--- a/drivers/platform/msm/qpnp-power-on.c
+++ b/drivers/platform/msm/qpnp-power-on.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -22,49 +22,62 @@
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/log2.h>
+#include <linux/qpnp/power-on.h>
+
+/* Common PNP defines */
+#define QPNP_PON_REVISION2(base) (base + 0x01)
/* PON common register addresses */
-#define QPNP_PON_RT_STS(base) (base + 0x10)
-#define QPNP_PON_PULL_CTL(base) (base + 0x70)
-#define QPNP_PON_DBC_CTL(base) (base + 0x71)
+#define QPNP_PON_RT_STS(base) (base + 0x10)
+#define QPNP_PON_PULL_CTL(base) (base + 0x70)
+#define QPNP_PON_DBC_CTL(base) (base + 0x71)
/* PON/RESET sources register addresses */
-#define QPNP_PON_KPDPWR_S1_TIMER(base) (base + 0x40)
-#define QPNP_PON_KPDPWR_S2_TIMER(base) (base + 0x41)
-#define QPNP_PON_KPDPWR_S2_CNTL(base) (base + 0x42)
-#define QPNP_PON_RESIN_S1_TIMER(base) (base + 0x44)
-#define QPNP_PON_RESIN_S2_TIMER(base) (base + 0x45)
-#define QPNP_PON_RESIN_S2_CNTL(base) (base + 0x46)
-#define QPNP_PON_PS_HOLD_RST_CTL(base) (base + 0x5A)
+#define QPNP_PON_WARM_RESET_REASON1(base) (base + 0xA)
+#define QPNP_PON_WARM_RESET_REASON2(base) (base + 0xB)
+#define QPNP_PON_KPDPWR_S1_TIMER(base) (base + 0x40)
+#define QPNP_PON_KPDPWR_S2_TIMER(base) (base + 0x41)
+#define QPNP_PON_KPDPWR_S2_CNTL(base) (base + 0x42)
+#define QPNP_PON_KPDPWR_S2_CNTL2(base) (base + 0x43)
+#define QPNP_PON_RESIN_S1_TIMER(base) (base + 0x44)
+#define QPNP_PON_RESIN_S2_TIMER(base) (base + 0x45)
+#define QPNP_PON_RESIN_S2_CNTL(base) (base + 0x46)
+#define QPNP_PON_RESIN_S2_CNTL2(base) (base + 0x47)
+#define QPNP_PON_PS_HOLD_RST_CTL(base) (base + 0x5A)
+#define QPNP_PON_PS_HOLD_RST_CTL2(base) (base + 0x5B)
+#define QPNP_PON_TRIGGER_EN(base) (base + 0x80)
-#define QPNP_PON_RESIN_PULL_UP BIT(0)
-#define QPNP_PON_KPDPWR_PULL_UP BIT(1)
-#define QPNP_PON_CBLPWR_PULL_UP BIT(2)
-#define QPNP_PON_S2_CNTL_EN BIT(7)
-#define QPNP_PON_S2_RESET_ENABLE BIT(7)
-#define QPNP_PON_DELAY_BIT_SHIFT 6
+#define QPNP_PON_WARM_RESET_TFT BIT(4)
-#define QPNP_PON_S1_TIMER_MASK (0xF)
-#define QPNP_PON_S2_TIMER_MASK (0x7)
-#define QPNP_PON_S2_CNTL_TYPE_MASK (0xF)
+#define QPNP_PON_RESIN_PULL_UP BIT(0)
+#define QPNP_PON_KPDPWR_PULL_UP BIT(1)
+#define QPNP_PON_CBLPWR_PULL_UP BIT(2)
+#define QPNP_PON_S2_CNTL_EN BIT(7)
+#define QPNP_PON_S2_RESET_ENABLE BIT(7)
+#define QPNP_PON_DELAY_BIT_SHIFT 6
-#define QPNP_PON_DBC_DELAY_MASK (0x7)
-#define QPNP_PON_KPDPWR_N_SET BIT(0)
-#define QPNP_PON_RESIN_N_SET BIT(1)
-#define QPNP_PON_CBLPWR_N_SET BIT(2)
-#define QPNP_PON_RESIN_BARK_N_SET BIT(4)
+#define QPNP_PON_S1_TIMER_MASK (0xF)
+#define QPNP_PON_S2_TIMER_MASK (0x7)
+#define QPNP_PON_S2_CNTL_TYPE_MASK (0xF)
-#define QPNP_PON_RESET_EN BIT(7)
-#define QPNP_PON_WARM_RESET BIT(0)
-#define QPNP_PON_SHUTDOWN BIT(2)
+#define QPNP_PON_DBC_DELAY_MASK (0x7)
+#define QPNP_PON_KPDPWR_N_SET BIT(0)
+#define QPNP_PON_RESIN_N_SET BIT(1)
+#define QPNP_PON_CBLPWR_N_SET BIT(2)
+#define QPNP_PON_RESIN_BARK_N_SET BIT(4)
+
+#define QPNP_PON_RESET_EN BIT(7)
+#define QPNP_PON_WARM_RESET BIT(0)
+#define QPNP_PON_SHUTDOWN BIT(2)
/* Ranges */
-#define QPNP_PON_S1_TIMER_MAX 10256
-#define QPNP_PON_S2_TIMER_MAX 2000
-#define QPNP_PON_RESET_TYPE_MAX 0xF
-#define PON_S1_COUNT_MAX 0xF
+#define QPNP_PON_S1_TIMER_MAX 10256
+#define QPNP_PON_S2_TIMER_MAX 2000
+#define QPNP_PON_RESET_TYPE_MAX 0xF
+#define PON_S1_COUNT_MAX 0xF
-#define QPNP_KEY_STATUS_DELAY msecs_to_jiffies(250)
+#define QPNP_KEY_STATUS_DELAY msecs_to_jiffies(250)
+#define QPNP_PON_REV_B 0x01
enum pon_type {
PON_KPDPWR,
@@ -82,6 +95,8 @@
u32 pull_up;
u32 state_irq;
u32 bark_irq;
+ u16 s2_cntl_addr;
+ u16 s2_cntl2_addr;
};
struct qpnp_pon {
@@ -136,17 +151,31 @@
int qpnp_pon_system_pwr_off(bool reset)
{
int rc;
+ u8 reg;
+ u16 rst_en_reg;
struct qpnp_pon *pon = sys_reset_dev;
if (!pon)
return -ENODEV;
- rc = qpnp_pon_masked_write(pon, QPNP_PON_PS_HOLD_RST_CTL(pon->base),
- QPNP_PON_RESET_EN, 0);
+ rc = spmi_ext_register_readl(pon->spmi->ctrl, pon->spmi->sid,
+ QPNP_PON_REVISION2(pon->base), ®, 1);
+ if (rc) {
+ dev_err(&pon->spmi->dev,
+ "Unable to read addr=%x, rc(%d)\n",
+ QPNP_PON_REVISION2(pon->base), rc);
+ return rc;
+ }
+
+ if (reg == 0x00)
+ rst_en_reg = QPNP_PON_PS_HOLD_RST_CTL(pon->base);
+ else
+ rst_en_reg = QPNP_PON_PS_HOLD_RST_CTL2(pon->base);
+
+ rc = qpnp_pon_masked_write(pon, rst_en_reg, QPNP_PON_RESET_EN, 0);
if (rc)
dev_err(&pon->spmi->dev,
- "Unable to write to addr=%x, rc(%d)\n",
- QPNP_PON_PS_HOLD_RST_CTL(pon->base), rc);
+ "Unable to write to addr=%x, rc(%d)\n", rst_en_reg, rc);
/*
* We need 10 sleep clock cycles here. But since the clock is
@@ -163,18 +192,93 @@
"Unable to write to addr=%x, rc(%d)\n",
QPNP_PON_PS_HOLD_RST_CTL(pon->base), rc);
- rc = qpnp_pon_masked_write(pon, QPNP_PON_PS_HOLD_RST_CTL(pon->base),
- QPNP_PON_RESET_EN,
- QPNP_PON_RESET_EN);
+ rc = qpnp_pon_masked_write(pon, rst_en_reg, QPNP_PON_RESET_EN,
+ QPNP_PON_RESET_EN);
if (rc)
dev_err(&pon->spmi->dev,
- "Unable to write to addr=%x, rc(%d)\n",
- QPNP_PON_PS_HOLD_RST_CTL(pon->base), rc);
+ "Unable to write to addr=%x, rc(%d)\n", rst_en_reg, rc);
return rc;
}
EXPORT_SYMBOL(qpnp_pon_system_pwr_off);
+/**
+ * qpnp_pon_is_warm_reset - Checks if the PMIC went through a warm reset.
+ *
+ * Returns > 0 for warm resets, 0 for not warm reset, < 0 for errors
+ *
+ * Note that this function will only return the warm vs not-warm reset status
+ * of the PMIC that is configured as the system-reset device.
+ */
+int qpnp_pon_is_warm_reset(void)
+{
+ struct qpnp_pon *pon = sys_reset_dev;
+ int rc;
+ u8 reg;
+
+ if (!pon)
+ return -EPROBE_DEFER;
+
+ rc = spmi_ext_register_readl(pon->spmi->ctrl, pon->spmi->sid,
+ QPNP_PON_WARM_RESET_REASON1(pon->base), ®, 1);
+ if (rc) {
+ dev_err(&pon->spmi->dev,
+ "Unable to read addr=%x, rc(%d)\n",
+ QPNP_PON_WARM_RESET_REASON1(pon->base), rc);
+ return rc;
+ }
+
+ if (reg)
+ return 1;
+
+ rc = spmi_ext_register_readl(pon->spmi->ctrl, pon->spmi->sid,
+ QPNP_PON_WARM_RESET_REASON2(pon->base), ®, 1);
+ if (rc) {
+ dev_err(&pon->spmi->dev,
+ "Unable to read addr=%x, rc(%d)\n",
+ QPNP_PON_WARM_RESET_REASON2(pon->base), rc);
+ return rc;
+ }
+ if (reg & QPNP_PON_WARM_RESET_TFT)
+ return 1;
+
+ return 0;
+}
+EXPORT_SYMBOL(qpnp_pon_is_warm_reset);
+
+/**
+ * qpnp_pon_trigger_config - Configures (enable/disable) the PON trigger source
+ * @pon_src: PON source to be configured
+ * @enable: to enable or disable the PON trigger
+ *
+ * This function configures the power-on trigger capability of a
+ * PON source. If a specific PON trigger is disabled it cannot act
+ * as a power-on source to the PMIC.
+ */
+
+int qpnp_pon_trigger_config(enum pon_trigger_source pon_src, bool enable)
+{
+ struct qpnp_pon *pon = sys_reset_dev;
+ int rc;
+
+ if (!pon)
+ return -EPROBE_DEFER;
+
+ if (pon_src < PON_SMPL || pon_src > PON_KPDPWR_N) {
+ dev_err(&pon->spmi->dev, "Invalid PON source\n");
+ return -EINVAL;
+ }
+
+ rc = qpnp_pon_masked_write(pon, QPNP_PON_TRIGGER_EN(pon->base),
+ BIT(pon_src), enable ? BIT(pon_src) : 0);
+ if (rc)
+ dev_err(&pon->spmi->dev, "Unable to write to addr=%x, rc(%d)\n",
+ QPNP_PON_TRIGGER_EN(pon->base), rc);
+
+ return rc;
+}
+EXPORT_SYMBOL(qpnp_pon_trigger_config);
+
static struct qpnp_pon_config *
qpnp_get_cfg(struct qpnp_pon *pon, u32 pon_type)
{
@@ -280,8 +384,14 @@
struct qpnp_pon *pon =
container_of(work, struct qpnp_pon, bark_work.work);
+ cfg = qpnp_get_cfg(pon, PON_RESIN);
+ if (!cfg) {
+ dev_err(&pon->spmi->dev, "Invalid config pointer\n");
+ goto err_return;
+ }
+
/* enable reset */
- rc = qpnp_pon_masked_write(pon, QPNP_PON_RESIN_S2_CNTL(pon->base),
+ rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr,
QPNP_PON_S2_CNTL_EN, QPNP_PON_S2_CNTL_EN);
if (rc) {
dev_err(&pon->spmi->dev, "Unable to configure S2 enable\n");
@@ -298,19 +408,13 @@
}
if (!(pon_rt_sts & QPNP_PON_RESIN_BARK_N_SET)) {
- cfg = qpnp_get_cfg(pon, PON_RESIN);
- if (!cfg) {
- dev_err(&pon->spmi->dev, "Invalid config pointer\n");
- goto err_return;
- }
/* report the key event and enable the bark IRQ */
input_report_key(pon->pon_input, cfg->key_code, 0);
input_sync(pon->pon_input);
enable_irq(cfg->bark_irq);
} else {
/* disable reset */
- rc = qpnp_pon_masked_write(pon,
- QPNP_PON_RESIN_S2_CNTL(pon->base),
+ rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr,
QPNP_PON_S2_CNTL_EN, 0);
if (rc) {
dev_err(&pon->spmi->dev,
@@ -334,20 +438,20 @@
/* disable the bark interrupt */
disable_irq_nosync(irq);
- /* disable reset */
- rc = qpnp_pon_masked_write(pon, QPNP_PON_RESIN_S2_CNTL(pon->base),
- QPNP_PON_S2_CNTL_EN, 0);
- if (rc) {
- dev_err(&pon->spmi->dev, "Unable to configure S2 enable\n");
- goto err_exit;
- }
-
cfg = qpnp_get_cfg(pon, PON_RESIN);
if (!cfg) {
dev_err(&pon->spmi->dev, "Invalid config pointer\n");
goto err_exit;
}
+ /* disable reset */
+ rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr,
+ QPNP_PON_S2_CNTL_EN, 0);
+ if (rc) {
+ dev_err(&pon->spmi->dev, "Unable to configure S2 enable\n");
+ goto err_exit;
+ }
+
/* report the key event */
input_report_key(pon->pon_input, cfg->key_code, 1);
input_sync(pon->pon_input);
@@ -390,24 +494,22 @@
{
int rc;
u8 i;
- u16 s1_timer_addr, s2_cntl_addr, s2_timer_addr;
+ u16 s1_timer_addr, s2_timer_addr;
switch (cfg->pon_type) {
case PON_KPDPWR:
s1_timer_addr = QPNP_PON_KPDPWR_S1_TIMER(pon->base);
s2_timer_addr = QPNP_PON_KPDPWR_S2_TIMER(pon->base);
- s2_cntl_addr = QPNP_PON_KPDPWR_S2_CNTL(pon->base);
break;
case PON_RESIN:
s1_timer_addr = QPNP_PON_RESIN_S1_TIMER(pon->base);
s2_timer_addr = QPNP_PON_RESIN_S2_TIMER(pon->base);
- s2_cntl_addr = QPNP_PON_RESIN_S2_CNTL(pon->base);
break;
default:
return -EINVAL;
}
/* disable S2 reset */
- rc = qpnp_pon_masked_write(pon, s2_cntl_addr,
+ rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr,
QPNP_PON_S2_CNTL_EN, 0);
if (rc) {
dev_err(&pon->spmi->dev, "Unable to configure S2 enable\n");
@@ -441,7 +543,7 @@
return rc;
}
- rc = qpnp_pon_masked_write(pon, s2_cntl_addr,
+ rc = qpnp_pon_masked_write(pon, cfg->s2_cntl_addr,
QPNP_PON_S2_CNTL_TYPE_MASK, (u8)cfg->s2_type);
if (rc) {
dev_err(&pon->spmi->dev, "Unable to configure S2 reset type\n");
@@ -449,7 +551,7 @@
}
/* enable S2 reset */
- rc = qpnp_pon_masked_write(pon, s2_cntl_addr,
+ rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr,
QPNP_PON_S2_CNTL_EN, QPNP_PON_S2_CNTL_EN);
if (rc) {
dev_err(&pon->spmi->dev, "Unable to configure S2 enable\n");
@@ -561,6 +663,17 @@
int rc = 0, i = 0;
struct device_node *pp = NULL;
struct qpnp_pon_config *cfg;
+ u8 reg;
+
+ /* Check if it is rev B */
+ rc = spmi_ext_register_readl(pon->spmi->ctrl, pon->spmi->sid,
+ QPNP_PON_REVISION2(pon->base), ®, 1);
+ if (rc) {
+ dev_err(&pon->spmi->dev,
+ "Unable to read addr=%x, rc(%d)\n",
+ QPNP_PON_REVISION2(pon->base), rc);
+ return rc;
+ }
/* iterate through the list of pon configs */
while ((pp = of_get_next_child(pon->spmi->dev.of_node, pp))) {
@@ -600,6 +713,17 @@
return cfg->bark_irq;
}
}
+
+ if (reg == QPNP_PON_REV_B) {
+ cfg->s2_cntl_addr =
+ QPNP_PON_KPDPWR_S2_CNTL(pon->base);
+ cfg->s2_cntl2_addr =
+ QPNP_PON_KPDPWR_S2_CNTL2(pon->base);
+ } else {
+ cfg->s2_cntl_addr = cfg->s2_cntl2_addr =
+ QPNP_PON_KPDPWR_S2_CNTL(pon->base);
+ }
+
break;
case PON_RESIN:
cfg->state_irq = spmi_get_irq_byname(pon->spmi,
@@ -627,6 +751,17 @@
return cfg->bark_irq;
}
}
+
+ if (reg == QPNP_PON_REV_B) {
+ cfg->s2_cntl_addr =
+ QPNP_PON_RESIN_S2_CNTL(pon->base);
+ cfg->s2_cntl2_addr =
+ QPNP_PON_RESIN_S2_CNTL2(pon->base);
+ } else {
+ cfg->s2_cntl_addr = cfg->s2_cntl2_addr =
+ QPNP_PON_RESIN_S2_CNTL(pon->base);
+ }
+
break;
case PON_CBLPWR:
cfg->state_irq = spmi_get_irq_byname(pon->spmi,
diff --git a/drivers/platform/msm/qpnp-pwm.c b/drivers/platform/msm/qpnp-pwm.c
index 52c523e..4e94b90 100644
--- a/drivers/platform/msm/qpnp-pwm.c
+++ b/drivers/platform/msm/qpnp-pwm.c
@@ -319,6 +319,7 @@
#define QPNP_ENABLE_LUT_CONTROL qpnp_set_control(0, 0, 0, 0, 1)
#define QPNP_ENABLE_PWM_CONTROL qpnp_set_control(0, 0, 0, 1, 0)
#define QPNP_ENABLE_PWM_MODE qpnp_set_control(1, 1, 1, 1, 0)
+#define QPNP_ENABLE_PWM_MODE_GPLED_CHANNEL qpnp_set_control(1, 1, 1, 1, 1)
#define QPNP_ENABLE_LPG_MODE qpnp_set_control(1, 1, 1, 0, 1)
#define QPNP_DISABLE_PWM_MODE qpnp_set_control(0, 0, 0, 1, 0)
#define QPNP_DISABLE_LPG_MODE qpnp_set_control(0, 0, 0, 0, 1)
@@ -857,7 +858,7 @@
struct qpnp_lpg_chip *chip = pwm->chip;
u8 value1, value2, mask1, mask2;
u8 *reg1, *reg2;
- u16 addr;
+ u16 addr, addr1;
int rc;
value1 = pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
@@ -877,7 +878,7 @@
value2 = QPNP_DISABLE_LPG_MODE;
}
mask1 = QPNP_RAMP_START_MASK;
- addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ addr1 = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
QPNP_RAMP_CONTROL);
break;
case QPNP_LPG_REVISION_1:
@@ -888,8 +889,8 @@
QPNP_DISABLE_LUT_V1(value1, pwm->pwm_config.channel_id);
value2 = QPNP_DISABLE_LPG_MODE;
}
- mask1 = BIT(pwm->pwm_config.channel_id);
- addr = lpg_config->lut_base_addr +
+ mask1 = value1;
+ addr1 = lpg_config->lut_base_addr +
SPMI_LPG_REV1_RAMP_CONTROL_OFFSET;
break;
default:
@@ -897,15 +898,27 @@
return -EINVAL;
}
- rc = qpnp_lpg_save_and_write(value1, mask1, reg1,
+ addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_ENABLE_CONTROL);
+
+ rc = qpnp_lpg_save_and_write(value2, mask2, reg2,
addr, 1, chip);
if (rc)
return rc;
- addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
- QPNP_ENABLE_CONTROL);
- return qpnp_lpg_save_and_write(value2, mask2, reg2,
- addr, 1, chip);
+ return qpnp_lpg_save_and_write(value1, mask1, reg1,
+ addr1, 1, chip);
+}
+
+#define QPNP_GPLED_LPG_CHANNEL_RANGE_START 8
+#define QPNP_GPLED_LPG_CHANNEL_RANGE_END 11
+
+static inline int qpnp_enable_pwm_mode(struct qpnp_pwm_config *pwm_conf)
+{
+ if (pwm_conf->channel_id >= QPNP_GPLED_LPG_CHANNEL_RANGE_START &&
+ pwm_conf->channel_id <= QPNP_GPLED_LPG_CHANNEL_RANGE_END)
+ return QPNP_ENABLE_PWM_MODE_GPLED_CHANNEL;
+ return QPNP_ENABLE_PWM_MODE;
}
static int qpnp_lpg_configure_pwm_state(struct pwm_device *pwm,
@@ -917,7 +930,7 @@
int rc;
if (state == QPNP_PWM_ENABLE)
- value = QPNP_ENABLE_PWM_MODE;
+ value = qpnp_enable_pwm_mode(&pwm->pwm_config);
else
value = QPNP_DISABLE_PWM_MODE;
diff --git a/drivers/platform/msm/qpnp-revid.c b/drivers/platform/msm/qpnp-revid.c
index f218a5a..897dc2a 100644
--- a/drivers/platform/msm/qpnp-revid.c
+++ b/drivers/platform/msm/qpnp-revid.c
@@ -52,17 +52,20 @@
}
#define PM8941_PERIPHERAL_SUBTYPE 0x01
+#define PM8226_PERIPHERAL_SUBTYPE 0x04
static size_t build_pmic_string(char *buf, size_t n, int sid,
u8 subtype, u8 rev1, u8 rev2, u8 rev3, u8 rev4)
{
size_t pos = 0;
/*
- * In early versions of PM8941, the major revision number started
- * incrementing from 0 (eg 0 = v1.0, 1 = v2.0).
+ * In early versions of PM8941 and PM8226, the major revision number
+ * started incrementing from 0 (eg 0 = v1.0, 1 = v2.0).
* Increment the major revision number here if the chip is an early
- * version of PM8941.
+ * version of PM8941 or PM8226.
*/
- if ((int)subtype == PM8941_PERIPHERAL_SUBTYPE && rev4 < 0x02)
+ if (((int)subtype == PM8941_PERIPHERAL_SUBTYPE
+ || (int)subtype == PM8226_PERIPHERAL_SUBTYPE)
+ && rev4 < 0x02)
rev4++;
pos += snprintf(buf + pos, n - pos, "PMIC@SID%d", sid);
diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c
index 23c346a..2e77114 100644
--- a/drivers/platform/msm/sps/sps.c
+++ b/drivers/platform/msm/sps/sps.c
@@ -1583,10 +1583,7 @@
for (i = 0; i < transfer->iovec_count; i++) {
u32 flags = iovec->flags;
- if (iovec->addr == 0) {
- SPS_ERR("sps:%s:iovec address is invalid.\n", __func__);
- return SPS_ERROR;
- } else if (iovec->size > SPS_IOVEC_MAX_SIZE) {
+ if (iovec->size > SPS_IOVEC_MAX_SIZE) {
SPS_ERR("sps:%s:iovec size is invalid.\n", __func__);
return SPS_ERROR;
}
@@ -1988,6 +1985,35 @@
EXPORT_SYMBOL(sps_get_unused_desc_num);
/**
+ * Vote for or relinquish BAM DMA clock
+ *
+ */
+int sps_ctrl_bam_dma_clk(bool clk_on)
+{
+ int ret;
+
+ SPS_DBG("sps:%s.", __func__);
+
+ if (!sps->is_ready)
+ return -EPROBE_DEFER;
+
+ if (clk_on == true) {
+ SPS_DBG("sps:vote for bam dma clk.\n");
+ ret = clk_prepare_enable(sps->bamdma_clk);
+ if (ret) {
+ SPS_ERR("sps:fail to enable bamdma_clk:ret=%d\n", ret);
+ return ret;
+ }
+ } else {
+ SPS_DBG("sps:relinquish bam dma clk.\n");
+ clk_disable_unprepare(sps->bamdma_clk);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(sps_ctrl_bam_dma_clk);
+
+/**
* Register a BAM device
*
*/
@@ -2528,11 +2554,13 @@
SPS_ERR("sps:sps_device_init err.");
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
clk_disable_unprepare(sps->dfab_clk);
+ clk_disable_unprepare(sps->bamdma_clk);
#endif
goto sps_device_init_err;
}
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
clk_disable_unprepare(sps->dfab_clk);
+ clk_disable_unprepare(sps->bamdma_clk);
#endif
sps->is_ready = true;
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index 1d0fb5d..c39de83 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -27,9 +27,11 @@
#include <linux/workqueue.h>
#include <linux/dma-mapping.h>
#include <mach/msm_smsm.h>
+#include <linux/pm_runtime.h>
#define USB_THRESHOLD 512
#define USB_BAM_MAX_STR_LEN 50
+#define USB_BAM_TIMEOUT (10*HZ)
enum usb_bam_sm {
USB_BAM_SM_INIT = 0,
@@ -73,6 +75,11 @@
* handle/device of the sps driver.
* @pipes_enabled_per_bam: This array stores for each BAM
* ("ssusb", "hsusb" or "hsic") the number of pipes currently enabled.
+* @inactivity_timer_ms: The timeout configuration per each bam for inactivity
+* timer feature.
+* @is_bam_inactivity: Is there no activity on all pipes belongs to a
+* specific bam. (no activity = no data is pulled or pushed
+* from/into ones of the pipes).
*/
struct usb_bam_ctx_type {
struct usb_bam_sps_type usb_bam_sps;
@@ -85,19 +92,112 @@
char qdss_core_name[USB_BAM_MAX_STR_LEN];
u32 h_bam[MAX_BAMS];
u8 pipes_enabled_per_bam[MAX_BAMS];
+ u32 inactivity_timer_ms[MAX_BAMS];
+ bool is_bam_inactivity[MAX_BAMS];
+ struct completion reset_done;
};
-static char *bam_enable_strings[3] = {
+static char *bam_enable_strings[MAX_BAMS] = {
[SSUSB_BAM] = "ssusb",
[HSUSB_BAM] = "hsusb",
[HSIC_BAM] = "hsic",
};
-static spinlock_t usb_bam_lock;
+static enum usb_bam ipa_rm_bams[] = {HSUSB_BAM, HSIC_BAM};
+
+static enum ipa_client_type ipa_rm_resource_prod[MAX_BAMS] = {
+ [HSUSB_BAM] = IPA_RM_RESOURCE_USB_PROD,
+ [HSIC_BAM] = IPA_RM_RESOURCE_HSIC_PROD,
+};
+
+static enum ipa_client_type ipa_rm_resource_cons[MAX_BAMS] = {
+ [HSUSB_BAM] = IPA_RM_RESOURCE_USB_CONS,
+ [HSIC_BAM] = IPA_RM_RESOURCE_HSIC_CONS,
+};
+
+static int usb_cons_request_resource(void);
+static int usb_cons_release_resource(void);
+static int hsic_cons_request_resource(void);
+static int hsic_cons_release_resource(void);
+
+static int (*request_resource_cb[MAX_BAMS])(void) = {
+ [HSUSB_BAM] = usb_cons_request_resource,
+ [HSIC_BAM] = hsic_cons_request_resource,
+};
+
+static int (*release_resource_cb[MAX_BAMS])(void) = {
+ [HSUSB_BAM] = usb_cons_release_resource,
+ [HSIC_BAM] = hsic_cons_release_resource,
+};
+
+struct usb_bam_ipa_handshake_info {
+ enum ipa_rm_event cur_prod_state[MAX_BAMS];
+ enum ipa_rm_event cur_cons_state[MAX_BAMS];
+
+ bool lpm_wait_handshake[MAX_BAMS];
+ int connect_complete;
+ bool lpm_wait_pipes;
+ int bus_suspend;
+ bool disconnected;
+ bool in_lpm[MAX_BAMS];
+
+ int (*wake_cb)(void *);
+ void *wake_param;
+ void (*start)(void *, enum usb_bam_pipe_dir);
+ void (*stop)(void *, enum usb_bam_pipe_dir);
+ void *start_stop_param;
+
+ u32 src_idx;
+ u32 dst_idx;
+ bool cons_stopped;
+ bool prod_stopped;
+
+ struct completion prod_avail[MAX_BAMS];
+ struct completion cons_avail[MAX_BAMS];
+ struct completion cons_released[MAX_BAMS];
+ struct completion prod_released[MAX_BAMS];
+
+ struct mutex suspend_resume_mutex;
+ struct work_struct resume_work;
+ struct work_struct suspend_work;
+ struct work_struct finish_suspend_work;
+};
+
+static spinlock_t usb_bam_ipa_handshake_info_lock;
+static struct usb_bam_ipa_handshake_info info;
+static spinlock_t usb_bam_peer_handshake_info_lock;
static struct usb_bam_peer_handshake_info peer_handshake_info;
+static spinlock_t usb_bam_lock; /* Protect ctx and usb_bam_connections */
static struct usb_bam_pipe_connect *usb_bam_connections;
static struct usb_bam_ctx_type ctx;
+static struct device *hsic_host_dev;
+static bool hsic_host_dev_resumed_from_cons_request;
+
+static int __usb_bam_register_wake_cb(u8 idx, int (*callback)(void *user),
+ void *param, bool trigger_cb_per_pipe);
+static void wait_for_prod_release(enum usb_bam cur_bam);
+static void wait_for_cons_release(enum usb_bam cur_bam);
+
+void msm_bam_set_hsic_host_dev(struct device *dev)
+{
+ if (dev) {
+ /* Hold the device until allowing lpm */
+ info.in_lpm[HSIC_BAM] = false;
+ pr_debug("%s: Getting hsic device %x\n", __func__,
+ (int)dev);
+ pm_runtime_get(dev);
+ } else if (hsic_host_dev) {
+ pr_debug("%s: Putting hsic device %x\n", __func__,
+ (int)hsic_host_dev);
+ /* Just free previous device*/
+ info.in_lpm[HSIC_BAM] = true;
+ pm_runtime_put(hsic_host_dev);
+ }
+
+ hsic_host_dev = dev;
+}
+
static int get_bam_type_from_core_name(const char *name)
{
if (strnstr(name, bam_enable_strings[SSUSB_BAM],
@@ -128,6 +228,45 @@
return false;
}
+static void usb_bam_set_inactivity_timer(enum usb_bam bam)
+{
+ struct sps_timer_ctrl timer_ctrl;
+ struct usb_bam_pipe_connect *pipe_connect;
+ struct sps_pipe *pipe = NULL;
+ int i;
+
+ pr_debug("%s: enter\n", __func__);
+
+ /*
+ * Since we configure global incativity timer for all pipes
+ * and not per each pipe, it is enough to use some pipe
+ * handle associated with this bam, so just find the first one.
+ * This pipe handle is required due to SPS driver API we use below.
+ */
+ for (i = 0; i < ctx.max_connections; i++) {
+ pipe_connect = &usb_bam_connections[i];
+ if (pipe_connect->bam_type == bam &&
+ pipe_connect->enabled) {
+ pipe = ctx.usb_bam_sps.sps_pipes[i];
+ break;
+ }
+ }
+
+ if (!pipe) {
+ pr_warning("%s: Bam %s has no connected pipes\n", __func__,
+ bam_enable_strings[bam]);
+ return;
+ }
+
+ timer_ctrl.op = SPS_TIMER_OP_CONFIG;
+ timer_ctrl.mode = SPS_TIMER_MODE_ONESHOT;
+ timer_ctrl.timeout_msec = ctx.inactivity_timer_ms[bam];
+ sps_timer_ctrl(pipe, &timer_ctrl, NULL);
+
+ timer_ctrl.op = SPS_TIMER_OP_RESET;
+ sps_timer_ctrl(pipe, &timer_ctrl, NULL);
+}
+
static int connect_pipe(u8 idx, u32 *usb_pipe_idx)
{
int ret, ram1_value;
@@ -281,6 +420,7 @@
pr_err("%s: sps_connect failed %d\n", __func__, ret);
goto error;
}
+
return 0;
error:
@@ -323,6 +463,10 @@
return ret;
}
+ pipe_connect->activity_notify = ipa_params->activity_notify;
+ pipe_connect->inactivity_notify = ipa_params->inactivity_notify;
+ pipe_connect->priv = ipa_params->priv;
+
/* IPA input parameters */
ipa_in_params.client_bam_hdl = usb_handle;
ipa_in_params.desc_fifo_sz = pipe_connect->desc_fifo_size;
@@ -367,6 +511,7 @@
pr_err("%s: ipa_connect failed\n", __func__);
return ret;
}
+ pipe_connect->ipa_clnt_hdl = clnt_hdl;
*pipe = sps_alloc_endpoint();
if (*pipe == NULL) {
@@ -395,6 +540,7 @@
__func__,
pipe_connect->src_pipe_index,
pipe_connect->dst_pipe_index);
+ sps_connection->options = SPS_O_NO_DISABLE;
} else {
/* IPA src, USB dest */
sps_connection->mode = SPS_MODE_DEST;
@@ -409,12 +555,13 @@
__func__,
pipe_connect->src_pipe_index,
pipe_connect->dst_pipe_index);
+ sps_connection->options = 0;
}
sps_connection->data = sps_out_params.data;
sps_connection->desc = sps_out_params.desc;
sps_connection->event_thresh = 16;
- sps_connection->options = SPS_O_AUTO_ENABLE;
+ sps_connection->options |= SPS_O_AUTO_ENABLE;
ret = sps_connect(*pipe, sps_connection);
if (ret < 0) {
@@ -477,6 +624,31 @@
return 0;
}
+static void usb_bam_resume_core(enum usb_bam cur_bam)
+{
+ struct usb_phy *trans = usb_get_transceiver();
+
+ if (cur_bam != HSUSB_BAM)
+ return;
+ BUG_ON(trans == NULL);
+ pr_debug("%s: resume core", __func__);
+ pm_runtime_resume(trans->dev);
+}
+
+static void usb_bam_start_lpm(bool disconnect)
+{
+ struct usb_phy *trans = usb_get_transceiver();
+ BUG_ON(trans == NULL);
+ pr_debug("%s: Going to LPM\n", __func__);
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ info.lpm_wait_handshake[HSUSB_BAM] = false;
+ info.lpm_wait_pipes = 0;
+ if (disconnect)
+ pm_runtime_put_noidle(trans->dev);
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ pm_runtime_suspend(trans->dev);
+}
+
int usb_bam_connect(u8 idx, u32 *bam_pipe_idx)
{
int ret;
@@ -505,10 +677,12 @@
return -EINVAL;
}
+ spin_lock(&usb_bam_lock);
/* Check if BAM requires RESET before connect and reset of first pipe */
if ((pdata->reset_on_connect[pipe_connect->bam_type] == true) &&
(ctx.pipes_enabled_per_bam[pipe_connect->bam_type] == 0))
sps_device_reset(ctx.h_bam[pipe_connect->bam_type]);
+ spin_unlock(&usb_bam_lock);
ret = connect_pipe(idx, bam_pipe_idx);
if (ret) {
@@ -517,20 +691,148 @@
}
pipe_connect->enabled = 1;
+ spin_lock(&usb_bam_lock);
ctx.pipes_enabled_per_bam[pipe_connect->bam_type] += 1;
+ spin_unlock(&usb_bam_lock);
return 0;
}
+static int ipa_suspend_pipes(void)
+{
+ struct usb_bam_pipe_connect *dst_pipe_connect, *src_pipe_connect;
+ int ret1, ret2;
+
+ dst_pipe_connect = &usb_bam_connections[info.dst_idx];
+ src_pipe_connect = &usb_bam_connections[info.src_idx];
+
+ if (dst_pipe_connect->ipa_clnt_hdl == -1 ||
+ src_pipe_connect->ipa_clnt_hdl == -1) {
+ pr_err("%s: One of handles is -1, not connected?", __func__);
+ }
+
+ ret1 = ipa_suspend(dst_pipe_connect->ipa_clnt_hdl);
+ if (ret1)
+ pr_err("%s: ipa_suspend on dst failed with %d", __func__, ret1);
+ ret2 = ipa_suspend(src_pipe_connect->ipa_clnt_hdl);
+ if (ret2)
+ pr_err("%s: ipa_suspend on src failed with %d", __func__, ret2);
+
+ return ret1 | ret2;
+}
+
+static int ipa_resume_pipes(void)
+{
+ struct usb_bam_pipe_connect *dst_pipe_connect, *src_pipe_connect;
+ int ret1, ret2;
+
+ src_pipe_connect = &usb_bam_connections[info.src_idx];
+ dst_pipe_connect = &usb_bam_connections[info.dst_idx];
+
+ if (dst_pipe_connect->ipa_clnt_hdl == -1 ||
+ src_pipe_connect->ipa_clnt_hdl == -1) {
+ pr_err("%s: One of handles is -1, not connected?", __func__);
+ }
+
+ ret1 = ipa_resume(dst_pipe_connect->ipa_clnt_hdl);
+ if (ret1)
+ pr_err("%s: ipa_resume on dst failed with %d", __func__, ret1);
+ ret2 = ipa_resume(src_pipe_connect->ipa_clnt_hdl);
+ if (ret2)
+ pr_err("%s: ipa_resume on src failed with %d", __func__, ret2);
+
+ return ret1 | ret2;
+}
+
+static void usb_bam_finish_suspend(void)
+{
+ int ret;
+ u32 cons_empty;
+ struct sps_pipe *cons_pipe = ctx.usb_bam_sps.sps_pipes[info.dst_idx];
+ struct usb_bam_pipe_connect *pipe_connect = &
+ usb_bam_connections[info.dst_idx];
+ enum usb_bam cur_bam = pipe_connect->bam_type;
+
+ if (cur_bam != HSUSB_BAM) {
+ pr_err("%s: Wrong type of BAM=%s\n", __func__,
+ bam_enable_strings[cur_bam]);
+ return;
+ }
+
+ mutex_lock(&info.suspend_resume_mutex);
+
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ /* If cable was disconnected, let disconnection seq do everything */
+ if (info.disconnected || info.cons_stopped) {
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ mutex_unlock(&info.suspend_resume_mutex);
+ pr_debug("%s: Cable disconnected\n", __func__);
+ return;
+ }
+
+ /* If resume was called don't finish this work */
+ if (!info.bus_suspend) {
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ pr_err("%s: Bus resume in progress\n", __func__);
+ goto no_lpm;
+ }
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+
+ ret = sps_is_pipe_empty(cons_pipe, &cons_empty);
+ if (ret) {
+ pr_err("%s: sps_is_pipe_empty failed with %d\n", __func__, ret);
+ goto no_lpm;
+ }
+
+ /* Stop CONS transfers and go to lpm if no more data in the pipe */
+ if (cons_empty) {
+ if (info.stop && !info.cons_stopped)
+ info.stop(info.start_stop_param,
+ PEER_PERIPHERAL_TO_USB);
+ pr_err("%s: Starting LPM on Bus Suspend\n", __func__);
+ info.cons_stopped = 1;
+ if (info.cur_cons_state[cur_bam] == IPA_RM_RESOURCE_RELEASED) {
+ ipa_rm_notify_completion(IPA_RM_RESOURCE_RELEASED,
+ ipa_rm_resource_cons[cur_bam]);
+ }
+ ipa_suspend_pipes();
+ usb_bam_start_lpm(0);
+ mutex_unlock(&info.suspend_resume_mutex);
+ return;
+ }
+
+no_lpm:
+
+ /* Finish the handshake. Resume Sequence will start automatically
+ by the data in the pipes */
+ if (info.cur_cons_state[cur_bam] == IPA_RM_RESOURCE_RELEASED)
+ ipa_rm_notify_completion(IPA_RM_RESOURCE_RELEASED,
+ ipa_rm_resource_cons[cur_bam]);
+ mutex_unlock(&info.suspend_resume_mutex);
+}
+
+void usb_bam_finish_suspend_(struct work_struct *w)
+{
+ usb_bam_finish_suspend();
+}
+
static void usb_prod_notify_cb(void *user_data, enum ipa_rm_event event,
unsigned long data)
{
+ enum usb_bam *cur_bam = (void *)user_data;
+
switch (event) {
case IPA_RM_RESOURCE_GRANTED:
- pr_debug("USB_PROD resource granted\n");
+ pr_debug("%s: %s_PROD resource granted\n",
+ __func__, bam_enable_strings[*cur_bam]);
+ info.cur_prod_state[*cur_bam] = IPA_RM_RESOURCE_GRANTED;
+ complete_all(&info.prod_avail[*cur_bam]);
break;
case IPA_RM_RESOURCE_RELEASED:
- pr_debug("USB_PROD resource released\n");
+ pr_debug("%s: %s_PROD resource released\n",
+ __func__, bam_enable_strings[*cur_bam]);
+ info.cur_prod_state[*cur_bam] = IPA_RM_RESOURCE_RELEASED;
+ complete_all(&info.prod_released[*cur_bam]);
break;
default:
break;
@@ -538,50 +840,601 @@
return;
}
+static void usb_bam_resume_hsic_host(void)
+{
+ int i;
+ struct usb_bam_pipe_connect *pipe_iter;
+
+ spin_lock(&usb_bam_lock);
+
+ /* Exit from "full suspend" in case of hsic host */
+ if (hsic_host_dev && info.in_lpm[HSIC_BAM]) {
+ pr_debug("%s: Getting hsic device %x\n", __func__,
+ (int)hsic_host_dev);
+ pm_runtime_get(hsic_host_dev);
+ info.in_lpm[HSIC_BAM] = false;
+
+ for (i = 0; i < ctx.max_connections; i++) {
+ pipe_iter = &usb_bam_connections[i];
+ if (pipe_iter->bam_type == HSIC_BAM &&
+ pipe_iter->enabled &&
+ pipe_iter->suspended) {
+ spin_unlock(&usb_bam_lock);
+ ipa_resume(pipe_iter->ipa_clnt_hdl);
+ pipe_iter->suspended = false;
+ spin_lock(&usb_bam_lock);
+ }
+ }
+ }
+
+ spin_unlock(&usb_bam_lock);
+}
+
+static int cons_request_resource(enum usb_bam cur_bam)
+{
+ int ret = -EINPROGRESS;
+
+ pr_debug("%s: Request %s_CONS resource\n",
+ __func__, bam_enable_strings[cur_bam]);
+
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ info.cur_cons_state[cur_bam] = IPA_RM_RESOURCE_GRANTED;
+ complete_all(&info.cons_avail[cur_bam]);
+
+ spin_lock(&usb_bam_lock);
+
+ switch (cur_bam) {
+ case HSUSB_BAM:
+ if (ctx.pipes_enabled_per_bam[HSUSB_BAM] &&
+ info.connect_complete &&
+ !info.cons_stopped && !info.prod_stopped) {
+ pr_debug("%s: ACK on cons_request", __func__);
+ ret = 0;
+ } else if (ctx.pipes_enabled_per_bam[HSUSB_BAM] &&
+ info.connect_complete && info.bus_suspend) {
+ info.bus_suspend = 0;
+ if (info.wake_cb)
+ info.wake_cb(info.wake_param);
+ }
+
+ break;
+ case HSIC_BAM:
+ hsic_host_dev_resumed_from_cons_request = true;
+
+ usb_bam_resume_hsic_host();
+
+ /*
+ * Return sucess if there are pipes connected
+ * and not in lpm
+ */
+ if (ctx.pipes_enabled_per_bam[cur_bam] &&
+ !info.in_lpm[cur_bam])
+ ret = 0;
+ break;
+ case SSUSB_BAM:
+ default:
+ break;
+ }
+
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ spin_unlock(&usb_bam_lock);
+
+ if (ret == -EINPROGRESS)
+ pr_debug("%s: EINPROGRESS on cons_request", __func__);
+ return ret;
+}
+
static int usb_cons_request_resource(void)
{
- pr_debug(": Requesting USB_CONS resource\n");
+ return cons_request_resource(HSUSB_BAM);
+}
+
+static int hsic_cons_request_resource(void)
+{
+ return cons_request_resource(HSIC_BAM);
+}
+
+static int cons_release_resource(enum usb_bam cur_bam)
+{
+ pr_debug("%s: Release %s_CONS resource\n",
+ __func__, bam_enable_strings[cur_bam]);
+
+ info.cur_cons_state[cur_bam] = IPA_RM_RESOURCE_RELEASED;
+ complete_all(&info.cons_released[cur_bam]);
+
+ spin_lock(&usb_bam_lock);
+ if (!ctx.pipes_enabled_per_bam[cur_bam]) {
+ spin_unlock(&usb_bam_lock);
+ pr_debug("%s: ACK on cons_release", __func__);
+ return 0;
+ }
+ spin_unlock(&usb_bam_lock);
+
+ if (cur_bam == HSUSB_BAM) {
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ if (info.bus_suspend)
+ queue_work(ctx.usb_bam_wq, &info.finish_suspend_work);
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+
+ pr_debug("%s: EINPROGRESS cons_release", __func__);
+ return -EINPROGRESS;
+ } else if (cur_bam == HSIC_BAM) {
+
+ /*
+ * Allow to go to lpm for now. Actual state will be checked
+ * in msm_bam_hsic_lpm_ok() just before going to lpm.
+ */
+ if (hsic_host_dev && !info.in_lpm[HSIC_BAM]) {
+ pr_debug("%s: Putting hsic device %x\n", __func__,
+ (int)hsic_host_dev);
+ pm_runtime_put(hsic_host_dev);
+ info.in_lpm[HSIC_BAM] = true;
+ /* In case consumer release before resume happned */
+ hsic_host_dev_resumed_from_cons_request = false;
+ }
+ }
+
return 0;
}
+static int hsic_cons_release_resource(void)
+{
+ return cons_release_resource(HSIC_BAM);
+}
+
static int usb_cons_release_resource(void)
{
- pr_debug(": Releasing USB_CONS resource\n");
- return 0;
+ return cons_release_resource(HSUSB_BAM);
}
static void usb_bam_ipa_create_resources(void)
{
struct ipa_rm_create_params usb_prod_create_params;
struct ipa_rm_create_params usb_cons_create_params;
+ enum usb_bam cur_bam;
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(ipa_rm_bams); i++) {
+ /* Create USB/HSIC_PROD entity */
+ cur_bam = ipa_rm_bams[i];
+
+ memset(&usb_prod_create_params, 0,
+ sizeof(usb_prod_create_params));
+ usb_prod_create_params.name = ipa_rm_resource_prod[cur_bam];
+ usb_prod_create_params.reg_params.notify_cb =
+ usb_prod_notify_cb;
+ usb_prod_create_params.reg_params.user_data = &ipa_rm_bams[i];
+ ret = ipa_rm_create_resource(&usb_prod_create_params);
+ if (ret) {
+ pr_err("%s: Failed to create USB_PROD resource\n",
+ __func__);
+ return;
+ }
+
+ /* Create USB_CONS entity */
+ memset(&usb_cons_create_params, 0,
+ sizeof(usb_cons_create_params));
+ usb_cons_create_params.name = ipa_rm_resource_cons[cur_bam];
+ usb_cons_create_params.request_resource =
+ request_resource_cb[cur_bam];
+ usb_cons_create_params.release_resource =
+ release_resource_cb[cur_bam];
+ ret = ipa_rm_create_resource(&usb_cons_create_params);
+ if (ret) {
+ pr_err("%s: Failed to create USB_CONS resource\n",
+ __func__);
+ return ;
+ }
+ }
+}
+
+static void wait_for_prod_granted(enum usb_bam cur_bam, bool start_cons)
+{
int ret;
- /* Create USB_PROD entity */
- memset(&usb_prod_create_params, 0, sizeof(usb_prod_create_params));
- usb_prod_create_params.name = IPA_RM_RESOURCE_USB_PROD;
- usb_prod_create_params.reg_params.notify_cb = usb_prod_notify_cb;
- usb_prod_create_params.reg_params.user_data = NULL;
- ret = ipa_rm_create_resource(&usb_prod_create_params);
- if (ret) {
- pr_err("%s: Failed to create USB_PROD resource\n", __func__);
+ pr_debug("%s Request %s_PROD_RES\n", __func__,
+ bam_enable_strings[cur_bam]);
+ if (info.cur_cons_state[cur_bam] == IPA_RM_RESOURCE_GRANTED)
+ pr_debug("%s: CONS already granted for some reason\n",
+ __func__);
+ if (info.cur_prod_state[cur_bam] == IPA_RM_RESOURCE_GRANTED)
+ pr_debug("%s: PROD already granted for some reason\n",
+ __func__);
+
+ init_completion(&info.prod_avail[cur_bam]);
+ if (start_cons)
+ init_completion(&info.cons_avail[cur_bam]);
+
+ ret = ipa_rm_request_resource(ipa_rm_resource_prod[cur_bam]);
+ if (!ret) {
+ info.cur_prod_state[cur_bam] = IPA_RM_RESOURCE_GRANTED;
+ complete_all(&info.prod_avail[cur_bam]);
+ pr_debug("%s: PROD_GRANTED without wait\n", __func__);
+ } else if (ret == -EINPROGRESS) {
+ pr_debug("%s: Waiting for PROD_GRANTED\n", __func__);
+ if (!wait_for_completion_timeout(&info.prod_avail[cur_bam],
+ USB_BAM_TIMEOUT))
+ pr_err("%s: Timeout wainting for PROD_GRANTED\n",
+ __func__);
+ } else
+ pr_err("%s: ipa_rm_request_resource ret =%d\n", __func__, ret);
+}
+
+void wait_for_cons_granted(enum usb_bam cur_bam)
+{
+ pr_debug("%s: Waiting for CONS\n", __func__);
+ if (info.cur_cons_state[cur_bam] != IPA_RM_RESOURCE_GRANTED) {
+ if (!wait_for_completion_timeout(&info.cons_avail[cur_bam],
+ USB_BAM_TIMEOUT))
+ pr_err("%s: Timeout wainting for CONS_REQUEST\n",
+ __func__);
+ pr_err("%s: Finished waiting for CONS\n", __func__);
+ }
+
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ info.connect_complete = 1;
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ pr_debug("%s: CONS is granted\n", __func__);
+
+ if (info.cur_cons_state[HSUSB_BAM] == IPA_RM_RESOURCE_GRANTED) {
+ pr_debug("%s: Notify CONS_GRANTED\n", __func__);
+ ipa_rm_notify_completion(IPA_RM_RESOURCE_GRANTED,
+ ipa_rm_resource_cons[HSUSB_BAM]);
+ }
+}
+
+void usb_bam_wait_for_cons_granted(
+ struct usb_bam_connect_ipa_params *ipa_params)
+{
+ struct usb_bam_pipe_connect *pipe_connect;
+ enum usb_bam cur_bam;
+ u8 src_idx;
+
+ src_idx = ipa_params->src_idx;
+ pipe_connect = &usb_bam_connections[src_idx];
+ cur_bam = pipe_connect->bam_type;
+
+ wait_for_cons_granted(cur_bam);
+}
+
+static void wait_for_prod_release(enum usb_bam cur_bam)
+{
+ int ret;
+
+ if (info.cur_cons_state[cur_bam] == IPA_RM_RESOURCE_RELEASED)
+ pr_debug("%s consumer already released\n", __func__);
+ if (info.cur_prod_state[cur_bam] == IPA_RM_RESOURCE_RELEASED)
+ pr_debug("%s producer already released\n", __func__);
+
+ init_completion(&info.prod_released[cur_bam]);
+ init_completion(&info.cons_released[cur_bam]);
+ pr_debug("%s: Releasing %s_PROD\n", __func__,
+ bam_enable_strings[cur_bam]);
+ ret = ipa_rm_release_resource(ipa_rm_resource_prod[cur_bam]);
+ if (!ret) {
+ pr_debug("%s: Released without waiting\n", __func__);
+ info.cur_prod_state[cur_bam] = IPA_RM_RESOURCE_RELEASED;
+ complete_all(&info.prod_released[cur_bam]);
+ } else if (ret == -EINPROGRESS) {
+ pr_debug("%s: Waiting for PROD_RELEASED\n", __func__);
+ if (!wait_for_completion_timeout(&info.prod_released[cur_bam],
+ USB_BAM_TIMEOUT))
+ pr_err("%s: Timeout waiting for PROD_RELEASED\n",
+ __func__);
+ } else
+ pr_err("%s: ipa_rm_request_resource ret =%d", __func__, ret);
+}
+
+static int check_pipes_empty(u8 src_idx, u8 dst_idx)
+{
+ struct sps_pipe *prod_pipe, *cons_pipe;
+ u32 prod_empty, cons_empty;
+
+ /* If we have any remaints in the pipes we don't go to sleep */
+ prod_pipe = ctx.usb_bam_sps.sps_pipes[src_idx];
+ cons_pipe = ctx.usb_bam_sps.sps_pipes[dst_idx];
+ if (sps_is_pipe_empty(prod_pipe, &prod_empty) ||
+ sps_is_pipe_empty(cons_pipe, &cons_empty)) {
+ pr_err("%s: sps_is_pipe_empty failed with\n", __func__);
+ return 0;
+ }
+ if (!prod_empty || !cons_empty) {
+ pr_err("%s: pipes not empty prod=%d cond=%d", __func__,
+ prod_empty, cons_empty);
+ return 0;
+ }
+
+ return 1;
+}
+
+void usb_bam_suspend(struct usb_bam_connect_ipa_params *ipa_params)
+{
+ struct usb_bam_pipe_connect *pipe_connect;
+ enum usb_bam cur_bam;
+ u8 src_idx, dst_idx;
+
+ if (!ipa_params) {
+ pr_err("%s: Invalid ipa params\n", __func__);
return;
}
- /* Create USB_CONS entity */
- memset(&usb_cons_create_params, 0, sizeof(usb_cons_create_params));
- usb_cons_create_params.name = IPA_RM_RESOURCE_USB_CONS;
- usb_cons_create_params.request_resource = usb_cons_request_resource;
- usb_cons_create_params.release_resource = usb_cons_release_resource;
- ret = ipa_rm_create_resource(&usb_cons_create_params);
- if (ret) {
- pr_err("%s: Failed to create USB_CONS resource\n", __func__);
- return ;
+ src_idx = ipa_params->src_idx;
+ dst_idx = ipa_params->dst_idx;
+
+ if (src_idx >= ctx.max_connections || dst_idx >= ctx.max_connections) {
+ pr_err("%s: Invalid connection index src=%d dst=%d\n",
+ __func__, src_idx, dst_idx);
}
+
+ pipe_connect = &usb_bam_connections[src_idx];
+ cur_bam = pipe_connect->bam_type;
+ if (cur_bam != HSUSB_BAM)
+ return;
+
+ info.src_idx = src_idx;
+ info.dst_idx = dst_idx;
+
+ pr_err("%s: Starting suspend sequence(BAM=%s)\n", __func__,
+ bam_enable_strings[cur_bam]);
+
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ info.bus_suspend = 1;
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+
+ /* Stop PROD transfers */
+ if (info.stop) {
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ info.stop(info.start_stop_param, USB_TO_PEER_PERIPHERAL);
+ info.prod_stopped = true;
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ }
+
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ /* If cable was disconnected, let disconnection seq do everything */
+ if (info.disconnected) {
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ pr_debug("%s: Cable disconnected\n", __func__);
+ return;
+ }
+
+ /* Don't go to LPM if data in the pipes */
+ if (!check_pipes_empty(src_idx, dst_idx)) {
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ pr_err("%s: pipes not empty, won't start suspend", __func__);
+ return;
+ }
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+
+ queue_work(ctx.usb_bam_wq, &info.suspend_work);
+}
+
+static void usb_bam_start_suspend(struct work_struct *w)
+{
+ pr_debug("%s: enter", __func__);
+ mutex_lock(&info.suspend_resume_mutex);
+
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ /* If cable was disconnected, let disconnection seq do everything */
+ if (info.disconnected) {
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ mutex_unlock(&info.suspend_resume_mutex);
+ pr_debug("%s: Cable disconnected\n", __func__);
+ return;
+ }
+
+ if (!info.bus_suspend) {
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ pr_err("%s: Resume started, not suspending", __func__);
+ mutex_unlock(&info.suspend_resume_mutex);
+ return;
+ }
+
+ /* Stop PROD transfers in case they were started */
+ if (info.stop && !info.prod_stopped) {
+ info.stop(info.start_stop_param, USB_TO_PEER_PERIPHERAL);
+ info.prod_stopped = true;
+ }
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+
+ /* Don't start LPM seq if data in the pipes */
+ if (!check_pipes_empty(info.src_idx, info.dst_idx)) {
+ mutex_unlock(&info.suspend_resume_mutex);
+ return;
+ }
+
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ info.lpm_wait_handshake[HSUSB_BAM] = true;
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+
+ wait_for_prod_release(HSUSB_BAM);
+
+ mutex_unlock(&info.suspend_resume_mutex);
+ if (info.cur_cons_state[HSUSB_BAM] == IPA_RM_RESOURCE_RELEASED)
+ usb_bam_finish_suspend();
+ else
+ pr_debug("Consumer not released yet\n");
+}
+
+static void usb_bam_finish_resume(struct work_struct *w)
+{
+ struct usb_phy *trans = usb_get_transceiver();
+
+ BUG_ON(trans == NULL);
+ pr_debug("%s: enter", __func__);
+ mutex_lock(&info.suspend_resume_mutex);
+ /* Suspend happened in the meantime */
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ if (info.bus_suspend) {
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ pr_err("%s: Bus suspended, not resuming", __func__);
+ mutex_unlock(&info.suspend_resume_mutex);
+ return;
+ }
+ info.lpm_wait_handshake[HSUSB_BAM] = true;
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+
+ wait_for_prod_granted(HSUSB_BAM, true);
+ wait_for_cons_granted(HSUSB_BAM);
+ if (info.cons_stopped) {
+ ipa_resume_pipes();
+ if (info.start) {
+ pr_debug("%s: Enqueue CONS transfer", __func__);
+ info.start(info.start_stop_param,
+ PEER_PERIPHERAL_TO_USB);
+ info.cons_stopped = 0;
+ }
+ }
+
+ if (info.start) {
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ info.start(info.start_stop_param, USB_TO_PEER_PERIPHERAL);
+ info.prod_stopped = false;
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ }
+ if (info.cur_cons_state[HSUSB_BAM] == IPA_RM_RESOURCE_GRANTED) {
+ pr_debug("%s: Notify CONS_GRANTED\n", __func__);
+ ipa_rm_notify_completion(IPA_RM_RESOURCE_GRANTED,
+ ipa_rm_resource_cons[HSUSB_BAM]);
+ }
+ mutex_unlock(&info.suspend_resume_mutex);
+ pr_debug("%s: done", __func__);
+}
+
+void usb_bam_resume(struct usb_bam_connect_ipa_params *ipa_params)
+{
+ enum usb_bam cur_bam;
+ u8 src_idx, dst_idx;
+ struct usb_bam_pipe_connect *pipe_connect;
+
+ pr_debug("%s: Resuming\n", __func__);
+
+ if (!ipa_params) {
+ pr_err("%s: Invalid ipa params\n", __func__);
+ return;
+ }
+
+ src_idx = ipa_params->src_idx;
+ dst_idx = ipa_params->dst_idx;
+
+ if (src_idx >= ctx.max_connections || dst_idx >= ctx.max_connections) {
+ pr_err("%s: Invalid connection index src=%d dst=%d\n",
+ __func__, src_idx, dst_idx);
+ return;
+ }
+
+ pipe_connect = &usb_bam_connections[src_idx];
+ cur_bam = pipe_connect->bam_type;
+ if (cur_bam != HSUSB_BAM)
+ return;
+
+ info.in_lpm[HSUSB_BAM] = false;
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ info.bus_suspend = 0;
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ queue_work(ctx.usb_bam_wq, &info.resume_work);
+}
+
+void msm_bam_wait_for_hsic_prod_granted(void)
+{
+ if (hsic_host_dev_resumed_from_cons_request)
+ return;
+
+ ctx.is_bam_inactivity[HSIC_BAM] = false;
+
+ /* Get back to resume state including wakeup ipa */
+ usb_bam_resume_hsic_host();
+
+ /* Ensure getting the producer resource */
+ wait_for_prod_granted(HSIC_BAM, false);
+}
+
+void msm_bam_hsic_notify_on_resume(void)
+{
+ /*
+ * This function is called to notify the usb bam driver
+ * that the hsic core and hsic bam hw are fully resumed
+ * and clocked on. Therefore we can now set the inactivity
+ * timer to the hsic bam hw.
+ */
+ if (ctx.inactivity_timer_ms[HSIC_BAM] &&
+ !hsic_host_dev_resumed_from_cons_request)
+ usb_bam_set_inactivity_timer(HSIC_BAM);
+
+ hsic_host_dev_resumed_from_cons_request = false;
+}
+
+bool msm_bam_hsic_lpm_ok(void)
+{
+ int i;
+ struct usb_bam_pipe_connect *pipe_iter;
+
+ if (hsic_host_dev) {
+
+ pr_debug("%s: Starting hsic full suspend sequence\n",
+ __func__);
+
+ /*
+ * Start low power mode by releasing the device
+ * only in case that indeed the resources were released
+ * and we are still in inactivity state (wake event
+ * have not been occured while we were waiting to the
+ * resources release)
+ */
+ spin_lock(&usb_bam_lock);
+
+ if (info.cur_cons_state[HSIC_BAM] ==
+ IPA_RM_RESOURCE_RELEASED &&
+ info.cur_prod_state[HSIC_BAM] ==
+ IPA_RM_RESOURCE_RELEASED &&
+ ctx.is_bam_inactivity[HSIC_BAM] && info.in_lpm[HSIC_BAM]) {
+
+ /* HSIC host will go now to lpm */
+ pr_debug("%s: vote for suspend hsic %x\n",
+ __func__, (int)hsic_host_dev);
+
+ for (i = 0; i < ctx.max_connections; i++) {
+ pipe_iter =
+ &usb_bam_connections[i];
+ if (pipe_iter->bam_type == HSIC_BAM &&
+ pipe_iter->enabled &&
+ !pipe_iter->suspended) {
+ spin_unlock(&usb_bam_lock);
+ ipa_suspend(
+ pipe_iter->ipa_clnt_hdl);
+ pipe_iter->suspended = true;
+ spin_lock(&usb_bam_lock);
+ }
+ }
+
+ spin_unlock(&usb_bam_lock);
+ return true;
+ }
+
+ /* We don't allow lpm, therefore renew our vote here */
+ if (info.in_lpm[HSIC_BAM]) {
+ pr_err("%s: Not allow lpm while ref count=0\n",
+ __func__);
+ pr_err("%s: inactivity=%d, c_s=%d p_s=%d lpm=%d\n",
+ __func__, ctx.is_bam_inactivity[HSIC_BAM],
+ info.cur_cons_state[HSIC_BAM],
+ info.cur_prod_state[HSIC_BAM],
+ info.in_lpm[HSIC_BAM]);
+ pm_runtime_get(hsic_host_dev);
+ info.in_lpm[HSIC_BAM] = false;
+ spin_unlock(&usb_bam_lock);
+ } else
+ spin_unlock(&usb_bam_lock);
+
+ return false;
+ }
+
+ return true;
}
int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
{
u8 idx;
+ enum usb_bam cur_bam;
struct usb_bam_pipe_connect *pipe_connect;
int ret;
struct msm_usb_bam_platform_data *pdata =
@@ -604,28 +1457,88 @@
return -EINVAL;
}
pipe_connect = &usb_bam_connections[idx];
+ cur_bam = pipe_connect->bam_type;
if (pipe_connect->enabled) {
- pr_debug("%s: connection %d was already established\n",
+ pr_err("%s: connection %d was already established\n",
__func__, idx);
return 0;
}
- /* Check if BAM requires RESET before connect and reset of first pipe */
- if ((pdata->reset_on_connect[pipe_connect->bam_type] == true) &&
- (ctx.pipes_enabled_per_bam[pipe_connect->bam_type] == 0))
- sps_device_reset(ctx.h_bam[pipe_connect->bam_type]);
+ pr_debug("%s: enter", __func__);
+
+ if (cur_bam == HSUSB_BAM) {
+ mutex_lock(&info.suspend_resume_mutex);
+
+ spin_lock(&usb_bam_lock);
+ if (ctx.pipes_enabled_per_bam[HSUSB_BAM] == 0) {
+ spin_unlock(&usb_bam_lock);
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ info.lpm_wait_handshake[HSUSB_BAM] = true;
+ info.connect_complete = 0;
+ info.disconnected = 0;
+ info.lpm_wait_pipes = 1;
+ info.bus_suspend = 0;
+ info.cons_stopped = 0;
+ info.prod_stopped = 0;
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ usb_bam_resume_core(cur_bam);
+ } else
+ spin_unlock(&usb_bam_lock);
+ }
+
+ /* Check if BAM requires RESET before connect and reset first pipe */
+ spin_lock(&usb_bam_lock);
+ if ((pdata->reset_on_connect[cur_bam] == true) &&
+ (ctx.pipes_enabled_per_bam[cur_bam] == 0)) {
+ spin_unlock(&usb_bam_lock);
+ sps_device_reset(ctx.h_bam[cur_bam]);
+
+ /* On re-connect assume out from lpm for HSIC BAM */
+ if (cur_bam == HSIC_BAM && hsic_host_dev &&
+ info.in_lpm[HSIC_BAM]) {
+ pr_debug("%s: Getting hsic device %x\n",
+ __func__, (int)hsic_host_dev);
+ pm_runtime_get(hsic_host_dev);
+ }
+
+ /* On re-connect assume out from lpm for all BAMs */
+ info.in_lpm[cur_bam] = false;
+ } else
+ spin_unlock(&usb_bam_lock);
+
+ if (ipa_params->dir == USB_TO_PEER_PERIPHERAL) {
+ pr_debug("%s: Starting connect sequence\n", __func__);
+ wait_for_prod_granted(cur_bam, true);
+ }
ret = connect_pipe_ipa(idx, ipa_params);
- ipa_rm_request_resource(IPA_RM_RESOURCE_USB_PROD);
-
if (ret) {
- pr_err("%s: dst pipe connection failure\n", __func__);
+ pr_err("%s: pipe connection failure\n", __func__);
+ if (cur_bam == HSUSB_BAM)
+ mutex_unlock(&info.suspend_resume_mutex);
return ret;
}
+ spin_lock(&usb_bam_lock);
pipe_connect->enabled = 1;
- ctx.pipes_enabled_per_bam[pipe_connect->bam_type] += 1;
+ pipe_connect->suspended = 0;
+
+ /* Set global inactivity timer upon first pipe connection */
+ if (ctx.pipes_enabled_per_bam[pipe_connect->bam_type] == 0 &&
+ ctx.inactivity_timer_ms[pipe_connect->bam_type] &&
+ pipe_connect->inactivity_notify)
+ usb_bam_set_inactivity_timer(pipe_connect->bam_type);
+
+ ctx.pipes_enabled_per_bam[cur_bam] += 1;
+ spin_unlock(&usb_bam_lock);
+ if (ipa_params->dir == PEER_PERIPHERAL_TO_USB && cur_bam == HSUSB_BAM)
+ wait_for_cons_granted(cur_bam);
+
+ if (cur_bam == HSUSB_BAM)
+ mutex_unlock(&info.suspend_resume_mutex);
+
+ pr_debug("%s: done", __func__);
return 0;
}
@@ -633,22 +1546,26 @@
int usb_bam_client_ready(bool ready)
{
- spin_lock(&usb_bam_lock);
+ spin_lock(&usb_bam_peer_handshake_info_lock);
if (peer_handshake_info.client_ready == ready) {
pr_debug("%s: client state is already %d\n",
__func__, ready);
- spin_unlock(&usb_bam_lock);
+ spin_unlock(&usb_bam_peer_handshake_info_lock);
return 0;
}
peer_handshake_info.client_ready = ready;
+ if (peer_handshake_info.state == USB_BAM_SM_PLUG_ACKED && !ready) {
+ pr_debug("Starting reset sequence");
+ INIT_COMPLETION(ctx.reset_done);
+ }
- spin_unlock(&usb_bam_lock);
+ spin_unlock(&usb_bam_peer_handshake_info_lock);
if (!queue_work(ctx.usb_bam_wq,
&peer_handshake_info.reset_event.event_w)) {
- spin_lock(&usb_bam_lock);
+ spin_lock(&usb_bam_peer_handshake_info_lock);
peer_handshake_info.pending_work++;
- spin_unlock(&usb_bam_lock);
+ spin_unlock(&usb_bam_peer_handshake_info_lock);
}
return 0;
@@ -656,18 +1573,165 @@
static void usb_bam_work(struct work_struct *w)
{
+ int i;
struct usb_bam_event_info *event_info =
container_of(w, struct usb_bam_event_info, event_w);
+ struct usb_bam_pipe_connect *pipe_connect =
+ container_of(event_info, struct usb_bam_pipe_connect, event);
+ struct usb_bam_pipe_connect *pipe_iter;
+ int (*callback)(void *priv);
+ void *param = NULL;
- event_info->callback(event_info->param);
+ switch (event_info->type) {
+ case USB_BAM_EVENT_WAKEUP:
+ case USB_BAM_EVENT_WAKEUP_PIPE:
+
+ pr_debug("%s recieved USB_BAM_EVENT_WAKEUP\n", __func__);
+
+ /*
+ * Make sure the PROD resource is granted before
+ * wakeup hsic host class driver (done by the callback below)
+ */
+ if (pipe_connect->peer_bam == IPA_P_BAM &&
+ pipe_connect->bam_type == HSIC_BAM &&
+ info.cur_prod_state[HSIC_BAM] != IPA_RM_RESOURCE_GRANTED) {
+ wait_for_prod_granted(HSIC_BAM, false);
+ }
+
+ /*
+ * Check if need to resume the hsic host.
+ * On one hand, since we got the wakeup interrupt
+ * the hsic bam clocks are already enabled, so no need
+ * to actualluy resume the hardware... However, we still need
+ * to update the usb bam driver state (to set in_lpm=false),
+ * and to wake ipa (ipa_resume) and to hold again the hsic host
+ * device again to avoid it going to low poer mode next time
+ * until we complete releasing the hsic consumer and producer
+ * resources against the ipa resource manager.
+ */
+ if (pipe_connect->bam_type == HSIC_BAM)
+ usb_bam_resume_hsic_host();
+
+ /* Notify about wakeup / activity of the bam */
+ if (event_info->callback)
+ event_info->callback(event_info->param);
+
+ /*
+ * Reset inactivity timer counter if this pipe's bam
+ * has inactivity timeout.
+ */
+ spin_lock(&usb_bam_lock);
+ if (ctx.inactivity_timer_ms[pipe_connect->bam_type])
+ usb_bam_set_inactivity_timer(pipe_connect->bam_type);
+ spin_unlock(&usb_bam_lock);
+
+ if (pipe_connect->bam_type == HSUSB_BAM) {
+ /* A2 wakeup not from LPM (CONS was up) */
+ wait_for_prod_granted(pipe_connect->bam_type, true);
+ if (info.start) {
+ pr_debug("%s: Enqueue PROD transfer", __func__);
+ info.start(info.start_stop_param,
+ USB_TO_PEER_PERIPHERAL);
+ }
+ }
+
+ break;
+
+ case USB_BAM_EVENT_INACTIVITY:
+
+ pr_debug("%s recieved USB_BAM_EVENT_INACTIVITY\n", __func__);
+
+ /*
+ * Since event info is one structure per pipe, it might be
+ * overriden when we will register the wakeup events below,
+ * and still we want ot register the wakeup events before we
+ * notify on the inactivity in order to identify the next
+ * activity as soon as possible.
+ */
+ callback = event_info->callback;
+ param = event_info->param;
+
+ /*
+ * Upon inactivity, configure wakeup irq for all pipes
+ * that are into the usb bam.
+ */
+ spin_lock(&usb_bam_lock);
+ for (i = 0; i < ctx.max_connections; i++) {
+ pipe_iter = &usb_bam_connections[i];
+ if (pipe_iter->bam_type ==
+ pipe_connect->bam_type &&
+ pipe_iter->dir ==
+ PEER_PERIPHERAL_TO_USB &&
+ pipe_iter->enabled) {
+ pr_debug("%s: Register wakeup on pipe %x\n",
+ __func__, (int)pipe_iter);
+ __usb_bam_register_wake_cb(i,
+ pipe_iter->activity_notify,
+ pipe_iter->priv,
+ false);
+ }
+ }
+ spin_unlock(&usb_bam_lock);
+
+ /* Notify about the inactivity to the USB class driver */
+ if (callback)
+ callback(param);
+
+ wait_for_prod_release(pipe_connect->bam_type);
+ pr_debug("%s: complete wait on hsic producer s=%d\n",
+ __func__, info.cur_prod_state[pipe_connect->bam_type]);
+
+ /*
+ * Allow to go to lpm for now if also consumer is down.
+ * If consumer is up, we will wait to the release consumer
+ * notification.
+ */
+ if (hsic_host_dev &&
+ info.cur_cons_state[HSIC_BAM] ==
+ IPA_RM_RESOURCE_RELEASED && !info.in_lpm[HSIC_BAM]) {
+ pr_debug("%s: Putting hsic device %x\n", __func__,
+ (int)hsic_host_dev);
+ pm_runtime_put(hsic_host_dev);
+ info.in_lpm[HSIC_BAM] = true;
+ }
+
+ break;
+ default:
+ pr_err("%s: unknown usb bam event type %d\n", __func__,
+ event_info->type);
+ }
}
static void usb_bam_wake_cb(struct sps_event_notify *notify)
{
- struct usb_bam_event_info *wake_event_info =
+ struct usb_bam_event_info *event_info =
(struct usb_bam_event_info *)notify->user;
+ struct usb_bam_pipe_connect *pipe_connect =
+ container_of(event_info,
+ struct usb_bam_pipe_connect,
+ event);
+ enum usb_bam bam = pipe_connect->bam_type;
- queue_work(ctx.usb_bam_wq, &wake_event_info->event_w);
+ spin_lock(&usb_bam_lock);
+
+ if (event_info->type == USB_BAM_EVENT_WAKEUP_PIPE)
+ queue_work(ctx.usb_bam_wq, &event_info->event_w);
+ else if (event_info->type == USB_BAM_EVENT_WAKEUP &&
+ ctx.is_bam_inactivity[bam]) {
+
+ /*
+ * Sps wake event is per pipe, so usb_bam_wake_cb is
+ * called per pipe. However, we want to filter the wake
+ * event to be wake event per all the pipes.
+ * Therefore, the first pipe that awaked will be considered
+ * as global bam wake event.
+ */
+ ctx.is_bam_inactivity[bam] = false;
+
+ queue_work(ctx.usb_bam_wq, &event_info->event_w);
+ }
+
+ spin_unlock(&usb_bam_lock);
}
static void usb_bam_sm_work(struct work_struct *w)
@@ -675,15 +1739,15 @@
pr_debug("%s: current state: %d\n", __func__,
peer_handshake_info.state);
- spin_lock(&usb_bam_lock);
+ spin_lock(&usb_bam_peer_handshake_info_lock);
switch (peer_handshake_info.state) {
case USB_BAM_SM_INIT:
if (peer_handshake_info.client_ready) {
- spin_unlock(&usb_bam_lock);
+ spin_unlock(&usb_bam_peer_handshake_info_lock);
smsm_change_state(SMSM_APPS_STATE, 0,
SMSM_USB_PLUG_UNPLUG);
- spin_lock(&usb_bam_lock);
+ spin_lock(&usb_bam_peer_handshake_info_lock);
peer_handshake_info.state = USB_BAM_SM_PLUG_NOTIFIED;
}
break;
@@ -695,19 +1759,22 @@
break;
case USB_BAM_SM_PLUG_ACKED:
if (!peer_handshake_info.client_ready) {
- spin_unlock(&usb_bam_lock);
+ spin_unlock(&usb_bam_peer_handshake_info_lock);
+ pr_debug("Starting A2 reset sequence");
smsm_change_state(SMSM_APPS_STATE,
SMSM_USB_PLUG_UNPLUG, 0);
- spin_lock(&usb_bam_lock);
+ spin_lock(&usb_bam_peer_handshake_info_lock);
peer_handshake_info.state = USB_BAM_SM_UNPLUG_NOTIFIED;
}
break;
case USB_BAM_SM_UNPLUG_NOTIFIED:
if (peer_handshake_info.ack_received) {
- spin_unlock(&usb_bam_lock);
+ spin_unlock(&usb_bam_peer_handshake_info_lock);
peer_handshake_info.reset_event.
callback(peer_handshake_info.reset_event.param);
- spin_lock(&usb_bam_lock);
+ spin_lock(&usb_bam_peer_handshake_info_lock);
+ complete_all(&ctx.reset_done);
+ pr_debug("Finished reset sequence");
peer_handshake_info.state = USB_BAM_SM_INIT;
peer_handshake_info.ack_received = 0;
}
@@ -716,12 +1783,12 @@
if (peer_handshake_info.pending_work) {
peer_handshake_info.pending_work--;
- spin_unlock(&usb_bam_lock);
+ spin_unlock(&usb_bam_peer_handshake_info_lock);
queue_work(ctx.usb_bam_wq,
&peer_handshake_info.reset_event.event_w);
- spin_lock(&usb_bam_lock);
+ spin_lock(&usb_bam_peer_handshake_info_lock);
}
- spin_unlock(&usb_bam_lock);
+ spin_unlock(&usb_bam_peer_handshake_info_lock);
}
static void usb_bam_ack_toggle_cb(void *priv,
@@ -730,29 +1797,29 @@
static int last_processed_state;
int current_state;
- spin_lock(&usb_bam_lock);
+ spin_lock(&usb_bam_peer_handshake_info_lock);
current_state = new_state & SMSM_USB_PLUG_UNPLUG;
if (current_state == last_processed_state) {
- spin_unlock(&usb_bam_lock);
+ spin_unlock(&usb_bam_peer_handshake_info_lock);
return;
}
last_processed_state = current_state;
peer_handshake_info.ack_received = true;
- spin_unlock(&usb_bam_lock);
+ spin_unlock(&usb_bam_peer_handshake_info_lock);
if (!queue_work(ctx.usb_bam_wq,
&peer_handshake_info.reset_event.event_w)) {
- spin_lock(&usb_bam_lock);
+ spin_lock(&usb_bam_peer_handshake_info_lock);
peer_handshake_info.pending_work++;
- spin_unlock(&usb_bam_lock);
+ spin_unlock(&usb_bam_peer_handshake_info_lock);
}
}
-int usb_bam_register_wake_cb(u8 idx, int (*callback)(void *user),
- void *param)
+static int __usb_bam_register_wake_cb(u8 idx, int (*callback)(void *user),
+ void *param, bool trigger_cb_per_pipe)
{
struct sps_pipe *pipe = ctx.usb_bam_sps.sps_pipes[idx];
struct sps_connect *sps_connection;
@@ -767,8 +1834,11 @@
pipe = ctx.usb_bam_sps.sps_pipes[idx];
sps_connection = &ctx.usb_bam_sps.sps_connections[idx];
pipe_connect = &usb_bam_connections[idx];
- wake_event_info = &pipe_connect->wake_event;
+ wake_event_info = &pipe_connect->event;
+ wake_event_info->type = (trigger_cb_per_pipe ?
+ USB_BAM_EVENT_WAKEUP_PIPE :
+ USB_BAM_EVENT_WAKEUP);
wake_event_info->param = param;
wake_event_info->callback = callback;
wake_event_info->event.mode = SPS_TRIGGER_CALLBACK;
@@ -794,6 +1864,25 @@
return 0;
}
+int usb_bam_register_wake_cb(u8 idx, int (*callback)(void *user),
+ void *param)
+{
+ info.wake_cb = callback;
+ info.wake_param = param;
+ return __usb_bam_register_wake_cb(idx, callback, param, true);
+}
+
+int usb_bam_register_start_stop_cbs(
+ void (*start)(void *, enum usb_bam_pipe_dir),
+ void (*stop)(void *, enum usb_bam_pipe_dir),
+ void *param)
+{
+ info.start = start;
+ info.stop = stop;
+ info.start_stop_param = param;
+ return 0;
+}
+
int usb_bam_register_peer_reset_cb(int (*callback)(void *), void *param)
{
u32 ret = 0;
@@ -831,44 +1920,86 @@
pipe_connect = &usb_bam_connections[idx];
if (!pipe_connect->enabled) {
- pr_debug("%s: connection %d isn't enabled\n",
+ pr_err("%s: connection %d isn't enabled\n",
__func__, idx);
return 0;
}
ret = disconnect_pipe(idx);
if (ret) {
- pr_err("%s: src pipe connection failure\n", __func__);
+ pr_err("%s: src pipe disconnection failure\n", __func__);
return ret;
}
pipe_connect->enabled = 0;
+ spin_lock(&usb_bam_lock);
if (ctx.pipes_enabled_per_bam[pipe_connect->bam_type] == 0)
pr_err("%s: wrong pipes enabled counter for bam_type=%d\n",
__func__, pipe_connect->bam_type);
else
ctx.pipes_enabled_per_bam[pipe_connect->bam_type] -= 1;
+ spin_unlock(&usb_bam_lock);
return 0;
}
+static void wait_for_cons_release(enum usb_bam cur_bam)
+{
+ pr_debug("%s: Waiting for CONS release\n", __func__);
+ if (info.cur_cons_state[cur_bam] != IPA_RM_RESOURCE_RELEASED) {
+ if (!wait_for_completion_timeout(&info.cons_released[cur_bam],
+ USB_BAM_TIMEOUT))
+ pr_err("%s: Timeout wainting for CONS_RELEASE\n",
+ __func__);
+ } else
+ pr_debug("%s Didn't need to wait for CONS release\n",
+ __func__);
+}
+
int usb_bam_disconnect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
{
int ret;
u8 idx;
struct usb_bam_pipe_connect *pipe_connect;
struct sps_connect *sps_connection;
+ enum usb_bam cur_bam;
+ if (!ipa_params->prod_clnt_hdl && !ipa_params->cons_clnt_hdl) {
+ pr_err("%s: Both of the handles is missing\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: Starting disconnect sequence\n", __func__);
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ info.connect_complete = 0;
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+
+ mutex_lock(&info.suspend_resume_mutex);
+ /* Delay USB core to go into lpm before we finish our handshake */
if (ipa_params->prod_clnt_hdl) {
- /* close USB -> IPA pipe */
idx = ipa_params->dst_idx;
+ pipe_connect = &usb_bam_connections[idx];
+
+ pipe_connect->activity_notify = NULL;
+ pipe_connect->inactivity_notify = NULL;
+ pipe_connect->priv = NULL;
+
+ /* Do the release handshake with the A2 via RM */
+ cur_bam = pipe_connect->bam_type;
+ info.lpm_wait_pipes = 1;
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ info.disconnected = 1;
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ wait_for_prod_release(cur_bam);
+ /* close USB -> IPA pipe */
+ usb_bam_resume_core(cur_bam);
ret = ipa_disconnect(ipa_params->prod_clnt_hdl);
if (ret) {
pr_err("%s: dst pipe disconnection failure\n",
__func__);
+ mutex_unlock(&info.suspend_resume_mutex);
return ret;
}
- pipe_connect = &usb_bam_connections[idx];
sps_connection = &ctx.usb_bam_sps.sps_connections[idx];
sps_connection->data.phys_base = 0;
sps_connection->desc.phys_base = 0;
@@ -877,19 +2008,30 @@
if (ret) {
pr_err("%s: failure to disconnect pipe %d\n",
__func__, idx);
+ mutex_unlock(&info.suspend_resume_mutex);
return ret;
}
}
+
if (ipa_params->cons_clnt_hdl) {
- /* close IPA -> USB pipe */
idx = ipa_params->src_idx;
+ pipe_connect = &usb_bam_connections[idx];
+
+ pipe_connect->activity_notify = NULL;
+ pipe_connect->inactivity_notify = NULL;
+ pipe_connect->priv = NULL;
+
+ cur_bam = pipe_connect->bam_type;
+ wait_for_cons_release(cur_bam);
+ /* close IPA -> USB pipe */
ret = ipa_disconnect(ipa_params->cons_clnt_hdl);
if (ret) {
pr_err("%s: src pipe disconnection failure\n",
__func__);
+ mutex_unlock(&info.suspend_resume_mutex);
return ret;
}
- pipe_connect = &usb_bam_connections[idx];
+
sps_connection = &ctx.usb_bam_sps.sps_connections[idx];
sps_connection->data.phys_base = 0;
sps_connection->desc.phys_base = 0;
@@ -898,16 +2040,39 @@
if (ret) {
pr_err("%s: failure to disconnect pipe %d\n",
__func__, idx);
+ mutex_unlock(&info.suspend_resume_mutex);
return ret;
}
+
+ pipe_connect->ipa_clnt_hdl = -1;
+
+ if (info.cur_cons_state[cur_bam] == IPA_RM_RESOURCE_RELEASED) {
+ pr_debug("%s Notify CONS _RELEASED\n", __func__);
+ ipa_rm_notify_completion(IPA_RM_RESOURCE_RELEASED,
+ ipa_rm_resource_cons[cur_bam]);
+ }
+ pr_debug("%s Ended disconnect sequence\n", __func__);
+ usb_bam_start_lpm(1);
+ mutex_unlock(&info.suspend_resume_mutex);
+ return 0;
}
- ipa_rm_release_resource(IPA_RM_RESOURCE_USB_PROD);
+ mutex_unlock(&info.suspend_resume_mutex);
return 0;
}
EXPORT_SYMBOL(usb_bam_disconnect_ipa);
-int usb_bam_a2_reset(void)
+void usb_bam_reset_complete(void)
+{
+ pr_debug("Waiting for reset compelte");
+ if (wait_for_completion_interruptible_timeout(&ctx.reset_done,
+ 10*HZ) <= 0)
+ pr_warn("Timeout while waiting for reset");
+
+ pr_debug("Finished Waiting for reset complete");
+}
+
+int usb_bam_a2_reset(bool to_reconnect)
{
struct usb_bam_pipe_connect *pipe_connect;
int i;
@@ -948,6 +2113,9 @@
if (bam != -1 && sps_device_reset(ctx.h_bam[bam]))
pr_err("%s: BAM reset failed\n", __func__);
+ if (!to_reconnect)
+ return ret;
+
/* Reconnect A2 pipes */
for (i = 0; i < ctx.max_connections; i++) {
pipe_connect = &usb_bam_connections[i];
@@ -965,6 +2133,55 @@
return ret;
}
+static void usb_bam_sps_events(enum sps_callback_case sps_cb_case, void *user)
+{
+ int i;
+ enum usb_bam bam;
+ struct usb_bam_pipe_connect *pipe_connect;
+ struct usb_bam_event_info *event_info;
+
+ switch (sps_cb_case) {
+ case SPS_CALLBACK_BAM_TIMER_IRQ:
+
+ pr_debug("%s:recieved SPS_CALLBACK_BAM_TIMER_IRQ\n", __func__);
+
+ spin_lock(&usb_bam_lock);
+
+ bam = get_bam_type_from_core_name((char *)user);
+ ctx.is_bam_inactivity[bam] = true;
+ pr_debug("%s: Incativity happened on bam=%s,%d\n", __func__,
+ (char *)user, bam);
+
+ for (i = 0; i < ctx.max_connections; i++) {
+ pipe_connect = &usb_bam_connections[i];
+
+ /*
+ * Notify inactivity once, Since it is global
+ * for all pipes on bam. Notify only if we have
+ * connected pipes.
+ */
+ if (pipe_connect->bam_type == bam &&
+ pipe_connect->enabled) {
+ event_info = &pipe_connect->event;
+ event_info->type = USB_BAM_EVENT_INACTIVITY;
+ event_info->param = pipe_connect->priv;
+ event_info->callback =
+ pipe_connect->inactivity_notify;
+ queue_work(ctx.usb_bam_wq,
+ &event_info->event_w);
+ break;
+ }
+ }
+
+ spin_unlock(&usb_bam_lock);
+
+ break;
+ default:
+ pr_debug("%s:received sps_cb_case=%d\n", __func__,
+ (int)sps_cb_case);
+ }
+}
+
static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata(
struct platform_device *pdev)
{
@@ -1174,6 +2391,10 @@
props.summing_threshold = USB_THRESHOLD;
props.event_threshold = USB_THRESHOLD;
props.num_pipes = pdata->usb_bam_num_pipes;
+ props.callback = usb_bam_sps_events;
+ props.user = bam_enable_strings[bam_idx];
+ props.options = SPS_BAM_OPT_IRQ_WAKEUP;
+
/*
* HSUSB and HSIC Cores don't support RESET ACK signal to BAMs
* Hence, let BAM to ignore acknowledge from USB while resetting PIPE
@@ -1234,6 +2455,79 @@
return 0;
}
+static ssize_t
+usb_bam_show_inactivity_timer(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ char *buff = buf;
+ int i;
+
+ spin_lock(&usb_bam_lock);
+
+ for (i = 0; i < ARRAY_SIZE(bam_enable_strings); i++) {
+ buff += snprintf(buff, PAGE_SIZE, "%s: %dms\n",
+ bam_enable_strings[i],
+ ctx.inactivity_timer_ms[i]);
+ }
+
+ spin_unlock(&usb_bam_lock);
+
+ return buff - buf;
+}
+
+static ssize_t usb_bam_store_inactivity_timer(struct device *dev,
+ struct device_attribute *attr,
+ const char *buff, size_t count)
+{
+ char buf[USB_BAM_MAX_STR_LEN];
+ char *trimmed_buf, *bam_str, *bam_name, *timer;
+ int timer_d;
+ enum usb_bam bam;
+
+ if (strnstr(buff, "help", USB_BAM_MAX_STR_LEN)) {
+ pr_info("Usage: <bam_name> <ms>,<bam_name> <ms>,...\n");
+ pr_info("\tbam_name: [%s, %s, %s]\n",
+ bam_enable_strings[SSUSB_BAM],
+ bam_enable_strings[HSUSB_BAM],
+ bam_enable_strings[HSIC_BAM]);
+ pr_info("\tms: time in ms. Use 0 to disable timer\n");
+ return count;
+ }
+
+ strlcpy(buf, buff, sizeof(buf));
+ trimmed_buf = strim(buf);
+
+ while (trimmed_buf) {
+ bam_str = strsep(&trimmed_buf, ",");
+ if (bam_str) {
+ bam_name = strsep(&bam_str, " ");
+ bam = get_bam_type_from_core_name(bam_name);
+
+ timer = strsep(&bam_str, " ");
+ sscanf(timer, "%d", &timer_d);
+
+ spin_lock(&usb_bam_lock);
+
+ /* Apply new timer setting if bam has running pipes */
+ if (ctx.inactivity_timer_ms[bam] != timer_d) {
+ ctx.inactivity_timer_ms[bam] = timer_d;
+ if (ctx.pipes_enabled_per_bam[bam] > 0 &&
+ !info.in_lpm[bam])
+ usb_bam_set_inactivity_timer(bam);
+ }
+
+ spin_unlock(&usb_bam_lock);
+ }
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(inactivity_timer, S_IWUSR | S_IRUSR,
+ usb_bam_show_inactivity_timer,
+ usb_bam_store_inactivity_timer);
+
+
static int usb_bam_probe(struct platform_device *pdev)
{
int ret, i;
@@ -1241,10 +2535,14 @@
dev_dbg(&pdev->dev, "usb_bam_probe\n");
+ ret = device_create_file(&pdev->dev, &dev_attr_inactivity_timer);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to create fs node\n");
+ return ret;
+ }
+
ctx.mem_clk = devm_clk_get(&pdev->dev, "mem_clk");
if (IS_ERR(ctx.mem_clk))
-
-
dev_dbg(&pdev->dev, "failed to get mem_clock\n");
ctx.mem_iface_clk = devm_clk_get(&pdev->dev, "mem_iface_clk");
@@ -1269,15 +2567,36 @@
for (i = 0; i < ctx.max_connections; i++) {
usb_bam_connections[i].enabled = 0;
- INIT_WORK(&usb_bam_connections[i].wake_event.event_w,
+ INIT_WORK(&usb_bam_connections[i].event.event_w,
usb_bam_work);
}
- for (i = 0; i < MAX_BAMS; i++)
+ for (i = 0; i < MAX_BAMS; i++) {
ctx.pipes_enabled_per_bam[i] = 0;
+ ctx.inactivity_timer_ms[i] = 0;
+ ctx.is_bam_inactivity[i] = false;
+ init_completion(&info.prod_avail[i]);
+ complete(&info.prod_avail[i]);
+ init_completion(&info.cons_avail[i]);
+ complete(&info.cons_avail[i]);
+ init_completion(&info.cons_released[i]);
+ complete(&info.cons_released[i]);
+ init_completion(&info.prod_released[i]);
+ complete(&info.prod_released[i]);
+ info.cur_prod_state[i] = IPA_RM_RESOURCE_RELEASED;
+ info.cur_cons_state[i] = IPA_RM_RESOURCE_RELEASED;
+ info.lpm_wait_handshake[i] = false;
+ }
- spin_lock_init(&usb_bam_lock);
+ INIT_WORK(&info.resume_work, usb_bam_finish_resume);
+ INIT_WORK(&info.suspend_work, usb_bam_start_suspend);
+ INIT_WORK(&info.finish_suspend_work, usb_bam_finish_suspend_);
+ mutex_init(&info.suspend_resume_mutex);
+
+ spin_lock_init(&usb_bam_peer_handshake_info_lock);
INIT_WORK(&peer_handshake_info.reset_event.event_w, usb_bam_sm_work);
+ init_completion(&ctx.reset_done);
+ complete(&ctx.reset_done);
ctx.usb_bam_wq = alloc_workqueue("usb_bam_wq",
WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
@@ -1291,7 +2610,9 @@
destroy_workqueue(ctx.usb_bam_wq);
return ret;
}
+ spin_lock_init(&usb_bam_ipa_handshake_info_lock);
usb_bam_ipa_create_resources();
+ spin_lock_init(&usb_bam_lock);
return ret;
}
@@ -1361,6 +2682,22 @@
}
EXPORT_SYMBOL(usb_bam_get_connection_idx);
+bool msm_bam_lpm_ok(void)
+{
+ spin_lock(&usb_bam_ipa_handshake_info_lock);
+ if (info.lpm_wait_handshake[HSUSB_BAM] || info.lpm_wait_pipes) {
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ pr_err("%s: Scheduling LPM for later\n", __func__);
+ return 0;
+ } else {
+ info.in_lpm[HSUSB_BAM] = true;
+ spin_unlock(&usb_bam_ipa_handshake_info_lock);
+ pr_err("%s: Going to LPM now\n", __func__);
+ return 1;
+ }
+}
+EXPORT_SYMBOL(msm_bam_lpm_ok);
+
static int usb_bam_remove(struct platform_device *pdev)
{
destroy_workqueue(ctx.usb_bam_wq);
diff --git a/drivers/power/battery_current_limit.c b/drivers/power/battery_current_limit.c
index ecda153..6b6b110 100644
--- a/drivers/power/battery_current_limit.c
+++ b/drivers/power/battery_current_limit.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -31,6 +31,7 @@
* Mininum BCL poll interval 10 msec
*/
#define MIN_BCL_POLL_INTERVAL 10
+#define BATTERY_VOLTAGE_MIN 3400
static const char bcl_type[] = "bcl";
@@ -43,20 +44,20 @@
};
/*
- * Battery Current Limit IBat Imax Threshold Mode
+ * Battery Current Limit Iavail Threshold Mode set
*/
-enum bcl_ibat_imax_threshold_mode {
- BCL_IBAT_IMAX_THRESHOLD_DISABLED = 0,
- BCL_IBAT_IMAX_THRESHOLD_ENABLED,
+enum bcl_iavail_threshold_mode {
+ BCL_IAVAIL_THRESHOLD_DISABLED = 0,
+ BCL_IAVAIL_THRESHOLD_ENABLED,
};
/*
- * Battery Current Limit Ibat Imax Trip Type (High and Low Threshold)
+ * Battery Current Limit Iavail Threshold Mode
*/
-enum bcl_ibat_imax_threshold_type {
- BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW = 0,
- BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH,
- BCL_IBAT_IMAX_THRESHOLD_TYPE_MAX,
+enum bcl_iavail_threshold_type {
+ BCL_IAVAIL_LOW_THRESHOLD_TYPE = 0,
+ BCL_IAVAIL_HIGH_THRESHOLD_TYPE,
+ BCL_IAVAIL_THRESHOLD_TYPE_MAX,
};
/**
@@ -70,117 +71,138 @@
/* BCL related config parameter */
/* BCL mode enable or not */
enum bcl_device_mode bcl_mode;
- /* BCL Ibat/IMax Threshold Activate or Not */
- enum bcl_ibat_imax_threshold_mode
- bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_MAX];
- /* BCL Ibat/IMax Threshold value in milli Amp */
- int bcl_threshold_value_ma[BCL_IBAT_IMAX_THRESHOLD_TYPE_MAX];
+ /* BCL Iavail Threshold Activate or Not */
+ enum bcl_iavail_threshold_mode
+ bcl_threshold_mode[BCL_IAVAIL_THRESHOLD_TYPE_MAX];
+ /* BCL Iavail Threshold value in milli Amp */
+ int bcl_threshold_value_ma[BCL_IAVAIL_THRESHOLD_TYPE_MAX];
/* BCL Type */
char bcl_type[BCL_NAME_LENGTH];
- /* BCL poll in usec */
+ /* BCL poll in msec */
int bcl_poll_interval_msec;
/* BCL realtime value based on poll */
- /* BCL realtime ibat in milli Amp*/
- int bcl_ibat_ma;
- /* BCL realtime calculated imax in milli Amp*/
- int bcl_imax_ma;
- /* BCL realtime calculated ocv in uV*/
- int bcl_ocv_uv;
/* BCL realtime vbat in mV*/
int bcl_vbat_mv;
/* BCL realtime rbat in mOhms*/
- int bcl_rbat;
+ int bcl_rbat_mohm;
+ /*BCL realtime iavail in milli Amp*/
+ int bcl_iavail;
+ /*BCL vbatt min in mV*/
+ int bcl_vbat_min;
/* BCL period poll delay work structure */
- struct delayed_work bcl_imax_work;
+ struct delayed_work bcl_iavail_work;
};
static struct bcl_context *gbcl;
-/*
- * BCL imax calculation and trigger notification to user space
- * if imax cross threshold
- */
-static void bcl_calculate_imax_trigger(void)
+static int bcl_get_battery_voltage(int *vbatt_mv)
{
- int ibatt_ua, vbatt_uv;
- int imax_ma;
- int ibatt_ma, vbatt_mv;
- int imax_low_threshold;
- int imax_high_threshold;
- bool threshold_cross = false;
- union power_supply_propval ret = {0,};
static struct power_supply *psy;
+ union power_supply_propval ret = {0,};
+
+ if (psy == NULL) {
+ psy = power_supply_get_by_name("battery");
+ if (psy == NULL) {
+ pr_err("failed to get ps battery\n");
+ return -EINVAL;
+ }
+ }
+
+ if (psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &ret))
+ return -EINVAL;
+
+ if (ret.intval <= 0)
+ return -EINVAL;
+
+ *vbatt_mv = ret.intval / 1000;
+ return 0;
+}
+
+
+static int bcl_get_resistance(int *rbatt_mohm)
+{
+ static struct power_supply *psy;
+ union power_supply_propval ret = {0,};
+
+ if (psy == NULL) {
+ psy = power_supply_get_by_name("bms");
+ if (psy == NULL) {
+ pr_err("failed to get ps bms\n");
+ return -EINVAL;
+ }
+ }
+ if (psy->get_property(psy, POWER_SUPPLY_PROP_RESISTANCE, &ret))
+ return -EINVAL;
+
+ if (ret.intval <= 0)
+ return -EINVAL;
+
+ *rbatt_mohm = ret.intval / 1000;
+
+ return 0;
+}
+
+/*
+ * BCL iavail calculation and trigger notification to user space
+ * if iavail cross threshold
+ */
+static void bcl_calculate_iavail_trigger(void)
+{
+ int iavail_ma = 0;
+ int vbatt_mv;
+ int rbatt_mohm;
+ bool threshold_cross = false;
if (!gbcl) {
pr_err("called before initialization\n");
return;
}
- if (psy == NULL) {
- psy = power_supply_get_by_name("battery");
- if (psy == NULL)
- return;
- }
-
- if (psy->get_property(psy, POWER_SUPPLY_PROP_CURRENT_NOW, &ret))
+ if (bcl_get_battery_voltage(&vbatt_mv))
return;
- ibatt_ua = ret.intval;
- if (psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &ret))
+ if (bcl_get_resistance(&rbatt_mohm))
return;
- vbatt_uv = ret.intval;
- if (psy->get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX, &ret))
- return;
- imax_ma = ret.intval/1000;
+ iavail_ma = (vbatt_mv - gbcl->bcl_vbat_min) * 1000 / rbatt_mohm;
- ibatt_ma = ibatt_ua/1000;
- vbatt_mv = vbatt_uv/1000;
-
- gbcl->bcl_ibat_ma = ibatt_ma;
- gbcl->bcl_imax_ma = imax_ma;
+ gbcl->bcl_rbat_mohm = rbatt_mohm;
gbcl->bcl_vbat_mv = vbatt_mv;
+ gbcl->bcl_iavail = iavail_ma;
- if (gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH]
- == BCL_IBAT_IMAX_THRESHOLD_ENABLED) {
- imax_high_threshold =
- imax_ma - gbcl->bcl_threshold_value_ma
- [BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH];
- if (ibatt_ma >= imax_high_threshold)
- threshold_cross = true;
- }
+ pr_debug("iavail %d, vbatt %d rbatt %d\n", iavail_ma, vbatt_mv,
+ rbatt_mohm);
- if (gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW]
- == BCL_IBAT_IMAX_THRESHOLD_ENABLED) {
- imax_low_threshold =
- imax_ma - gbcl->bcl_threshold_value_ma
- [BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW];
- if (ibatt_ma <= imax_low_threshold)
- threshold_cross = true;
- }
+ if ((gbcl->bcl_threshold_mode[BCL_IAVAIL_HIGH_THRESHOLD_TYPE] ==
+ BCL_IAVAIL_THRESHOLD_ENABLED)
+ && (iavail_ma >=
+ gbcl->bcl_threshold_value_ma[BCL_IAVAIL_HIGH_THRESHOLD_TYPE]))
+ threshold_cross = true;
+ else if ((gbcl->bcl_threshold_mode[BCL_IAVAIL_LOW_THRESHOLD_TYPE]
+ == BCL_IAVAIL_THRESHOLD_ENABLED)
+ && (iavail_ma <=
+ gbcl->bcl_threshold_value_ma[BCL_IAVAIL_LOW_THRESHOLD_TYPE]))
+ threshold_cross = true;
- if (threshold_cross) {
- sysfs_notify(&gbcl->dev->kobj,
- NULL, "type");
- }
+ if (threshold_cross)
+ sysfs_notify(&gbcl->dev->kobj, NULL, "type");
}
/*
- * BCL imax work
+ * BCL iavail work
*/
-static void bcl_imax_work(struct work_struct *work)
+static void bcl_iavail_work(struct work_struct *work)
{
struct bcl_context *bcl = container_of(work,
- struct bcl_context, bcl_imax_work.work);
+ struct bcl_context, bcl_iavail_work.work);
if (gbcl->bcl_mode == BCL_DEVICE_ENABLED) {
- bcl_calculate_imax_trigger();
+ bcl_calculate_iavail_trigger();
/* restart the delay work for caculating imax */
- schedule_delayed_work(&bcl->bcl_imax_work,
- round_jiffies_relative(msecs_to_jiffies
- (bcl->bcl_poll_interval_msec)));
+ schedule_delayed_work(&bcl->bcl_iavail_work,
+ msecs_to_jiffies(bcl->bcl_poll_interval_msec));
}
}
@@ -198,12 +220,12 @@
if (gbcl->bcl_mode == BCL_DEVICE_DISABLED
&& mode == BCL_DEVICE_ENABLED) {
gbcl->bcl_mode = mode;
- bcl_imax_work(&(gbcl->bcl_imax_work.work));
+ bcl_iavail_work(&(gbcl->bcl_iavail_work.work));
return;
} else if (gbcl->bcl_mode == BCL_DEVICE_ENABLED
&& mode == BCL_DEVICE_DISABLED) {
gbcl->bcl_mode = mode;
- cancel_delayed_work_sync(&(gbcl->bcl_imax_work));
+ cancel_delayed_work_sync(&(gbcl->bcl_iavail_work));
return;
}
@@ -221,11 +243,10 @@
}
show_bcl(type, bcl_type, "%s\n")
-show_bcl(ibat, bcl_ibat_ma, "%d\n")
-show_bcl(imax, bcl_imax_ma, "%d\n")
show_bcl(vbat, bcl_vbat_mv, "%d\n")
-show_bcl(rbat, bcl_rbat, "%d\n")
-show_bcl(ocv, bcl_ocv_uv, "%d\n")
+show_bcl(rbat, bcl_rbat_mohm, "%d\n")
+show_bcl(iavail, bcl_iavail, "%d\n")
+show_bcl(vbat_min, bcl_vbat_min, "%d\n");
show_bcl(poll_interval, bcl_poll_interval_msec, "%d\n")
static ssize_t
@@ -257,136 +278,6 @@
}
static ssize_t
-ibat_imax_low_threshold_mode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- if (!gbcl)
- return -EPERM;
-
- return snprintf(buf, PAGE_SIZE, "%s\n",
- gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW]
- == BCL_IBAT_IMAX_THRESHOLD_ENABLED ? "enabled" : "disabled");
-}
-
-static ssize_t
-ibat_imax_low_threshold_mode_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- if (!gbcl)
- return -EPERM;
-
- if (!strncmp(buf, "enabled", 7))
- gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW]
- = BCL_IBAT_IMAX_THRESHOLD_ENABLED;
- else if (!strncmp(buf, "disabled", 8))
- gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW]
- = BCL_IBAT_IMAX_THRESHOLD_DISABLED;
- else
- return -EINVAL;
-
- return count;
-}
-
-static ssize_t
-ibat_imax_low_threshold_value_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- if (!gbcl)
- return -EPERM;
-
- return snprintf(buf, PAGE_SIZE, "%d\n",
- gbcl->bcl_threshold_value_ma[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW]);
-}
-
-static ssize_t
-ibat_imax_low_threshold_value_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- int value;
-
- if (!gbcl)
- return -EPERM;
-
- if (!sscanf(buf, "%d", &value))
- return -EINVAL;
-
- if (value < 0)
- return -EINVAL;
-
- gbcl->bcl_threshold_value_ma[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW]
- = value;
-
- return count;
-}
-
-static ssize_t
-ibat_imax_high_threshold_mode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- if (!gbcl)
- return -EPERM;
-
- return snprintf(buf, PAGE_SIZE, "%s\n",
- gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH]
- == BCL_IBAT_IMAX_THRESHOLD_ENABLED ? "enabled" : "disabled");
-}
-
-static ssize_t
-ibat_imax_high_threshold_mode_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- if (!gbcl)
- return -EPERM;
-
- if (!strncmp(buf, "enabled", 7))
- gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH]
- = BCL_IBAT_IMAX_THRESHOLD_ENABLED;
- else if (!strncmp(buf, "disabled", 8))
- gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH]
- = BCL_IBAT_IMAX_THRESHOLD_DISABLED;
- else
- return -EINVAL;
-
- return count;
-}
-
-static ssize_t
-ibat_imax_high_threshold_value_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- if (!gbcl)
- return -EPERM;
-
- return snprintf(buf, PAGE_SIZE, "%d\n",
- gbcl->bcl_threshold_value_ma[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH]);
-}
-
-static ssize_t
-ibat_imax_high_threshold_value_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- int value;
-
- if (!gbcl)
- return -EPERM;
-
- if (!sscanf(buf, "%d", &value))
- return -EINVAL;
-
- if (value < 0)
- return -EINVAL;
-
- gbcl->bcl_threshold_value_ma[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH]
- = value;
-
- return count;
-}
-
-static ssize_t
poll_interval_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -407,31 +298,168 @@
return count;
}
+static ssize_t vbat_min_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int value;
+ int ret;
+
+ if (!gbcl)
+ return -EPERM;
+
+ ret = kstrtoint(buf, 10, &value);
+
+ if (ret || (value < 0)) {
+ pr_err("Incorrect vbatt min value\n");
+ return -EINVAL;
+ }
+
+ gbcl->bcl_vbat_min = value;
+ return count;
+}
+
+static ssize_t iavail_low_threshold_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!gbcl)
+ return -EPERM;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ gbcl->bcl_threshold_mode[BCL_IAVAIL_LOW_THRESHOLD_TYPE]
+ == BCL_IAVAIL_THRESHOLD_ENABLED ? "enabled" : "disabled");
+}
+
+static ssize_t iavail_low_threshold_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ if (!gbcl)
+ return -EPERM;
+
+ if (!strncmp(buf, "enabled", 7))
+ gbcl->bcl_threshold_mode[BCL_IAVAIL_LOW_THRESHOLD_TYPE]
+ = BCL_IAVAIL_THRESHOLD_ENABLED;
+ else if (!strncmp(buf, "disabled", 7))
+ gbcl->bcl_threshold_mode[BCL_IAVAIL_LOW_THRESHOLD_TYPE]
+ = BCL_IAVAIL_THRESHOLD_DISABLED;
+ else
+ return -EINVAL;
+
+ return count;
+}
+static ssize_t iavail_high_threshold_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!gbcl)
+ return -EPERM;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ gbcl->bcl_threshold_mode[BCL_IAVAIL_HIGH_THRESHOLD_TYPE]
+ == BCL_IAVAIL_THRESHOLD_ENABLED ? "enabled" : "disabled");
+}
+
+static ssize_t iavail_high_threshold_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ if (!gbcl)
+ return -EPERM;
+
+ if (!strncmp(buf, "enabled", 7))
+ gbcl->bcl_threshold_mode[BCL_IAVAIL_HIGH_THRESHOLD_TYPE]
+ = BCL_IAVAIL_THRESHOLD_ENABLED;
+ else if (!strncmp(buf, "disabled", 7))
+ gbcl->bcl_threshold_mode[BCL_IAVAIL_HIGH_THRESHOLD_TYPE]
+ = BCL_IAVAIL_THRESHOLD_DISABLED;
+ else
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t iavail_low_threshold_value_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!gbcl)
+ return -EPERM;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ gbcl->bcl_threshold_value_ma[BCL_IAVAIL_LOW_THRESHOLD_TYPE]);
+}
+
+
+static ssize_t iavail_low_threshold_value_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int val;
+ int ret;
+
+ ret = kstrtoint(buf, 10, &val);
+
+ if (ret || (val < 0)) {
+ pr_err("Incorrect available current threshold value\n");
+ return -EINVAL;
+ }
+
+ gbcl->bcl_threshold_value_ma[BCL_IAVAIL_LOW_THRESHOLD_TYPE] = val;
+
+ return count;
+}
+static ssize_t iavail_high_threshold_value_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!gbcl)
+ return -EPERM;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ gbcl->bcl_threshold_value_ma[BCL_IAVAIL_HIGH_THRESHOLD_TYPE]);
+}
+
+static ssize_t iavail_high_threshold_value_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int val;
+ int ret;
+
+ ret = kstrtoint(buf, 10, &val);
+
+ if (ret || (val < 0)) {
+ pr_err("Incorrect available current threshold value\n");
+ return -EINVAL;
+ }
+
+ gbcl->bcl_threshold_value_ma[BCL_IAVAIL_HIGH_THRESHOLD_TYPE] = val;
+
+ return count;
+}
+
/*
* BCL device attributes
*/
static struct device_attribute bcl_dev_attr[] = {
__ATTR(type, 0444, type_show, NULL),
- __ATTR(ibat, 0444, ibat_show, NULL),
+ __ATTR(iavail, 0444, iavail_show, NULL),
+ __ATTR(vbat_min, 0644, vbat_min_show, vbat_min_store),
__ATTR(vbat, 0444, vbat_show, NULL),
__ATTR(rbat, 0444, rbat_show, NULL),
- __ATTR(ocv, 0444, ocv_show, NULL),
- __ATTR(imax, 0444, imax_show, NULL),
__ATTR(mode, 0644, mode_show, mode_store),
__ATTR(poll_interval, 0644,
poll_interval_show, poll_interval_store),
- __ATTR(ibat_imax_low_threshold_mode, 0644,
- ibat_imax_low_threshold_mode_show,
- ibat_imax_low_threshold_mode_store),
- __ATTR(ibat_imax_high_threshold_mode, 0644,
- ibat_imax_high_threshold_mode_show,
- ibat_imax_high_threshold_mode_store),
- __ATTR(ibat_imax_low_threshold_value, 0644,
- ibat_imax_low_threshold_value_show,
- ibat_imax_low_threshold_value_store),
- __ATTR(ibat_imax_high_threshold_value, 0644,
- ibat_imax_high_threshold_value_show,
- ibat_imax_high_threshold_value_store)
+ __ATTR(iavail_low_threshold_mode, 0644,
+ iavail_low_threshold_mode_show,
+ iavail_low_threshold_mode_store),
+ __ATTR(iavail_high_threshold_mode, 0644,
+ iavail_high_threshold_mode_show,
+ iavail_high_threshold_mode_store),
+ __ATTR(iavail_low_threshold_value, 0644,
+ iavail_low_threshold_value_show,
+ iavail_low_threshold_value_store),
+ __ATTR(iavail_high_threshold_value, 0644,
+ iavail_high_threshold_value_show,
+ iavail_high_threshold_value_store),
};
static int create_bcl_sysfs(struct bcl_context *bcl)
@@ -478,12 +506,13 @@
/* Init default BCL params */
bcl->dev = &pdev->dev;
bcl->bcl_mode = BCL_DEVICE_DISABLED;
- bcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW]
- = BCL_IBAT_IMAX_THRESHOLD_DISABLED;
- bcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH]
- = BCL_IBAT_IMAX_THRESHOLD_DISABLED;
- bcl->bcl_threshold_value_ma[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW] = 0;
- bcl->bcl_threshold_value_ma[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH] = 0;
+ bcl->bcl_threshold_mode[BCL_IAVAIL_LOW_THRESHOLD_TYPE] =
+ BCL_IAVAIL_THRESHOLD_DISABLED;
+ bcl->bcl_threshold_mode[BCL_IAVAIL_HIGH_THRESHOLD_TYPE] =
+ BCL_IAVAIL_THRESHOLD_DISABLED;
+ bcl->bcl_threshold_value_ma[BCL_IAVAIL_LOW_THRESHOLD_TYPE] = 0;
+ bcl->bcl_threshold_value_ma[BCL_IAVAIL_HIGH_THRESHOLD_TYPE] = 0;
+ bcl->bcl_vbat_min = BATTERY_VOLTAGE_MIN;
snprintf(bcl->bcl_type, BCL_NAME_LENGTH, "%s", bcl_type);
bcl->bcl_poll_interval_msec = BCL_POLL_INTERVAL;
ret = create_bcl_sysfs(bcl);
@@ -493,7 +522,7 @@
return ret;
}
platform_set_drvdata(pdev, bcl);
- INIT_DELAYED_WORK(&bcl->bcl_imax_work, bcl_imax_work);
+ INIT_DELAYED_WORK_DEFERRABLE(&bcl->bcl_iavail_work, bcl_iavail_work);
return 0;
}
diff --git a/drivers/power/bq28400_battery.c b/drivers/power/bq28400_battery.c
index beab4e2..79ed2b1 100644
--- a/drivers/power/bq28400_battery.c
+++ b/drivers/power/bq28400_battery.c
@@ -164,6 +164,9 @@
u16 subcmd;
};
+static int fake_battery = -EINVAL;
+module_param(fake_battery, int, 0644);
+
#define BQ28400_DEBUG_REG(x) {#x, SBS_##x, 0}
#define BQ28400_DEBUG_SUBREG(x, y) {#y, SBS_##x, SUBCMD_##y}
@@ -403,6 +406,11 @@
{
int percentage = 0;
+ if (fake_battery != -EINVAL) {
+ pr_debug("Reporting Fake SOC = %d\n", fake_battery);
+ return fake_battery;
+ }
+
/* This register is only 1 byte */
percentage = i2c_smbus_read_byte_data(client, SBS_RSOC);
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index c09373a..e244df4 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -79,6 +79,14 @@
int last_good_ocv_uv;
};
+struct fcc_data {
+ int fcc_new;
+ int chargecycles;
+ int batt_temp;
+ int fcc_real;
+ int temp_real;
+};
+
/**
* struct pm8921_bms_chip -
* @bms_output_lock: lock to prevent concurrent bms reads
@@ -144,7 +152,16 @@
int pon_ocv_uv;
int last_cc_uah;
unsigned long tm_sec;
+
int enable_fcc_learning;
+ int min_fcc_learning_soc;
+ int min_fcc_ocv_pc;
+ int max_fcc_learning_samples;
+ struct fcc_data *fcc_table;
+ int fcc_new;
+ int start_real_soc;
+ int pc_at_start_charge;
+
int shutdown_soc;
int shutdown_iavg_ua;
struct delayed_work calculate_soc_delayed_work;
@@ -177,6 +194,9 @@
int vbatt_cutoff_count;
int low_voltage_detect;
int vbatt_cutoff_retries;
+ bool first_report_after_suspend;
+ bool soc_updated_on_resume;
+ int last_soc_at_suspend;
};
/*
@@ -191,18 +211,28 @@
#define DEFAULT_OCV_MICROVOLTS 3900000
#define DEFAULT_CHARGE_CYCLES 0
+#define DELTA_FCC_PERCENT 5
+#define MIN_START_PERCENT_FOR_LEARNING 20
+#define MIN_START_OCV_PERCENT_FOR_LEARNING 30
+#define MAX_FCC_LEARNING_COUNT 5
+#define VALID_FCC_CHGCYL_RANGE 50
+
static int last_usb_cal_delta_uv = 1800;
module_param(last_usb_cal_delta_uv, int, 0644);
static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
static int last_charge_increase;
+static int last_fcc_update_count;
+static int max_fcc_cycles = -EINVAL;
module_param(last_chargecycles, int, 0644);
module_param(last_charge_increase, int, 0644);
+module_param(last_fcc_update_count, int, 0644);
static int calculated_soc = -EINVAL;
static int last_soc = -EINVAL;
static int last_real_fcc_mah = -EINVAL;
static int last_real_fcc_batt_temp = -EINVAL;
+static int battery_removed;
static int pm8921_battery_gauge_alarm_notify(struct notifier_block *nb,
unsigned long status, void *unused);
@@ -211,20 +241,20 @@
.notifier_call = pm8921_battery_gauge_alarm_notify,
};
-static int bms_ops_set(const char *val, const struct kernel_param *kp)
+static int bms_ro_ops_set(const char *val, const struct kernel_param *kp)
{
- if (*(int *)kp->arg == -EINVAL)
- return param_set_int(val, kp);
- else
- return 0;
+ return -EINVAL;
}
static struct kernel_param_ops bms_param_ops = {
- .set = bms_ops_set,
+ .set = bms_ro_ops_set,
.get = param_get_int,
};
-
+/* Make last_soc as read only as it is already calculated from shutdown_soc */
module_param_cb(last_soc, &bms_param_ops, &last_soc, 0644);
+module_param_cb(battery_removed, &bms_param_ops, &battery_removed, 0644);
+module_param_cb(max_fcc_cycles, &bms_param_ops,
+ &max_fcc_cycles, 0644);
/*
* bms_fake_battery is set in setups where a battery emulator is used instead
@@ -242,11 +272,6 @@
static int bms_end_ocv_uv;
static int bms_end_cc_uah;
-static int bms_ro_ops_set(const char *val, const struct kernel_param *kp)
-{
- return -EINVAL;
-}
-
static struct kernel_param_ops bms_ro_param_ops = {
.set = bms_ro_ops_set,
.get = param_get_int,
@@ -264,6 +289,9 @@
struct single_row_lut *temp, *old;
int i, fcc, ratio;
+ if (!the_chip->enable_fcc_learning || battery_removed)
+ return;
+
if (!the_chip->fcc_temp_lut) {
pr_err("The static fcc lut table is NULL\n");
return;
@@ -298,6 +326,9 @@
{
int rc = 0;
+ if (battery_removed)
+ return rc;
+
if (last_real_fcc_mah == -EINVAL)
rc = param_set_int(val, kp);
if (rc) {
@@ -320,6 +351,9 @@
{
int rc = 0;
+ if (battery_removed)
+ return rc;
+
if (last_real_fcc_batt_temp == -EINVAL)
rc = param_set_int(val, kp);
if (rc) {
@@ -1549,33 +1583,6 @@
pr_debug("UUC = %uuAh\n", *unusable_charge_uah);
}
-static int calculate_real_fcc_uah(struct pm8921_bms_chip *chip,
- struct pm8921_soc_params *raw,
- int batt_temp, int chargecycles,
- int *ret_fcc_uah)
-{
- int fcc_uah, unusable_charge_uah;
- int remaining_charge_uah;
- int cc_uah;
- int real_fcc_uah;
- int rbatt;
- int iavg_ua;
-
- calculate_soc_params(chip, raw, batt_temp, chargecycles,
- &fcc_uah,
- &unusable_charge_uah,
- &remaining_charge_uah,
- &cc_uah,
- &rbatt,
- &iavg_ua);
-
- real_fcc_uah = remaining_charge_uah - cc_uah;
- *ret_fcc_uah = fcc_uah;
- pr_debug("real_fcc = %d, RC = %d CC = %d fcc = %d\n",
- real_fcc_uah, remaining_charge_uah, cc_uah, fcc_uah);
- return real_fcc_uah;
-}
-
int pm8921_bms_get_simultaneous_battery_voltage_and_current(int *ibat_ua,
int *vbat_uv)
{
@@ -2387,10 +2394,11 @@
rbatt, fcc_uah, unusable_charge_uah, cc_uah);
pr_debug("calculated SOC = %d\n", new_calculated_soc);
- if (new_calculated_soc != calculated_soc)
+ if (new_calculated_soc != calculated_soc) {
+ calculated_soc = new_calculated_soc;
update_power_supply(chip);
+ }
- calculated_soc = new_calculated_soc;
firsttime = 0;
get_current_time(&chip->last_recalc_time);
@@ -2495,15 +2503,31 @@
/* last_soc < soc ... scale and catch up */
if (last_soc != -EINVAL && last_soc < soc && soc != 100)
- soc = scale_soc_while_chg(chip, delta_time_us,
- soc, last_soc);
+ soc = scale_soc_while_chg(chip, delta_time_us, soc, last_soc);
- /* restrict soc to 1% change */
if (last_soc != -EINVAL) {
- if (soc < last_soc && soc != 0)
+ if (chip->first_report_after_suspend) {
+ chip->first_report_after_suspend = false;
+ if (chip->soc_updated_on_resume) {
+ /* coming here after a long suspend */
+ chip->soc_updated_on_resume = false;
+ if (last_soc < soc)
+ /* if soc has falsely increased during
+ * suspend, set the soc_at_suspend
+ */
+ soc = chip->last_soc_at_suspend;
+ } else {
+ /*
+ * suspended for a short time
+ * report the last_soc before suspend
+ */
+ soc = chip->last_soc_at_suspend;
+ }
+ } else if (soc < last_soc && soc != 0) {
soc = last_soc - 1;
- if (soc > last_soc && soc != 100)
+ } else if (soc > last_soc && soc != 100) {
soc = last_soc + 1;
+ }
}
last_soc = bound_soc(soc);
@@ -2555,6 +2579,12 @@
/* store invalid soc */
pm8xxx_writeb(the_chip->dev->parent, TEMP_SOC_STORAGE, 0);
+ /* fcc learning cleanup */
+ if (the_chip->enable_fcc_learning) {
+ battery_removed = 1;
+ sysfs_notify(&the_chip->dev->kobj, NULL, "fcc_data");
+ }
+
/* UUC related data is left as is - use the same historical load avg */
update_power_supply(the_chip);
}
@@ -2578,6 +2608,14 @@
int calculate_soc = 0;
struct pm8921_bms_chip *chip = the_chip;
+ /* clean up the fcc learning table */
+ if (!the_chip)
+ the_chip->adjusted_fcc_temp_lut = NULL;
+ last_fcc_update_count = 0;
+ last_real_fcc_mah = -EINVAL;
+ last_real_fcc_batt_temp = -EINVAL;
+ battery_removed = 1;
+
pr_debug("Invalidating shutdown soc - the battery was removed\n");
if (shutdown_soc_invalid)
return;
@@ -2699,6 +2737,20 @@
return calculate_fcc_uah(the_chip, batt_temp, last_chargecycles);
}
EXPORT_SYMBOL_GPL(pm8921_bms_get_fcc);
+
+static void calculate_real_soc(struct pm8921_bms_chip *chip, int *soc,
+ int batt_temp, struct pm8921_soc_params *raw, int cc_uah)
+{
+ int fcc_uah = 0, rc_uah = 0;
+
+ fcc_uah = calculate_fcc_uah(chip, batt_temp, last_chargecycles);
+ rc_uah = calculate_remaining_charge_uah(chip, raw,
+ fcc_uah, batt_temp, last_chargecycles);
+ *soc = ((rc_uah - cc_uah) * 100) / fcc_uah;
+ pr_debug("fcc = %d, rc = %d, cc = %d Real SOC = %d\n",
+ fcc_uah, rc_uah, cc_uah, *soc);
+}
+
void pm8921_bms_charging_began(void)
{
struct pm8921_soc_params raw;
@@ -2722,12 +2774,123 @@
the_chip->soc_at_cv = -EINVAL;
the_chip->prev_chg_soc = -EINVAL;
+ if (the_chip->enable_fcc_learning) {
+ calculate_real_soc(the_chip, &the_chip->start_real_soc,
+ batt_temp, &raw, bms_start_cc_uah);
+ the_chip->pc_at_start_charge =
+ interpolate_pc(the_chip->pc_temp_ocv_lut, batt_temp,
+ bms_start_ocv_uv / 1000);
+ pr_debug("Start real soc = %d, start pc = %d\n",
+ the_chip->start_real_soc, the_chip->pc_at_start_charge);
+ }
+
pr_debug("start_percent = %u%%\n", the_chip->start_percent);
}
EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
-#define DELTA_FCC_PERCENT 3
-#define MIN_START_PERCENT_FOR_LEARNING 30
+static void invalidate_fcc(struct pm8921_bms_chip *chip)
+{
+ memset(chip->fcc_table, 0, chip->max_fcc_learning_samples *
+ sizeof(*(chip->fcc_table)));
+ last_fcc_update_count = 0;
+ chip->adjusted_fcc_temp_lut = NULL;
+ last_real_fcc_mah = -EINVAL;
+ last_real_fcc_batt_temp = -EINVAL;
+ last_chargecycles = 0;
+ last_charge_increase = 0;
+}
+
+static void update_fcc_table_for_temp(struct pm8921_bms_chip *chip,
+ int batt_temp_final)
+{
+ int i, fcc_t1, fcc_t2, fcc_final;
+ struct fcc_data *ft;
+
+ /* Interpolate all the FCC entries to the same temperature */
+ for (i = 0; i < chip->max_fcc_learning_samples; i++) {
+ ft = &chip->fcc_table[i];
+ if (ft->batt_temp == batt_temp_final)
+ continue;
+ fcc_t1 = interpolate_fcc(chip->fcc_temp_lut, ft->batt_temp);
+ fcc_t2 = interpolate_fcc(chip->fcc_temp_lut, batt_temp_final);
+ fcc_final = (ft->fcc_new / fcc_t1) * fcc_t2;
+ ft->fcc_new = fcc_final;
+ ft->batt_temp = batt_temp_final;
+ }
+}
+
+static void update_fcc_learning_table(struct pm8921_bms_chip *chip,
+ int fcc_uah, int new_fcc_uah, int chargecycles, int batt_temp)
+{
+ int i, temp_fcc_avg = 0, new_fcc_avg = 0, temp_fcc_delta = 0, count;
+ struct fcc_data *ft;
+
+ count = last_fcc_update_count % chip->max_fcc_learning_samples;
+ ft = &chip->fcc_table[count];
+ ft->fcc_new = ft->fcc_real = new_fcc_uah;
+ ft->batt_temp = ft->temp_real = batt_temp;
+ ft->chargecycles = chargecycles;
+ chip->fcc_new = new_fcc_uah;
+ last_fcc_update_count++;
+ /* update userspace with the new data */
+ sysfs_notify(&chip->dev->kobj, NULL, "fcc_data");
+
+ pr_debug("Updated fcc table. new_fcc=%d, chargecycle=%d, temp=%d fcc_update_count=%d\n",
+ new_fcc_uah, chargecycles, batt_temp, last_fcc_update_count);
+
+ if (last_fcc_update_count < chip->max_fcc_learning_samples) {
+ pr_debug("Not enough FCC samples. Current count = %d\n",
+ last_fcc_update_count);
+ return; /* Not enough samples to update fcc */
+ }
+
+ /* reject entries if they are > 50 chargecycles apart */
+ for (i = 0; i < chip->max_fcc_learning_samples; i++) {
+ if ((chip->fcc_table[i].chargecycles + VALID_FCC_CHGCYL_RANGE)
+ < chargecycles) {
+ pr_debug("Charge cycle too old (> %d cycles apart)\n",
+ VALID_FCC_CHGCYL_RANGE);
+ return; /* Samples old, > 50 cycles apart*/
+ }
+ }
+ /* update the fcc table for temperature difference*/
+ update_fcc_table_for_temp(chip, batt_temp);
+
+ /* Calculate the avg. and SD for all the fcc entries */
+ for (i = 0; i < chip->max_fcc_learning_samples; i++)
+ temp_fcc_avg += chip->fcc_table[i].fcc_new;
+
+ temp_fcc_avg /= chip->max_fcc_learning_samples;
+ temp_fcc_delta = div_u64(temp_fcc_avg * DELTA_FCC_PERCENT, 100);
+
+ /* fix the fcc if its an outlier i.e. > 5% of the average */
+ for (i = 0; i < chip->max_fcc_learning_samples; i++) {
+ ft = &chip->fcc_table[i];
+ if (abs(ft->fcc_new - temp_fcc_avg) > temp_fcc_delta)
+ ft->fcc_new = temp_fcc_avg;
+ new_fcc_avg += ft->fcc_new;
+ }
+ new_fcc_avg /= chip->max_fcc_learning_samples;
+
+ last_real_fcc_mah = new_fcc_avg/1000;
+ last_real_fcc_batt_temp = batt_temp;
+
+ pr_debug("FCC update: last_real_fcc_mah=%d, last_real_fcc_batt_temp=%d\n",
+ new_fcc_avg, batt_temp);
+ readjust_fcc_table();
+}
+
+static bool is_new_fcc_valid(int new_fcc_uah, int fcc_uah)
+{
+ /* reject the new fcc if < 50% and > 105% of nominal fcc */
+ if ((new_fcc_uah >= (fcc_uah / 2)) &&
+ ((new_fcc_uah * 100) <= (fcc_uah * 105)))
+ return true;
+
+ pr_debug("FCC rejected - not within valid limit\n");
+ return false;
+}
+
void pm8921_bms_charging_end(int is_battery_full)
{
int batt_temp;
@@ -2746,37 +2909,27 @@
bms_end_ocv_uv = raw.last_good_ocv_uv;
- if (is_battery_full && the_chip->enable_fcc_learning
- && the_chip->start_percent <= MIN_START_PERCENT_FOR_LEARNING) {
- int fcc_uah, new_fcc_uah, delta_fcc_uah;
+ pr_debug("battery_full = %d, fcc_learning = %d, pc_start_chg = %d\n",
+ is_battery_full, the_chip->enable_fcc_learning,
+ the_chip->pc_at_start_charge);
+ if (is_battery_full && the_chip->enable_fcc_learning &&
+ (the_chip->start_percent <= the_chip->min_fcc_learning_soc) &&
+ (the_chip->pc_at_start_charge <= the_chip->min_fcc_ocv_pc)) {
- new_fcc_uah = calculate_real_fcc_uah(the_chip, &raw,
- batt_temp, last_chargecycles,
- &fcc_uah);
- delta_fcc_uah = new_fcc_uah - fcc_uah;
- if (delta_fcc_uah < 0)
- delta_fcc_uah = -delta_fcc_uah;
+ int fcc_uah, new_fcc_uah, delta_cc_uah, delta_soc;
+ /* new_fcc = (cc_end - cc_start) / (end_soc - start_soc) */
+ delta_soc = 100 - the_chip->start_real_soc;
+ delta_cc_uah = abs(bms_end_cc_uah - bms_start_cc_uah);
+ new_fcc_uah = div_u64(delta_cc_uah * 100, delta_soc);
- if (delta_fcc_uah * 100 > (DELTA_FCC_PERCENT * fcc_uah)) {
- /* new_fcc_uah is outside the scope limit it */
- if (new_fcc_uah > fcc_uah)
- new_fcc_uah
- = (fcc_uah +
- (DELTA_FCC_PERCENT * fcc_uah) / 100);
- else
- new_fcc_uah
- = (fcc_uah -
- (DELTA_FCC_PERCENT * fcc_uah) / 100);
-
- pr_debug("delta_fcc=%d > %d percent of fcc=%d"
- "restring it to %d\n",
- delta_fcc_uah, DELTA_FCC_PERCENT,
- fcc_uah, new_fcc_uah);
- }
-
- last_real_fcc_mah = new_fcc_uah/1000;
- last_real_fcc_batt_temp = batt_temp;
- readjust_fcc_table();
+ fcc_uah = calculate_fcc_uah(the_chip, batt_temp,
+ last_chargecycles);
+ pr_info("start_real_soc = %d, end_real_soc = 100, start_cc = %d, end_cc = %d, nominal_fcc = %d, new_fcc = %d\n",
+ the_chip->start_real_soc, bms_start_cc_uah,
+ bms_end_cc_uah, fcc_uah, new_fcc_uah);
+ if (is_new_fcc_valid(new_fcc_uah, fcc_uah))
+ update_fcc_learning_table(the_chip, fcc_uah,
+ new_fcc_uah, last_chargecycles, batt_temp);
}
if (is_battery_full) {
@@ -3330,6 +3483,127 @@
}
}
+static ssize_t fcc_data_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
+ static int i;
+ int fcc_new = 0, rc;
+
+ if (battery_removed) {
+ pr_debug("Invalid FCC table. Possible battery removal\n");
+ last_fcc_update_count = 0;
+ return count;
+ }
+
+ i %= chip->max_fcc_learning_samples;
+ rc = sscanf(buf, "%d", &fcc_new);
+ if (rc != 1)
+ return -EINVAL;
+ chip->fcc_table[i].fcc_new = fcc_new;
+ chip->fcc_table[i].fcc_real = fcc_new;
+ pr_debug("Rcvd: [%d] fcc_new=%d\n", i, fcc_new);
+ i++;
+
+ return count;
+}
+
+static ssize_t fcc_data_get(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int count = 0;
+ struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
+
+ if (battery_removed) {
+ pr_debug("Invalidate the fcc table\n");
+ invalidate_fcc(chip);
+ battery_removed = 0;
+ return count;
+ }
+
+ count = snprintf(buf, PAGE_SIZE, "%d", chip->fcc_new);
+
+ pr_debug("Sent: fcc_new=%d\n", chip->fcc_new);
+
+ return count;
+}
+
+static ssize_t fcc_temp_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ static int i;
+ int batt_temp = 0, rc;
+ struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
+
+ i %= chip->max_fcc_learning_samples;
+ rc = sscanf(buf, "%d", &batt_temp);
+ if (rc != 1)
+ return -EINVAL;
+ chip->fcc_table[i].batt_temp = batt_temp;
+ chip->fcc_table[i].temp_real = batt_temp;
+ pr_debug("Rcvd: [%d] batt_temp=%d\n", i, batt_temp);
+ i++;
+
+ return count;
+}
+
+static ssize_t fcc_chgcyl_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ static int i;
+ int chargecycle = 0, rc;
+ struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
+
+ i %= chip->max_fcc_learning_samples;
+ rc = sscanf(buf, "%d", &chargecycle);
+ if (rc != 1)
+ return -EINVAL;
+ chip->fcc_table[i].chargecycles = chargecycle;
+ pr_debug("Rcvd: [%d] chargecycle=%d\n", i, chargecycle);
+ i++;
+
+ return count;
+}
+
+static ssize_t fcc_list_get(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
+ struct fcc_data *ft;
+ int i = 0, j, count = 0;
+
+ if (last_fcc_update_count < chip->max_fcc_learning_samples)
+ i = last_fcc_update_count;
+ else
+ i = chip->max_fcc_learning_samples;
+
+ for (j = 0; j < i; j++) {
+ ft = &chip->fcc_table[j];
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "%d %d %d %d %d\n", ft->fcc_new, ft->chargecycles,
+ ft->batt_temp, ft->fcc_real, ft->temp_real);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(fcc_data, 0664, fcc_data_get, fcc_data_set);
+static DEVICE_ATTR(fcc_temp, 0664, NULL, fcc_temp_set);
+static DEVICE_ATTR(fcc_chgcyl, 0664, NULL, fcc_chgcyl_set);
+static DEVICE_ATTR(fcc_list, 0664, fcc_list_get, NULL);
+
+static struct attribute *fcc_attrs[] = {
+ &dev_attr_fcc_data.attr,
+ &dev_attr_fcc_temp.attr,
+ &dev_attr_fcc_chgcyl.attr,
+ &dev_attr_fcc_list.attr,
+ NULL
+};
+
+static const struct attribute_group fcc_attr_group = {
+ .attrs = fcc_attrs,
+};
+
#define REG_SBI_CONFIG 0x04F
#define PAGE3_ENABLE_MASK 0x6
#define PROGRAM_REV_MASK 0x0F
@@ -3457,6 +3731,34 @@
chip->batt_id_channel = pdata->bms_cdata.batt_id_channel;
chip->revision = pm8xxx_get_revision(chip->dev->parent);
chip->enable_fcc_learning = pdata->enable_fcc_learning;
+ chip->min_fcc_learning_soc = pdata->min_fcc_learning_soc;
+ chip->min_fcc_ocv_pc = pdata->min_fcc_ocv_pc;
+ chip->max_fcc_learning_samples = pdata->max_fcc_learning_samples;
+ if (chip->enable_fcc_learning) {
+ if (!chip->min_fcc_learning_soc)
+ chip->min_fcc_learning_soc =
+ MIN_START_PERCENT_FOR_LEARNING;
+ if (!chip->min_fcc_ocv_pc)
+ chip->min_fcc_ocv_pc =
+ MIN_START_OCV_PERCENT_FOR_LEARNING;
+ if (!chip->max_fcc_learning_samples ||
+ chip->max_fcc_learning_samples > MAX_FCC_LEARNING_COUNT)
+ chip->max_fcc_learning_samples = MAX_FCC_LEARNING_COUNT;
+
+ max_fcc_cycles = chip->max_fcc_learning_samples;
+ chip->fcc_table = kzalloc(sizeof(struct fcc_data) *
+ chip->max_fcc_learning_samples, GFP_KERNEL);
+ if (!chip->fcc_table) {
+ pr_err("Unable to allocate table for fcc learning\n");
+ rc = -ENOMEM;
+ goto free_chip;
+ }
+ rc = sysfs_create_group(&pdev->dev.kobj, &fcc_attr_group);
+ if (rc) {
+ pr_err("Unable to create sysfs entries\n");
+ goto free_chip;
+ }
+ }
chip->disable_flat_portion_ocv = pdata->disable_flat_portion_ocv;
chip->ocv_dis_high_soc = pdata->ocv_dis_high_soc;
@@ -3560,12 +3862,11 @@
static int pm8921_bms_suspend(struct device *dev)
{
- /*
- * set the last reported soc to invalid, so that
- * next time we resume we don't want to restrict
- * the decrease of soc by only 1%
- */
- last_soc = -EINVAL;
+ struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&chip->calculate_soc_delayed_work);
+
+ chip->last_soc_at_suspend = last_soc;
return 0;
}
@@ -3575,22 +3876,30 @@
int rc;
unsigned long time_since_last_recalc;
unsigned long tm_now_sec;
+ struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
rc = get_current_time(&tm_now_sec);
if (rc) {
pr_err("Could not read current time: %d\n", rc);
return 0;
}
- if (tm_now_sec > the_chip->last_recalc_time) {
+
+ if (tm_now_sec > chip->last_recalc_time) {
time_since_last_recalc = tm_now_sec -
- the_chip->last_recalc_time;
+ chip->last_recalc_time;
pr_debug("Time since last recalc: %lu\n",
time_since_last_recalc);
- if (time_since_last_recalc >= the_chip->soc_calc_period) {
- the_chip->last_recalc_time = tm_now_sec;
- recalculate_soc(the_chip);
+ if ((time_since_last_recalc * 1000) >=
+ chip->soc_calc_period) {
+ chip->last_recalc_time = tm_now_sec;
+ recalculate_soc(chip);
+ chip->soc_updated_on_resume = true;
}
}
+ chip->first_report_after_suspend = true;
+ update_power_supply(chip);
+ schedule_delayed_work(&chip->calculate_soc_delayed_work,
+ msecs_to_jiffies(chip->soc_calc_period));
return 0;
}
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index e00e1c3..1ad7f21 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -83,6 +83,7 @@
#define CHG_COMP_OVR 0x20A
#define IUSB_FINE_RES 0x2B6
#define OVP_USB_UVD 0x2B7
+#define PM8921_USB_TRIM_SEL 0x339
/* check EOC every 10 seconds */
#define EOC_CHECK_PERIOD_MS 10000
@@ -777,6 +778,44 @@
};
/* USB Trim tables */
+static int usb_trim_pm8921_table_1[USB_TRIM_ENTRIES] = {
+ 0x0,
+ 0x0,
+ -0x5,
+ 0x0,
+ -0x7,
+ 0x0,
+ -0x9,
+ -0xA,
+ 0x0,
+ 0x0,
+ -0xE,
+ 0x0,
+ -0xF,
+ 0x0,
+ -0x10,
+ 0x0
+};
+
+static int usb_trim_pm8921_table_2[USB_TRIM_ENTRIES] = {
+ 0x0,
+ 0x0,
+ -0x2,
+ 0x0,
+ -0x4,
+ 0x0,
+ -0x4,
+ -0x5,
+ 0x0,
+ 0x0,
+ -0x6,
+ 0x0,
+ -0x6,
+ 0x0,
+ -0x6,
+ 0x0
+};
+
static int usb_trim_8038_table[USB_TRIM_ENTRIES] = {
0x0,
0x0,
@@ -844,6 +883,8 @@
#define REG_USB_OVP_TRIM_ORIG_MSB 0x09C
#define REG_USB_OVP_TRIM_PM8917 0x2B5
#define REG_USB_OVP_TRIM_PM8917_BIT BIT(0)
+#define USB_TRIM_MAX_DATA_PM8917 0x3F
+#define USB_TRIM_POLARITY_PM8917_BIT BIT(6)
static int pm_chg_usb_trim(struct pm8921_chg_chip *chip, int index)
{
u8 temp, sbi_config, msb, lsb, mask;
@@ -3805,11 +3846,14 @@
}
}
+#define PM8921_USB_TRIM_SEL_BIT BIT(6)
/* determines the initial present states */
static void __devinit determine_initial_state(struct pm8921_chg_chip *chip)
{
int fsm_state;
int is_fast_chg;
+ int rc = 0;
+ u8 trim_sel_reg = 0, regsbi;
chip->dc_present = !!is_dc_chg_plugged_in(chip);
chip->usb_present = !!is_usb_chg_plugged_in(chip);
@@ -3872,10 +3916,26 @@
fsm_state);
/* Determine which USB trim column to use */
- if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8917)
+ if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8917) {
chip->usb_trim_table = usb_trim_8917_table;
- else if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8038)
+ } else if (pm8xxx_get_version(chip->dev->parent) ==
+ PM8XXX_VERSION_8038) {
chip->usb_trim_table = usb_trim_8038_table;
+ } else if (pm8xxx_get_version(chip->dev->parent) ==
+ PM8XXX_VERSION_8921) {
+ rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, ®sbi);
+ rc |= pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, 0x5E);
+ rc |= pm8xxx_readb(chip->dev->parent, PM8921_USB_TRIM_SEL,
+ &trim_sel_reg);
+ rc |= pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, regsbi);
+ if (rc)
+ pr_err("Failed to read trim sel register rc=%d\n", rc);
+
+ if (trim_sel_reg & PM8921_USB_TRIM_SEL_BIT)
+ chip->usb_trim_table = usb_trim_pm8921_table_1;
+ else
+ chip->usb_trim_table = usb_trim_pm8921_table_2;
+ }
}
struct pm_chg_irq_init_data {
diff --git a/drivers/power/pm8xxx-ccadc.c b/drivers/power/pm8xxx-ccadc.c
index 5313593..c53bcd2 100644
--- a/drivers/power/pm8xxx-ccadc.c
+++ b/drivers/power/pm8xxx-ccadc.c
@@ -790,6 +790,15 @@
return 0;
}
+static int pm8xxx_ccadc_suspend(struct device *dev)
+{
+ struct pm8xxx_ccadc_chip *chip = dev_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&chip->calib_ccadc_work);
+
+ return 0;
+}
+
#define CCADC_CALIB_TEMP_THRESH 20
static int pm8xxx_ccadc_resume(struct device *dev)
{
@@ -823,8 +832,11 @@
|| delta_temp > CCADC_CALIB_TEMP_THRESH) {
the_chip->last_calib_time = current_time_sec;
the_chip->last_calib_temp = batt_temp;
- cancel_delayed_work(&the_chip->calib_ccadc_work);
schedule_delayed_work(&the_chip->calib_ccadc_work, 0);
+ } else {
+ schedule_delayed_work(&the_chip->calib_ccadc_work,
+ msecs_to_jiffies(the_chip->calib_delay_ms -
+ (time_since_last_calib * 1000)));
}
}
@@ -832,6 +844,7 @@
}
static const struct dev_pm_ops pm8xxx_ccadc_pm_ops = {
+ .suspend = pm8xxx_ccadc_suspend,
.resume = pm8xxx_ccadc_resume,
};
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 63fb0a5..5cdb666 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -176,6 +176,7 @@
POWER_SUPPLY_ATTR(type),
POWER_SUPPLY_ATTR(scope),
POWER_SUPPLY_ATTR(system_temp_level),
+ POWER_SUPPLY_ATTR(resistance),
/* Properties of type `const char *' */
POWER_SUPPLY_ATTR(model_name),
POWER_SUPPLY_ATTR(manufacturer),
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index fd42c47..d9a216d 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -24,6 +24,7 @@
#include <linux/rtc.h>
#include <linux/delay.h>
#include <linux/qpnp/qpnp-adc.h>
+#include <linux/qpnp/power-on.h>
#include <linux/mfd/pm8xxx/batterydata-lib.h>
/* BMS Register Offsets */
@@ -73,8 +74,13 @@
/* Extra bms registers */
#define SOC_STORAGE_REG 0xB0
#define IAVG_STORAGE_REG 0xB1
-#define BMS1_BMS_DATA_REG_2 0xB2
+#define BMS_BATT_REMOVED_REG 0xB2
#define BMS1_BMS_DATA_REG_3 0xB3
+#define CHARGE_INCREASE_STORAGE 0xB4
+#define FCC_BATT_TEMP_STORAGE 0xB5
+#define FCC_STORAGE_LSB 0xBC /* LSB=0xBC, MSB=0xBD */
+#define CHARGE_CYCLE_STORAGE_LSB 0xBE /* LSB=0xBE, MSB=0xBF */
+
/* IADC Channel Select */
#define IADC1_BMS_ADC_CH_SEL_CTL 0x48
@@ -87,6 +93,10 @@
#define IAVG_SAMPLES 16
+/* FCC learning constants */
+#define DELTA_FCC_PERCENT 5
+#define VALID_FCC_CHGCYL_RANGE 50
+
#define QPNP_BMS_DEV_NAME "qcom,qpnp-bms"
struct soc_params {
@@ -105,6 +115,12 @@
int last_good_ocv_uv;
};
+struct fcc_data {
+ int fcc_new;
+ int batt_temp;
+ int chargecycles;
+};
+
struct qpnp_bms_chip {
struct device *dev;
struct power_supply bms_psy;
@@ -116,7 +132,9 @@
u8 revision1;
u8 revision2;
int battery_present;
+ int battery_status;
bool new_battery;
+ bool done_charging;
bool last_soc_invalid;
/* platform data */
int r_sense_uohm;
@@ -127,7 +145,7 @@
int adjust_soc_low_threshold;
int chg_term_ua;
enum battery_type batt_type;
- unsigned int fcc;
+ unsigned int fcc_mah;
struct single_row_lut *fcc_temp_lut;
struct single_row_lut *fcc_sf_lut;
struct pc_temp_ocv_lut *pc_temp_ocv_lut;
@@ -135,13 +153,16 @@
struct sf_lut *rbatt_sf_lut;
int default_rbatt_mohm;
int rbatt_capacitive_mohm;
+ int rbatt_mohm;
struct delayed_work calculate_soc_delayed_work;
struct work_struct recalc_work;
struct mutex bms_output_lock;
struct mutex last_ocv_uv_mutex;
+ struct mutex vbat_monitor_mutex;
struct mutex soc_invalidation_mutex;
+ struct mutex last_soc_mutex;
bool use_external_rsense;
bool use_ocv_thresholds;
@@ -152,22 +173,25 @@
int shutdown_iavg_ma;
struct wake_lock low_voltage_wake_lock;
- bool low_voltage_wake_lock_held;
int low_voltage_threshold;
int low_soc_calc_threshold;
int low_soc_calculate_soc_ms;
int calculate_soc_ms;
struct wake_lock soc_wake_lock;
+ struct wake_lock cv_wake_lock;
uint16_t ocv_reading_at_100;
uint16_t prev_last_good_ocv_raw;
int last_ocv_uv;
+ int charging_adjusted_ocv;
int last_ocv_temp;
int last_cc_uah;
+ unsigned long last_soc_change_sec;
unsigned long tm_sec;
+ unsigned long report_tm_sec;
bool first_time_calc_soc;
bool first_time_calc_uuc;
- int pon_ocv_uv;
+ int64_t software_cc_uah;
int iavg_samples_ma[IAVG_SAMPLES];
int iavg_index;
@@ -176,15 +200,17 @@
int last_soc;
int last_soc_est;
int last_soc_unbound;
-
- int charge_time_us;
- int catch_up_time_us;
+ bool was_charging_at_sleep;
+ int charge_start_tm_sec;
+ int catch_up_time_sec;
struct single_row_lut *adjusted_fcc_temp_lut;
+ struct qpnp_adc_tm_btm_param vbat_monitor_params;
+ struct qpnp_adc_tm_btm_param die_temp_monitor_params;
+ int temperature_margin;
unsigned int vadc_v0625;
unsigned int vadc_v1250;
- int ibat_max_ua;
int prev_uuc_iavg_ma;
int prev_pc_unusable;
int ibat_at_cv_ua;
@@ -193,6 +219,7 @@
int calculated_soc;
int prev_voltage_based_soc;
bool use_voltage_soc;
+ bool in_cv_range;
int prev_batt_terminal_uv;
int high_ocv_correction_limit_uv;
@@ -203,6 +230,23 @@
int ocv_high_threshold_uv;
int ocv_low_threshold_uv;
unsigned long last_recalc_time;
+
+ struct fcc_data *fcc_table;
+ int enable_fcc_learning;
+ int min_fcc_learning_soc;
+ int min_fcc_ocv_pc;
+ int min_fcc_learning_samples;
+ int start_soc;
+ int end_soc;
+ int start_pc;
+ int start_cc_uah;
+ int start_real_soc;
+ int end_cc_uah;
+ uint16_t fcc_new_mah;
+ int fcc_new_batt_temp;
+ uint16_t charge_cycles;
+ u8 charge_increase;
+ int fcc_new_sysfs;
};
static struct of_device_id qpnp_bms_match_table[] = {
@@ -215,14 +259,31 @@
};
static enum power_supply_property msm_bms_power_props[] = {
- POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CURRENT_NOW,
- POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_RESISTANCE,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
};
static bool bms_reset;
+static int min_fcc_cycles = -EINVAL;
+static int last_fcc_update_count;
+static int battery_removed;
+
+static int
+bms_ro_ops_set(const char *val, const struct kernel_param *kp)
+{
+ return -EINVAL;
+}
+static struct kernel_param_ops bms_ro_param_ops = {
+ .set = bms_ro_ops_set,
+ .get = param_get_int,
+};
+module_param_cb(min_fcc_cycles, &bms_ro_param_ops, &min_fcc_cycles, 0644);
+module_param_cb(battery_removed, &bms_ro_param_ops, &battery_removed, 0644);
+module_param(last_fcc_update_count, int, 0644);
static int qpnp_read_wrapper(struct qpnp_bms_chip *chip, u8 *val,
u16 base, int count)
@@ -364,18 +425,23 @@
static inline int convert_vbatt_raw_to_uv(struct qpnp_bms_chip *chip,
uint16_t reading)
{
- int uv;
+ int64_t uv;
+ int rc;
uv = vadc_reading_to_uv(reading);
- pr_debug("%u raw converted into %d uv\n", reading, uv);
+ pr_debug("%u raw converted into %lld uv\n", reading, uv);
uv = adjust_vbatt_reading(chip, uv);
- pr_debug("adjusted into %d uv\n", uv);
+ pr_debug("adjusted into %lld uv\n", uv);
+ rc = qpnp_vbat_sns_comp_result(&uv);
+ if (rc)
+ pr_debug("could not compensate vbatt\n");
+ pr_debug("compensated into %lld uv\n", uv);
return uv;
}
#define CC_READING_RESOLUTION_N 542535
#define CC_READING_RESOLUTION_D 100000
-static int cc_reading_to_uv(int16_t reading)
+static s64 cc_reading_to_uv(s64 reading)
{
return div_s64(reading * CC_READING_RESOLUTION_N,
CC_READING_RESOLUTION_D);
@@ -429,7 +495,8 @@
static int get_battery_current(struct qpnp_bms_chip *chip, int *result_ua)
{
- int vsense_uv = 0;
+ int rc, vsense_uv = 0;
+ int64_t temp_current;
if (chip->r_sense_uohm == 0) {
pr_err("r_sense is zero\n");
@@ -444,8 +511,15 @@
pr_debug("vsense_uv=%duV\n", vsense_uv);
/* cast for signed division */
- *result_ua = div_s64((vsense_uv * 1000000LL), (int)chip->r_sense_uohm);
- pr_debug("ibat=%duA\n", *result_ua);
+ temp_current = div_s64((vsense_uv * 1000000LL),
+ (int)chip->r_sense_uohm);
+
+ rc = qpnp_iadc_comp_result(&temp_current);
+ if (rc)
+ pr_debug("error compensation failed: %d\n", rc);
+
+ *result_ua = temp_current;
+ pr_debug("err compensated ibat=%duA\n", *result_ua);
return 0;
}
@@ -533,6 +607,7 @@
raw->last_good_ocv_raw);
chip->last_ocv_uv = raw->last_good_ocv_uv;
chip->last_ocv_temp = batt_temp;
+ chip->software_cc_uah = 0;
pr_debug("last_good_ocv_uv = %d\n", raw->last_good_ocv_uv);
}
@@ -564,7 +639,7 @@
pr_err("cc reenable failed: %d\n", rc);
}
-static bool is_battery_charging(struct qpnp_bms_chip *chip)
+static int get_battery_status(struct qpnp_bms_chip *chip)
{
union power_supply_propval ret = {0,};
@@ -574,7 +649,30 @@
/* if battery has been registered, use the status property */
chip->batt_psy->get_property(chip->batt_psy,
POWER_SUPPLY_PROP_STATUS, &ret);
- return ret.intval == POWER_SUPPLY_STATUS_CHARGING;
+ return ret.intval;
+ }
+
+ /* Default to false if the battery power supply is not registered. */
+ pr_debug("battery power supply is not registered\n");
+ return POWER_SUPPLY_STATUS_UNKNOWN;
+}
+
+static bool is_battery_charging(struct qpnp_bms_chip *chip)
+{
+ return get_battery_status(chip) == POWER_SUPPLY_STATUS_CHARGING;
+}
+
+static bool is_battery_present(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (chip->batt_psy) {
+ /* if battery has been registered, use the status property */
+ chip->batt_psy->get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_PRESENT, &ret);
+ return ret.intval;
}
/* Default to false if the battery power supply is not registered. */
@@ -582,22 +680,9 @@
return false;
}
-static bool is_batfet_open(struct qpnp_bms_chip *chip)
+static bool is_battery_full(struct qpnp_bms_chip *chip)
{
- union power_supply_propval ret = {0,};
-
- if (chip->batt_psy == NULL)
- chip->batt_psy = power_supply_get_by_name("battery");
- if (chip->batt_psy) {
- /* if battery has been registered, use the status property */
- chip->batt_psy->get_property(chip->batt_psy,
- POWER_SUPPLY_PROP_STATUS, &ret);
- return ret.intval == POWER_SUPPLY_STATUS_FULL;
- }
-
- /* Default to true if the battery power supply is not registered. */
- pr_debug("battery power supply is not registered\n");
- return true;
+ return get_battery_status(chip) == POWER_SUPPLY_STATUS_FULL;
}
static int get_simultaneous_batt_v_and_i(struct qpnp_bms_chip *chip,
@@ -610,18 +695,32 @@
iadc_channel = chip->use_external_rsense ?
EXTERNAL_RSENSE : INTERNAL_RSENSE;
- rc = qpnp_iadc_vadc_sync_read(iadc_channel, &i_result,
- VBAT_SNS, &v_result);
- if (rc) {
- pr_err("vadc read failed with rc: %d\n", rc);
- return rc;
+ if (is_battery_full(chip)) {
+ rc = get_battery_current(chip, ibat_ua);
+ if (rc) {
+ pr_err("bms current read failed with rc: %d\n", rc);
+ return rc;
+ }
+ rc = qpnp_vadc_read(VBAT_SNS, &v_result);
+ if (rc) {
+ pr_err("vadc read failed with rc: %d\n", rc);
+ return rc;
+ }
+ *vbat_uv = (int)v_result.physical;
+ } else {
+ rc = qpnp_iadc_vadc_sync_read(iadc_channel, &i_result,
+ VBAT_SNS, &v_result);
+ if (rc) {
+ pr_err("adc sync read failed with rc: %d\n", rc);
+ return rc;
+ }
+ /*
+ * reverse the current read by the iadc, since the bms uses
+ * flipped battery current polarity.
+ */
+ *ibat_ua = -1 * (int)i_result.result_ua;
+ *vbat_uv = (int)v_result.physical;
}
- /*
- * reverse the current read by the iadc, since the bms uses
- * flipped battery current polarity.
- */
- *ibat_ua = -1 * (int)i_result.result_ua;
- *vbat_uv = (int)v_result.physical;
return 0;
}
@@ -647,24 +746,29 @@
static void reset_for_new_battery(struct qpnp_bms_chip *chip, int batt_temp)
{
chip->last_ocv_uv = estimate_ocv(chip);
+ mutex_lock(&chip->last_soc_mutex);
chip->last_soc = -EINVAL;
+ chip->last_soc_invalid = true;
+ mutex_unlock(&chip->last_soc_mutex);
chip->soc_at_cv = -EINVAL;
chip->shutdown_soc_invalid = true;
chip->shutdown_soc = 0;
chip->shutdown_iavg_ma = 0;
chip->prev_pc_unusable = -EINVAL;
reset_cc(chip);
+ chip->software_cc_uah = 0;
chip->last_cc_uah = INT_MIN;
chip->last_ocv_temp = batt_temp;
- chip->last_soc_invalid = true;
chip->prev_batt_terminal_uv = 0;
}
#define OCV_RAW_UNINITIALIZED 0xFFFF
+#define MIN_OCV_UV 2000000
static int read_soc_params_raw(struct qpnp_bms_chip *chip,
struct raw_soc_params *raw,
int batt_temp)
{
+ bool warm_reset = false;
int rc;
mutex_lock(&chip->bms_output_lock);
@@ -697,13 +801,37 @@
if (chip->prev_last_good_ocv_raw == OCV_RAW_UNINITIALIZED) {
convert_and_store_ocv(chip, raw, batt_temp);
- pr_debug("PON_OCV_UV = %d\n", chip->last_ocv_uv);
+ pr_debug("PON_OCV_UV = %d, cc = %llx\n",
+ chip->last_ocv_uv, raw->cc);
+ warm_reset = qpnp_pon_is_warm_reset();
+ if (raw->last_good_ocv_uv < MIN_OCV_UV
+ || warm_reset > 0) {
+ pr_debug("OCV is stale or bad, estimating new OCV.\n");
+ chip->last_ocv_uv = estimate_ocv(chip);
+ raw->last_good_ocv_uv = chip->last_ocv_uv;
+ reset_cc(chip);
+ pr_debug("New PON_OCV_UV = %d, cc = %llx\n",
+ chip->last_ocv_uv, raw->cc);
+ }
} else if (chip->new_battery) {
/* if a new battery was inserted, estimate the ocv */
reset_for_new_battery(chip, batt_temp);
raw->cc = 0;
raw->last_good_ocv_uv = chip->last_ocv_uv;
chip->new_battery = false;
+ } else if (chip->done_charging) {
+ chip->done_charging = false;
+ /* if we just finished charging, reset CC and fake 100% */
+ chip->ocv_reading_at_100 = raw->last_good_ocv_raw;
+ chip->last_ocv_uv = chip->max_voltage_uv;
+ raw->last_good_ocv_uv = chip->max_voltage_uv;
+ raw->cc = 0;
+ reset_cc(chip);
+ chip->last_ocv_temp = batt_temp;
+ chip->software_cc_uah = 0;
+ chip->last_cc_uah = INT_MIN;
+ pr_debug("EOC Battery full ocv_reading = 0x%x\n",
+ chip->ocv_reading_at_100);
} else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
convert_and_store_ocv(chip, raw, batt_temp);
/* forget the old cc value upon ocv */
@@ -712,20 +840,10 @@
raw->last_good_ocv_uv = chip->last_ocv_uv;
}
- /* fake a high OCV if done charging */
- if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) {
+ /* stop faking a high OCV if we get a new OCV */
+ if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw)
chip->ocv_reading_at_100 = OCV_RAW_UNINITIALIZED;
- } else {
- /*
- * force 100% ocv by selecting the highest voltage the
- * battery could ever reach
- */
- raw->last_good_ocv_uv = chip->max_voltage_uv;
- chip->last_ocv_uv = chip->max_voltage_uv;
- chip->last_ocv_temp = batt_temp;
- reset_cc(chip);
- raw->cc = 0;
- }
+
pr_debug("last_good_ocv_raw= 0x%x, last_good_ocv_uv= %duV\n",
raw->last_good_ocv_raw, raw->last_good_ocv_uv);
pr_debug("cc_raw= 0x%llx\n", raw->cc);
@@ -774,14 +892,6 @@
return (fcc_uah * pc) / 100;
}
-#define CC_RESOLUTION_N 542535
-#define CC_RESOLUTION_D 100000
-
-static s64 cc_to_uv(s64 cc)
-{
- return div_s64(cc * CC_RESOLUTION_N, CC_RESOLUTION_D);
-}
-
#define CC_READING_TICKS 56
#define SLEEP_CLK_HZ 32764
#define SECONDS_PER_HOUR 3600
@@ -801,23 +911,33 @@
}
/**
- * calculate_cc-
+ * calculate_cc() - converts a hardware coulomb counter reading into uah
* @chip: the bms chip pointer
* @cc: the cc reading from bms h/w
- * @val: return value
- * @coulomb_counter: adjusted coulomb counter for 100%
+ * @clear_cc: whether this function should clear the hardware counter
+ * after reading
*
- * RETURNS: in val pointer coulomb counter based charger in uAh
- * (micro Amp hour)
+ * Converts the 64 bit hardware coulomb counter into microamp-hour by taking
+ * into account hardware resolution and adc errors.
+ *
+ * Return: the coulomb counter based charge in uAh (micro-amp hour)
*/
-static int calculate_cc(struct qpnp_bms_chip *chip, int64_t cc)
+static int calculate_cc(struct qpnp_bms_chip *chip, int64_t cc, bool clear_cc)
{
- int64_t cc_voltage_uv, cc_pvh, cc_uah;
struct qpnp_iadc_calib calibration;
+ struct qpnp_vadc_result result;
+ int64_t cc_voltage_uv, cc_pvh, cc_uah;
+ int rc;
+
+ rc = qpnp_vadc_read(DIE_TEMP, &result);
+ if (rc) {
+ pr_err("could not read pmic die temperature: %d\n", rc);
+ return chip->software_cc_uah;
+ }
qpnp_iadc_get_gain_and_offset(&calibration);
- pr_debug("cc = %lld\n", cc);
- cc_voltage_uv = cc_to_uv(cc);
+ pr_debug("cc = %lld, die_temp = %lld\n", cc, result.physical);
+ cc_voltage_uv = cc_reading_to_uv(cc);
cc_voltage_uv = cc_adjust_for_gain(cc_voltage_uv,
calibration.gain_raw
- calibration.offset_raw);
@@ -825,9 +945,17 @@
cc_pvh = cc_uv_to_pvh(cc_voltage_uv);
pr_debug("cc_pvh = %lld pvh\n", cc_pvh);
cc_uah = div_s64(cc_pvh, chip->r_sense_uohm);
- /* cc_raw had 4 bits of extra precision.
- By now it should be within 32 bit range */
- return (int)cc_uah;
+ rc = qpnp_iadc_comp_result(&cc_uah);
+ if (rc)
+ pr_debug("error compensation failed: %d\n", rc);
+
+ if (clear_cc) {
+ chip->software_cc_uah += cc_uah;
+ reset_cc(chip);
+ return (int)chip->software_cc_uah;
+ } else {
+ return chip->software_cc_uah + cc_uah;
+ }
}
static int get_rbatt(struct qpnp_bms_chip *chip,
@@ -836,7 +964,6 @@
int rbatt_mohm, scalefactor;
rbatt_mohm = chip->default_rbatt_mohm;
- pr_debug("rbatt before scaling = %d\n", rbatt_mohm);
if (chip->rbatt_sf_lut == NULL) {
pr_debug("RBATT = %d\n", rbatt_mohm);
return rbatt_mohm;
@@ -845,18 +972,10 @@
batt_temp = batt_temp / 10;
scalefactor = interpolate_scalingfactor(chip->rbatt_sf_lut,
batt_temp, soc_rbatt_mohm);
- pr_debug("rbatt sf = %d for batt_temp = %d, soc_rbatt = %d\n",
- scalefactor, batt_temp, soc_rbatt_mohm);
rbatt_mohm = (rbatt_mohm * scalefactor) / 100;
rbatt_mohm += chip->r_conn_mohm;
- pr_debug("adding r_conn_mohm = %d rbatt = %d\n",
- chip->r_conn_mohm, rbatt_mohm);
rbatt_mohm += chip->rbatt_capacitive_mohm;
- pr_debug("adding rbatt_capacitive_mohm = %d rbatt = %d\n",
- chip->rbatt_capacitive_mohm, rbatt_mohm);
-
- pr_debug("RBATT = %d\n", rbatt_mohm);
return rbatt_mohm;
}
@@ -909,9 +1028,6 @@
+ (chip->v_cutoff_uv);
delta_uv = ocv_mv * 1000 - unusable_uv;
- pr_debug("soc = %d ocv = %d rbat = %d u_uv = %d delta_v = %d\n",
- i, ocv_mv, rbatt_mohm, unusable_uv, delta_uv);
-
if (delta_uv > 0)
break;
@@ -927,10 +1043,10 @@
pc_unusable = calculate_pc(chip, unusable_uv, batt_temp);
uuc_uah = (params->fcc_uah * pc_unusable) / 100;
- pr_debug("For uuc_iavg_ma = %d, unusable_rbatt = %d unusable_uv = %d unusable_pc = %d uuc = %d\n",
+ pr_debug("For uuc_iavg_ma = %d, unusable_rbatt = %d unusable_uv = %d unusable_pc = %d rbatt_pc = %d uuc = %d\n",
uuc_iavg_ma,
uuc_rbatt_mohm, unusable_uv,
- pc_unusable, uuc_uah);
+ pc_unusable, i, uuc_uah);
*ret_pc_unusable = pc_unusable;
return uuc_uah;
}
@@ -1106,8 +1222,7 @@
if (rtc == NULL) {
pr_err("%s: unable to open rtc device (%s)\n",
__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
- rc = -EINVAL;
- goto close_time;
+ return -EINVAL;
}
rc = rtc_read_time(rtc, &tm);
@@ -1130,21 +1245,22 @@
return rc;
}
-static int calculate_delta_time(struct qpnp_bms_chip *chip, int *delta_time_s)
+static int calculate_delta_time(unsigned long *time_stamp, int *delta_time_s)
{
unsigned long now_tm_sec = 0;
/* default to delta time = 0 if anything fails */
*delta_time_s = 0;
- get_current_time(&now_tm_sec);
+ if (get_current_time(&now_tm_sec)) {
+ pr_err("RTC read failed\n");
+ return 0;
+ }
- *delta_time_s = (now_tm_sec - chip->tm_sec);
- pr_debug("tm_sec = %ld, now_tm_sec = %ld delta_s = %d\n",
- chip->tm_sec, now_tm_sec, *delta_time_s);
+ *delta_time_s = (now_tm_sec - *time_stamp);
/* remember this time */
- chip->tm_sec = now_tm_sec;
+ *time_stamp = now_tm_sec;
return 0;
}
@@ -1155,7 +1271,9 @@
{
int soc_rbatt;
- calculate_delta_time(chip, ¶ms->delta_time_s);
+ calculate_delta_time(&chip->tm_sec, ¶ms->delta_time_s);
+ pr_debug("tm_sec = %ld, delta_s = %d\n",
+ chip->tm_sec, params->delta_time_s);
params->fcc_uah = calculate_fcc(chip, batt_temp);
pr_debug("FCC = %uuAh batt_temp = %d\n", params->fcc_uah, batt_temp);
@@ -1166,7 +1284,7 @@
pr_debug("ocv_charge_uah = %uuAh\n", params->ocv_charge_uah);
/* calculate cc micro_volt_hour */
- params->cc_uah = calculate_cc(chip, raw->cc);
+ params->cc_uah = calculate_cc(chip, raw->cc, true);
pr_debug("cc_uah = %duAh raw->cc = %llx\n", params->cc_uah, raw->cc);
soc_rbatt = ((params->ocv_charge_uah - params->cc_uah) * 100)
@@ -1174,6 +1292,13 @@
if (soc_rbatt < 0)
soc_rbatt = 0;
params->rbatt_mohm = get_rbatt(chip, soc_rbatt, batt_temp);
+ pr_debug("rbatt_mohm = %d\n", params->rbatt_mohm);
+
+ if (params->rbatt_mohm != chip->rbatt_mohm) {
+ chip->rbatt_mohm = params->rbatt_mohm;
+ if (chip->bms_psy.name != NULL)
+ power_supply_changed(&chip->bms_psy);
+ }
calculate_iavg(chip, params->cc_uah, ¶ms->iavg_ua,
params->delta_time_s);
@@ -1240,9 +1365,12 @@
ocv_est_uv = vbat_uv + (ibat_ua * chip->r_conn_mohm) / 1000;
pr_debug("forcing ocv to be %d due to bms reset mode\n", ocv_est_uv);
chip->last_ocv_uv = ocv_est_uv;
+ mutex_lock(&chip->last_soc_mutex);
chip->last_soc = -EINVAL;
chip->last_soc_invalid = true;
+ mutex_unlock(&chip->last_soc_mutex);
reset_cc(chip);
+ chip->software_cc_uah = 0;
chip->last_cc_uah = INT_MIN;
stop_ocv_updates(chip);
@@ -1283,25 +1411,214 @@
module_param_cb(bms_reset, &bms_reset_ops, &bms_reset, 0644);
+static void backup_soc_and_iavg(struct qpnp_bms_chip *chip, int batt_temp,
+ int soc)
+{
+ u8 temp;
+ int rc;
+ int iavg_ma = chip->prev_uuc_iavg_ma;
+
+ if (iavg_ma > IAVG_START)
+ temp = (iavg_ma - IAVG_START) / IAVG_STEP_SIZE_MA;
+ else
+ temp = 0;
+
+ rc = qpnp_write_wrapper(chip, &temp,
+ chip->base + IAVG_STORAGE_REG, 1);
+
+ temp = soc;
+
+ /* don't store soc if temperature is below 5degC */
+ if (batt_temp > IGNORE_SOC_TEMP_DECIDEG)
+ rc = qpnp_write_wrapper(chip, &temp,
+ chip->base + SOC_STORAGE_REG, 1);
+}
+
+static int scale_soc_while_chg(struct qpnp_bms_chip *chip, int chg_time_sec,
+ int catch_up_sec, int new_soc, int prev_soc)
+{
+ int scaled_soc;
+ int numerator;
+
+ /*
+ * Don't report a high value immediately slowly scale the
+ * value from prev_soc to the new soc based on a charge time
+ * weighted average
+ */
+ pr_debug("cts = %d catch_up_sec = %d\n", chg_time_sec, catch_up_sec);
+ if (catch_up_sec == 0)
+ return new_soc;
+
+ if (chg_time_sec > catch_up_sec)
+ return new_soc;
+
+ numerator = (catch_up_sec - chg_time_sec) * prev_soc
+ + chg_time_sec * new_soc;
+ scaled_soc = numerator / catch_up_sec;
+
+ pr_debug("cts = %d new_soc = %d prev_soc = %d scaled_soc = %d\n",
+ chg_time_sec, new_soc, prev_soc, scaled_soc);
+
+ return scaled_soc;
+}
+
+/*
+ * bms_fake_battery is set in setups where a battery emulator is used instead
+ * of a real battery. This makes the bms driver report a different/fake value
+ * regardless of the calculated state of charge.
+ */
+static int bms_fake_battery = -EINVAL;
+module_param(bms_fake_battery, int, 0644);
+
+static int report_voltage_based_soc(struct qpnp_bms_chip *chip)
+{
+ pr_debug("Reported voltage based soc = %d\n",
+ chip->prev_voltage_based_soc);
+ return chip->prev_voltage_based_soc;
+}
+
+#define SOC_CATCHUP_SEC_MAX 600
+#define SOC_CATCHUP_SEC_PER_PERCENT 60
+#define MAX_CATCHUP_SOC (SOC_CATCHUP_SEC_MAX / SOC_CATCHUP_SEC_PER_PERCENT)
+#define SOC_CHANGE_PER_SEC 20
+static int report_cc_based_soc(struct qpnp_bms_chip *chip)
+{
+ int soc, soc_change;
+ int time_since_last_change_sec, charge_time_sec = 0;
+ unsigned long last_change_sec;
+ struct timespec now;
+ struct qpnp_vadc_result result;
+ int batt_temp;
+ int rc;
+ bool charging, charging_since_last_report;
+
+ rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result);
+
+ if (rc) {
+ pr_err("error reading adc channel = %d, rc = %d\n",
+ LR_MUX1_BATT_THERM, rc);
+ return rc;
+ }
+ pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+ result.measurement);
+ batt_temp = (int)result.physical;
+
+ mutex_lock(&chip->last_soc_mutex);
+ soc = chip->calculated_soc;
+
+ last_change_sec = chip->last_soc_change_sec;
+ calculate_delta_time(&last_change_sec, &time_since_last_change_sec);
+
+ charging = is_battery_charging(chip);
+ charging_since_last_report = charging || (chip->last_soc_unbound
+ && chip->was_charging_at_sleep);
+ /*
+ * account for charge time - limit it to SOC_CATCHUP_SEC to
+ * avoid overflows when charging continues for extended periods
+ */
+ if (charging) {
+ if (chip->charge_start_tm_sec == 0) {
+ /*
+ * calculating soc for the first time
+ * after start of chg. Initialize catchup time
+ */
+ if (abs(soc - chip->last_soc) < MAX_CATCHUP_SOC)
+ chip->catch_up_time_sec =
+ (soc - chip->last_soc)
+ * SOC_CATCHUP_SEC_PER_PERCENT;
+ else
+ chip->catch_up_time_sec = SOC_CATCHUP_SEC_MAX;
+
+ if (chip->catch_up_time_sec < 0)
+ chip->catch_up_time_sec = 0;
+ chip->charge_start_tm_sec = last_change_sec;
+ }
+
+ charge_time_sec = min(SOC_CATCHUP_SEC_MAX, (int)last_change_sec
+ - chip->charge_start_tm_sec);
+
+ /* end catchup if calculated soc and last soc are same */
+ if (chip->last_soc == soc)
+ chip->catch_up_time_sec = 0;
+ }
+
+ if (chip->last_soc != -EINVAL) {
+ /*
+ * last_soc < soc ... if we have not been charging at all
+ * since the last time this was called, report previous SoC.
+ * Otherwise, scale and catch up.
+ */
+ if (chip->last_soc < soc && !charging_since_last_report)
+ soc = chip->last_soc;
+ else if (chip->last_soc < soc && soc != 100)
+ soc = scale_soc_while_chg(chip, charge_time_sec,
+ chip->catch_up_time_sec,
+ soc, chip->last_soc);
+
+ soc_change = min((int)abs(chip->last_soc - soc),
+ time_since_last_change_sec / SOC_CHANGE_PER_SEC);
+ if (chip->last_soc_unbound) {
+ chip->last_soc_unbound = false;
+ } else {
+ /*
+ * if soc have not been unbound by resume,
+ * only change reported SoC by 1.
+ */
+ soc_change = min(1, soc_change);
+ }
+
+ if (soc < chip->last_soc && soc != 0)
+ soc = chip->last_soc - soc_change;
+ if (soc > chip->last_soc && soc != 100)
+ soc = chip->last_soc + soc_change;
+ }
+
+ if (chip->last_soc != soc && !chip->last_soc_unbound)
+ chip->last_soc_change_sec = last_change_sec;
+
+ pr_debug("last_soc = %d, calculated_soc = %d, soc = %d, time since last change = %d\n",
+ chip->last_soc, chip->calculated_soc,
+ soc, time_since_last_change_sec);
+ chip->last_soc = bound_soc(soc);
+ backup_soc_and_iavg(chip, batt_temp, chip->last_soc);
+ pr_debug("Reported SOC = %d\n", chip->last_soc);
+ chip->t_soc_queried = now;
+ mutex_unlock(&chip->last_soc_mutex);
+
+ return soc;
+}
+
+static int report_state_of_charge(struct qpnp_bms_chip *chip)
+{
+ if (bms_fake_battery != -EINVAL) {
+ pr_debug("Returning Fake SOC = %d%%\n", bms_fake_battery);
+ return bms_fake_battery;
+ } else if (chip->use_voltage_soc)
+ return report_voltage_based_soc(chip);
+ else
+ return report_cc_based_soc(chip);
+}
+
+#define VDD_MAX_ERR 5000
+#define VDD_STEP_SIZE 10000
static int charging_adjustments(struct qpnp_bms_chip *chip,
struct soc_params *params, int soc,
int vbat_uv, int ibat_ua, int batt_temp)
{
- int chg_soc;
- int batt_terminal_uv = vbat_uv + (ibat_ua * chip->r_conn_mohm) / 1000;
+ int chg_soc, soc_ibat, batt_terminal_uv, weight_ibat, weight_cc;
+
+ batt_terminal_uv = vbat_uv + (ibat_ua * chip->r_conn_mohm) / 1000;
if (chip->soc_at_cv == -EINVAL) {
- /* In constant current charging return the calc soc */
- if (batt_terminal_uv <= chip->max_voltage_uv)
- pr_debug("CC CHG SOC %d\n", soc);
-
- /* Note the CC to CV point */
- if (batt_terminal_uv >= chip->max_voltage_uv) {
+ if (batt_terminal_uv >= chip->max_voltage_uv - VDD_MAX_ERR) {
chip->soc_at_cv = soc;
chip->prev_chg_soc = soc;
chip->ibat_at_cv_ua = ibat_ua;
pr_debug("CC_TO_CV ibat_ua = %d CHG SOC %d\n",
ibat_ua, soc);
+ } else {
+ /* In constant current charging return the calc soc */
+ pr_debug("CC CHG SOC %d\n", soc);
}
chip->prev_batt_terminal_uv = batt_terminal_uv;
@@ -1317,7 +1634,7 @@
* if voltage lessened (possibly because of a system load)
* keep reporting the prev chg soc
*/
- if (batt_terminal_uv <= chip->prev_batt_terminal_uv) {
+ if (batt_terminal_uv <= chip->prev_batt_terminal_uv - VDD_STEP_SIZE) {
pr_debug("batt_terminal_uv %d < (max = %d - 10000); CC CHG SOC %d\n",
batt_terminal_uv, chip->prev_batt_terminal_uv,
chip->prev_chg_soc);
@@ -1325,10 +1642,18 @@
return chip->prev_chg_soc;
}
- chg_soc = linear_interpolate(chip->soc_at_cv, chip->ibat_at_cv_ua,
+ soc_ibat = bound_soc(linear_interpolate(chip->soc_at_cv,
+ chip->ibat_at_cv_ua,
100, -1 * chip->chg_term_ua,
- ibat_ua);
- chg_soc = bound_soc(chg_soc);
+ ibat_ua));
+ weight_ibat = bound_soc(linear_interpolate(1, chip->soc_at_cv,
+ 100, 100, chip->prev_chg_soc));
+ weight_cc = 100 - weight_ibat;
+ chg_soc = bound_soc(DIV_ROUND_CLOSEST(soc_ibat * weight_ibat
+ + weight_cc * soc, 100));
+
+ pr_debug("weight_ibat = %d, weight_cc = %d, soc_ibat = %d, soc_cc = %d\n",
+ weight_ibat, weight_cc, soc_ibat, soc);
/* always report a higher soc */
if (chg_soc > chip->prev_chg_soc) {
@@ -1337,9 +1662,8 @@
chip->prev_chg_soc = chg_soc;
find_ocv_for_soc(chip, params, batt_temp, chg_soc, &new_ocv_uv);
- chip->last_ocv_uv = new_ocv_uv;
- pr_debug("CC CHG ADJ OCV = %d CHG SOC %d\n",
- new_ocv_uv,
+ chip->charging_adjusted_ocv = new_ocv_uv;
+ pr_debug("CC CHG ADJ OCV = %d CHG SOC %d\n", new_ocv_uv,
chip->prev_chg_soc);
}
@@ -1355,18 +1679,40 @@
* a wakelock untill soc = 0%
*/
if (vbat_uv <= chip->low_voltage_threshold
- && !chip->low_voltage_wake_lock_held) {
+ && !wake_lock_active(&chip->low_voltage_wake_lock)) {
pr_debug("voltage = %d low holding wakelock\n", vbat_uv);
wake_lock(&chip->low_voltage_wake_lock);
- chip->low_voltage_wake_lock_held = 1;
} else if (vbat_uv > chip->low_voltage_threshold
- && chip->low_voltage_wake_lock_held) {
+ && wake_lock_active(&chip->low_voltage_wake_lock)) {
pr_debug("voltage = %d releasing wakelock\n", vbat_uv);
- chip->low_voltage_wake_lock_held = 0;
wake_unlock(&chip->low_voltage_wake_lock);
}
}
+#define VBATT_ERROR_MARGIN 20000
+static void cv_voltage_check(struct qpnp_bms_chip *chip, int vbat_uv)
+{
+ /*
+ * if battery is very low (v_cutoff voltage + 20mv) hold
+ * a wakelock untill soc = 0%
+ */
+ if (wake_lock_active(&chip->cv_wake_lock)) {
+ if (chip->soc_at_cv != -EINVAL) {
+ pr_debug("hit CV, releasing cv wakelock\n");
+ wake_unlock(&chip->cv_wake_lock);
+ } else if (!is_battery_charging(chip)) {
+ pr_debug("charging stopped, releasing cv wakelock\n");
+ wake_unlock(&chip->cv_wake_lock);
+ }
+ } else if (vbat_uv > chip->max_voltage_uv - VBATT_ERROR_MARGIN
+ && chip->soc_at_cv == -EINVAL
+ && is_battery_charging(chip)
+ && !wake_lock_active(&chip->cv_wake_lock)) {
+ pr_debug("voltage = %d holding cv wakelock\n", vbat_uv);
+ wake_lock(&chip->cv_wake_lock);
+ }
+}
+
#define NO_ADJUST_HIGH_SOC_THRESHOLD 90
static int adjust_soc(struct qpnp_bms_chip *chip, struct soc_params *params,
int soc, int batt_temp)
@@ -1390,14 +1736,12 @@
}
very_low_voltage_check(chip, vbat_uv);
+ cv_voltage_check(chip, vbat_uv);
delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000);
ocv_est_uv = vbat_uv + (ibat_ua * params->rbatt_mohm)/1000;
- chip->ibat_max_ua = (ocv_est_uv - chip->v_cutoff_uv) * 1000
- / (params->rbatt_mohm);
-
pc_est = calculate_pc(chip, ocv_est_uv, batt_temp);
soc_est = div_s64((s64)params->fcc_uah * pc_est - params->uuc_uah*100,
(s64)params->fcc_uah - params->uuc_uah);
@@ -1409,7 +1753,7 @@
goto out;
}
- if (ibat_ua < 0 && !is_batfet_open(chip)) {
+ if (ibat_ua < 0 && !is_battery_full(chip)) {
soc = charging_adjustments(chip, params, soc, vbat_uv, ibat_ua,
batt_temp);
goto out;
@@ -1528,10 +1872,6 @@
pr_debug("clamping soc to 1, vbat (%d) > cutoff (%d)\n",
vbat_uv, chip->v_cutoff_uv);
return 1;
- } else if (soc > 0 && vbat_uv < chip->v_cutoff_uv) {
- pr_debug("forcing soc to 0, vbat (%d) < cutoff (%d)\n",
- vbat_uv, chip->v_cutoff_uv);
- return 0;
} else {
pr_debug("not clamping, using soc = %d, vbat = %d and cutoff = %d\n",
soc, vbat_uv, chip->v_cutoff_uv);
@@ -1539,6 +1879,7 @@
}
}
+#define SLEEP_RECALC_INTERVAL 3
static int calculate_state_of_charge(struct qpnp_bms_chip *chip,
struct raw_soc_params *raw,
int batt_temp)
@@ -1547,7 +1888,7 @@
int shutdown_soc, new_calculated_soc, remaining_usable_charge_uah;
struct soc_params params;
- if (!chip->battery_present) {
+ if (!is_battery_present(chip)) {
pr_debug("battery gone, reporting 100\n");
new_calculated_soc = 100;
goto done_calculating;
@@ -1619,7 +1960,6 @@
soc, shutdown_soc);
find_ocv_for_soc(chip, ¶ms, batt_temp,
shutdown_soc, &new_ocv_uv);
- chip->pon_ocv_uv = chip->last_ocv_uv;
chip->last_ocv_uv = new_ocv_uv;
remaining_usable_charge_uah = params.ocv_charge_uah
@@ -1643,20 +1983,45 @@
new_calculated_soc);
done_calculating:
- if (new_calculated_soc != chip->calculated_soc
- && chip->bms_psy.name != NULL) {
- power_supply_changed(&chip->bms_psy);
- pr_debug("power supply changed\n");
- }
-
+ mutex_lock(&chip->last_soc_mutex);
chip->calculated_soc = new_calculated_soc;
+ pr_debug("CC based calculated SOC = %d\n", chip->calculated_soc);
if (chip->last_soc_invalid) {
chip->last_soc_invalid = false;
chip->last_soc = -EINVAL;
}
- pr_debug("CC based calculated SOC = %d\n", chip->calculated_soc);
- chip->first_time_calc_soc = 0;
+ /*
+ * Check if more than a long time has passed since the last
+ * calculation (more than n times compared to the soc recalculation
+ * rate, where n is defined by SLEEP_RECALC_INTERVAL). If this is true,
+ * then the system must have gone through a long sleep, and SoC can be
+ * allowed to become unbounded by the last reported SoC
+ */
+ if (params.delta_time_s * 1000 >
+ chip->calculate_soc_ms * SLEEP_RECALC_INTERVAL
+ && !chip->first_time_calc_soc) {
+ chip->last_soc_unbound = true;
+ chip->last_soc_change_sec = chip->last_recalc_time;
+ pr_debug("last_soc unbound because elapsed time = %d\n",
+ params.delta_time_s);
+ }
+ mutex_unlock(&chip->last_soc_mutex);
+
+ if (new_calculated_soc != chip->calculated_soc
+ && chip->bms_psy.name != NULL) {
+ power_supply_changed(&chip->bms_psy);
+ pr_debug("power supply changed\n");
+ } else {
+ /*
+ * Call report state of charge anyways to periodically update
+ * reported SoC. This prevents reported SoC from being stuck
+ * when calculated soc doesn't change.
+ */
+ report_state_of_charge(chip);
+ }
+
get_current_time(&chip->last_recalc_time);
+ chip->first_time_calc_soc = 0;
return chip->calculated_soc;
}
@@ -1694,10 +2059,15 @@
struct qpnp_vadc_result result;
struct raw_soc_params raw;
- wake_lock(&chip->soc_wake_lock);
+ if (!wake_lock_active(&chip->soc_wake_lock))
+ wake_lock(&chip->soc_wake_lock);
+ mutex_lock(&chip->vbat_monitor_mutex);
+ qpnp_adc_tm_channel_measure(&chip->vbat_monitor_params);
+ mutex_unlock(&chip->vbat_monitor_mutex);
if (chip->use_voltage_soc) {
soc = calculate_soc_from_voltage(chip);
} else {
+ qpnp_iadc_calibrate_for_trim();
rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result);
if (rc) {
pr_err("error reading vadc LR_MUX1_BATT_THERM = %d, rc = %d\n",
@@ -1736,7 +2106,7 @@
int soc = recalculate_soc(chip);
if (soc < chip->low_soc_calc_threshold
- || chip->low_voltage_wake_lock_held)
+ || wake_lock_active(&chip->low_voltage_wake_lock))
schedule_delayed_work(&chip->calculate_soc_delayed_work,
round_jiffies_relative(msecs_to_jiffies
(chip->low_soc_calculate_soc_ms)));
@@ -1746,187 +2116,557 @@
(chip->calculate_soc_ms)));
}
-static void backup_soc_and_iavg(struct qpnp_bms_chip *chip, int batt_temp,
- int soc)
+static void configure_vbat_monitor_low(struct qpnp_bms_chip *chip)
{
- u8 temp;
- int rc;
- int iavg_ma = chip->prev_uuc_iavg_ma;
-
- if (iavg_ma > IAVG_START)
- temp = (iavg_ma - IAVG_START) / IAVG_STEP_SIZE_MA;
- else
- temp = 0;
-
- rc = qpnp_write_wrapper(chip, &temp,
- chip->base + IAVG_STORAGE_REG, 1);
-
- temp = soc;
-
- /* don't store soc if temperature is below 5degC */
- if (batt_temp > IGNORE_SOC_TEMP_DECIDEG)
- rc = qpnp_write_wrapper(chip, &temp,
- chip->base + SOC_STORAGE_REG, 1);
+ mutex_lock(&chip->vbat_monitor_mutex);
+ if (chip->vbat_monitor_params.state_request
+ == ADC_TM_HIGH_LOW_THR_ENABLE) {
+ /*
+ * Battery is now around or below v_cutoff
+ */
+ pr_debug("battery entered cutoff range\n");
+ if (!wake_lock_active(&chip->low_voltage_wake_lock)) {
+ pr_debug("voltage low, holding wakelock\n");
+ wake_lock(&chip->low_voltage_wake_lock);
+ cancel_delayed_work_sync(
+ &chip->calculate_soc_delayed_work);
+ schedule_delayed_work(
+ &chip->calculate_soc_delayed_work, 0);
+ }
+ chip->vbat_monitor_params.state_request =
+ ADC_TM_HIGH_THR_ENABLE;
+ chip->vbat_monitor_params.high_thr =
+ (chip->low_voltage_threshold + VBATT_ERROR_MARGIN);
+ pr_debug("set low thr to %d and high to %d\n",
+ chip->vbat_monitor_params.low_thr,
+ chip->vbat_monitor_params.high_thr);
+ chip->vbat_monitor_params.low_thr = 0;
+ } else if (chip->vbat_monitor_params.state_request
+ == ADC_TM_LOW_THR_ENABLE) {
+ /*
+ * Battery is in normal operation range.
+ */
+ pr_debug("battery entered normal range\n");
+ if (wake_lock_active(&chip->cv_wake_lock)) {
+ wake_unlock(&chip->cv_wake_lock);
+ pr_debug("releasing cv wake lock\n");
+ }
+ chip->in_cv_range = false;
+ chip->vbat_monitor_params.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ chip->vbat_monitor_params.high_thr = chip->max_voltage_uv
+ - VBATT_ERROR_MARGIN;
+ chip->vbat_monitor_params.low_thr =
+ chip->low_voltage_threshold;
+ pr_debug("set low thr to %d and high to %d\n",
+ chip->vbat_monitor_params.low_thr,
+ chip->vbat_monitor_params.high_thr);
+ }
+ qpnp_adc_tm_channel_measure(&chip->vbat_monitor_params);
+ mutex_unlock(&chip->vbat_monitor_mutex);
}
-#define SOC_CATCHUP_SEC_MAX 600
-#define SOC_CATCHUP_SEC_PER_PERCENT 60
-#define MAX_CATCHUP_SOC (SOC_CATCHUP_SEC_MAX/SOC_CATCHUP_SEC_PER_PERCENT)
-static int scale_soc_while_chg(struct qpnp_bms_chip *chip,
- int delta_time_us, int new_soc, int prev_soc)
+#define CV_LOW_THRESHOLD_HYST_UV 100000
+static void configure_vbat_monitor_high(struct qpnp_bms_chip *chip)
{
- int chg_time_sec;
- int catch_up_sec;
- int scaled_soc;
- int numerator;
-
- /*
- * The device must be charging for reporting a higher soc, if
- * not ignore this soc and continue reporting the prev_soc.
- * Also don't report a high value immediately slowly scale the
- * value from prev_soc to the new soc based on a charge time
- * weighted average
- */
-
- /* if not charging, return last soc */
- if (!is_battery_charging(chip))
- return prev_soc;
-
- chg_time_sec = DIV_ROUND_UP(chip->charge_time_us, USEC_PER_SEC);
- catch_up_sec = DIV_ROUND_UP(chip->catch_up_time_us, USEC_PER_SEC);
- if (catch_up_sec == 0)
- return new_soc;
- pr_debug("cts= %d catch_up_sec = %d\n", chg_time_sec, catch_up_sec);
-
- /*
- * if charging for more than catch_up time, simply return
- * new soc
- */
- if (chg_time_sec > catch_up_sec)
- return new_soc;
-
- numerator = (catch_up_sec - chg_time_sec) * prev_soc
- + chg_time_sec * new_soc;
- scaled_soc = numerator / catch_up_sec;
-
- pr_debug("cts = %d new_soc = %d prev_soc = %d scaled_soc = %d\n",
- chg_time_sec, new_soc, prev_soc, scaled_soc);
-
- return scaled_soc;
+ mutex_lock(&chip->vbat_monitor_mutex);
+ if (chip->vbat_monitor_params.state_request
+ == ADC_TM_HIGH_LOW_THR_ENABLE) {
+ /*
+ * Battery is around vddmax
+ */
+ pr_debug("battery entered vddmax range\n");
+ chip->in_cv_range = true;
+ if (!wake_lock_active(&chip->cv_wake_lock)) {
+ wake_lock(&chip->cv_wake_lock);
+ pr_debug("holding cv wake lock\n");
+ }
+ schedule_work(&chip->recalc_work);
+ chip->vbat_monitor_params.state_request =
+ ADC_TM_LOW_THR_ENABLE;
+ chip->vbat_monitor_params.low_thr =
+ (chip->max_voltage_uv - CV_LOW_THRESHOLD_HYST_UV);
+ chip->vbat_monitor_params.high_thr = chip->max_voltage_uv * 2;
+ pr_debug("set low thr to %d and high to %d\n",
+ chip->vbat_monitor_params.low_thr,
+ chip->vbat_monitor_params.high_thr);
+ } else if (chip->vbat_monitor_params.state_request
+ == ADC_TM_HIGH_THR_ENABLE) {
+ /*
+ * Battery is in normal operation range.
+ */
+ pr_debug("battery entered normal range\n");
+ if (wake_lock_active(&chip->low_voltage_wake_lock)) {
+ pr_debug("voltage high, releasing wakelock\n");
+ wake_unlock(&chip->low_voltage_wake_lock);
+ }
+ chip->vbat_monitor_params.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ chip->vbat_monitor_params.high_thr =
+ chip->max_voltage_uv - VBATT_ERROR_MARGIN;
+ chip->vbat_monitor_params.low_thr =
+ chip->low_voltage_threshold;
+ pr_debug("set low thr to %d and high to %d\n",
+ chip->vbat_monitor_params.low_thr,
+ chip->vbat_monitor_params.high_thr);
+ }
+ qpnp_adc_tm_channel_measure(&chip->vbat_monitor_params);
+ mutex_unlock(&chip->vbat_monitor_mutex);
}
-/*
- * bms_fake_battery is set in setups where a battery emulator is used instead
- * of a real battery. This makes the bms driver report a different/fake value
- * regardless of the calculated state of charge.
- */
-static int bms_fake_battery = -EINVAL;
-module_param(bms_fake_battery, int, 0644);
-
-static int report_voltage_based_soc(struct qpnp_bms_chip *chip)
+static void btm_notify_vbat(enum qpnp_tm_state state, void *ctx)
{
- pr_debug("Reported voltage based soc = %d\n",
- chip->prev_voltage_based_soc);
- return chip->prev_voltage_based_soc;
-}
-
-static int report_cc_based_soc(struct qpnp_bms_chip *chip)
-{
- int soc;
- int delta_time_us;
- struct timespec now;
+ struct qpnp_bms_chip *chip = ctx;
+ int vbat_uv;
struct qpnp_vadc_result result;
- int batt_temp;
int rc;
- soc = chip->calculated_soc;
+ rc = qpnp_vadc_read(VBAT_SNS, &result);
+ pr_debug("vbat = %lld, raw = 0x%x\n", result.physical, result.adc_code);
- rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result);
+ get_battery_voltage(&vbat_uv);
+ pr_debug("vbat is at %d, state is at %d\n", vbat_uv, state);
+ if (state == ADC_TM_LOW_STATE) {
+ pr_debug("low voltage btm notification triggered\n");
+ if (vbat_uv - VBATT_ERROR_MARGIN
+ < chip->vbat_monitor_params.low_thr) {
+ configure_vbat_monitor_low(chip);
+ } else {
+ pr_debug("faulty btm trigger, discarding\n");
+ qpnp_adc_tm_channel_measure(
+ &chip->vbat_monitor_params);
+ }
+ } else if (state == ADC_TM_HIGH_STATE) {
+ pr_debug("high voltage btm notification triggered\n");
+ if (vbat_uv + VBATT_ERROR_MARGIN
+ > chip->vbat_monitor_params.high_thr) {
+ configure_vbat_monitor_high(chip);
+ } else {
+ pr_debug("faulty btm trigger, discarding\n");
+ qpnp_adc_tm_channel_measure(
+ &chip->vbat_monitor_params);
+ }
+ } else {
+ pr_debug("unknown voltage notification state: %d\n", state);
+ }
+ power_supply_changed(&chip->bms_psy);
+}
+
+static int reset_vbat_monitoring(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ chip->vbat_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_DISABLE;
+ rc = qpnp_adc_tm_channel_measure(&chip->vbat_monitor_params);
if (rc) {
- pr_err("error reading adc channel = %d, rc = %d\n",
- LR_MUX1_BATT_THERM, rc);
+ pr_err("tm measure failed: %d\n", rc);
return rc;
}
- pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
- result.measurement);
- batt_temp = (int)result.physical;
-
- do_posix_clock_monotonic_gettime(&now);
- if (chip->t_soc_queried.tv_sec != 0) {
- delta_time_us
- = (now.tv_sec - chip->t_soc_queried.tv_sec) * USEC_PER_SEC
- + (now.tv_nsec - chip->t_soc_queried.tv_nsec) / 1000;
- } else {
- /* calculation for the first time */
- delta_time_us = 0;
+ mutex_lock(&chip->vbat_monitor_mutex);
+ if (wake_lock_active(&chip->low_voltage_wake_lock)) {
+ pr_debug("battery removed, releasing wakelock\n");
+ wake_unlock(&chip->low_voltage_wake_lock);
}
-
- /*
- * account for charge time - limit it to SOC_CATCHUP_SEC to
- * avoid overflows when charging continues for extended periods
- */
- if (is_battery_charging(chip)) {
- if (chip->charge_time_us == 0) {
- /*
- * calculating soc for the first time
- * after start of chg. Initialize catchup time
- */
- if (abs(soc - chip->last_soc) < MAX_CATCHUP_SOC)
- chip->catch_up_time_us =
- (soc - chip->last_soc)
- * SOC_CATCHUP_SEC_PER_PERCENT
- * USEC_PER_SEC;
- else
- chip->catch_up_time_us =
- SOC_CATCHUP_SEC_MAX * USEC_PER_SEC;
-
- if (chip->catch_up_time_us < 0)
- chip->catch_up_time_us = 0;
- }
-
- /* add charge time */
- if (chip->charge_time_us < SOC_CATCHUP_SEC_MAX * USEC_PER_SEC)
- chip->charge_time_us += delta_time_us;
-
- /* end catchup if calculated soc and last soc are same */
- if (chip->last_soc == soc)
- chip->catch_up_time_us = 0;
+ if (chip->in_cv_range) {
+ pr_debug("battery removed, removing in_cv_range state\n");
+ chip->in_cv_range = false;
}
-
- /* last_soc < soc ... scale and catch up */
- if (chip->last_soc != -EINVAL && chip->last_soc < soc && soc != 100)
- soc = scale_soc_while_chg(chip, delta_time_us,
- soc, chip->last_soc);
-
- if (chip->last_soc_unbound)
- chip->last_soc_unbound = false;
- else if (chip->last_soc != -EINVAL) {
- if (soc < chip->last_soc && soc != 0)
- soc = chip->last_soc - 1;
- if (soc > chip->last_soc && soc != 100)
- soc = chip->last_soc + 1;
- }
-
- pr_debug("last_soc = %d, calculated_soc = %d, soc = %d\n",
- chip->last_soc, chip->calculated_soc, soc);
- chip->last_soc = bound_soc(soc);
- backup_soc_and_iavg(chip, batt_temp, chip->last_soc);
- pr_debug("Reported SOC = %d\n", chip->last_soc);
- chip->t_soc_queried = now;
-
- return soc;
+ mutex_unlock(&chip->vbat_monitor_mutex);
+ return 0;
}
-static int report_state_of_charge(struct qpnp_bms_chip *chip)
+static int setup_vbat_monitoring(struct qpnp_bms_chip *chip)
{
- if (bms_fake_battery != -EINVAL) {
- pr_debug("Returning Fake SOC = %d%%\n", bms_fake_battery);
- return bms_fake_battery;
- } else if (chip->use_voltage_soc)
- return report_voltage_based_soc(chip);
- else
- return report_cc_based_soc(chip);
+ int rc;
+
+ rc = qpnp_adc_tm_is_ready();
+ if (rc) {
+ pr_info("adc tm is not ready yet: %d, defer probe\n", rc);
+ return -EPROBE_DEFER;
+ }
+
+ if (!is_battery_present(chip)) {
+ pr_debug("no battery inserted, do not setup vbat monitoring\n");
+ return 0;
+ }
+
+ chip->vbat_monitor_params.low_thr = chip->low_voltage_threshold;
+ chip->vbat_monitor_params.high_thr = chip->max_voltage_uv
+ - VBATT_ERROR_MARGIN;
+ chip->vbat_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
+ chip->vbat_monitor_params.channel = VBAT_SNS;
+ chip->vbat_monitor_params.btm_ctx = (void *)chip;
+ chip->vbat_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
+ chip->vbat_monitor_params.threshold_notification = &btm_notify_vbat;
+ pr_debug("set low thr to %d and high to %d\n",
+ chip->vbat_monitor_params.low_thr,
+ chip->vbat_monitor_params.high_thr);
+ rc = qpnp_adc_tm_channel_measure(&chip->vbat_monitor_params);
+ if (rc) {
+ pr_err("tm setup failed: %d\n", rc);
+ return rc;
+ }
+ pr_debug("setup complete\n");
+ return 0;
+}
+
+static void readjust_fcc_table(struct qpnp_bms_chip *chip)
+{
+ struct single_row_lut *temp, *old;
+ int i, fcc, ratio;
+
+ if (!chip->enable_fcc_learning)
+ return;
+
+ if (!chip->fcc_temp_lut) {
+ pr_err("The static fcc lut table is NULL\n");
+ return;
+ }
+
+ temp = kzalloc(sizeof(struct single_row_lut), GFP_KERNEL);
+ if (!temp) {
+ pr_err("Cannot allocate memory for adjusted fcc table\n");
+ return;
+ }
+
+ fcc = interpolate_fcc(chip->fcc_temp_lut, chip->fcc_new_batt_temp);
+
+ temp->cols = chip->fcc_temp_lut->cols;
+ for (i = 0; i < chip->fcc_temp_lut->cols; i++) {
+ temp->x[i] = chip->fcc_temp_lut->x[i];
+ ratio = div_u64(chip->fcc_temp_lut->y[i] * 1000, fcc);
+ temp->y[i] = (ratio * chip->fcc_new_mah);
+ temp->y[i] /= 1000;
+ }
+
+ old = chip->adjusted_fcc_temp_lut;
+ chip->adjusted_fcc_temp_lut = temp;
+ kfree(old);
+}
+
+
+static void backup_fcc_new(struct qpnp_bms_chip *chip)
+{
+ int rc = 0;
+ u8 temp = 0;
+
+ if (chip->fcc_new_mah > 0) {
+ rc = qpnp_write_wrapper(chip, (u8 *)&chip->fcc_new_mah,
+ chip->base + FCC_STORAGE_LSB, 2);
+ if (rc)
+ pr_err("Unable to backup new_fcc\n");
+
+ temp = chip->fcc_new_batt_temp / 10;
+ rc = qpnp_write_wrapper(chip, &temp,
+ chip->base + FCC_BATT_TEMP_STORAGE, 1);
+ if (rc)
+ pr_err("Unable to backup fcc temp.\n");
+ }
+}
+
+
+static void backup_charge_cycle(struct qpnp_bms_chip *chip)
+{
+ int rc = 0;
+
+ if (chip->charge_increase >= 0) {
+ rc = qpnp_write_wrapper(chip, &chip->charge_increase,
+ chip->base + CHARGE_INCREASE_STORAGE, 1);
+ if (rc)
+ pr_err("Unable to backup charge_increase\n");
+ }
+
+ if (chip->charge_cycles >= 0) {
+ rc = qpnp_write_wrapper(chip, (u8 *)&chip->charge_cycles,
+ chip->base + CHARGE_CYCLE_STORAGE_LSB, 2);
+ if (rc)
+ pr_err("Unable to backup charge_cycles\n");
+ }
+}
+
+static void restore_fcc_data(struct qpnp_bms_chip *chip)
+{
+ int rc = 0;
+ uint16_t temp_u16 = 0;
+ u8 temp_u8 = 0;
+
+ rc = qpnp_read_wrapper(chip, &temp_u8,
+ chip->base + CHARGE_INCREASE_STORAGE, 1);
+ if (!rc && temp_u8 != 0xFF)
+ chip->charge_increase = temp_u8;
+
+ rc = qpnp_read_wrapper(chip, (u8 *)&temp_u16,
+ chip->base + CHARGE_CYCLE_STORAGE_LSB, 2);
+ if (!rc && temp_u16 != 0xFFFF)
+ chip->charge_cycles = temp_u16;
+
+ rc = qpnp_read_wrapper(chip, (u8 *)&temp_u16,
+ chip->base + FCC_STORAGE_LSB, 2);
+ if (!rc && temp_u16 != 0xFFFF) {
+ chip->fcc_new_mah = temp_u16;
+ } else {
+ pr_debug("Backed-up FCC not initialized, FCC not updated\n");
+ return;
+ }
+
+ rc = qpnp_read_wrapper(chip, &temp_u8,
+ chip->base + FCC_BATT_TEMP_STORAGE, 1);
+ if (!rc && temp_u8 != 0xFF) {
+ chip->fcc_new_batt_temp = (s8)temp_u8 * 10;
+ } else {
+ pr_debug("Backed-up temp. not initialized, FCC not updated\n");
+ return;
+ }
+ /* readjust the FCC table if fcc and temp are valid */
+ readjust_fcc_table(chip);
+}
+
+static int calculate_real_soc(struct qpnp_bms_chip *chip,
+ int batt_temp, struct raw_soc_params *raw, int cc_uah)
+{
+ int fcc_uah, rc_uah;
+
+ fcc_uah = calculate_fcc(chip, batt_temp);
+ rc_uah = calculate_ocv_charge(chip, raw, fcc_uah);
+
+ return ((rc_uah - cc_uah) * 100) / fcc_uah;
+}
+
+static void update_fcc_table_for_temp(struct qpnp_bms_chip *chip,
+ int batt_temp_final)
+{
+ int i, fcc_t1, fcc_t2, fcc_final;
+ struct fcc_data *ft;
+
+ /* Interpolate all the FCC entries to the same temperature */
+ for (i = 0; i < chip->min_fcc_learning_samples; i++) {
+ ft = &chip->fcc_table[i];
+ if (ft->batt_temp == batt_temp_final)
+ continue;
+ fcc_t1 = interpolate_fcc(chip->fcc_temp_lut, ft->batt_temp);
+ fcc_t2 = interpolate_fcc(chip->fcc_temp_lut, batt_temp_final);
+ fcc_final = (ft->fcc_new / fcc_t1) * fcc_t2;
+ ft->fcc_new = fcc_final;
+ ft->batt_temp = batt_temp_final;
+ }
+}
+
+static void update_fcc_learning_table(struct qpnp_bms_chip *chip,
+ int fcc_uah, int new_fcc_uah, int chargecycles, int batt_temp)
+{
+ int i, count, new_fcc_avg = 0, temp_fcc_avg = 0, temp_fcc_delta = 0;
+ struct fcc_data *ft;
+
+ count = last_fcc_update_count % chip->min_fcc_learning_samples;
+ ft = &chip->fcc_table[count];
+ ft->fcc_new = chip->fcc_new_sysfs = new_fcc_uah;
+ ft->batt_temp = batt_temp;
+ ft->chargecycles = chargecycles;
+ last_fcc_update_count++;
+ /* update userspace */
+ sysfs_notify(&chip->dev->kobj, NULL, "fcc_data");
+
+ pr_debug("Updated fcc table. new_fcc=%d, chargecycle=%d, temp=%d fcc_update_count=%d\n",
+ new_fcc_uah, chargecycles, batt_temp, last_fcc_update_count);
+
+ if (last_fcc_update_count < chip->min_fcc_learning_samples) {
+ pr_debug("Not enough FCC samples. Current count = %d\n",
+ last_fcc_update_count);
+ return; /* Not enough samples to update fcc */
+ }
+
+ /* reject entries if they are > 50 chargecycles apart */
+ for (i = 0; i < chip->min_fcc_learning_samples; i++) {
+ if ((chip->fcc_table[i].chargecycles + VALID_FCC_CHGCYL_RANGE)
+ < chargecycles) {
+ pr_debug("Charge cycle too old (> %d cycles apart)\n",
+ VALID_FCC_CHGCYL_RANGE);
+ return; /* Samples old, > 50 cycles apart*/
+ }
+ }
+ /* update the fcc table for temperature difference*/
+ update_fcc_table_for_temp(chip, batt_temp);
+
+ for (i = 0; i < chip->min_fcc_learning_samples; i++)
+ temp_fcc_avg += chip->fcc_table[i].fcc_new;
+
+ temp_fcc_avg /= chip->min_fcc_learning_samples;
+ temp_fcc_delta = div_u64(temp_fcc_avg * DELTA_FCC_PERCENT, 100);
+
+ /* fix the fcc if its an outlier i.e. > 5% of the average */
+ for (i = 0; i < chip->min_fcc_learning_samples; i++) {
+ ft = &chip->fcc_table[i];
+ if (abs(ft->fcc_new - temp_fcc_avg) > temp_fcc_delta)
+ ft->fcc_new = temp_fcc_avg;
+ new_fcc_avg += ft->fcc_new;
+ }
+ new_fcc_avg /= chip->min_fcc_learning_samples;
+
+ chip->fcc_new_mah = new_fcc_avg / 1000;
+ chip->fcc_new_batt_temp = batt_temp;
+ pr_info("FCC update: New fcc_mah=%d, fcc_batt_temp=%d\n",
+ new_fcc_avg, batt_temp);
+ readjust_fcc_table(chip);
+ backup_fcc_new(chip);
+}
+
+static bool is_new_fcc_valid(int new_fcc_uah, int fcc_uah)
+{
+ if ((new_fcc_uah >= (fcc_uah / 2)) &&
+ ((new_fcc_uah * 100) <= (fcc_uah * 105)))
+ return true;
+
+ pr_debug("FCC rejected - not within valid limit\n");
+ return false;
+}
+
+static void fcc_learning_config(struct qpnp_bms_chip *chip, bool start)
+{
+ int rc, batt_temp;
+ struct raw_soc_params raw;
+ struct qpnp_vadc_result result;
+ int fcc_uah, new_fcc_uah, delta_cc_uah, delta_soc;
+
+ rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result);
+ if (rc) {
+ pr_err("Unable to read batt_temp\n");
+ return;
+ } else {
+ batt_temp = (int)result.physical;
+ }
+
+ rc = read_soc_params_raw(chip, &raw, batt_temp);
+ if (rc) {
+ pr_err("Unable to read CC, cannot update FCC\n");
+ return;
+ }
+
+ if (start) {
+ chip->start_pc = interpolate_pc(chip->pc_temp_ocv_lut,
+ batt_temp / 10, raw.last_good_ocv_uv / 1000);
+ chip->start_cc_uah = calculate_cc(chip, raw.cc, false);
+ chip->start_real_soc = calculate_real_soc(chip,
+ batt_temp, &raw, chip->start_cc_uah);
+ pr_debug("start_pc=%d, start_cc=%d, start_soc=%d real_soc=%d\n",
+ chip->start_pc, chip->start_cc_uah,
+ chip->start_soc, chip->start_real_soc);
+ } else {
+ chip->end_cc_uah = calculate_cc(chip, raw.cc, false);
+ delta_soc = 100 - chip->start_real_soc;
+ delta_cc_uah = abs(chip->end_cc_uah - chip->start_cc_uah);
+ new_fcc_uah = div_u64(delta_cc_uah * 100, delta_soc);
+ fcc_uah = calculate_fcc(chip, batt_temp);
+ pr_debug("start_soc=%d, start_pc=%d, start_real_soc=%d, start_cc=%d, end_cc=%d, new_fcc=%d\n",
+ chip->start_soc, chip->start_pc, chip->start_real_soc,
+ chip->start_cc_uah, chip->end_cc_uah, new_fcc_uah);
+
+ if (is_new_fcc_valid(new_fcc_uah, fcc_uah))
+ update_fcc_learning_table(chip, fcc_uah,
+ new_fcc_uah, chip->charge_cycles, batt_temp);
+ }
+}
+
+static void charging_began(struct qpnp_bms_chip *chip)
+{
+
+ mutex_lock(&chip->last_soc_mutex);
+ chip->charge_start_tm_sec = 0;
+ chip->catch_up_time_sec = 0;
+ mutex_unlock(&chip->last_soc_mutex);
+
+ chip->start_soc = report_state_of_charge(chip);
+
+ mutex_lock(&chip->last_ocv_uv_mutex);
+ if (chip->enable_fcc_learning)
+ fcc_learning_config(chip, true);
+ chip->soc_at_cv = -EINVAL;
+ chip->prev_chg_soc = -EINVAL;
+ mutex_unlock(&chip->last_ocv_uv_mutex);
+}
+
+static void charging_ended(struct qpnp_bms_chip *chip)
+{
+ mutex_lock(&chip->last_soc_mutex);
+ chip->charge_start_tm_sec = 0;
+ chip->catch_up_time_sec = 0;
+ mutex_unlock(&chip->last_soc_mutex);
+
+ chip->end_soc = report_state_of_charge(chip);
+
+ mutex_lock(&chip->last_ocv_uv_mutex);
+ chip->soc_at_cv = -EINVAL;
+ chip->prev_chg_soc = -EINVAL;
+
+ /* update the chargecycles */
+ if (chip->end_soc > chip->start_soc) {
+ chip->charge_increase += (chip->end_soc - chip->start_soc);
+ if (chip->charge_increase > 100) {
+ chip->charge_cycles++;
+ chip->charge_increase = chip->charge_increase % 100;
+ }
+ if (chip->enable_fcc_learning)
+ backup_charge_cycle(chip);
+ }
+
+ if (get_battery_status(chip) == POWER_SUPPLY_STATUS_FULL) {
+ if (chip->enable_fcc_learning &&
+ (chip->start_soc <= chip->min_fcc_learning_soc) &&
+ (chip->start_pc <= chip->min_fcc_ocv_pc))
+ fcc_learning_config(chip, false);
+ chip->done_charging = true;
+ chip->last_soc_invalid = true;
+ } else if (chip->charging_adjusted_ocv > 0) {
+ pr_debug("Charging stopped before full, adjusted OCV = %d\n",
+ chip->charging_adjusted_ocv);
+ chip->last_ocv_uv = chip->charging_adjusted_ocv;
+ }
+
+ chip->charging_adjusted_ocv = -EINVAL;
+
+ mutex_unlock(&chip->last_ocv_uv_mutex);
+}
+
+static void battery_status_check(struct qpnp_bms_chip *chip)
+{
+ int status = get_battery_status(chip);
+
+ if (chip->battery_status != status) {
+ if (status == POWER_SUPPLY_STATUS_CHARGING) {
+ pr_debug("charging started\n");
+ charging_began(chip);
+ } else if (chip->battery_status
+ == POWER_SUPPLY_STATUS_CHARGING) {
+ pr_debug("charging ended\n");
+ charging_ended(chip);
+ }
+ chip->battery_status = status;
+ /* a new battery was inserted or removed, so force a soc
+ * recalculation to update the SoC */
+ schedule_work(&chip->recalc_work);
+ }
+}
+
+static void battery_insertion_check(struct qpnp_bms_chip *chip)
+{
+ bool present = is_battery_present(chip);
+
+ mutex_lock(&chip->vbat_monitor_mutex);
+ if (chip->battery_present != present) {
+ if (chip->battery_present != -EINVAL) {
+ if (present) {
+ setup_vbat_monitoring(chip);
+ chip->new_battery = true;
+ } else {
+ reset_vbat_monitoring(chip);
+ }
+ }
+ chip->battery_present = present;
+ /* a new battery was inserted or removed, so force a soc
+ * recalculation to update the SoC */
+ schedule_work(&chip->recalc_work);
+ }
+ mutex_unlock(&chip->vbat_monitor_mutex);
}
/* Returns capacity as a SoC percentage between 0 and 100 */
@@ -1935,10 +2675,10 @@
return report_state_of_charge(chip);
}
-/* Returns estimated max current that the battery can supply in uA */
-static int get_prop_bms_current_max(struct qpnp_bms_chip *chip)
+/* Returns estimated battery resistance */
+static int get_prop_bms_batt_resistance(struct qpnp_bms_chip *chip)
{
- return chip->ibat_max_ua;
+ return chip->rbatt_mohm * 1000;
}
/* Returns instantaneous current in uA */
@@ -1954,31 +2694,33 @@
return result_ua;
}
+/* Returns coulomb counter in uAh */
+static int get_prop_bms_charge_counter(struct qpnp_bms_chip *chip)
+{
+ int64_t cc_raw;
+
+ mutex_lock(&chip->bms_output_lock);
+ lock_output_data(chip);
+ read_cc_raw(chip, &cc_raw);
+ unlock_output_data(chip);
+ mutex_unlock(&chip->bms_output_lock);
+
+ return calculate_cc(chip, cc_raw, false);
+}
+
/* Returns full charge design in uAh */
static int get_prop_bms_charge_full_design(struct qpnp_bms_chip *chip)
{
- return chip->fcc;
-}
-
-static int get_prop_bms_present(struct qpnp_bms_chip *chip)
-{
- return chip->battery_present;
-}
-
-static void set_prop_bms_present(struct qpnp_bms_chip *chip, int present)
-{
- if (chip->battery_present != present) {
- chip->battery_present = present;
- if (present)
- chip->new_battery = true;
- /* a new battery was inserted or removed, so force a soc
- * recalculation to update the SoC */
- schedule_work(&chip->recalc_work);
- }
+ return chip->fcc_mah * 1000;
}
static void qpnp_bms_external_power_changed(struct power_supply *psy)
{
+ struct qpnp_bms_chip *chip = container_of(psy, struct qpnp_bms_chip,
+ bms_psy);
+
+ battery_insertion_check(chip);
+ battery_status_check(chip);
}
static int qpnp_bms_power_get_property(struct power_supply *psy,
@@ -1995,14 +2737,17 @@
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = get_prop_bms_current_now(chip);
break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- val->intval = get_prop_bms_current_max(chip);
+ case POWER_SUPPLY_PROP_RESISTANCE:
+ val->intval = get_prop_bms_batt_resistance(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+ val->intval = get_prop_bms_charge_counter(chip);
break;
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
val->intval = get_prop_bms_charge_full_design(chip);
break;
- case POWER_SUPPLY_PROP_PRESENT:
- val->intval = get_prop_bms_present(chip);
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ val->intval = chip->charge_cycles;
break;
default:
return -EINVAL;
@@ -2010,23 +2755,111 @@
return 0;
}
-static int qpnp_bms_power_set_property(struct power_supply *psy,
- enum power_supply_property psp,
- const union power_supply_propval *val)
+static ssize_t fcc_data_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
- struct qpnp_bms_chip *chip = container_of(psy, struct qpnp_bms_chip,
- bms_psy);
+ struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
+ static int i;
+ int fcc_new = 0, rc;
- switch (psp) {
- case POWER_SUPPLY_PROP_PRESENT:
- set_prop_bms_present(chip, val->intval);
- break;
- default:
+ i %= chip->min_fcc_learning_samples;
+ rc = sscanf(buf, "%d", &fcc_new);
+ if (rc != 1)
return -EINVAL;
- }
- return 0;
+ chip->fcc_table[i].fcc_new = fcc_new;
+ pr_debug("Rcvd: [%d] fcc_new=%d\n", i, fcc_new);
+ i++;
+
+ return count;
}
+static ssize_t fcc_data_get(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int count = 0;
+ struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
+
+ count = snprintf(buf, PAGE_SIZE, "%d", chip->fcc_new_sysfs);
+ pr_debug("Sent: fcc_new=%d\n", chip->fcc_new_sysfs);
+
+ return count;
+}
+
+static ssize_t fcc_temp_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ static int i;
+ int batt_temp = 0, rc;
+ struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
+
+ i %= chip->min_fcc_learning_samples;
+ rc = sscanf(buf, "%d", &batt_temp);
+ if (rc != 1)
+ return -EINVAL;
+ chip->fcc_table[i].batt_temp = batt_temp;
+ pr_debug("Rcvd: [%d] batt_temp=%d\n", i, batt_temp);
+ i++;
+
+ return count;
+}
+
+static ssize_t fcc_chgcyl_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ static int i;
+ int chargecycle = 0, rc;
+ struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
+
+ i %= chip->min_fcc_learning_samples;
+ rc = sscanf(buf, "%d", &chargecycle);
+ if (rc != 1)
+ return -EINVAL;
+ chip->fcc_table[i].chargecycles = chargecycle;
+ pr_debug("Rcvd: [%d] chargecycle=%d\n", i, chargecycle);
+ i++;
+
+ return count;
+}
+
+static ssize_t fcc_list_get(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
+ struct fcc_data *ft;
+ int i = 0, j, count = 0;
+
+ if (last_fcc_update_count < chip->min_fcc_learning_samples)
+ i = last_fcc_update_count;
+ else
+ i = chip->min_fcc_learning_samples;
+
+ for (j = 0; j < i; j++) {
+ ft = &chip->fcc_table[j];
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "%d %d %d\n", ft->fcc_new, ft->chargecycles,
+ ft->batt_temp);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(fcc_data, 0664, fcc_data_get, fcc_data_set);
+static DEVICE_ATTR(fcc_temp, 0664, NULL, fcc_temp_set);
+static DEVICE_ATTR(fcc_chgcyl, 0664, NULL, fcc_chgcyl_set);
+static DEVICE_ATTR(fcc_list, 0664, fcc_list_get, NULL);
+
+static struct attribute *fcc_attrs[] = {
+ &dev_attr_fcc_data.attr,
+ &dev_attr_fcc_temp.attr,
+ &dev_attr_fcc_chgcyl.attr,
+ &dev_attr_fcc_list.attr,
+ NULL
+};
+
+static const struct attribute_group fcc_attr_group = {
+ .attrs = fcc_attrs,
+};
+
#define OCV_USE_LIMIT_EN BIT(7)
static int set_ocv_voltage_thresholds(struct qpnp_bms_chip *chip,
int low_voltage_threshold,
@@ -2148,6 +2981,10 @@
batt_data = &palladium_1500_data;
} else if (chip->batt_type == BATT_OEM) {
batt_data = &oem_batt_data;
+ } else if (chip->batt_type == BATT_QRD_4V35_2000MAH) {
+ batt_data = &QRD_4v35_2000mAh_data;
+ } else if (chip->batt_type == BATT_QRD_4V2_1300MAH) {
+ batt_data = &qrd_4v2_1300mah_data;
} else {
battery_id = read_battery_id(chip);
if (battery_id < 0) {
@@ -2169,7 +3006,7 @@
}
}
- chip->fcc = batt_data->fcc;
+ chip->fcc_mah = batt_data->fcc;
chip->fcc_temp_lut = batt_data->fcc_temp_lut;
chip->fcc_sf_lut = batt_data->fcc_sf_lut;
chip->pc_temp_ocv_lut = batt_data->pc_temp_ocv_lut;
@@ -2239,10 +3076,28 @@
SPMI_PROP_READ(ocv_low_threshold_uv,
"ocv-voltage-low-threshold-uv", rc);
SPMI_PROP_READ(low_voltage_threshold, "low-voltage-threshold", rc);
+ SPMI_PROP_READ(temperature_margin, "tm-temp-margin", rc);
if (chip->adjust_soc_low_threshold >= 45)
chip->adjust_soc_low_threshold = 45;
+ chip->enable_fcc_learning = of_property_read_bool(
+ chip->spmi->dev.of_node, "qcom,enable-fcc-learning");
+ if (chip->enable_fcc_learning) {
+ SPMI_PROP_READ(min_fcc_learning_soc,
+ "min-fcc-learning-soc", rc);
+ SPMI_PROP_READ(min_fcc_ocv_pc,
+ "min-fcc-ocv-pc", rc);
+ SPMI_PROP_READ(min_fcc_learning_samples,
+ "min-fcc-learning-samples", rc);
+ chip->fcc_table = kzalloc((sizeof(struct fcc_data) *
+ chip->min_fcc_learning_samples), GFP_KERNEL);
+ pr_debug("min-fcc-soc=%d, min-fcc-pc=%d, min-fcc-cycles=%d\n",
+ chip->min_fcc_learning_soc, chip->min_fcc_ocv_pc,
+ chip->min_fcc_learning_samples);
+ min_fcc_cycles = chip->min_fcc_learning_samples;
+ }
+
pr_debug("dts data: r_sense_uohm:%d, v_cutoff_uv:%d, max_v:%d\n",
chip->r_sense_uohm, chip->v_cutoff_uv,
chip->max_voltage_uv);
@@ -2265,6 +3120,8 @@
chip->calculated_soc = -EINVAL;
chip->last_soc = -EINVAL;
chip->last_soc_est = -EINVAL;
+ chip->battery_present = -EINVAL;
+ chip->battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
chip->last_cc_uah = INT_MIN;
chip->ocv_reading_at_100 = OCV_RAW_UNINITIALIZED;
chip->prev_last_good_ocv_raw = OCV_RAW_UNINITIALIZED;
@@ -2378,6 +3235,7 @@
return rc;
}
reset_cc(chip);
+ chip->software_cc_uah = 0;
}
} else {
pr_debug("Internal rsense selected\n");
@@ -2394,6 +3252,7 @@
return rc;
}
reset_cc(chip);
+ chip->software_cc_uah = 0;
}
rc = qpnp_iadc_get_rsense(&rds_rsense_nohm);
@@ -2409,10 +3268,90 @@
return 0;
}
+static int refresh_die_temp_monitor(struct qpnp_bms_chip *chip)
+{
+ struct qpnp_vadc_result result;
+ int rc;
+
+ rc = qpnp_vadc_read(DIE_TEMP, &result);
+
+ pr_debug("low = %lld, high = %lld\n",
+ result.physical - chip->temperature_margin,
+ result.physical + chip->temperature_margin);
+ chip->die_temp_monitor_params.high_temp = result.physical
+ + chip->temperature_margin;
+ chip->die_temp_monitor_params.low_temp = result.physical
+ - chip->temperature_margin;
+ chip->die_temp_monitor_params.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ return qpnp_adc_tm_channel_measure(&chip->die_temp_monitor_params);
+}
+
+static void btm_notify_die_temp(enum qpnp_tm_state state, void *ctx)
+{
+ struct qpnp_bms_chip *chip = ctx;
+ struct qpnp_vadc_result result;
+ int rc;
+
+ rc = qpnp_vadc_read(DIE_TEMP, &result);
+
+ if (state == ADC_TM_LOW_STATE)
+ pr_debug("low state triggered\n");
+ else if (state == ADC_TM_HIGH_STATE)
+ pr_debug("high state triggered\n");
+ pr_debug("die temp = %lld, raw = 0x%x\n",
+ result.physical, result.adc_code);
+ schedule_work(&chip->recalc_work);
+ refresh_die_temp_monitor(chip);
+}
+
+static int setup_die_temp_monitoring(struct qpnp_bms_chip *chip)
+{
+ int rc = qpnp_adc_tm_is_ready();
+ if (rc) {
+ pr_info("adc tm is not ready yet: %d, defer probe\n", rc);
+ return -EPROBE_DEFER;
+ }
+ chip->die_temp_monitor_params.channel = DIE_TEMP;
+ chip->die_temp_monitor_params.btm_ctx = (void *)chip;
+ chip->die_temp_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
+ chip->die_temp_monitor_params.threshold_notification =
+ &btm_notify_die_temp;
+ refresh_die_temp_monitor(chip);
+ if (rc) {
+ pr_err("tm setup failed: %d\n", rc);
+ return rc;
+ }
+ pr_debug("setup complete\n");
+ return 0;
+}
+
+static void check_battery_removal(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ u8 temp = 0;
+
+ /* check if battery was removed at PON */
+ rc = qpnp_read_wrapper(chip, &temp,
+ chip->base + BMS_BATT_REMOVED_REG, 1);
+ if (temp == 0xFF) {
+ pr_debug("New battery inserted at PON\n");
+ temp = battery_removed = 1;
+ rc = qpnp_write_wrapper(chip, &temp,
+ chip->base + BMS_BATT_REMOVED_REG, 1);
+ if (rc)
+ pr_err("Unable to set BMS_BATT_REMOVED_REG\n");
+ } else {
+ if (rc)
+ pr_err("Unable to read BMS_BATT_REMOVED_REG\n");
+ battery_removed = 0;
+ }
+}
+
static int __devinit qpnp_bms_probe(struct spmi_device *spmi)
{
struct qpnp_bms_chip *chip;
- union power_supply_propval retval = {0,};
+ bool warm_reset;
int rc, vbatt;
chip = kzalloc(sizeof *chip, GFP_KERNEL);
@@ -2425,15 +3364,22 @@
rc = qpnp_vadc_is_ready();
if (rc) {
pr_info("vadc not ready: %d, deferring probe\n", rc);
+ rc = -EPROBE_DEFER;
goto error_read;
}
rc = qpnp_iadc_is_ready();
if (rc) {
pr_info("iadc not ready: %d, deferring probe\n", rc);
+ rc = -EPROBE_DEFER;
goto error_read;
}
+ warm_reset = qpnp_pon_is_warm_reset();
+ rc = warm_reset;
+ if (rc < 0)
+ goto error_read;
+
rc = register_spmi(chip, spmi);
if (rc) {
pr_err("error registering spmi resource %d\n", rc);
@@ -2488,30 +3434,47 @@
mutex_init(&chip->bms_output_lock);
mutex_init(&chip->last_ocv_uv_mutex);
+ mutex_init(&chip->vbat_monitor_mutex);
mutex_init(&chip->soc_invalidation_mutex);
+ mutex_init(&chip->last_soc_mutex);
wake_lock_init(&chip->soc_wake_lock, WAKE_LOCK_SUSPEND,
"qpnp_soc_lock");
wake_lock_init(&chip->low_voltage_wake_lock, WAKE_LOCK_SUSPEND,
"qpnp_low_voltage_lock");
+ wake_lock_init(&chip->cv_wake_lock, WAKE_LOCK_SUSPEND,
+ "qpnp_cv_lock");
INIT_DELAYED_WORK(&chip->calculate_soc_delayed_work,
calculate_soc_work);
INIT_WORK(&chip->recalc_work, recalculate_work);
read_shutdown_soc_and_iavg(chip);
+ if (chip->enable_fcc_learning) {
+ restore_fcc_data(chip);
+ rc = sysfs_create_group(&spmi->dev.kobj, &fcc_attr_group);
+ if (rc) {
+ pr_err("Unable to create sysfs entries\n");
+ goto error_setup;
+ }
+ }
+
dev_set_drvdata(&spmi->dev, chip);
device_init_wakeup(&spmi->dev, 1);
- if (!chip->batt_psy)
- chip->batt_psy = power_supply_get_by_name("battery");
- if (chip->batt_psy) {
- chip->batt_psy->get_property(chip->batt_psy,
- POWER_SUPPLY_PROP_PRESENT, &retval);
- chip->battery_present = retval.intval;
- pr_debug("present = %d\n", chip->battery_present);
- } else {
- chip->battery_present = 1;
+ check_battery_removal(chip);
+
+
+ rc = setup_vbat_monitoring(chip);
+ if (rc < 0) {
+ pr_err("failed to set up voltage notifications: %d\n", rc);
+ goto error_setup;
+ }
+
+ rc = setup_die_temp_monitoring(chip);
+ if (rc < 0) {
+ pr_err("failed to set up die temp notifications: %d\n", rc);
+ goto error_setup;
}
calculate_soc_work(&(chip->calculate_soc_delayed_work.work));
@@ -2522,7 +3485,6 @@
chip->bms_psy.properties = msm_bms_power_props;
chip->bms_psy.num_properties = ARRAY_SIZE(msm_bms_power_props);
chip->bms_psy.get_property = qpnp_bms_power_get_property;
- chip->bms_psy.set_property = qpnp_bms_power_set_property;
chip->bms_psy.external_power_changed =
qpnp_bms_external_power_changed;
chip->bms_psy.supplied_to = qpnp_bms_supplicants;
@@ -2543,16 +3505,18 @@
goto unregister_dc;
}
- pr_info("probe success: soc =%d vbatt = %d ocv = %d r_sense_uohm = %u\n",
- get_prop_bms_capacity(chip),
- vbatt, chip->last_ocv_uv, chip->r_sense_uohm);
+ pr_info("probe success: soc =%d vbatt = %d ocv = %d r_sense_uohm = %u warm_reset = %d\n",
+ get_prop_bms_capacity(chip), vbatt, chip->last_ocv_uv,
+ chip->r_sense_uohm, warm_reset);
return 0;
unregister_dc:
+ power_supply_unregister(&chip->bms_psy);
+error_setup:
+ dev_set_drvdata(&spmi->dev, NULL);
wake_lock_destroy(&chip->soc_wake_lock);
wake_lock_destroy(&chip->low_voltage_wake_lock);
- power_supply_unregister(&chip->bms_psy);
- dev_set_drvdata(&spmi->dev, NULL);
+ wake_lock_destroy(&chip->cv_wake_lock);
error_resource:
error_read:
kfree(chip);
@@ -2569,10 +3533,20 @@
return 0;
}
+static int bms_suspend(struct device *dev)
+{
+ struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&chip->calculate_soc_delayed_work);
+ chip->was_charging_at_sleep = is_battery_charging(chip);
+ return 0;
+}
+
static int bms_resume(struct device *dev)
{
int rc;
- unsigned long soc_calc_period;
+ int soc_calc_period;
+ int time_until_next_recalc = 0;
unsigned long time_since_last_recalc;
unsigned long tm_now_sec;
struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
@@ -2580,30 +3554,30 @@
rc = get_current_time(&tm_now_sec);
if (rc) {
pr_err("Could not read current time: %d\n", rc);
- } else if (tm_now_sec > chip->last_recalc_time) {
- /*
- * unbind the last soc so that the next
- * recalculation is not limited to changing by 1%
- */
- chip->last_soc_unbound = true;
- time_since_last_recalc = tm_now_sec - chip->last_recalc_time;
- pr_debug("Time since last recalc: %lu\n",
- time_since_last_recalc);
+ } else {
if (chip->calculated_soc < chip->low_soc_calc_threshold)
soc_calc_period = chip->low_soc_calculate_soc_ms;
else
soc_calc_period = chip->calculate_soc_ms;
-
- if (time_since_last_recalc >= soc_calc_period) {
- chip->last_recalc_time = tm_now_sec;
- recalculate_soc(chip);
- }
+ time_since_last_recalc = tm_now_sec - chip->last_recalc_time;
+ pr_debug("Time since last recalc: %lu\n",
+ time_since_last_recalc);
+ time_until_next_recalc = max(0, soc_calc_period
+ - (int)(time_since_last_recalc * 1000));
}
+
+ if (!wake_lock_active(&chip->soc_wake_lock)
+ && time_until_next_recalc == 0)
+ wake_lock(&chip->soc_wake_lock);
+ schedule_delayed_work(&chip->calculate_soc_delayed_work,
+ round_jiffies_relative(msecs_to_jiffies
+ (time_until_next_recalc)));
return 0;
}
static const struct dev_pm_ops qpnp_bms_pm_ops = {
.resume = bms_resume,
+ .suspend = bms_suspend,
};
static struct spmi_driver qpnp_bms_driver = {
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index a875c92..439a8ae 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -25,6 +25,9 @@
#include <linux/power_supply.h>
#include <linux/bitops.h>
#include <linux/ratelimit.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/machine.h>
/* Interrupt offsets */
#define INT_RT_STS(base) (base + 0x10)
@@ -81,13 +84,23 @@
#define CHGR_BAT_IF_VCP 0x42
#define CHGR_BAT_IF_BATFET_CTRL1 0x90
#define CHGR_MISC_BOOT_DONE 0x42
+#define CHGR_BUCK_COMPARATOR_OVRIDE_1 0xEB
#define CHGR_BUCK_COMPARATOR_OVRIDE_3 0xED
#define CHGR_BUCK_BCK_VBAT_REG_MODE 0x74
#define MISC_REVISION2 0x01
#define USB_OVP_CTL 0x42
+#define USB_CHG_GONE_REV_BST 0xED
+#define BUCK_VCHG_OV 0x77
+#define BUCK_TEST_SMBC_MODES 0xE6
#define SEC_ACCESS 0xD0
+#define BAT_IF_VREF_BAT_THM_CTRL 0x4A
+#define BAT_IF_BPD_CTRL 0x48
+#define BOOST_VSET 0x41
+#define BOOST_ENABLE_CONTROL 0x46
+#define COMP_OVR1 0xEA
#define REG_OFFSET_PERP_SUBTYPE 0x05
+
/* SMBB peripheral subtype values */
#define SMBB_CHGR_SUBTYPE 0x01
#define SMBB_BUCK_SUBTYPE 0x02
@@ -120,6 +133,13 @@
#define CHGR_ON_BAT_FORCE_BIT BIT(0)
#define USB_VALID_DEB_20MS 0x03
#define BUCK_VBAT_REG_NODE_SEL_BIT BIT(0)
+#define VREF_BATT_THERM_FORCE_ON 0xC0
+#define BAT_IF_BPD_CTRL_SEL 0x03
+#define VREF_BAT_THM_ENABLED_FSM 0x80
+#define REV_BST_DETECTED BIT(0)
+#define BAT_THM_EN BIT(1)
+#define BAT_ID_EN BIT(0)
+#define BOOST_PWR_EN BIT(7)
/* Interrupt definitions */
/* smbb_chg_interrupts */
@@ -171,6 +191,17 @@
/* Workaround flags */
#define CHG_FLAGS_VCP_WA BIT(0)
+#define BOOST_FLASH_WA BIT(1)
+
+struct qpnp_chg_irq {
+ unsigned int irq;
+ unsigned long disabled;
+};
+
+struct qpnp_chg_regulator {
+ struct regulator_desc rdesc;
+ struct regulator_dev *rdev;
+};
/**
* struct qpnp_chg_chip - device information
@@ -230,12 +261,14 @@
u16 boost_base;
u16 misc_base;
u16 freq_base;
- unsigned int usbin_valid_irq;
- unsigned int dcin_valid_irq;
- unsigned int chg_fastchg_irq;
- unsigned int chg_trklchg_irq;
- unsigned int chg_failed_irq;
- unsigned int batt_pres_irq;
+ struct qpnp_chg_irq usbin_valid;
+ struct qpnp_chg_irq dcin_valid;
+ struct qpnp_chg_irq chg_gone;
+ struct qpnp_chg_irq chg_fastchg;
+ struct qpnp_chg_irq chg_trklchg;
+ struct qpnp_chg_irq chg_failed;
+ struct qpnp_chg_irq chg_vbatdet_lo;
+ struct qpnp_chg_irq batt_pres;
bool bat_is_cool;
bool bat_is_warm;
bool chg_done;
@@ -244,16 +277,20 @@
bool batt_present;
bool charging_disabled;
bool use_default_batt_values;
+ bool duty_cycle_100p;
+ unsigned int bpd_detection;
unsigned int max_bat_chg_current;
unsigned int warm_bat_chg_ma;
unsigned int cool_bat_chg_ma;
unsigned int safe_voltage_mv;
unsigned int max_voltage_mv;
unsigned int min_voltage_mv;
+ int set_vddmax_mv;
+ int delta_vddmax_mv;
unsigned int warm_bat_mv;
unsigned int cool_bat_mv;
unsigned int resume_delta_mv;
- unsigned int term_current;
+ int term_current;
unsigned int maxinput_usb_ma;
unsigned int maxinput_dc_ma;
unsigned int warm_bat_decidegc;
@@ -272,22 +309,56 @@
uint32_t flags;
struct qpnp_adc_tm_btm_param adc_param;
struct work_struct adc_measure_work;
+ struct delayed_work arb_stop_work;
+ struct delayed_work eoc_work;
+ struct wake_lock eoc_wake_lock;
+ struct qpnp_chg_regulator otg_vreg;
+ struct qpnp_chg_regulator boost_vreg;
};
+
static struct of_device_id qpnp_charger_match_table[] = {
{ .compatible = QPNP_CHARGER_DEV_NAME, },
{}
};
+enum bpd_type {
+ BPD_TYPE_BAT_ID,
+ BPD_TYPE_BAT_THM,
+ BPD_TYPE_BAT_THM_BAT_ID,
+};
+
+static const char * const bpd_label[] = {
+ [BPD_TYPE_BAT_ID] = "bpd_id",
+ [BPD_TYPE_BAT_THM] = "bpd_thm",
+ [BPD_TYPE_BAT_THM_BAT_ID] = "bpd_thm_id",
+};
+
+static inline int
+get_bpd(const char *name)
+{
+ int i = 0;
+ for (i = 0; i < ARRAY_SIZE(bpd_label); i++) {
+ if (strcmp(bpd_label[i], name) == 0)
+ return i;
+ }
+ return -EINVAL;
+}
+
static int
qpnp_chg_read(struct qpnp_chg_chip *chip, u8 *val,
u16 base, int count)
{
- int rc;
+ int rc = 0;
struct spmi_device *spmi = chip->spmi;
- rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, base, val,
- count);
+ if (base == 0) {
+ pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
+ base, spmi->sid, rc);
+ return -EINVAL;
+ }
+
+ rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, base, val, count);
if (rc) {
pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", base,
spmi->sid, rc);
@@ -300,11 +371,16 @@
qpnp_chg_write(struct qpnp_chg_chip *chip, u8 *val,
u16 base, int count)
{
- int rc;
+ int rc = 0;
struct spmi_device *spmi = chip->spmi;
- rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, base, val,
- count);
+ if (base == 0) {
+ pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
+ base, spmi->sid, rc);
+ return -EINVAL;
+ }
+
+ rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, base, val, count);
if (rc) {
pr_err("write failed base=0x%02x sid=0x%02x rc=%d\n",
base, spmi->sid, rc);
@@ -342,6 +418,24 @@
return 0;
}
+static void
+qpnp_chg_enable_irq(struct qpnp_chg_irq *irq)
+{
+ if (__test_and_clear_bit(0, &irq->disabled)) {
+ pr_debug("number = %d\n", irq->irq);
+ enable_irq(irq->irq);
+ }
+}
+
+static void
+qpnp_chg_disable_irq(struct qpnp_chg_irq *irq)
+{
+ if (!__test_and_set_bit(0, &irq->disabled)) {
+ pr_debug("number = %d\n", irq->irq);
+ disable_irq_nosync(irq->irq);
+ }
+}
+
#define USB_OTG_EN_BIT BIT(0)
static int
qpnp_chg_is_otg_en_set(struct qpnp_chg_chip *chip)
@@ -364,6 +458,25 @@
}
static int
+qpnp_chg_is_boost_en_set(struct qpnp_chg_chip *chip)
+{
+ u8 boost_en_ctl;
+ int rc;
+
+ rc = qpnp_chg_read(chip, &boost_en_ctl,
+ chip->boost_base + BOOST_ENABLE_CONTROL, 1);
+ if (rc) {
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ chip->boost_base + BOOST_ENABLE_CONTROL, rc);
+ return rc;
+ }
+
+ pr_debug("boost en 0x%x\n", boost_en_ctl);
+
+ return (boost_en_ctl & BOOST_PWR_EN) ? 1 : 0;
+}
+
+static int
qpnp_chg_is_batt_present(struct qpnp_chg_chip *chip)
{
u8 batt_pres_rt_sts;
@@ -524,7 +637,118 @@
enable ? USB_SUSPEND_BIT : 0, 1);
}
-static void qpnp_bat_if_adc_measure_work(struct work_struct *work)
+static int
+qpnp_chg_charge_en(struct qpnp_chg_chip *chip, int enable)
+{
+ return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
+ CHGR_CHG_EN,
+ enable ? CHGR_CHG_EN : 0, 1);
+}
+
+static int
+qpnp_chg_force_run_on_batt(struct qpnp_chg_chip *chip, int disable)
+{
+ /* Don't run on battery for batteryless hardware */
+ if (chip->use_default_batt_values)
+ return 0;
+ /* Don't force on battery if battery is not present */
+ if (!qpnp_chg_is_batt_present(chip))
+ return 0;
+
+ /* This bit forces the charger to run off of the battery rather
+ * than a connected charger */
+ return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
+ CHGR_ON_BAT_FORCE_BIT,
+ disable ? CHGR_ON_BAT_FORCE_BIT : 0, 1);
+}
+
+#define BUCK_DUTY_MASK_100P 0x30
+static int
+qpnp_buck_set_100_duty_cycle_enable(struct qpnp_chg_chip *chip, int enable)
+{
+ int rc;
+
+ pr_debug("enable: %d\n", enable);
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + SEC_ACCESS, 0xA5, 0xA5, 1);
+ if (rc) {
+ pr_debug("failed to write sec access rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + BUCK_TEST_SMBC_MODES,
+ BUCK_DUTY_MASK_100P, enable ? 0x00 : 0x10, 1);
+ if (rc) {
+ pr_debug("failed enable 100p duty cycle rc=%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+#define COMPATATOR_OVERRIDE_0 0x80
+static int
+qpnp_chg_toggle_chg_done_logic(struct qpnp_chg_chip *chip, int enable)
+{
+ int rc;
+
+ pr_debug("toggle: %d\n", enable);
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + SEC_ACCESS, 0xA5, 0xA5, 1);
+ if (rc) {
+ pr_debug("failed to write sec access rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + CHGR_BUCK_COMPARATOR_OVRIDE_1,
+ 0xC0, enable ? 0x00 : COMPATATOR_OVERRIDE_0, 1);
+ if (rc) {
+ pr_debug("failed to toggle chg done override rc=%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+#define QPNP_CHG_VBATDET_MIN_MV 3240
+#define QPNP_CHG_VBATDET_MAX_MV 5780
+#define QPNP_CHG_VBATDET_STEP_MV 20
+static int
+qpnp_chg_vbatdet_set(struct qpnp_chg_chip *chip, int vbatdet_mv)
+{
+ u8 temp;
+
+ if (vbatdet_mv < QPNP_CHG_VBATDET_MIN_MV
+ || vbatdet_mv > QPNP_CHG_VBATDET_MAX_MV) {
+ pr_err("bad mV=%d asked to set\n", vbatdet_mv);
+ return -EINVAL;
+ }
+ temp = (vbatdet_mv - QPNP_CHG_VBATDET_MIN_MV)
+ / QPNP_CHG_VBATDET_STEP_MV;
+
+ pr_debug("voltage=%d setting %02x\n", vbatdet_mv, temp);
+ return qpnp_chg_write(chip, &temp,
+ chip->chgr_base + CHGR_VBAT_DET, 1);
+}
+
+static void
+qpnp_arb_stop_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct qpnp_chg_chip *chip = container_of(dwork,
+ struct qpnp_chg_chip, arb_stop_work);
+
+ if (!chip->chg_done)
+ qpnp_chg_charge_en(chip, !chip->charging_disabled);
+ qpnp_chg_force_run_on_batt(chip, chip->charging_disabled);
+}
+
+static void
+qpnp_bat_if_adc_measure_work(struct work_struct *work)
{
struct qpnp_chg_chip *chip = container_of(work,
struct qpnp_chg_chip, adc_measure_work);
@@ -533,6 +757,55 @@
pr_err("request ADC error\n");
}
+#define EOC_CHECK_PERIOD_MS 10000
+static irqreturn_t
+qpnp_chg_vbatdet_lo_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_chg_chip *chip = _chip;
+ u8 chg_sts = 0;
+ int rc;
+
+ pr_debug("vbatdet-lo triggered\n");
+
+ rc = qpnp_chg_read(chip, &chg_sts, INT_RT_STS(chip->chgr_base), 1);
+ if (rc)
+ pr_err("failed to read chg_sts rc=%d\n", rc);
+
+ pr_debug("chg_done chg_sts: 0x%x triggered\n", chg_sts);
+ if (!chip->charging_disabled && (chg_sts & FAST_CHG_ON_IRQ)) {
+ schedule_delayed_work(&chip->eoc_work,
+ msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
+ wake_lock(&chip->eoc_wake_lock);
+ qpnp_chg_disable_irq(&chip->chg_vbatdet_lo);
+ } else {
+ qpnp_chg_charge_en(chip, !chip->charging_disabled);
+ }
+
+ power_supply_changed(chip->usb_psy);
+ if (chip->dc_chgpth_base)
+ power_supply_changed(&chip->dc_psy);
+ if (chip->bat_if_base)
+ power_supply_changed(&chip->batt_psy);
+ return IRQ_HANDLED;
+}
+
+#define ARB_STOP_WORK_MS 1000
+static irqreturn_t
+qpnp_chg_usb_chg_gone_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_chg_chip *chip = _chip;
+
+ pr_debug("chg_gone triggered\n");
+ if (qpnp_chg_is_usb_chg_plugged_in(chip)) {
+ qpnp_chg_charge_en(chip, 0);
+ qpnp_chg_force_run_on_batt(chip, 1);
+ schedule_delayed_work(&chip->arb_stop_work,
+ msecs_to_jiffies(ARB_STOP_WORK_MS));
+ }
+
+ return IRQ_HANDLED;
+}
+
#define ENUM_T_STOP_BIT BIT(0)
static irqreturn_t
qpnp_chg_usb_usbin_valid_irq_handler(int irq, void *_chip)
@@ -551,10 +824,15 @@
if (chip->usb_present ^ usb_present) {
chip->usb_present = usb_present;
- if (!usb_present)
- qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MIN_100);
- power_supply_set_present(chip->usb_psy,
- chip->usb_present);
+ if (!usb_present) {
+ qpnp_chg_usb_suspend_enable(chip, 1);
+ chip->chg_done = false;
+ } else {
+ schedule_delayed_work(&chip->eoc_work,
+ msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
+ }
+
+ power_supply_set_present(chip->usb_psy, chip->usb_present);
}
return IRQ_HANDLED;
@@ -572,6 +850,7 @@
if (chip->batt_present ^ batt_present) {
chip->batt_present = batt_present;
power_supply_changed(&chip->batt_psy);
+ power_supply_changed(chip->usb_psy);
if (chip->cool_bat_decidegc && chip->warm_bat_decidegc
&& batt_present) {
@@ -579,9 +858,6 @@
}
}
- if (chip->bms_psy)
- power_supply_set_present(chip->bms_psy, batt_present);
-
return IRQ_HANDLED;
}
@@ -596,7 +872,13 @@
if (chip->dc_present ^ dc_present) {
chip->dc_present = dc_present;
+ if (!dc_present)
+ chip->chg_done = false;
+ else
+ schedule_delayed_work(&chip->eoc_work,
+ msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
power_supply_changed(&chip->dc_psy);
+ power_supply_changed(&chip->batt_psy);
}
return IRQ_HANDLED;
@@ -609,6 +891,8 @@
struct qpnp_chg_chip *chip = _chip;
int rc;
+ pr_debug("chg_failed triggered\n");
+
rc = qpnp_chg_masked_write(chip,
chip->chgr_base + CHGR_CHG_FAILED,
CHGR_CHG_FAILED_BIT,
@@ -616,6 +900,11 @@
if (rc)
pr_err("Failed to write chg_fail clear bit!\n");
+ if (chip->bat_if_base)
+ power_supply_changed(&chip->batt_psy);
+ power_supply_changed(chip->usb_psy);
+ if (chip->dc_chgpth_base)
+ power_supply_changed(&chip->dc_psy);
return IRQ_HANDLED;
}
@@ -627,7 +916,8 @@
pr_debug("TRKL IRQ triggered\n");
chip->chg_done = false;
- power_supply_changed(&chip->batt_psy);
+ if (chip->bat_if_base)
+ power_supply_changed(&chip->batt_psy);
return IRQ_HANDLED;
}
@@ -638,9 +928,13 @@
struct qpnp_chg_chip *chip = _chip;
pr_debug("FAST_CHG IRQ triggered\n");
-
chip->chg_done = false;
- power_supply_changed(&chip->batt_psy);
+ if (chip->bat_if_base)
+ power_supply_changed(&chip->batt_psy);
+ power_supply_changed(chip->usb_psy);
+ if (chip->dc_chgpth_base)
+ power_supply_changed(&chip->dc_psy);
+ qpnp_chg_enable_irq(&chip->chg_vbatdet_lo);
return IRQ_HANDLED;
}
@@ -661,28 +955,6 @@
}
static int
-qpnp_chg_charge_en(struct qpnp_chg_chip *chip, int enable)
-{
- return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
- CHGR_CHG_EN,
- enable ? CHGR_CHG_EN : 0, 1);
-}
-
-static int
-qpnp_chg_force_run_on_batt(struct qpnp_chg_chip *chip, int disable)
-{
- /* Don't run on battery for batteryless hardware */
- if (chip->use_default_batt_values)
- return 0;
-
- /* This bit forces the charger to run off of the battery rather
- * than a connected charger */
- return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
- CHGR_ON_BAT_FORCE_BIT,
- disable ? CHGR_ON_BAT_FORCE_BIT : 0, 1);
-}
-
-static int
qpnp_chg_buck_control(struct qpnp_chg_chip *chip, int enable)
{
int rc;
@@ -777,17 +1049,21 @@
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CAPACITY,
- POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
};
static char *pm_power_supplied_to[] = {
"battery",
};
+static char *pm_batt_supplied_to[] = {
+ "bms",
+};
+
#define USB_WALL_THRESHOLD_MA 500
static int
qpnp_power_get_property_mains(struct power_supply *psy,
@@ -900,11 +1176,12 @@
int rc;
u8 chgr_sts;
- if (chip->chg_done)
+ if ((qpnp_chg_is_usb_chg_plugged_in(chip) ||
+ qpnp_chg_is_dc_chg_plugged_in(chip)) && chip->chg_done) {
return POWER_SUPPLY_STATUS_FULL;
+ }
- rc = qpnp_chg_read(chip, &chgr_sts,
- INT_RT_STS(chip->chgr_base), 1);
+ rc = qpnp_chg_read(chip, &chgr_sts, INT_RT_STS(chip->chgr_base), 1);
if (rc) {
pr_err("failed to read interrupt sts %d\n", rc);
return POWER_SUPPLY_CHARGE_TYPE_NONE;
@@ -917,21 +1194,6 @@
return POWER_SUPPLY_STATUS_DISCHARGING;
}
-static int
-get_prop_current_max(struct qpnp_chg_chip *chip)
-{
- union power_supply_propval ret = {0,};
-
- if (chip->bms_psy) {
- chip->bms_psy->get_property(chip->bms_psy,
- POWER_SUPPLY_PROP_CURRENT_MAX, &ret);
- return ret.intval;
- } else {
- pr_debug("No BMS supply registered return 0\n");
- }
-
- return 0;
-}
static int
get_prop_current_now(struct qpnp_chg_chip *chip)
@@ -970,7 +1232,6 @@
get_prop_capacity(struct qpnp_chg_chip *chip)
{
union power_supply_propval ret = {0,};
- bool usb_online, dc_online;
if (chip->use_default_batt_values || !get_prop_batt_present(chip))
return DEFAULT_CAPACITY;
@@ -979,11 +1240,8 @@
chip->bms_psy->get_property(chip->bms_psy,
POWER_SUPPLY_PROP_CAPACITY, &ret);
if (ret.intval == 0) {
- usb_online = chip->usb_psy->get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_ONLINE, &ret);
- dc_online = chip->dc_psy.get_property(&chip->dc_psy,
- POWER_SUPPLY_PROP_ONLINE, &ret);
- if (!usb_online && !dc_online)
+ if (!qpnp_chg_is_usb_chg_plugged_in(chip)
+ && !qpnp_chg_is_usb_chg_plugged_in(chip))
pr_warn_ratelimited("Battery 0, CHG absent\n");
}
return ret.intval;
@@ -1025,6 +1283,16 @@
return DEFAULT_TEMP;
}
+static int get_prop_cycle_count(struct qpnp_chg_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->bms_psy)
+ chip->bms_psy->get_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_CYCLE_COUNT, &ret);
+ return ret.intval;
+}
+
static void
qpnp_batt_external_power_changed(struct power_supply *psy)
{
@@ -1036,21 +1304,6 @@
chip->bms_psy = power_supply_get_by_name("bms");
chip->usb_psy->get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_SCOPE, &ret);
- if (ret.intval) {
- if ((ret.intval == POWER_SUPPLY_SCOPE_SYSTEM)
- && !qpnp_chg_is_otg_en_set(chip)) {
- switch_usb_to_host_mode(chip);
- return;
- }
- if ((ret.intval == POWER_SUPPLY_SCOPE_DEVICE)
- && qpnp_chg_is_otg_en_set(chip)) {
- switch_usb_to_charge_mode(chip);
- return;
- }
- }
-
- chip->usb_psy->get_property(chip->usb_psy,
POWER_SUPPLY_PROP_ONLINE, &ret);
/* Only honour requests while USB is present */
@@ -1059,8 +1312,8 @@
POWER_SUPPLY_PROP_CURRENT_MAX, &ret);
if (ret.intval <= 2 && !chip->use_default_batt_values &&
get_prop_batt_present(chip)) {
- qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MIN_100);
qpnp_chg_usb_suspend_enable(chip, 1);
+ qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MIN_100);
} else {
qpnp_chg_usb_suspend_enable(chip, 0);
qpnp_chg_iusbmax_set(chip, ret.intval / 1000);
@@ -1110,9 +1363,6 @@
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = get_prop_capacity(chip);
break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- val->intval = get_prop_current_max(chip);
- break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = get_prop_current_now(chip);
break;
@@ -1125,6 +1375,9 @@
case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
val->intval = chip->therm_lvl_sel;
break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ val->intval = get_prop_cycle_count(chip);
+ break;
default:
return -EINVAL;
}
@@ -1241,27 +1494,7 @@
temp = (minutes - 1)/QPNP_CHG_TCHG_STEP;
return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_TCHG_MAX,
- QPNP_CHG_I_MASK, temp, 1);
-}
-#define QPNP_CHG_VBATDET_MIN_MV 3240
-#define QPNP_CHG_VBATDET_MAX_MV 5780
-#define QPNP_CHG_VBATDET_STEP_MV 20
-static int
-qpnp_chg_vbatdet_set(struct qpnp_chg_chip *chip, int vbatdet_mv)
-{
- u8 temp;
-
- if (vbatdet_mv < QPNP_CHG_VBATDET_MIN_MV
- || vbatdet_mv > QPNP_CHG_VBATDET_MAX_MV) {
- pr_err("bad mV=%d asked to set\n", vbatdet_mv);
- return -EINVAL;
- }
- temp = (vbatdet_mv - QPNP_CHG_VBATDET_MIN_MV)
- / QPNP_CHG_VBATDET_STEP_MV;
-
- pr_debug("voltage=%d setting %02x\n", vbatdet_mv, temp);
- return qpnp_chg_write(chip, &temp,
- chip->chgr_base + CHGR_VBAT_DET, 1);
+ QPNP_CHG_TCHG_MASK, temp, 1);
}
#define QPNP_CHG_V_MIN_MV 3240
@@ -1294,13 +1527,56 @@
pr_err("bad mV=%d asked to set\n", voltage);
return -EINVAL;
}
+ chip->set_vddmax_mv = voltage + chip->delta_vddmax_mv;
- temp = (voltage - QPNP_CHG_V_MIN_MV) / QPNP_CHG_V_STEP_MV;
+ temp = (chip->set_vddmax_mv - QPNP_CHG_V_MIN_MV) / QPNP_CHG_V_STEP_MV;
- pr_debug("voltage=%d setting %02x\n", voltage, temp);
+ pr_debug("voltage=%d setting %02x\n", chip->set_vddmax_mv, temp);
return qpnp_chg_write(chip, &temp, chip->chgr_base + CHGR_VDD_MAX, 1);
}
+#define BOOST_MIN_UV 4200000
+#define BOOST_MAX_UV 5500000
+#define BOOST_STEP_UV 50000
+#define BOOST_MIN 16
+#define N_BOOST_V ((BOOST_MAX_UV - BOOST_MIN_UV) / BOOST_STEP_UV + 1)
+static int
+qpnp_boost_vset(struct qpnp_chg_chip *chip, int voltage)
+{
+ u8 reg = 0;
+
+ if (voltage < BOOST_MIN_UV || voltage > BOOST_MAX_UV) {
+ pr_err("invalid voltage requested %d uV\n", voltage);
+ return -EINVAL;
+ }
+
+ reg = DIV_ROUND_UP(voltage - BOOST_MIN_UV, BOOST_STEP_UV) + BOOST_MIN;
+
+ pr_debug("voltage=%d setting %02x\n", voltage, reg);
+ return qpnp_chg_write(chip, ®, chip->boost_base + BOOST_VSET, 1);
+}
+
+static int
+qpnp_boost_vget_uv(struct qpnp_chg_chip *chip)
+{
+ int rc;
+ u8 boost_reg;
+
+ rc = qpnp_chg_read(chip, &boost_reg,
+ chip->boost_base + BOOST_VSET, 1);
+ if (rc) {
+ pr_err("failed to read BOOST_VSET rc=%d\n", rc);
+ return rc;
+ }
+
+ if (boost_reg < BOOST_MIN) {
+ pr_err("Invalid reading from 0x%x\n", boost_reg);
+ return -EINVAL;
+ }
+
+ return BOOST_MIN_UV + ((boost_reg - BOOST_MIN) * BOOST_STEP_UV);
+}
+
/* JEITA compliance logic */
static void
qpnp_chg_set_appropriate_vddmax(struct qpnp_chg_chip *chip)
@@ -1363,6 +1639,348 @@
}
}
+/* OTG regulator operations */
+static int
+qpnp_chg_regulator_otg_enable(struct regulator_dev *rdev)
+{
+ struct qpnp_chg_chip *chip = rdev_get_drvdata(rdev);
+
+ return switch_usb_to_host_mode(chip);
+}
+
+static int
+qpnp_chg_regulator_otg_disable(struct regulator_dev *rdev)
+{
+ struct qpnp_chg_chip *chip = rdev_get_drvdata(rdev);
+
+ return switch_usb_to_charge_mode(chip);
+}
+
+static int
+qpnp_chg_regulator_otg_is_enabled(struct regulator_dev *rdev)
+{
+ struct qpnp_chg_chip *chip = rdev_get_drvdata(rdev);
+
+ return qpnp_chg_is_otg_en_set(chip);
+}
+
+static int
+qpnp_chg_regulator_boost_enable(struct regulator_dev *rdev)
+{
+ struct qpnp_chg_chip *chip = rdev_get_drvdata(rdev);
+ int rc;
+
+ if (qpnp_chg_is_usb_chg_plugged_in(chip) &&
+ (chip->flags & BOOST_FLASH_WA)) {
+ qpnp_chg_usb_suspend_enable(chip, 1);
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->usb_chgpth_base + SEC_ACCESS,
+ 0xFF,
+ 0xA5, 1);
+ if (rc) {
+ pr_err("failed to write SEC_ACCESS rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->usb_chgpth_base + COMP_OVR1,
+ 0xFF,
+ 0x2F, 1);
+ if (rc) {
+ pr_err("failed to write COMP_OVR1 rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ return qpnp_chg_masked_write(chip,
+ chip->boost_base + BOOST_ENABLE_CONTROL,
+ BOOST_PWR_EN,
+ BOOST_PWR_EN, 1);
+}
+
+/* Boost regulator operations */
+#define ABOVE_VBAT_WEAK BIT(1)
+static int
+qpnp_chg_regulator_boost_disable(struct regulator_dev *rdev)
+{
+ struct qpnp_chg_chip *chip = rdev_get_drvdata(rdev);
+ int rc;
+ u8 vbat_sts;
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->boost_base + BOOST_ENABLE_CONTROL,
+ BOOST_PWR_EN,
+ 0, 1);
+ if (rc) {
+ pr_err("failed to disable boost rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_chg_read(chip, &vbat_sts,
+ chip->chgr_base + CHGR_VBAT_STATUS, 1);
+ if (rc) {
+ pr_err("failed to read bat sts rc=%d\n", rc);
+ return rc;
+ }
+
+ if (!(vbat_sts & ABOVE_VBAT_WEAK) && (chip->flags & BOOST_FLASH_WA)) {
+ rc = qpnp_chg_masked_write(chip,
+ chip->chgr_base + SEC_ACCESS,
+ 0xFF,
+ 0xA5, 1);
+ if (rc) {
+ pr_err("failed to write SEC_ACCESS rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->chgr_base + COMP_OVR1,
+ 0xFF,
+ 0x20, 1);
+ if (rc) {
+ pr_err("failed to write COMP_OVR1 rc=%d\n", rc);
+ return rc;
+ }
+
+ usleep(2000);
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->chgr_base + SEC_ACCESS,
+ 0xFF,
+ 0xA5, 1);
+ if (rc) {
+ pr_err("failed to write SEC_ACCESS rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->chgr_base + COMP_OVR1,
+ 0xFF,
+ 0x00, 1);
+ if (rc) {
+ pr_err("failed to write COMP_OVR1 rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (qpnp_chg_is_usb_chg_plugged_in(chip)
+ && (chip->flags & BOOST_FLASH_WA)) {
+ rc = qpnp_chg_masked_write(chip,
+ chip->usb_chgpth_base + SEC_ACCESS,
+ 0xFF,
+ 0xA5, 1);
+ if (rc) {
+ pr_err("failed to write SEC_ACCESS rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->usb_chgpth_base + COMP_OVR1,
+ 0xFF,
+ 0x00, 1);
+ if (rc) {
+ pr_err("failed to write COMP_OVR1 rc=%d\n", rc);
+ return rc;
+ }
+
+ usleep(1000);
+
+ qpnp_chg_usb_suspend_enable(chip, 0);
+ }
+
+ return rc;
+}
+
+static int
+qpnp_chg_regulator_boost_is_enabled(struct regulator_dev *rdev)
+{
+ struct qpnp_chg_chip *chip = rdev_get_drvdata(rdev);
+
+ return qpnp_chg_is_boost_en_set(chip);
+}
+
+static int
+qpnp_chg_regulator_boost_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ int uV = min_uV;
+ int rc;
+ struct qpnp_chg_chip *chip = rdev_get_drvdata(rdev);
+
+ if (uV < BOOST_MIN_UV && max_uV >= BOOST_MIN_UV)
+ uV = BOOST_MIN_UV;
+
+
+ if (uV < BOOST_MIN_UV || uV > BOOST_MAX_UV) {
+ pr_err("request %d uV is out of bounds\n", uV);
+ return -EINVAL;
+ }
+
+ *selector = DIV_ROUND_UP(uV - BOOST_MIN_UV, BOOST_STEP_UV);
+ if ((*selector * BOOST_STEP_UV + BOOST_MIN_UV) > max_uV) {
+ pr_err("no available setpoint [%d, %d] uV\n", min_uV, max_uV);
+ return -EINVAL;
+ }
+
+ rc = qpnp_boost_vset(chip, uV);
+
+ return rc;
+}
+
+static int
+qpnp_chg_regulator_boost_get_voltage(struct regulator_dev *rdev)
+{
+ struct qpnp_chg_chip *chip = rdev_get_drvdata(rdev);
+
+ return qpnp_boost_vget_uv(chip);
+}
+
+static int
+qpnp_chg_regulator_boost_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ if (selector >= N_BOOST_V)
+ return 0;
+
+ return BOOST_MIN_UV + (selector * BOOST_STEP_UV);
+}
+
+static struct regulator_ops qpnp_chg_otg_reg_ops = {
+ .enable = qpnp_chg_regulator_otg_enable,
+ .disable = qpnp_chg_regulator_otg_disable,
+ .is_enabled = qpnp_chg_regulator_otg_is_enabled,
+};
+
+static struct regulator_ops qpnp_chg_boost_reg_ops = {
+ .enable = qpnp_chg_regulator_boost_enable,
+ .disable = qpnp_chg_regulator_boost_disable,
+ .is_enabled = qpnp_chg_regulator_boost_is_enabled,
+ .set_voltage = qpnp_chg_regulator_boost_set_voltage,
+ .get_voltage = qpnp_chg_regulator_boost_get_voltage,
+ .list_voltage = qpnp_chg_regulator_boost_list_voltage,
+};
+
+#define MIN_DELTA_MV_TO_INCREASE_VDD_MAX 13
+#define MAX_DELTA_VDD_MAX_MV 30
+static void
+qpnp_chg_adjust_vddmax(struct qpnp_chg_chip *chip, int vbat_mv)
+{
+ int delta_mv, closest_delta_mv, sign;
+
+ delta_mv = chip->max_voltage_mv - vbat_mv;
+ if (delta_mv > 0 && delta_mv < MIN_DELTA_MV_TO_INCREASE_VDD_MAX) {
+ pr_debug("vbat is not low enough to increase vdd\n");
+ return;
+ }
+
+ sign = delta_mv > 0 ? 1 : -1;
+ closest_delta_mv = ((delta_mv + sign * QPNP_CHG_V_STEP_MV / 2)
+ / QPNP_CHG_V_STEP_MV) * QPNP_CHG_V_STEP_MV;
+ pr_debug("max_voltage = %d, vbat_mv = %d, delta_mv = %d, closest = %d\n",
+ chip->max_voltage_mv, vbat_mv,
+ delta_mv, closest_delta_mv);
+ chip->delta_vddmax_mv = clamp(chip->delta_vddmax_mv + closest_delta_mv,
+ -MAX_DELTA_VDD_MAX_MV, MAX_DELTA_VDD_MAX_MV);
+ pr_debug("using delta_vddmax_mv = %d\n", chip->delta_vddmax_mv);
+ qpnp_chg_set_appropriate_vddmax(chip);
+}
+
+#define CONSECUTIVE_COUNT 3
+static void
+qpnp_eoc_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct qpnp_chg_chip *chip = container_of(dwork,
+ struct qpnp_chg_chip, eoc_work);
+ static int count;
+ int ibat_ma, vbat_mv, rc = 0;
+ u8 batt_sts = 0, buck_sts = 0, chg_sts = 0;
+
+ wake_lock(&chip->eoc_wake_lock);
+ qpnp_chg_charge_en(chip, !chip->charging_disabled);
+
+ rc = qpnp_chg_read(chip, &batt_sts, INT_RT_STS(chip->bat_if_base), 1);
+ if (rc) {
+ pr_err("failed to read batt_if rc=%d\n", rc);
+ return;
+ }
+
+ rc = qpnp_chg_read(chip, &buck_sts, INT_RT_STS(chip->buck_base), 1);
+ if (rc) {
+ pr_err("failed to read buck rc=%d\n", rc);
+ return;
+ }
+
+ rc = qpnp_chg_read(chip, &chg_sts, INT_RT_STS(chip->chgr_base), 1);
+ if (rc) {
+ pr_err("failed to read chg_sts rc=%d\n", rc);
+ return;
+ }
+
+ pr_debug("chgr: 0x%x, bat_if: 0x%x, buck: 0x%x\n",
+ chg_sts, batt_sts, buck_sts);
+
+ if (!qpnp_chg_is_usb_chg_plugged_in(chip) &&
+ !qpnp_chg_is_dc_chg_plugged_in(chip)) {
+ pr_debug("no chg connected, stopping\n");
+ goto stop_eoc;
+ }
+
+ if ((batt_sts & BAT_FET_ON_IRQ) && (chg_sts & FAST_CHG_ON_IRQ
+ || chg_sts & TRKL_CHG_ON_IRQ)) {
+ ibat_ma = get_prop_current_now(chip) / 1000;
+ vbat_mv = get_prop_battery_voltage_now(chip) / 1000;
+
+ pr_debug("ibat_ma = %d vbat_mv = %d term_current_ma = %d\n",
+ ibat_ma, vbat_mv, chip->term_current);
+
+ if ((!(chg_sts & VBAT_DET_LOW_IRQ)) && (vbat_mv <
+ (chip->max_voltage_mv - chip->resume_delta_mv))) {
+ pr_debug("woke up too early\n");
+ qpnp_chg_enable_irq(&chip->chg_vbatdet_lo);
+ goto stop_eoc;
+ }
+
+ if (buck_sts & VDD_LOOP_IRQ)
+ qpnp_chg_adjust_vddmax(chip, vbat_mv);
+
+ if (!(buck_sts & VDD_LOOP_IRQ)) {
+ pr_debug("Not in CV\n");
+ count = 0;
+ } else if ((ibat_ma * -1) > chip->term_current) {
+ pr_debug("Not at EOC, battery current too high\n");
+ count = 0;
+ } else if (ibat_ma > 0) {
+ pr_debug("Charging but system demand increased\n");
+ count = 0;
+ } else {
+ if (count == CONSECUTIVE_COUNT) {
+ pr_info("End of Charging\n");
+ qpnp_chg_charge_en(chip, 0);
+ chip->chg_done = true;
+ power_supply_changed(&chip->batt_psy);
+ qpnp_chg_enable_irq(&chip->chg_vbatdet_lo);
+ goto stop_eoc;
+ } else {
+ count += 1;
+ pr_debug("EOC count = %d\n", count);
+ }
+ }
+ } else {
+ pr_debug("not charging\n");
+ goto stop_eoc;
+ }
+
+ schedule_delayed_work(&chip->eoc_work,
+ msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
+ return;
+
+stop_eoc:
+ count = 0;
+ wake_unlock(&chip->eoc_wake_lock);
+}
+
#define HYSTERISIS_DECIDEGC 20
static void
qpnp_chg_adc_notification(enum qpnp_tm_state state, void *ctx)
@@ -1467,6 +2085,10 @@
{
if (chip->revision > 0 && chip->type == SMBB)
chip->flags |= CHG_FLAGS_VCP_WA;
+ if (chip->type == SMBB)
+ chip->flags |= BOOST_FLASH_WA;
+ if (chip->type == SMBBP)
+ chip->flags |= BOOST_FLASH_WA;
}
static int
@@ -1503,119 +2125,157 @@
case SMBB_CHGR_SUBTYPE:
case SMBBP_CHGR_SUBTYPE:
case SMBCL_CHGR_SUBTYPE:
- chip->chg_fastchg_irq = spmi_get_irq_byname(spmi,
+ chip->chg_fastchg.irq = spmi_get_irq_byname(spmi,
spmi_resource, "fast-chg-on");
- if (chip->chg_fastchg_irq < 0) {
+ if (chip->chg_fastchg.irq < 0) {
pr_err("Unable to get fast-chg-on irq\n");
return rc;
}
- chip->chg_trklchg_irq = spmi_get_irq_byname(spmi,
+ chip->chg_trklchg.irq = spmi_get_irq_byname(spmi,
spmi_resource, "trkl-chg-on");
- if (chip->chg_trklchg_irq < 0) {
+ if (chip->chg_trklchg.irq < 0) {
pr_err("Unable to get trkl-chg-on irq\n");
return rc;
}
- chip->chg_failed_irq = spmi_get_irq_byname(spmi,
+ chip->chg_failed.irq = spmi_get_irq_byname(spmi,
spmi_resource, "chg-failed");
- if (chip->chg_failed_irq < 0) {
+ if (chip->chg_failed.irq < 0) {
pr_err("Unable to get chg_failed irq\n");
return rc;
}
- rc |= devm_request_irq(chip->dev, chip->chg_failed_irq,
- qpnp_chg_chgr_chg_failed_irq_handler,
- IRQF_TRIGGER_RISING, "chg_failed", chip);
- if (rc < 0) {
- pr_err("Can't request %d chg_failed chg: %d\n",
- chip->chg_failed_irq, rc);
+ chip->chg_vbatdet_lo.irq = spmi_get_irq_byname(spmi,
+ spmi_resource, "vbat-det-lo");
+ if (chip->chg_vbatdet_lo.irq < 0) {
+ pr_err("Unable to get fast-chg-on irq\n");
return rc;
}
- rc |= devm_request_irq(chip->dev, chip->chg_fastchg_irq,
+ rc |= devm_request_irq(chip->dev, chip->chg_failed.irq,
+ qpnp_chg_chgr_chg_failed_irq_handler,
+ IRQF_TRIGGER_RISING, "chg-failed", chip);
+ if (rc < 0) {
+ pr_err("Can't request %d chg-failed: %d\n",
+ chip->chg_failed.irq, rc);
+ return rc;
+ }
+
+ rc |= devm_request_irq(chip->dev, chip->chg_fastchg.irq,
qpnp_chg_chgr_chg_fastchg_irq_handler,
IRQF_TRIGGER_RISING,
"fast-chg-on", chip);
if (rc < 0) {
pr_err("Can't request %d fast-chg-on: %d\n",
- chip->chg_fastchg_irq, rc);
+ chip->chg_fastchg.irq, rc);
return rc;
}
- rc |= devm_request_irq(chip->dev, chip->chg_trklchg_irq,
+ rc |= devm_request_irq(chip->dev, chip->chg_trklchg.irq,
qpnp_chg_chgr_chg_trklchg_irq_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "fast-chg-on", chip);
+ "trkl-chg-on", chip);
if (rc < 0) {
pr_err("Can't request %d trkl-chg-on: %d\n",
- chip->chg_trklchg_irq, rc);
+ chip->chg_trklchg.irq, rc);
return rc;
}
- enable_irq_wake(chip->chg_fastchg_irq);
- enable_irq_wake(chip->chg_trklchg_irq);
- enable_irq_wake(chip->chg_failed_irq);
+
+ rc |= devm_request_irq(chip->dev,
+ chip->chg_vbatdet_lo.irq,
+ qpnp_chg_vbatdet_lo_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "vbat-det-lo", chip);
+ if (rc < 0) {
+ pr_err("Can't request %d vbat-det-lo: %d\n",
+ chip->chg_vbatdet_lo.irq, rc);
+ return rc;
+ }
+
+ enable_irq_wake(chip->chg_trklchg.irq);
+ enable_irq_wake(chip->chg_failed.irq);
+ qpnp_chg_disable_irq(&chip->chg_vbatdet_lo);
+ enable_irq_wake(chip->chg_vbatdet_lo.irq);
break;
case SMBB_BAT_IF_SUBTYPE:
case SMBBP_BAT_IF_SUBTYPE:
case SMBCL_BAT_IF_SUBTYPE:
- chip->batt_pres_irq = spmi_get_irq_byname(spmi,
+ chip->batt_pres.irq = spmi_get_irq_byname(spmi,
spmi_resource, "batt-pres");
- if (chip->batt_pres_irq < 0) {
+ if (chip->batt_pres.irq < 0) {
pr_err("Unable to get batt-pres irq\n");
return rc;
}
- rc = devm_request_irq(chip->dev, chip->batt_pres_irq,
+ rc = devm_request_irq(chip->dev, chip->batt_pres.irq,
qpnp_chg_bat_if_batt_pres_irq_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "bat_if_batt_pres", chip);
+ "batt-pres", chip);
if (rc < 0) {
pr_err("Can't request %d batt-pres irq: %d\n",
- chip->batt_pres_irq, rc);
+ chip->batt_pres.irq, rc);
return rc;
}
- enable_irq_wake(chip->batt_pres_irq);
+ enable_irq_wake(chip->batt_pres.irq);
break;
case SMBB_USB_CHGPTH_SUBTYPE:
case SMBBP_USB_CHGPTH_SUBTYPE:
case SMBCL_USB_CHGPTH_SUBTYPE:
- chip->usbin_valid_irq = spmi_get_irq_byname(spmi,
+ chip->usbin_valid.irq = spmi_get_irq_byname(spmi,
spmi_resource, "usbin-valid");
- if (chip->usbin_valid_irq < 0) {
+ if (chip->usbin_valid.irq < 0) {
pr_err("Unable to get usbin irq\n");
return rc;
}
- rc = devm_request_irq(chip->dev, chip->usbin_valid_irq,
+ rc = devm_request_irq(chip->dev, chip->usbin_valid.irq,
qpnp_chg_usb_usbin_valid_irq_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "chg_usbin_valid", chip);
+ "usbin-valid", chip);
if (rc < 0) {
- pr_err("Can't request %d usbinvalid: %d\n",
- chip->usbin_valid_irq, rc);
- return rc;
- }
- enable_irq_wake(chip->usbin_valid_irq);
- break;
- case SMBB_DC_CHGPTH_SUBTYPE:
- chip->dcin_valid_irq = spmi_get_irq_byname(spmi,
- spmi_resource, "dcin-valid");
- if (chip->dcin_valid_irq < 0) {
- pr_err("Unable to get dcin irq\n");
- return -rc;
- }
- rc = devm_request_irq(chip->dev, chip->dcin_valid_irq,
- qpnp_chg_dc_dcin_valid_irq_handler,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "chg_dcin_valid", chip);
- if (rc < 0) {
- pr_err("Can't request %d dcinvalid: %d\n",
- chip->dcin_valid_irq, rc);
+ pr_err("Can't request %d usbin-valid: %d\n",
+ chip->usbin_valid.irq, rc);
return rc;
}
- enable_irq_wake(chip->dcin_valid_irq);
+ chip->chg_gone.irq = spmi_get_irq_byname(spmi,
+ spmi_resource, "chg-gone");
+ if (chip->chg_gone.irq < 0) {
+ pr_err("Unable to get chg-gone irq\n");
+ return rc;
+ }
+ rc = devm_request_irq(chip->dev, chip->chg_gone.irq,
+ qpnp_chg_usb_chg_gone_irq_handler,
+ IRQF_TRIGGER_RISING,
+ "chg-gone", chip);
+ if (rc < 0) {
+ pr_err("Can't request %d chg-gone: %d\n",
+ chip->chg_gone.irq, rc);
+ return rc;
+ }
+
+ enable_irq_wake(chip->usbin_valid.irq);
+ enable_irq_wake(chip->chg_gone.irq);
+ break;
+ case SMBB_DC_CHGPTH_SUBTYPE:
+ chip->dcin_valid.irq = spmi_get_irq_byname(spmi,
+ spmi_resource, "dcin-valid");
+ if (chip->dcin_valid.irq < 0) {
+ pr_err("Unable to get dcin irq\n");
+ return -rc;
+ }
+ rc = devm_request_irq(chip->dev, chip->dcin_valid.irq,
+ qpnp_chg_dc_dcin_valid_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "dcin-valid", chip);
+ if (rc < 0) {
+ pr_err("Can't request %d dcin-valid: %d\n",
+ chip->dcin_valid.irq, rc);
+ return rc;
+ }
+
+ enable_irq_wake(chip->dcin_valid.irq);
break;
}
}
@@ -1629,7 +2289,9 @@
struct spmi_resource *spmi_resource)
{
int rc = 0;
- u8 reg;
+ u8 reg = 0;
+ struct regulator_init_data *init_data;
+ struct regulator_desc *rdesc;
switch (subtype) {
case SMBB_CHGR_SUBTYPE:
@@ -1686,14 +2348,18 @@
/* HACK: use analog EOC */
rc = qpnp_chg_masked_write(chip, chip->chgr_base +
CHGR_IBAT_TERM_CHGR,
- 0x80, 0x00, 1);
+ 0xFF, 0x08, 1);
break;
case SMBB_BUCK_SUBTYPE:
case SMBBP_BUCK_SUBTYPE:
case SMBCL_BUCK_SUBTYPE:
+ rc = qpnp_chg_toggle_chg_done_logic(chip, 0);
+ if (rc)
+ return rc;
+
rc = qpnp_chg_masked_write(chip,
- chip->chgr_base + CHGR_BUCK_BCK_VBAT_REG_MODE,
+ chip->buck_base + CHGR_BUCK_BCK_VBAT_REG_MODE,
BUCK_VBAT_REG_NODE_SEL_BIT,
BUCK_VBAT_REG_NODE_SEL_BIT, 1);
if (rc) {
@@ -1704,12 +2370,44 @@
case SMBB_BAT_IF_SUBTYPE:
case SMBBP_BAT_IF_SUBTYPE:
case SMBCL_BAT_IF_SUBTYPE:
+ /* Select battery presence detection */
+ switch (chip->bpd_detection) {
+ case BPD_TYPE_BAT_THM:
+ reg = BAT_THM_EN;
+ break;
+ case BPD_TYPE_BAT_ID:
+ reg = BAT_ID_EN;
+ break;
+ case BPD_TYPE_BAT_THM_BAT_ID:
+ reg = BAT_THM_EN | BAT_ID_EN;
+ break;
+ default:
+ reg = BAT_THM_EN;
+ break;
+ }
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->bat_if_base + BAT_IF_BPD_CTRL,
+ BAT_IF_BPD_CTRL_SEL,
+ reg, 1);
+ if (rc) {
+ pr_debug("failed to chose BPD rc=%d\n", rc);
+ return rc;
+ }
+ /* Force on VREF_BAT_THM */
+ rc = qpnp_chg_masked_write(chip,
+ chip->bat_if_base + BAT_IF_VREF_BAT_THM_CTRL,
+ VREF_BATT_THERM_FORCE_ON,
+ VREF_BATT_THERM_FORCE_ON, 1);
+ if (rc) {
+ pr_debug("failed to force on VREF_BAT_THM rc=%d\n", rc);
+ return rc;
+ }
break;
case SMBB_USB_CHGPTH_SUBTYPE:
case SMBBP_USB_CHGPTH_SUBTYPE:
case SMBCL_USB_CHGPTH_SUBTYPE:
- chip->usb_present = qpnp_chg_is_usb_chg_plugged_in(chip);
- if (chip->usb_present) {
+ if (qpnp_chg_is_usb_chg_plugged_in(chip)) {
rc = qpnp_chg_masked_write(chip,
chip->usb_chgpth_base + CHGR_USB_ENUM_T_STOP,
ENUM_T_STOP_BIT,
@@ -1720,6 +2418,39 @@
}
}
+ init_data = of_get_regulator_init_data(chip->dev,
+ spmi_resource->of_node);
+ if (!init_data) {
+ pr_err("unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ if (init_data->constraints.name) {
+ if (of_get_property(chip->dev->of_node,
+ "otg-parent-supply", NULL))
+ init_data->supply_regulator = "otg-parent";
+
+ rdesc = &(chip->otg_vreg.rdesc);
+ rdesc->owner = THIS_MODULE;
+ rdesc->type = REGULATOR_VOLTAGE;
+ rdesc->ops = &qpnp_chg_otg_reg_ops;
+ rdesc->name = init_data->constraints.name;
+
+ init_data->constraints.valid_ops_mask
+ |= REGULATOR_CHANGE_STATUS;
+
+ chip->otg_vreg.rdev = regulator_register(rdesc,
+ chip->dev, init_data, chip,
+ spmi_resource->of_node);
+ if (IS_ERR(chip->otg_vreg.rdev)) {
+ rc = PTR_ERR(chip->otg_vreg.rdev);
+ chip->otg_vreg.rdev = NULL;
+ if (rc != -EPROBE_DEFER)
+ pr_err("OTG reg failed, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
rc = qpnp_chg_masked_write(chip,
chip->usb_chgpth_base + USB_OVP_CTL,
USB_VALID_DEB_20MS,
@@ -1730,18 +2461,65 @@
ENUM_T_STOP_BIT,
ENUM_T_STOP_BIT, 1);
+ rc = qpnp_chg_masked_write(chip,
+ chip->usb_chgpth_base + SEC_ACCESS,
+ 0xFF,
+ 0xA5, 1);
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->usb_chgpth_base + USB_CHG_GONE_REV_BST,
+ 0xFF,
+ 0x80, 1);
+
break;
case SMBB_DC_CHGPTH_SUBTYPE:
break;
case SMBB_BOOST_SUBTYPE:
case SMBBP_BOOST_SUBTYPE:
+ init_data = of_get_regulator_init_data(chip->dev,
+ spmi_resource->of_node);
+ if (!init_data) {
+ pr_err("unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ if (init_data->constraints.name) {
+ if (of_get_property(chip->dev->of_node,
+ "boost-parent-supply", NULL))
+ init_data->supply_regulator = "boost-parent";
+
+ rdesc = &(chip->boost_vreg.rdesc);
+ rdesc->owner = THIS_MODULE;
+ rdesc->type = REGULATOR_VOLTAGE;
+ rdesc->ops = &qpnp_chg_boost_reg_ops;
+ rdesc->name = init_data->constraints.name;
+
+ init_data->constraints.valid_ops_mask
+ |= REGULATOR_CHANGE_STATUS
+ | REGULATOR_CHANGE_VOLTAGE;
+
+ chip->boost_vreg.rdev = regulator_register(rdesc,
+ chip->dev, init_data, chip,
+ spmi_resource->of_node);
+ if (IS_ERR(chip->boost_vreg.rdev)) {
+ rc = PTR_ERR(chip->boost_vreg.rdev);
+ chip->boost_vreg.rdev = NULL;
+ if (rc != -EPROBE_DEFER)
+ pr_err("boost reg failed, rc=%d\n", rc);
+ return rc;
+ }
+ }
break;
case SMBB_MISC_SUBTYPE:
- chip->type = SMBB;
case SMBBP_MISC_SUBTYPE:
- chip->type = SMBBP;
case SMBCL_MISC_SUBTYPE:
- chip->type = SMBCL;
+ if (subtype == SMBB_MISC_SUBTYPE)
+ chip->type = SMBB;
+ else if (subtype == SMBBP_MISC_SUBTYPE)
+ chip->type = SMBBP;
+ else if (subtype == SMBCL_MISC_SUBTYPE)
+ chip->type = SMBCL;
+
pr_debug("Setting BOOT_DONE\n");
rc = qpnp_chg_masked_write(chip,
chip->misc_base + CHGR_MISC_BOOT_DONE,
@@ -1781,6 +2559,7 @@
qpnp_charger_read_dt_props(struct qpnp_chg_chip *chip)
{
int rc = 0;
+ const char *bpd;
OF_PROP_READ(chip, max_voltage_mv, "vddmax-mv", rc, 0);
OF_PROP_READ(chip, min_voltage_mv, "vinmin-mv", rc, 0);
@@ -1800,6 +2579,19 @@
if (rc)
return rc;
+ rc = of_property_read_string(chip->spmi->dev.of_node,
+ "qcom,bpd-detection", &bpd);
+ if (rc) {
+ /* Select BAT_THM as default BPD scheme */
+ chip->bpd_detection = BPD_TYPE_BAT_THM;
+ } else {
+ chip->bpd_detection = get_bpd(bpd);
+ if (chip->bpd_detection < 0) {
+ pr_err("failed to determine bpd schema %d\n", rc);
+ return rc;
+ }
+ }
+
/* Look up JEITA compliance parameters if cool and warm temp provided */
if (chip->cool_bat_decidegc && chip->warm_bat_decidegc) {
rc = qpnp_adc_tm_is_ready();
@@ -1820,6 +2612,18 @@
chip->charging_disabled = of_property_read_bool(chip->spmi->dev.of_node,
"qcom,charging-disabled");
+ /* Get the duty-cycle-100p property */
+ chip->duty_cycle_100p = of_property_read_bool(
+ chip->spmi->dev.of_node,
+ "qcom,duty-cycle-100p");
+ if (chip->duty_cycle_100p) {
+ rc = qpnp_buck_set_100_duty_cycle_enable(chip, 1);
+ if (rc) {
+ pr_err("failed to enable duty cycle %d\n", rc);
+ return rc;
+ }
+ }
+
/* Get the fake-batt-values property */
chip->use_default_batt_values =
of_property_read_bool(chip->spmi->dev.of_node,
@@ -1862,7 +2666,6 @@
struct qpnp_chg_chip *chip;
struct resource *resource;
struct spmi_resource *spmi_resource;
- bool present;
int rc = 0;
chip = kzalloc(sizeof *chip, GFP_KERNEL);
@@ -1886,6 +2689,39 @@
if (rc)
goto fail_chg_enable;
+ /* Check if bat_if is set in DT and make sure VADC is present */
+ spmi_for_each_container_dev(spmi_resource, spmi) {
+ if (!spmi_resource) {
+ pr_err("qpnp_chg: spmi resource absent\n");
+ rc = -ENXIO;
+ goto fail_chg_enable;
+ }
+
+ resource = spmi_get_resource(spmi, spmi_resource,
+ IORESOURCE_MEM, 0);
+ if (!(resource && resource->start)) {
+ pr_err("node %s IO resource absent!\n",
+ spmi->dev.of_node->full_name);
+ rc = -ENXIO;
+ goto fail_chg_enable;
+ }
+
+ rc = qpnp_chg_read(chip, &subtype,
+ resource->start + REG_OFFSET_PERP_SUBTYPE, 1);
+ if (rc) {
+ pr_err("Peripheral subtype read failed rc=%d\n", rc);
+ goto fail_chg_enable;
+ }
+
+ if (subtype == SMBB_BAT_IF_SUBTYPE ||
+ subtype == SMBBP_BAT_IF_SUBTYPE ||
+ subtype == SMBCL_BAT_IF_SUBTYPE){
+ rc = qpnp_vadc_is_ready();
+ if (rc)
+ goto fail_chg_enable;
+ }
+ }
+
spmi_for_each_container_dev(spmi_resource, spmi) {
if (!spmi_resource) {
pr_err("qpnp_chg: spmi resource absent\n");
@@ -1931,6 +2767,27 @@
subtype, rc);
goto fail_chg_enable;
}
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + SEC_ACCESS,
+ 0xFF,
+ 0xA5, 1);
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + BUCK_VCHG_OV,
+ 0xff,
+ 0x00, 1);
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + SEC_ACCESS,
+ 0xFF,
+ 0xA5, 1);
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + BUCK_TEST_SMBC_MODES,
+ 0xFF,
+ 0x80, 1);
+
break;
case SMBB_BAT_IF_SUBTYPE:
case SMBBP_BAT_IF_SUBTYPE:
@@ -1949,7 +2806,8 @@
chip->usb_chgpth_base = resource->start;
rc = qpnp_chg_hwinit(chip, subtype, spmi_resource);
if (rc) {
- pr_err("Failed to init subtype 0x%x rc=%d\n",
+ if (rc != -EPROBE_DEFER)
+ pr_err("Failed to init subtype 0x%x rc=%d\n",
subtype, rc);
goto fail_chg_enable;
}
@@ -1968,7 +2826,8 @@
chip->boost_base = resource->start;
rc = qpnp_chg_hwinit(chip, subtype, spmi_resource);
if (rc) {
- pr_err("Failed to init subtype 0x%x rc=%d\n",
+ if (rc != -EPROBE_DEFER)
+ pr_err("Failed to init subtype 0x%x rc=%d\n",
subtype, rc);
goto fail_chg_enable;
}
@@ -1994,18 +2853,6 @@
device_init_wakeup(&spmi->dev, 1);
if (chip->bat_if_base) {
- rc = qpnp_vadc_is_ready();
- if (rc)
- goto fail_chg_enable;
-
- /* if bms exists, notify it of the presence of the battery */
- if (!chip->bms_psy)
- chip->bms_psy = power_supply_get_by_name("bms");
- if (chip->bms_psy) {
- present = get_prop_batt_present(chip);
- power_supply_set_present(chip->bms_psy, present);
- }
-
chip->batt_psy.name = "battery";
chip->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY;
chip->batt_psy.properties = msm_batt_power_props;
@@ -2017,6 +2864,9 @@
qpnp_batt_property_is_writeable;
chip->batt_psy.external_power_changed =
qpnp_batt_external_power_changed;
+ chip->batt_psy.supplied_to = pm_batt_supplied_to;
+ chip->batt_psy.num_supplicants =
+ ARRAY_SIZE(pm_batt_supplied_to);
rc = power_supply_register(chip->dev, &chip->batt_psy);
if (rc < 0) {
@@ -2027,6 +2877,11 @@
qpnp_bat_if_adc_measure_work);
}
+ wake_lock_init(&chip->eoc_wake_lock,
+ WAKE_LOCK_SUSPEND, "qpnp-chg-eoc-lock");
+ INIT_DELAYED_WORK(&chip->eoc_work, qpnp_eoc_work);
+ INIT_DELAYED_WORK(&chip->arb_stop_work, qpnp_arb_stop_work);
+
if (chip->dc_chgpth_base) {
chip->dc_psy.name = "qpnp-dc";
chip->dc_psy.type = POWER_SUPPLY_TYPE_MAINS;
@@ -2046,9 +2901,6 @@
/* Turn on appropriate workaround flags */
qpnp_chg_setup_flags(chip);
- power_supply_set_present(chip->usb_psy,
- qpnp_chg_is_usb_chg_plugged_in(chip));
-
if (chip->maxinput_dc_ma && chip->dc_chgpth_base) {
rc = qpnp_chg_idcmax_set(chip, chip->maxinput_dc_ma);
if (rc) {
@@ -2078,6 +2930,7 @@
qpnp_chg_charge_en(chip, !chip->charging_disabled);
qpnp_chg_force_run_on_batt(chip, chip->charging_disabled);
+ qpnp_chg_set_appropriate_vddmax(chip);
rc = qpnp_chg_request_irqs(chip);
if (rc) {
@@ -2085,8 +2938,18 @@
goto unregister_batt;
}
- pr_info("success chg_dis = %d, usb = %d, dc = %d b_health = %d batt_present = %d\n",
+ qpnp_chg_usb_usbin_valid_irq_handler(USBIN_VALID_IRQ, chip);
+ power_supply_set_present(chip->usb_psy,
+ qpnp_chg_is_usb_chg_plugged_in(chip));
+
+ /* Set USB psy online to avoid userspace from shutting down if battery
+ * capacity is at zero and no chargers online. */
+ if (qpnp_chg_is_usb_chg_plugged_in(chip))
+ power_supply_set_online(chip->usb_psy, 1);
+
+ pr_info("success chg_dis = %d, bpd = %d, usb = %d, dc = %d b_health = %d batt_present = %d\n",
chip->charging_disabled,
+ chip->bpd_detection,
qpnp_chg_is_usb_chg_plugged_in(chip),
qpnp_chg_is_dc_chg_plugged_in(chip),
get_prop_batt_present(chip),
@@ -2097,6 +2960,8 @@
if (chip->bat_if_base)
power_supply_unregister(&chip->batt_psy);
fail_chg_enable:
+ regulator_unregister(chip->otg_vreg.rdev);
+ regulator_unregister(chip->boost_vreg.rdev);
kfree(chip->thermal_mitigation);
kfree(chip);
dev_set_drvdata(&spmi->dev, NULL);
@@ -2112,6 +2977,10 @@
qpnp_adc_tm_disable_chan_meas(&chip->adc_param);
}
cancel_work_sync(&chip->adc_measure_work);
+ cancel_delayed_work_sync(&chip->eoc_work);
+
+ regulator_unregister(chip->otg_vreg.rdev);
+ regulator_unregister(chip->boost_vreg.rdev);
dev_set_drvdata(&spmi->dev, NULL);
kfree(chip);
@@ -2119,13 +2988,53 @@
return 0;
}
+static int qpnp_chg_resume(struct device *dev)
+{
+ struct qpnp_chg_chip *chip = dev_get_drvdata(dev);
+ int rc = 0;
+
+ if (chip->bat_if_base) {
+ rc = qpnp_chg_masked_write(chip,
+ chip->bat_if_base + BAT_IF_VREF_BAT_THM_CTRL,
+ VREF_BATT_THERM_FORCE_ON,
+ VREF_BATT_THERM_FORCE_ON, 1);
+ if (rc)
+ pr_debug("failed to force on VREF_BAT_THM rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+static int qpnp_chg_suspend(struct device *dev)
+{
+ struct qpnp_chg_chip *chip = dev_get_drvdata(dev);
+ int rc = 0;
+
+ if (chip->bat_if_base) {
+ rc = qpnp_chg_masked_write(chip,
+ chip->bat_if_base + BAT_IF_VREF_BAT_THM_CTRL,
+ VREF_BATT_THERM_FORCE_ON,
+ VREF_BAT_THM_ENABLED_FSM, 1);
+ if (rc)
+ pr_debug("failed to set FSM VREF_BAT_THM rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+static const struct dev_pm_ops qpnp_chg_pm_ops = {
+ .resume = qpnp_chg_resume,
+ .suspend = qpnp_chg_suspend,
+};
+
static struct spmi_driver qpnp_charger_driver = {
.probe = qpnp_charger_probe,
.remove = __devexit_p(qpnp_charger_remove),
.driver = {
- .name = QPNP_CHARGER_DEV_NAME,
- .owner = THIS_MODULE,
- .of_match_table = qpnp_charger_match_table,
+ .name = QPNP_CHARGER_DEV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = qpnp_charger_match_table,
+ .pm = &qpnp_chg_pm_ops,
},
};
diff --git a/drivers/power/smb350_charger.c b/drivers/power/smb350_charger.c
index 21d7aea..11fff43 100644
--- a/drivers/power/smb350_charger.c
+++ b/drivers/power/smb350_charger.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013 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
@@ -263,6 +263,9 @@
if (!chg_enabled) {
pr_warn("Charging not enabled.\n");
+ /* release the wake-lock when DC power removed */
+ if (wake_lock_active(&dev->chg_wake_lock))
+ wake_unlock(&dev->chg_wake_lock);
return POWER_SUPPLY_CHARGE_TYPE_NONE;
}
diff --git a/drivers/regulator/qpnp-regulator.c b/drivers/regulator/qpnp-regulator.c
index 2d10f89..c9d0500 100644
--- a/drivers/regulator/qpnp-regulator.c
+++ b/drivers/regulator/qpnp-regulator.c
@@ -514,8 +514,10 @@
{
struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
- if (vreg->ocp_irq)
+ if (vreg->ocp_irq) {
+ vreg->ocp_count = 0;
vreg->vs_enable_time = ktime_get();
+ }
return qpnp_regulator_common_enable(rdev);
}
diff --git a/drivers/rtc/alarm-dev.c b/drivers/rtc/alarm-dev.c
index bfcaebc..1d60e97 100644
--- a/drivers/rtc/alarm-dev.c
+++ b/drivers/rtc/alarm-dev.c
@@ -98,6 +98,8 @@
wake_unlock(&alarm_wake_lock);
}
alarm_enabled &= ~alarm_type_mask;
+ if (alarm_type == ANDROID_ALARM_RTC_WAKEUP)
+ set_power_on_alarm(0);
spin_unlock_irqrestore(&alarm_slock, flags);
break;
@@ -125,6 +127,10 @@
alarm_start_range(&alarms[alarm_type],
timespec_to_ktime(new_alarm_time),
timespec_to_ktime(new_alarm_time));
+ if ((alarm_type == ANDROID_ALARM_RTC_WAKEUP) &&
+ (ANDROID_ALARM_BASE_CMD(cmd) ==
+ ANDROID_ALARM_SET(0)))
+ set_power_on_alarm(new_alarm_time.tv_sec);
spin_unlock_irqrestore(&alarm_slock, flags);
if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0)
&& cmd != ANDROID_ALARM_SET_AND_WAIT_OLD)
diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c
index 9340af7..e318ecf 100644
--- a/drivers/rtc/alarm.c
+++ b/drivers/rtc/alarm.c
@@ -68,6 +68,13 @@
static struct platform_device *alarm_platform_dev;
struct alarm_queue alarms[ANDROID_ALARM_TYPE_COUNT];
static bool suspended;
+static long power_on_alarm;
+
+void set_power_on_alarm(long secs)
+{
+ power_on_alarm = secs;
+}
+
static void update_timer_locked(struct alarm_queue *base, bool head_removed)
{
@@ -486,6 +493,45 @@
return 0;
}
+static void alarm_shutdown(struct platform_device *dev)
+{
+ struct timespec wall_time;
+ struct rtc_time rtc_time;
+ struct rtc_wkalrm alarm;
+ unsigned long flags;
+ long rtc_secs, alarm_delta, alarm_time;
+ int rc;
+
+ spin_lock_irqsave(&alarm_slock, flags);
+
+ if (!power_on_alarm)
+ goto disable_alarm;
+
+ rtc_read_time(alarm_rtc_dev, &rtc_time);
+ getnstimeofday(&wall_time);
+ rtc_tm_to_time(&rtc_time, &rtc_secs);
+ alarm_delta = wall_time.tv_sec - rtc_secs;
+ alarm_time = power_on_alarm - alarm_delta;
+ if (alarm_time <= rtc_secs)
+ goto disable_alarm;
+
+ rtc_time_to_tm(alarm_time, &alarm.time);
+ alarm.enabled = 1;
+ rc = rtc_set_alarm(alarm_rtc_dev, &alarm);
+ if (rc)
+ pr_alarm(ERROR, "Unable to set power-on alarm\n");
+ else
+ pr_alarm(FLOW, "Power-on alarm set to %lu\n",
+ alarm_time);
+
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ return;
+
+disable_alarm:
+ rtc_alarm_irq_enable(alarm_rtc_dev, 0);
+ spin_unlock_irqrestore(&alarm_slock, flags);
+}
+
static struct rtc_task alarm_rtc_task = {
.func = alarm_triggered_func
};
@@ -545,6 +591,7 @@
static struct platform_driver alarm_driver = {
.suspend = alarm_suspend,
.resume = alarm_resume,
+ .shutdown = alarm_shutdown,
.driver = {
.name = "alarm"
}
diff --git a/drivers/rtc/qpnp-rtc.c b/drivers/rtc/qpnp-rtc.c
index 6d8985e..e0bffb9 100644
--- a/drivers/rtc/qpnp-rtc.c
+++ b/drivers/rtc/qpnp-rtc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-13, 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
@@ -503,12 +503,17 @@
}
}
- rtc_dd->rtc_ctrl_reg = BIT_RTC_ENABLE;
- rc = qpnp_write_wrapper(rtc_dd, &rtc_dd->rtc_ctrl_reg,
+ rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->rtc_ctrl_reg,
rtc_dd->rtc_base + REG_OFFSET_RTC_CTRL, 1);
if (rc) {
dev_err(&spmi->dev,
- "Write to RTC control reg failed\n");
+ "Read from RTC control reg failed\n");
+ goto fail_rtc_enable;
+ }
+
+ if (!(rtc_dd->rtc_ctrl_reg & BIT_RTC_ENABLE)) {
+ dev_err(&spmi->dev,
+ "RTC h/w disabled, rtc not registered\n");
goto fail_rtc_enable;
}
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 139bc06..086ff03 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -36,10 +36,20 @@
#ifndef _UFS_H
#define _UFS_H
+#include <linux/mutex.h>
+#include <linux/types.h>
+
#define MAX_CDB_SIZE 16
+#define GENERAL_UPIU_REQUEST_SIZE 32
+#define UPIU_HEADER_DATA_SEGMENT_MAX_SIZE ((ALIGNED_UPIU_SIZE) - \
+ (GENERAL_UPIU_REQUEST_SIZE))
+#define QUERY_OSF_SIZE ((GENERAL_UPIU_REQUEST_SIZE) - \
+ (sizeof(struct utp_upiu_header)))
+#define UFS_QUERY_RESERVED_SCSI_CMD 0xCC
+#define UFS_QUERY_CMD_SIZE 10
#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
- ((byte3 << 24) | (byte2 << 16) |\
+ cpu_to_be32((byte3 << 24) | (byte2 << 16) |\
(byte1 << 8) | (byte0))
/*
@@ -62,7 +72,7 @@
UPIU_TRANSACTION_COMMAND = 0x01,
UPIU_TRANSACTION_DATA_OUT = 0x02,
UPIU_TRANSACTION_TASK_REQ = 0x04,
- UPIU_TRANSACTION_QUERY_REQ = 0x26,
+ UPIU_TRANSACTION_QUERY_REQ = 0x16,
};
/* UTP UPIU Transaction Codes Target to Initiator */
@@ -73,6 +83,7 @@
UPIU_TRANSACTION_TASK_RSP = 0x24,
UPIU_TRANSACTION_READY_XFER = 0x31,
UPIU_TRANSACTION_QUERY_RSP = 0x36,
+ UPIU_TRANSACTION_REJECT_UPIU = 0x3F,
};
/* UPIU Read/Write flags */
@@ -90,6 +101,12 @@
UPIU_TASK_ATTR_ACA = 0x03,
};
+/* UPIU Query request function */
+enum {
+ UPIU_QUERY_FUNC_STANDARD_READ_REQUEST = 0x01,
+ UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81,
+};
+
/* UTP QUERY Transaction Specific Fields OpCode */
enum {
UPIU_QUERY_OPCODE_NOP = 0x0,
@@ -103,6 +120,21 @@
UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8,
};
+/* Query response result code */
+enum {
+ QUERY_RESULT_SUCCESS = 0x00,
+ QUERY_RESULT_NOT_READABLE = 0xF6,
+ QUERY_RESULT_NOT_WRITEABLE = 0xF7,
+ QUERY_RESULT_ALREADY_WRITTEN = 0xF8,
+ QUERY_RESULT_INVALID_LENGTH = 0xF9,
+ QUERY_RESULT_INVALID_VALUE = 0xFA,
+ QUERY_RESULT_INVALID_SELECTOR = 0xFB,
+ QUERY_RESULT_INVALID_INDEX = 0xFC,
+ QUERY_RESULT_INVALID_IDN = 0xFD,
+ QUERY_RESULT_INVALID_OPCODE = 0xFE,
+ QUERY_RESULT_GENERAL_FAILURE = 0xFF,
+};
+
/* UTP Transfer Request Command Type (CT) */
enum {
UPIU_COMMAND_SET_TYPE_SCSI = 0x0,
@@ -110,10 +142,17 @@
UPIU_COMMAND_SET_TYPE_QUERY = 0x2,
};
+/* UTP Transfer Request Command Offset */
+#define UPIU_COMMAND_TYPE_OFFSET 28
+
+/* Offset of the response code in the UPIU header */
+#define UPIU_RSP_CODE_OFFSET 8
+
enum {
MASK_SCSI_STATUS = 0xFF,
MASK_TASK_RESPONSE = 0xFF00,
MASK_RSP_UPIU_RESULT = 0xFFFF,
+ MASK_QUERY_DATA_SEG_LEN = 0xFFFF,
};
/* Task management service response */
@@ -138,26 +177,59 @@
/**
* struct utp_upiu_cmd - Command UPIU structure
- * @header: UPIU header structure DW-0 to DW-2
* @data_transfer_len: Data Transfer Length DW-3
* @cdb: Command Descriptor Block CDB DW-4 to DW-7
*/
struct utp_upiu_cmd {
- struct utp_upiu_header header;
u32 exp_data_transfer_len;
u8 cdb[MAX_CDB_SIZE];
};
/**
- * struct utp_upiu_rsp - Response UPIU structure
- * @header: UPIU header DW-0 to DW-2
+ * struct utp_upiu_query - upiu request buffer structure for
+ * query request.
+ * @opcode: command to perform B-0
+ * @idn: a value that indicates the particular type of data B-1
+ * @index: Index to further identify data B-2
+ * @selector: Index to further identify data B-3
+ * @reserved_osf: spec reserved field B-4,5
+ * @length: number of descriptor bytes to read/write B-6,7
+ * @value: Attribute value to be written DW-6
+ * @reserved: spec reserved DW-7,8
+ */
+struct utp_upiu_query {
+ u8 opcode;
+ u8 idn;
+ u8 index;
+ u8 selector;
+ u16 reserved_osf;
+ u16 length;
+ u32 value;
+ u32 reserved[2];
+};
+
+/**
+ * struct utp_upiu_req - general upiu request structure
+ * @header:UPIU header structure DW-0 to DW-2
+ * @sc: fields structure for scsi command
+ * @qr: fields structure for query request
+ */
+struct utp_upiu_req {
+ struct utp_upiu_header header;
+ union {
+ struct utp_upiu_cmd sc;
+ struct utp_upiu_query qr;
+ };
+};
+
+/**
+ * struct utp_cmd_rsp - Response UPIU structure
* @residual_transfer_count: Residual transfer count DW-3
* @reserved: Reserved double words DW-4 to DW-7
* @sense_data_len: Sense data length DW-8 U16
* @sense_data: Sense data field DW-8 to DW-12
*/
-struct utp_upiu_rsp {
- struct utp_upiu_header header;
+struct utp_cmd_rsp {
u32 residual_transfer_count;
u32 reserved[4];
u16 sense_data_len;
@@ -165,6 +237,20 @@
};
/**
+ * struct utp_upiu_rsp - general upiu response structure
+ * @header: UPIU header structure DW-0 to DW-2
+ * @sc: fields structure for scsi command
+ * @qr: fields structure for query request
+ */
+struct utp_upiu_rsp {
+ struct utp_upiu_header header;
+ union {
+ struct utp_cmd_rsp sc;
+ struct utp_upiu_query qr;
+ };
+};
+
+/**
* struct utp_upiu_task_req - Task request UPIU structure
* @header - UPIU header structure DW0 to DW-2
* @input_param1: Input parameter 1 DW-3
@@ -194,4 +280,24 @@
u32 reserved[3];
};
+/**
+ * struct ufs_query_req - parameters for building a query request
+ * @query_func: UPIU header query function
+ * @upiu_req: the query request data
+ */
+struct ufs_query_req {
+ u8 query_func;
+ struct utp_upiu_query upiu_req;
+};
+
+/**
+ * struct ufs_query_resp - UPIU QUERY
+ * @response: device response code
+ * @upiu_res: query response data
+ */
+struct ufs_query_res {
+ u8 response;
+ struct utp_upiu_query upiu_res;
+};
+
#endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index c32a478..5f21c7a 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -202,18 +202,13 @@
}
/**
- * ufshcd_is_valid_req_rsp - checks if controller TR response is valid
+ * ufshcd_get_req_rsp - returns the TR response
* @ucd_rsp_ptr: pointer to response UPIU
- *
- * This function checks the response UPIU for valid transaction type in
- * response field
- * Returns 0 on success, non-zero on failure
*/
static inline int
-ufshcd_is_valid_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr)
+ufshcd_get_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr)
{
- return ((be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24) ==
- UPIU_TRANSACTION_RESPONSE) ? 0 : DID_ERROR << 16;
+ return be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24;
}
/**
@@ -316,14 +311,71 @@
{
int len;
if (lrbp->sense_buffer) {
- len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
+ len = be16_to_cpu(lrbp->ucd_rsp_ptr->sc.sense_data_len);
memcpy(lrbp->sense_buffer,
- lrbp->ucd_rsp_ptr->sense_data,
+ lrbp->ucd_rsp_ptr->sc.sense_data,
min_t(int, len, SCSI_SENSE_BUFFERSIZE));
}
}
/**
+ * ufshcd_query_to_cpu() - formats the received buffer in to the native cpu
+ * endian
+ * @response: upiu query response to convert
+ */
+static inline void ufshcd_query_to_cpu(struct utp_upiu_query *response)
+{
+ response->length = be16_to_cpu(response->length);
+ response->value = be32_to_cpu(response->value);
+}
+
+/**
+ * ufshcd_query_to_be() - formats the buffer before sending in to big endian
+ * @response: upiu query request to convert
+ */
+static inline void ufshcd_query_to_be(struct utp_upiu_query *request)
+{
+ request->length = cpu_to_be16(request->length);
+ request->value = cpu_to_be32(request->value);
+}
+
+/**
+ * ufshcd_copy_query_response() - Copy Query Response and descriptor
+ * @lrb - pointer to local reference block
+ * @query_res - pointer to the query result
+ */
+static
+void ufshcd_copy_query_response(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
+{
+ struct ufs_query_res *query_res = hba->query.response;
+
+ /* Get the UPIU response */
+ if (query_res) {
+ query_res->response = ufshcd_get_rsp_upiu_result(
+ lrbp->ucd_rsp_ptr) >> UPIU_RSP_CODE_OFFSET;
+
+ memcpy(&query_res->upiu_res, &lrbp->ucd_rsp_ptr->qr,
+ QUERY_OSF_SIZE);
+ ufshcd_query_to_cpu(&query_res->upiu_res);
+ }
+
+ /* Get the descriptor */
+ if (hba->query.descriptor && lrbp->ucd_rsp_ptr->qr.opcode ==
+ UPIU_QUERY_OPCODE_READ_DESC) {
+ u8 *descp = (u8 *)&lrbp->ucd_rsp_ptr +
+ GENERAL_UPIU_REQUEST_SIZE;
+ u16 len;
+
+ /* data segment length */
+ len = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) &
+ MASK_QUERY_DATA_SEG_LEN;
+
+ memcpy(hba->query.descriptor, descp,
+ min_t(u16, len, UPIU_HEADER_DATA_SEGMENT_MAX_SIZE));
+ }
+}
+
+/**
* ufshcd_hba_capabilities - Read controller capabilities
* @hba: per adapter instance
*/
@@ -423,76 +475,154 @@
}
/**
+ * ufshcd_prepare_req_desc - Fills the requests header
+ * descriptor according to request
+ * lrbp: pointer to local reference block
+ * upiu_flags: flags required in the header
+ */
+static void ufshcd_prepare_req_desc(struct ufshcd_lrb *lrbp, u32 *upiu_flags)
+{
+ struct utp_transfer_req_desc *req_desc = lrbp->utr_descriptor_ptr;
+ enum dma_data_direction cmd_dir =
+ lrbp->cmd->sc_data_direction;
+ u32 data_direction;
+ u32 dword_0;
+
+ if (cmd_dir == DMA_FROM_DEVICE) {
+ data_direction = UTP_DEVICE_TO_HOST;
+ *upiu_flags = UPIU_CMD_FLAGS_READ;
+ } else if (cmd_dir == DMA_TO_DEVICE) {
+ data_direction = UTP_HOST_TO_DEVICE;
+ *upiu_flags = UPIU_CMD_FLAGS_WRITE;
+ } else {
+ data_direction = UTP_NO_DATA_TRANSFER;
+ *upiu_flags = UPIU_CMD_FLAGS_NONE;
+ }
+
+ dword_0 = data_direction | (lrbp->command_type
+ << UPIU_COMMAND_TYPE_OFFSET);
+
+ /* Transfer request descriptor header fields */
+ req_desc->header.dword_0 = cpu_to_le32(dword_0);
+
+ /*
+ * assigning invalid value for command status. Controller
+ * updates OCS on command completion, with the command
+ * status
+ */
+ req_desc->header.dword_2 =
+ cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
+}
+
+static inline bool ufshcd_is_query_req(struct ufshcd_lrb *lrbp)
+{
+ return lrbp->cmd ? lrbp->cmd->cmnd[0] == UFS_QUERY_RESERVED_SCSI_CMD :
+ false;
+}
+
+/**
+ * ufshcd_prepare_utp_scsi_cmd_upiu() - fills the utp_transfer_req_desc,
+ * for scsi commands
+ * @lrbp - local reference block pointer
+ * @upiu_flags - flags
+ */
+static
+void ufshcd_prepare_utp_scsi_cmd_upiu(struct ufshcd_lrb *lrbp, u32 upiu_flags)
+{
+ struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
+
+ /* command descriptor fields */
+ ucd_req_ptr->header.dword_0 = UPIU_HEADER_DWORD(
+ UPIU_TRANSACTION_COMMAND, upiu_flags,
+ lrbp->lun, lrbp->task_tag);
+ ucd_req_ptr->header.dword_1 = UPIU_HEADER_DWORD(
+ UPIU_COMMAND_SET_TYPE_SCSI, 0, 0, 0);
+
+ /* Total EHS length and Data segment length will be zero */
+ ucd_req_ptr->header.dword_2 = 0;
+
+ ucd_req_ptr->sc.exp_data_transfer_len =
+ cpu_to_be32(lrbp->cmd->sdb.length);
+
+ memcpy(ucd_req_ptr->sc.cdb, lrbp->cmd->cmnd,
+ (min_t(unsigned short, lrbp->cmd->cmd_len, MAX_CDB_SIZE)));
+}
+
+/**
+ * ufshcd_prepare_utp_query_req_upiu() - fills the utp_transfer_req_desc,
+ * for query requsts
+ * @hba: UFS hba
+ * @lrbp: local reference block pointer
+ * @upiu_flags: flags
+ */
+static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba,
+ struct ufshcd_lrb *lrbp,
+ u32 upiu_flags)
+{
+ struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
+ u16 len = hba->query.request->upiu_req.length;
+ u8 *descp = (u8 *)lrbp->ucd_req_ptr + GENERAL_UPIU_REQUEST_SIZE;
+
+ /* Query request header */
+ ucd_req_ptr->header.dword_0 = UPIU_HEADER_DWORD(
+ UPIU_TRANSACTION_QUERY_REQ, upiu_flags,
+ lrbp->lun, lrbp->task_tag);
+ ucd_req_ptr->header.dword_1 = UPIU_HEADER_DWORD(
+ 0, hba->query.request->query_func, 0, 0);
+
+ /* Data segment length */
+ ucd_req_ptr->header.dword_2 = UPIU_HEADER_DWORD(
+ 0, 0, len >> 8, (u8)len);
+
+ /* Copy the Query Request buffer as is */
+ memcpy(&lrbp->ucd_req_ptr->qr, &hba->query.request->upiu_req,
+ QUERY_OSF_SIZE);
+ ufshcd_query_to_be(&lrbp->ucd_req_ptr->qr);
+
+ /* Copy the Descriptor */
+ if (hba->query.descriptor != NULL && len > 0 &&
+ (hba->query.request->upiu_req.opcode ==
+ UPIU_QUERY_OPCODE_WRITE_DESC)) {
+ memcpy(descp, hba->query.descriptor,
+ min_t(u16, len, UPIU_HEADER_DATA_SEGMENT_MAX_SIZE));
+ }
+
+}
+
+/**
* ufshcd_compose_upiu - form UFS Protocol Information Unit(UPIU)
+ * @hba - UFS hba
* @lrb - pointer to local reference block
*/
-static void ufshcd_compose_upiu(struct ufshcd_lrb *lrbp)
+static int ufshcd_compose_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
{
- struct utp_transfer_req_desc *req_desc;
- struct utp_upiu_cmd *ucd_cmd_ptr;
- u32 data_direction;
u32 upiu_flags;
-
- ucd_cmd_ptr = lrbp->ucd_cmd_ptr;
- req_desc = lrbp->utr_descriptor_ptr;
+ int ret = 0;
switch (lrbp->command_type) {
case UTP_CMD_TYPE_SCSI:
- if (lrbp->cmd->sc_data_direction == DMA_FROM_DEVICE) {
- data_direction = UTP_DEVICE_TO_HOST;
- upiu_flags = UPIU_CMD_FLAGS_READ;
- } else if (lrbp->cmd->sc_data_direction == DMA_TO_DEVICE) {
- data_direction = UTP_HOST_TO_DEVICE;
- upiu_flags = UPIU_CMD_FLAGS_WRITE;
- } else {
- data_direction = UTP_NO_DATA_TRANSFER;
- upiu_flags = UPIU_CMD_FLAGS_NONE;
- }
-
- /* Transfer request descriptor header fields */
- req_desc->header.dword_0 =
- cpu_to_le32(data_direction | UTP_SCSI_COMMAND);
-
- /*
- * assigning invalid value for command status. Controller
- * updates OCS on command completion, with the command
- * status
- */
- req_desc->header.dword_2 =
- cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
-
- /* command descriptor fields */
- ucd_cmd_ptr->header.dword_0 =
- cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_COMMAND,
- upiu_flags,
- lrbp->lun,
- lrbp->task_tag));
- ucd_cmd_ptr->header.dword_1 =
- cpu_to_be32(
- UPIU_HEADER_DWORD(UPIU_COMMAND_SET_TYPE_SCSI,
- 0,
- 0,
- 0));
-
- /* Total EHS length and Data segment length will be zero */
- ucd_cmd_ptr->header.dword_2 = 0;
-
- ucd_cmd_ptr->exp_data_transfer_len =
- cpu_to_be32(lrbp->cmd->sdb.length);
-
- memcpy(ucd_cmd_ptr->cdb,
- lrbp->cmd->cmnd,
- (min_t(unsigned short,
- lrbp->cmd->cmd_len,
- MAX_CDB_SIZE)));
- break;
case UTP_CMD_TYPE_DEV_MANAGE:
- /* For query function implementation */
+ ufshcd_prepare_req_desc(lrbp, &upiu_flags);
+ if (lrbp->command_type == UTP_CMD_TYPE_SCSI)
+ ufshcd_prepare_utp_scsi_cmd_upiu(lrbp, upiu_flags);
+ else
+ ufshcd_prepare_utp_query_req_upiu(hba, lrbp,
+ upiu_flags);
break;
case UTP_CMD_TYPE_UFS:
/* For UFS native command implementation */
+ dev_err(hba->dev, "%s: UFS native command are not supported\n",
+ __func__);
+ ret = -ENOTSUPP;
+ break;
+ default:
+ ret = -ENOTSUPP;
+ dev_err(hba->dev, "%s: unknown command type: 0x%x\n",
+ __func__, lrbp->command_type);
break;
} /* end of switch */
+
+ return ret;
}
/**
@@ -527,10 +657,13 @@
lrbp->task_tag = tag;
lrbp->lun = cmd->device->lun;
- lrbp->command_type = UTP_CMD_TYPE_SCSI;
+ if (ufshcd_is_query_req(lrbp))
+ lrbp->command_type = UTP_CMD_TYPE_DEV_MANAGE;
+ else
+ lrbp->command_type = UTP_CMD_TYPE_SCSI;
/* form UPIU before issuing the command */
- ufshcd_compose_upiu(lrbp);
+ ufshcd_compose_upiu(hba, lrbp);
err = ufshcd_map_sg(lrbp);
if (err)
goto out;
@@ -544,6 +677,109 @@
}
/**
+ * ufshcd_query_request() - Entry point for issuing query request to a
+ * ufs device.
+ * @hba: ufs driver context
+ * @query: params for query request
+ * @descriptor: buffer for sending/receiving descriptor
+ * @response: pointer to a buffer that will contain the response code and
+ * response upiu
+ * @timeout: time limit for the command in seconds
+ * @retries: number of times to try executing the command
+ *
+ * The query request is submitted to the same request queue as the rest of
+ * the scsi commands passed to the UFS controller. In order to use this
+ * queue, we need to receive a tag, same as all other commands. The tags
+ * are issued from the block layer. To simulate a request from the block
+ * layer, we use the same interface as the SCSI layer does when it issues
+ * commands not generated by users. To distinguish a query request from
+ * the SCSI commands, we use a vendor specific unused SCSI command
+ * op-code. This op-code is not part of the SCSI command subset used in
+ * UFS. In such way it is easy to check the command in the driver and
+ * handle it appropriately.
+ *
+ * All necessary fields for issuing a query and receiving its response are
+ * stored in the UFS hba struct. We can use this method since we know
+ * there is only one active query request at all times.
+ *
+ * The request that will pass to the device is stored in "query" argument
+ * passed to this function, while the "response" argument (which is output
+ * field) will hold the query response from the device along with the
+ * response code.
+ */
+int ufshcd_query_request(struct ufs_hba *hba,
+ struct ufs_query_req *query,
+ u8 *descriptor,
+ struct ufs_query_res *response,
+ int timeout,
+ int retries)
+{
+ struct scsi_device *sdev;
+ u8 cmd[UFS_QUERY_CMD_SIZE] = {0};
+ int result;
+ bool sdev_lookup = true;
+
+ if (!hba || !query || !response) {
+ pr_err("%s: NULL pointer hba = %p, query = %p response = %p\n",
+ __func__, hba, query, response);
+ return -EINVAL;
+ }
+
+ /*
+ * A SCSI command structure is composed from opcode at the
+ * begining and 0 at the end.
+ */
+ cmd[0] = UFS_QUERY_RESERVED_SCSI_CMD;
+
+ /* extracting the SCSI Device */
+ sdev = scsi_device_lookup(hba->host, 0, 0, 0);
+ if (!sdev) {
+ /**
+ * There are some Query Requests that are sent during device
+ * initialization, this happens before the scsi device was
+ * initialized. If there is no scsi device, we generate a
+ * temporary device to allow the Query Request flow.
+ */
+ sdev_lookup = false;
+ sdev = scsi_get_host_dev(hba->host);
+ }
+
+ if (!sdev) {
+ dev_err(hba->dev, "%s: Could not fetch scsi device\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&hba->query.lock_ufs_query);
+ hba->query.request = query;
+ hba->query.descriptor = descriptor;
+ hba->query.response = response;
+
+ /* wait until request is completed */
+ result = scsi_execute(sdev, cmd, DMA_NONE, NULL, 0, NULL,
+ timeout, retries, 0, NULL);
+ if (result) {
+ dev_err(hba->dev,
+ "%s: Query with opcode 0x%x, failed with result %d\n",
+ __func__, query->upiu_req.opcode, result);
+ result = -EIO;
+ }
+
+ hba->query.request = NULL;
+ hba->query.descriptor = NULL;
+ hba->query.response = NULL;
+ mutex_unlock(&hba->query.lock_ufs_query);
+
+ /* Releasing scsi device resource */
+ if (sdev_lookup)
+ scsi_device_put(sdev);
+ else
+ scsi_free_host_dev(sdev);
+
+ return result;
+}
+
+/**
* ufshcd_memory_alloc - allocate memory for host memory space data structures
* @hba: per adapter instance
*
@@ -677,8 +913,8 @@
cpu_to_le16(ALIGNED_UPIU_SIZE);
hba->lrb[i].utr_descriptor_ptr = (utrdlp + i);
- hba->lrb[i].ucd_cmd_ptr =
- (struct utp_upiu_cmd *)(cmd_descp + i);
+ hba->lrb[i].ucd_req_ptr =
+ (struct utp_upiu_req *)(cmd_descp + i);
hba->lrb[i].ucd_rsp_ptr =
(struct utp_upiu_rsp *)cmd_descp[i].response_upiu;
hba->lrb[i].ucd_prdt_ptr =
@@ -1101,7 +1337,9 @@
* @hba: per adapter instance
* @lrb: pointer to local reference block of completed command
*
- * Returns result of the command to notify SCSI midlayer
+ * Returns result of the command to notify SCSI midlayer. In
+ * case of query request specific result, returns DID_OK, and
+ * the error will be handled by the dispatcher.
*/
static inline int
ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
@@ -1115,27 +1353,46 @@
switch (ocs) {
case OCS_SUCCESS:
-
/* check if the returned transfer response is valid */
- result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);
- if (result) {
- dev_err(hba->dev,
- "Invalid response = %x\n", result);
+ result = ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr);
+
+ switch (result) {
+ case UPIU_TRANSACTION_RESPONSE:
+ /*
+ * get the response UPIU result to extract
+ * the SCSI command status
+ */
+ result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
+
+ /*
+ * get the result based on SCSI status response
+ * to notify the SCSI midlayer of the command status
+ */
+ scsi_status = result & MASK_SCSI_STATUS;
+ result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
break;
+ case UPIU_TRANSACTION_QUERY_RSP:
+ /*
+ * Return result = ok, since SCSI layer wouldn't
+ * know how to handle errors from query requests.
+ * The result is saved with the response so that
+ * the ufs_core layer will handle it.
+ */
+ result = DID_OK << 16;
+ ufshcd_copy_query_response(hba, lrbp);
+ break;
+ case UPIU_TRANSACTION_REJECT_UPIU:
+ /* TODO: handle Reject UPIU Response */
+ result = DID_ERROR << 16;
+ dev_err(hba->dev,
+ "Reject UPIU not fully implemented\n");
+ break;
+ default:
+ result = DID_ERROR << 16;
+ dev_err(hba->dev,
+ "Unexpected request response code = %x\n",
+ result);
}
-
- /*
- * get the response UPIU result to extract
- * the SCSI command status
- */
- result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
-
- /*
- * get the result based on SCSI status response
- * to notify the SCSI midlayer of the command status
- */
- scsi_status = result & MASK_SCSI_STATUS;
- result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
break;
case OCS_ABORTED:
result |= DID_ABORT << 16;
@@ -1364,10 +1621,10 @@
task_req_upiup =
(struct utp_upiu_task_req *) task_req_descp->task_req_upiu;
task_req_upiup->header.dword_0 =
- cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
- lrbp->lun, lrbp->task_tag));
+ UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
+ lrbp->lun, lrbp->task_tag);
task_req_upiup->header.dword_1 =
- cpu_to_be32(UPIU_HEADER_DWORD(0, tm_function, 0, 0));
+ UPIU_HEADER_DWORD(0, tm_function, 0, 0);
task_req_upiup->input_param1 = lrbp->lun;
task_req_upiup->input_param1 =
@@ -1670,6 +1927,9 @@
INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler);
+ /* Initialize mutex for query requests */
+ mutex_init(&hba->query.lock_ufs_query);
+
/* IRQ registration */
err = request_irq(irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
if (err) {
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 6b99a42..336980b 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -60,6 +60,7 @@
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_eh.h>
+#include <scsi/scsi_device.h>
#include "ufs.h"
#include "ufshci.h"
@@ -88,7 +89,7 @@
/**
* struct ufshcd_lrb - local reference block
* @utr_descriptor_ptr: UTRD address of the command
- * @ucd_cmd_ptr: UCD address of the command
+ * @ucd_req_ptr: UCD address of the command
* @ucd_rsp_ptr: Response UPIU address for this command
* @ucd_prdt_ptr: PRDT address of the command
* @cmd: pointer to SCSI command
@@ -101,7 +102,7 @@
*/
struct ufshcd_lrb {
struct utp_transfer_req_desc *utr_descriptor_ptr;
- struct utp_upiu_cmd *ucd_cmd_ptr;
+ struct utp_upiu_req *ucd_req_ptr;
struct utp_upiu_rsp *ucd_rsp_ptr;
struct ufshcd_sg_entry *ucd_prdt_ptr;
@@ -115,6 +116,19 @@
unsigned int lun;
};
+/**
+ * struct ufs_query - keeps the query request information
+ * @request: request upiu and function
+ * @descriptor: buffer for sending/receiving descriptor
+ * @response: response upiu and response
+ * @mutex: lock to allow one query at a time
+ */
+struct ufs_query {
+ struct ufs_query_req *request;
+ u8 *descriptor;
+ struct ufs_query_res *response;
+ struct mutex lock_ufs_query;
+};
/**
* struct ufs_hba - per adapter private structure
@@ -143,6 +157,7 @@
* @uic_workq: Work queue for UIC completion handling
* @feh_workq: Work queue for fatal controller error handling
* @errors: HBA errors
+ * @query: query request information
*/
struct ufs_hba {
void __iomem *mmio_base;
@@ -184,6 +199,9 @@
/* HBA Errors */
u32 errors;
+
+ /* Query Request */
+ struct ufs_query query;
};
int ufshcd_init(struct device *, struct ufs_hba ** , void __iomem * ,
diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h
index 0c16484..4a86247 100644
--- a/drivers/scsi/ufs/ufshci.h
+++ b/drivers/scsi/ufs/ufshci.h
@@ -39,7 +39,7 @@
enum {
TASK_REQ_UPIU_SIZE_DWORDS = 8,
TASK_RSP_UPIU_SIZE_DWORDS = 8,
- ALIGNED_UPIU_SIZE = 128,
+ ALIGNED_UPIU_SIZE = 512,
};
/* UFSHCI Registers */
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index a0179cb..6962d53 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -9,7 +9,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -222,7 +221,7 @@
u8 *puc;
int ret = 0;
u8 la = txn->la;
- u8 wbuf[SLIM_RX_MSGQ_BUF_LEN];
+ u8 wbuf[SLIM_MSGQ_BUF_LEN];
if (!pm_runtime_enabled(dev->dev) && dev->state == MSM_CTRL_ASLEEP &&
txn->mc != SLIM_USR_MC_REPORT_SATELLITE) {
@@ -235,17 +234,14 @@
*/
ngd_slim_runtime_resume(dev->dev);
}
- if (txn->mc == (SLIM_MSG_CLK_PAUSE_SEQ_FLG |
- SLIM_MSG_MC_RECONFIGURE_NOW)) {
- if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED) {
- ret = sps_disconnect(dev->rx_msgq.sps);
- dev->use_rx_msgqs = MSM_MSGQ_RESET;
- }
- if (!ret)
- ret = msm_slim_qmi_power_request(dev, false);
- else
- pr_err("SPS pipe disconnect error:%d", ret);
- return ret;
+ if ((txn->mc == (SLIM_MSG_CLK_PAUSE_SEQ_FLG |
+ SLIM_MSG_MC_RECONFIGURE_NOW)) &&
+ dev->state <= MSM_CTRL_SLEEPING) {
+ msm_slim_disconnect_endp(dev, &dev->rx_msgq,
+ &dev->use_rx_msgqs);
+ msm_slim_disconnect_endp(dev, &dev->tx_msgq,
+ &dev->use_tx_msgqs);
+ return msm_slim_qmi_power_request(dev, false);
}
else if (txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG)
return 0;
@@ -389,10 +385,19 @@
NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_TX_MSG);
if (!ret) {
int timeout = wait_for_completion_timeout(&tx_sent, HZ);
- if (!timeout)
+ if (!timeout) {
ret = -ETIMEDOUT;
- else
+ /*
+ * disconnect/recoonect pipe so that subsequent
+ * transactions don't timeout due to unavailable
+ * descriptors
+ */
+ msm_slim_disconnect_endp(dev, &dev->tx_msgq,
+ &dev->use_tx_msgqs);
+ msm_slim_connect_endp(dev, &dev->tx_msgq, NULL);
+ } else {
ret = dev->err;
+ }
}
dev->wr_comp = NULL;
if (ret) {
@@ -469,8 +474,10 @@
struct slim_msg_txn txn;
struct slim_controller *ctrl = sb->ctrl;
DECLARE_COMPLETION_ONSTACK(done);
- u8 wbuf[SLIM_RX_MSGQ_BUF_LEN];
+ u8 wbuf[SLIM_MSGQ_BUF_LEN];
+ *clkgear = ctrl->clkgear;
+ *subfrmc = 0;
txn.mt = SLIM_MSG_MT_DEST_REFERRED_USER;
txn.dt = SLIM_MSG_DEST_LOGICALADDR;
txn.la = SLIM_LA_MGR;
@@ -479,6 +486,34 @@
txn.wbuf = wbuf;
txn.rbuf = NULL;
+ if (ctrl->sched.msgsl != ctrl->sched.pending_msgsl) {
+ pr_debug("slim reserve BW for messaging: req: %d",
+ ctrl->sched.pending_msgsl);
+ txn.mc = SLIM_USR_MC_REQ_BW;
+ wbuf[txn.len++] = ((sb->laddr & 0x1f) |
+ ((u8)(ctrl->sched.pending_msgsl & 0x7) << 5));
+ wbuf[txn.len++] = (u8)(ctrl->sched.pending_msgsl >> 3);
+ ret = ngd_get_tid(ctrl, &txn, &wbuf[txn.len++], &done);
+ if (ret)
+ return ret;
+ txn.rl = txn.len + 4;
+ ret = ngd_xferandwait_ack(ctrl, &txn);
+ if (ret)
+ return ret;
+
+ txn.mc = SLIM_USR_MC_RECONFIG_NOW;
+ txn.len = 2;
+ wbuf[1] = sb->laddr;
+ txn.rl = txn.len + 4;
+ ret = ngd_get_tid(ctrl, &txn, &wbuf[0], &done);
+ if (ret)
+ return ret;
+ ret = ngd_xferandwait_ack(ctrl, &txn);
+ if (ret)
+ return ret;
+
+ txn.len = 0;
+ }
list_for_each_entry(pch, &sb->mark_define, pending) {
struct slim_ich *slc;
slc = &ctrl->chans[pch->chan];
@@ -601,24 +636,26 @@
return ret;
}
-static void ngd_slim_setup_rx_path(struct msm_slim_ctrl *dev)
+static void ngd_slim_setup_msg_path(struct msm_slim_ctrl *dev)
{
- int ret;
if (dev->state == MSM_CTRL_DOWN) {
msm_slim_sps_init(dev, dev->bam_mem,
NGD_BASE(dev->ctrl.nr,
dev->ver) + NGD_STATUS, true);
} else {
if (dev->use_rx_msgqs == MSM_MSGQ_DISABLED)
- return;
- ret = msm_slim_connect_endp(dev, &dev->rx_msgq,
+ goto setup_tx_msg_path;
+ msm_slim_connect_endp(dev, &dev->rx_msgq,
&dev->rx_msgq_notify);
- if (!ret)
- dev->use_rx_msgqs = MSM_MSGQ_ENABLED;
- else
- pr_err("RX msgq not being used:%d", ret);
+
+setup_tx_msg_path:
+ if (dev->use_tx_msgqs == MSM_MSGQ_DISABLED)
+ return;
+ msm_slim_connect_endp(dev, &dev->tx_msgq,
+ NULL);
}
}
+
static void ngd_slim_rx(struct msm_slim_ctrl *dev, u8 *buf)
{
u8 mc, mt, len;
@@ -647,9 +684,11 @@
txn.len = 4;
pr_info("SLIM SAT: Received master capability");
if (dev->state >= MSM_CTRL_ASLEEP) {
- ngd_slim_setup_rx_path(dev);
+ ngd_slim_setup_msg_path(dev);
if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED)
msgq_en |= NGD_CFG_RX_MSGQ_EN;
+ if (dev->use_tx_msgqs == MSM_MSGQ_ENABLED)
+ msgq_en |= NGD_CFG_TX_MSGQ_EN;
writel_relaxed(msgq_en, dev->base +
NGD_BASE(dev->ctrl.nr, dev->ver));
/* make sure NGD MSG-Q config goes through */
@@ -763,7 +802,7 @@
* ADSP power collapse case, where HW wasn't reset.
* Reconnect BAM pipes if disconnected
*/
- ngd_slim_setup_rx_path(dev);
+ ngd_slim_setup_msg_path(dev);
return 0;
} else if (cur_state != MSM_CTRL_DOWN) {
pr_info("ADSP P.C. CTRL state:%d NGD not enumerated:0x%x",
@@ -776,6 +815,12 @@
sps_free_endpoint(endpoint->sps);
dev->use_rx_msgqs = MSM_MSGQ_RESET;
}
+ if (dev->use_tx_msgqs == MSM_MSGQ_DOWN) {
+ struct msm_slim_endp *endpoint = &dev->tx_msgq;
+ sps_disconnect(endpoint->sps);
+ sps_free_endpoint(endpoint->sps);
+ dev->use_tx_msgqs = MSM_MSGQ_RESET;
+ }
/*
* ADSP power collapse case (OR SSR), where HW was reset
* BAM programming will happen when capability message is received
@@ -927,6 +972,8 @@
/* disconnect BAM pipes */
if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED)
dev->use_rx_msgqs = MSM_MSGQ_DOWN;
+ if (dev->use_tx_msgqs == MSM_MSGQ_ENABLED)
+ dev->use_tx_msgqs = MSM_MSGQ_DOWN;
msm_slim_sps_exit(dev, false);
mutex_lock(&ctrl->m_ctrl);
/* device up should be called again after SSR */
@@ -1039,8 +1086,6 @@
dev->ctrl.config_port = msm_config_port;
dev->ctrl.port_xfer = msm_slim_port_xfer;
dev->ctrl.port_xfer_status = msm_slim_port_xfer_status;
- /* Reserve some messaging BW for satellite-apps driver communication */
- dev->ctrl.sched.pending_msgsl = 30;
dev->bam_mem = bam_mem;
init_completion(&dev->reconf);
@@ -1055,6 +1100,10 @@
dev->use_rx_msgqs = MSM_MSGQ_DISABLED;
else
dev->use_rx_msgqs = MSM_MSGQ_RESET;
+
+ /* Enable TX message queues by default as recommended by HW */
+ dev->use_tx_msgqs = MSM_MSGQ_RESET;
+
init_completion(&dev->rx_msgq_notify);
/* Register with framework */
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
index 30341e2..0166196 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -269,16 +269,66 @@
return ret;
}
+/* Queue up Tx message buffer */
+static int msm_slim_post_tx_msgq(struct msm_slim_ctrl *dev, u8 *buf, int len)
+{
+ int ret;
+ struct msm_slim_endp *endpoint = &dev->tx_msgq;
+ struct sps_mem_buffer *mem = &endpoint->buf;
+ struct sps_pipe *pipe = endpoint->sps;
+ int ix = (buf - (u8 *)mem->base) / SLIM_MSGQ_BUF_LEN;
+
+ u32 phys_addr = mem->phys_base + (SLIM_MSGQ_BUF_LEN * ix);
+
+ for (ret = 0; ret < ((len + 3) >> 2); ret++)
+ pr_debug("BAM TX buf[%d]:0x%x", ret, ((u32 *)buf)[ret]);
+
+ ret = sps_transfer_one(pipe, phys_addr, ((len + 3) & 0xFC), NULL,
+ SPS_IOVEC_FLAG_EOT);
+ if (ret)
+ dev_err(dev->dev, "transfer_one() failed 0x%x, %d\n", ret, ix);
+
+ return ret;
+}
+
+static u32 *msm_slim_tx_msgq_return(struct msm_slim_ctrl *dev)
+{
+ struct msm_slim_endp *endpoint = &dev->tx_msgq;
+ struct sps_mem_buffer *mem = &endpoint->buf;
+ struct sps_pipe *pipe = endpoint->sps;
+ struct sps_iovec iovec;
+ int ret;
+
+ /* first transaction after establishing connection */
+ if (dev->tx_idx == -1) {
+ dev->tx_idx = 0;
+ return mem->base;
+ }
+ ret = sps_get_iovec(pipe, &iovec);
+ if (ret || iovec.addr == 0) {
+ dev_err(dev->dev, "sps_get_iovec() failed 0x%x\n", ret);
+ return NULL;
+ }
+
+ /* Calculate buffer index */
+ dev->tx_idx = (iovec.addr - mem->phys_base) / SLIM_MSGQ_BUF_LEN;
+
+ return (u32 *)((u8 *)mem->base + (dev->tx_idx * SLIM_MSGQ_BUF_LEN));
+}
+
int msm_send_msg_buf(struct msm_slim_ctrl *dev, u32 *buf, u8 len, u32 tx_reg)
{
- int i;
- for (i = 0; i < (len + 3) >> 2; i++) {
- dev_dbg(dev->dev, "TX data:0x%x\n", buf[i]);
- writel_relaxed(buf[i], dev->base + tx_reg + (i * 4));
+ if (dev->use_tx_msgqs != MSM_MSGQ_ENABLED) {
+ int i;
+ for (i = 0; i < (len + 3) >> 2; i++) {
+ dev_dbg(dev->dev, "AHB TX data:0x%x\n", buf[i]);
+ writel_relaxed(buf[i], dev->base + tx_reg + (i * 4));
+ }
+ /* Guarantee that message is sent before returning */
+ mb();
+ return 0;
}
- /* Guarantee that message is sent before returning */
- mb();
- return 0;
+ return msm_slim_post_tx_msgq(dev, (u8 *)buf, len);
}
u32 *msm_get_msg_buf(struct msm_slim_ctrl *dev, int len)
@@ -287,7 +337,10 @@
* Currently we block a transaction until the current one completes.
* In case we need multiple transactions, use message Q
*/
- return dev->tx_buf;
+ if (dev->use_tx_msgqs != MSM_MSGQ_ENABLED)
+ return dev->tx_buf;
+
+ return msm_slim_tx_msgq_return(dev);
}
static void
@@ -394,15 +447,17 @@
memset(&sps_descr_event, 0x00, sizeof(sps_descr_event));
- sps_descr_event.mode = SPS_TRIGGER_CALLBACK;
- sps_descr_event.options = SPS_O_DESC_DONE;
- sps_descr_event.user = (void *)dev;
- sps_descr_event.xfer_done = notify;
+ if (notify) {
+ sps_descr_event.mode = SPS_TRIGGER_CALLBACK;
+ sps_descr_event.options = SPS_O_DESC_DONE;
+ sps_descr_event.user = (void *)dev;
+ sps_descr_event.xfer_done = notify;
- ret = sps_register_event(endpoint->sps, &sps_descr_event);
- if (ret) {
- dev_err(dev->dev, "sps_connect() failed 0x%x\n", ret);
- goto sps_reg_event_failed;
+ ret = sps_register_event(endpoint->sps, &sps_descr_event);
+ if (ret) {
+ dev_err(dev->dev, "sps_connect() failed 0x%x\n", ret);
+ goto sps_reg_event_failed;
+ }
}
/* Register callback for errors */
@@ -423,13 +478,20 @@
* Use (buf->size/4) - 1 for the number of buffer to post
*/
- /* Setup the transfer */
- for (i = 0; i < (MSM_SLIM_DESC_NUM - 1); i++) {
- ret = msm_slim_post_rx_msgq(dev, i);
- if (ret) {
- dev_err(dev->dev, "post_rx_msgq() failed 0x%x\n", ret);
- goto sps_transfer_failed;
+ if (endpoint == &dev->rx_msgq) {
+ /* Setup the transfer */
+ for (i = 0; i < (MSM_SLIM_DESC_NUM - 1); i++) {
+ ret = msm_slim_post_rx_msgq(dev, i);
+ if (ret) {
+ dev_err(dev->dev,
+ "post_rx_msgq() failed 0x%x\n", ret);
+ goto sps_transfer_failed;
+ }
}
+ dev->use_rx_msgqs = MSM_MSGQ_ENABLED;
+ } else {
+ dev->tx_idx = -1;
+ dev->use_tx_msgqs = MSM_MSGQ_ENABLED;
}
return 0;
@@ -440,6 +502,7 @@
sps_disconnect(endpoint->sps);
return ret;
}
+
static int msm_slim_init_rx_msgq(struct msm_slim_ctrl *dev, u32 pipe_reg)
{
int ret;
@@ -489,10 +552,8 @@
ret = msm_slim_connect_endp(dev, endpoint, notify);
- if (!ret) {
- dev->use_rx_msgqs = MSM_MSGQ_ENABLED;
+ if (!ret)
return 0;
- }
msm_slim_sps_mem_free(dev, mem);
alloc_buffer_failed:
@@ -504,6 +565,67 @@
return ret;
}
+static int msm_slim_init_tx_msgq(struct msm_slim_ctrl *dev, u32 pipe_reg)
+{
+ int ret;
+ u32 pipe_offset;
+ struct msm_slim_endp *endpoint = &dev->tx_msgq;
+ struct sps_connect *config = &endpoint->config;
+ struct sps_mem_buffer *descr = &config->desc;
+ struct sps_mem_buffer *mem = &endpoint->buf;
+
+ if (dev->use_tx_msgqs == MSM_MSGQ_DISABLED)
+ return 0;
+
+ /* Allocate the endpoint */
+ ret = msm_slim_init_endpoint(dev, endpoint);
+ if (ret) {
+ dev_err(dev->dev, "init_endpoint failed 0x%x\n", ret);
+ goto sps_init_endpoint_failed;
+ }
+
+ /* Get the pipe indices for the message queues */
+ pipe_offset = (readl_relaxed(dev->base + pipe_reg) & 0xfc) >> 2;
+ pipe_offset += 1;
+ dev_dbg(dev->dev, "TX Message queue pipe offset %d\n", pipe_offset);
+
+ config->mode = SPS_MODE_DEST;
+ config->source = SPS_DEV_HANDLE_MEM;
+ config->destination = dev->bam.hdl;
+ config->dest_pipe_index = pipe_offset;
+ config->src_pipe_index = 0;
+ config->options = SPS_O_ERROR | SPS_O_NO_Q |
+ SPS_O_ACK_TRANSFERS | SPS_O_AUTO_ENABLE;
+
+ /* Allocate memory for the FIFO descriptors */
+ ret = msm_slim_sps_mem_alloc(dev, descr,
+ MSM_TX_BUFS * sizeof(struct sps_iovec));
+ if (ret) {
+ dev_err(dev->dev, "unable to allocate SPS descriptors\n");
+ goto alloc_descr_failed;
+ }
+
+ /* Allocate memory for the message buffer(s), N descrs, 40-byte mesg */
+ ret = msm_slim_sps_mem_alloc(dev, mem, MSM_TX_BUFS * SLIM_MSGQ_BUF_LEN);
+ if (ret) {
+ dev_err(dev->dev, "dma_alloc_coherent failed\n");
+ goto alloc_buffer_failed;
+ }
+ ret = msm_slim_connect_endp(dev, endpoint, NULL);
+
+ if (!ret)
+ return 0;
+
+ msm_slim_sps_mem_free(dev, mem);
+alloc_buffer_failed:
+ msm_slim_sps_mem_free(dev, descr);
+alloc_descr_failed:
+ msm_slim_free_endpoint(endpoint);
+sps_init_endpoint_failed:
+ dev->use_tx_msgqs = MSM_MSGQ_DISABLED;
+ return ret;
+}
+
/* Registers BAM h/w resource with SPS driver and initializes msgq endpoints */
int msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem,
u32 pipe_reg, bool remote)
@@ -529,8 +651,10 @@
},
};
- if (dev->bam.hdl)
- goto init_rx_msgq;
+ if (dev->bam.hdl) {
+ bam_handle = dev->bam.hdl;
+ goto init_msgq;
+ }
bam_props.ee = dev->ee;
bam_props.virt_addr = dev->bam.base;
bam_props.phys_addr = bam_mem->start;
@@ -563,40 +687,68 @@
if (ret) {
dev_err(dev->dev, "disabling BAM: reg-bam failed 0x%x\n", ret);
dev->use_rx_msgqs = MSM_MSGQ_DISABLED;
- goto init_rx_msgq;
+ dev->use_tx_msgqs = MSM_MSGQ_DISABLED;
+ return ret;
}
dev->bam.hdl = bam_handle;
dev_dbg(dev->dev, "SLIM BAM registered, handle = 0x%x\n", bam_handle);
-init_rx_msgq:
+init_msgq:
ret = msm_slim_init_rx_msgq(dev, pipe_reg);
if (ret)
dev_err(dev->dev, "msm_slim_init_rx_msgq failed 0x%x\n", ret);
- if (ret && bam_handle) {
+ if (ret && bam_handle)
+ dev->use_rx_msgqs = MSM_MSGQ_DISABLED;
+
+ ret = msm_slim_init_tx_msgq(dev, pipe_reg);
+ if (ret)
+ dev_err(dev->dev, "msm_slim_init_tx_msgq failed 0x%x\n", ret);
+ if (ret && bam_handle)
+ dev->use_tx_msgqs = MSM_MSGQ_DISABLED;
+
+ if (dev->use_tx_msgqs == MSM_MSGQ_DISABLED &&
+ dev->use_rx_msgqs == MSM_MSGQ_DISABLED && bam_handle) {
sps_deregister_bam_device(bam_handle);
dev->bam.hdl = 0L;
}
+
return ret;
}
+void msm_slim_disconnect_endp(struct msm_slim_ctrl *dev,
+ struct msm_slim_endp *endpoint,
+ enum msm_slim_msgq *msgq_flag)
+{
+ if (*msgq_flag == MSM_MSGQ_ENABLED) {
+ sps_disconnect(endpoint->sps);
+ *msgq_flag = MSM_MSGQ_RESET;
+ }
+}
+
+static void msm_slim_remove_ep(struct msm_slim_ctrl *dev,
+ struct msm_slim_endp *endpoint,
+ enum msm_slim_msgq *msgq_flag)
+{
+ struct sps_connect *config = &endpoint->config;
+ struct sps_mem_buffer *descr = &config->desc;
+ struct sps_mem_buffer *mem = &endpoint->buf;
+ struct sps_register_event sps_event;
+ memset(&sps_event, 0x00, sizeof(sps_event));
+ msm_slim_sps_mem_free(dev, mem);
+ sps_register_event(endpoint->sps, &sps_event);
+ if (*msgq_flag == MSM_MSGQ_ENABLED) {
+ msm_slim_disconnect_endp(dev, endpoint, msgq_flag);
+ msm_slim_free_endpoint(endpoint);
+ }
+ msm_slim_sps_mem_free(dev, descr);
+}
+
void msm_slim_sps_exit(struct msm_slim_ctrl *dev, bool dereg)
{
- if (dev->use_rx_msgqs >= MSM_MSGQ_ENABLED) {
- struct msm_slim_endp *endpoint = &dev->rx_msgq;
- struct sps_connect *config = &endpoint->config;
- struct sps_mem_buffer *descr = &config->desc;
- struct sps_mem_buffer *mem = &endpoint->buf;
- struct sps_register_event sps_event;
- memset(&sps_event, 0x00, sizeof(sps_event));
- msm_slim_sps_mem_free(dev, mem);
- sps_register_event(endpoint->sps, &sps_event);
- if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED) {
- sps_disconnect(endpoint->sps);
- msm_slim_free_endpoint(endpoint);
- dev->use_rx_msgqs = MSM_MSGQ_RESET;
- }
- msm_slim_sps_mem_free(dev, descr);
- }
+ if (dev->use_rx_msgqs >= MSM_MSGQ_ENABLED)
+ msm_slim_remove_ep(dev, &dev->rx_msgq, &dev->use_rx_msgqs);
+ if (dev->use_tx_msgqs >= MSM_MSGQ_ENABLED)
+ msm_slim_remove_ep(dev, &dev->tx_msgq, &dev->use_tx_msgqs);
if (dereg) {
sps_deregister_bam_device(dev->bam.hdl);
dev->bam.hdl = 0L;
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index cf2d26f..f8f625e 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -17,7 +17,9 @@
#include <mach/msm_qmi_interface.h>
/* Per spec.max 40 bytes per received message */
-#define SLIM_RX_MSGQ_BUF_LEN 40
+#define SLIM_MSGQ_BUF_LEN 40
+
+#define MSM_TX_BUFS 2
#define SLIM_USR_MC_GENERIC_ACK 0x25
#define SLIM_USR_MC_MASTER_CAPABILITY 0x0
@@ -200,7 +202,8 @@
u32 curr_bw;
u8 msg_cnt;
u32 tx_buf[10];
- u8 rx_msgs[MSM_CONCUR_MSG][SLIM_RX_MSGQ_BUF_LEN];
+ u8 rx_msgs[MSM_CONCUR_MSG][SLIM_MSGQ_BUF_LEN];
+ int tx_idx;
spinlock_t rx_lock;
int head;
int tail;
@@ -211,6 +214,7 @@
struct msm_slim_sat *satd[MSM_MAX_NSATS];
struct msm_slim_endp pipes[7];
struct msm_slim_sps_bam bam;
+ struct msm_slim_endp tx_msgq;
struct msm_slim_endp rx_msgq;
struct completion rx_msgq_notify;
struct task_struct *rx_msgq_thread;
@@ -219,6 +223,7 @@
struct mutex tx_lock;
u8 pgdla;
enum msm_slim_msgq use_rx_msgqs;
+ enum msm_slim_msgq use_tx_msgqs;
int pipe_b;
struct completion reconf;
bool reconf_busy;
@@ -285,6 +290,9 @@
int msm_slim_connect_endp(struct msm_slim_ctrl *dev,
struct msm_slim_endp *endpoint,
struct completion *notify);
+void msm_slim_disconnect_endp(struct msm_slim_ctrl *dev,
+ struct msm_slim_endp *endpoint,
+ enum msm_slim_msgq *msgq_flag);
void msm_slim_qmi_exit(struct msm_slim_ctrl *dev);
int msm_slim_qmi_init(struct msm_slim_ctrl *dev, bool apps_is_master);
int msm_slim_qmi_power_request(struct msm_slim_ctrl *dev, bool active);
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index b89f608..53aa475 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -14,6 +14,7 @@
* SPI driver for Qualcomm MSM platforms
*
*/
+
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -39,11 +40,18 @@
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/atomic.h>
+#include <linux/pm_runtime.h>
#include <mach/msm_spi.h>
#include <mach/sps.h>
#include <mach/dma.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
#include "spi_qsd.h"
+static int msm_spi_pm_resume_runtime(struct device *device);
+static int msm_spi_pm_suspend_runtime(struct device *device);
+
+
static inline int msm_spi_configure_gsbi(struct msm_spi *dd,
struct platform_device *pdev)
{
@@ -195,6 +203,177 @@
dd->clock_speed = rate;
}
+static void msm_spi_clk_path_vote(struct msm_spi *dd)
+{
+ if (dd->clk_path_vote.client_hdl)
+ msm_bus_scale_client_update_request(
+ dd->clk_path_vote.client_hdl,
+ MSM_SPI_CLK_PATH_RESUME_VEC);
+}
+
+static void msm_spi_clk_path_unvote(struct msm_spi *dd)
+{
+ if (dd->clk_path_vote.client_hdl)
+ msm_bus_scale_client_update_request(
+ dd->clk_path_vote.client_hdl,
+ MSM_SPI_CLK_PATH_SUSPEND_VEC);
+}
+
+static void msm_spi_clk_path_teardown(struct msm_spi *dd)
+{
+ if (dd->pdata->active_only)
+ msm_spi_clk_path_unvote(dd);
+
+ if (dd->clk_path_vote.client_hdl) {
+ msm_bus_scale_unregister_client(dd->clk_path_vote.client_hdl);
+ dd->clk_path_vote.client_hdl = 0;
+ }
+}
+
+/**
+ * msm_spi_clk_path_init_structs: internal impl detail of msm_spi_clk_path_init
+ *
+ * allocates and initilizes the bus scaling vectors.
+ */
+static int msm_spi_clk_path_init_structs(struct msm_spi *dd)
+{
+ struct msm_bus_vectors *paths = NULL;
+ struct msm_bus_paths *usecases = NULL;
+
+ dev_dbg(dd->dev, "initialises path clock voting structs");
+
+ paths = devm_kzalloc(dd->dev, sizeof(*paths) * 2, GFP_KERNEL);
+ if (!paths) {
+ dev_err(dd->dev,
+ "msm_bus_paths.paths memory allocation failed");
+ return -ENOMEM;
+ }
+
+ usecases = devm_kzalloc(dd->dev, sizeof(*usecases) * 2, GFP_KERNEL);
+ if (!usecases) {
+ dev_err(dd->dev,
+ "msm_bus_scale_pdata.usecases memory allocation failed");
+ goto path_init_err;
+ }
+
+ dd->clk_path_vote.pdata = devm_kzalloc(dd->dev,
+ sizeof(*dd->clk_path_vote.pdata),
+ GFP_KERNEL);
+ if (!dd->clk_path_vote.pdata) {
+ dev_err(dd->dev,
+ "msm_bus_scale_pdata memory allocation failed");
+ goto path_init_err;
+ }
+
+ paths[MSM_SPI_CLK_PATH_SUSPEND_VEC] = (struct msm_bus_vectors) {
+ .src = dd->pdata->master_id,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ };
+
+ paths[MSM_SPI_CLK_PATH_RESUME_VEC] = (struct msm_bus_vectors) {
+ .src = dd->pdata->master_id,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = MSM_SPI_CLK_PATH_AVRG_BW(dd),
+ .ib = MSM_SPI_CLK_PATH_BRST_BW(dd),
+ };
+
+ usecases[MSM_SPI_CLK_PATH_SUSPEND_VEC] = (struct msm_bus_paths) {
+ .num_paths = 1,
+ .vectors = &paths[MSM_SPI_CLK_PATH_SUSPEND_VEC],
+ };
+
+ usecases[MSM_SPI_CLK_PATH_RESUME_VEC] = (struct msm_bus_paths) {
+ .num_paths = 1,
+ .vectors = &paths[MSM_SPI_CLK_PATH_RESUME_VEC],
+ };
+
+ *dd->clk_path_vote.pdata = (struct msm_bus_scale_pdata) {
+ .active_only = dd->pdata->active_only,
+ .name = dev_name(dd->dev),
+ .num_usecases = 2,
+ .usecase = usecases,
+ };
+
+ return 0;
+
+path_init_err:
+ devm_kfree(dd->dev, paths);
+ devm_kfree(dd->dev, usecases);
+ devm_kfree(dd->dev, dd->clk_path_vote.pdata);
+ dd->clk_path_vote.pdata = NULL;
+ return -ENOMEM;
+}
+
+/**
+ * msm_spi_clk_path_postponed_register: reg with bus-scaling after it is probed
+ *
+ * @return zero on success
+ *
+ * Workaround: SPI driver may be probed before the bus scaling driver. Calling
+ * msm_bus_scale_register_client() will fail if the bus scaling driver is not
+ * ready yet. Thus, this function should be called not from probe but from a
+ * later context. Also, this function may be called more then once before
+ * register succeed. At this case only one error message will be logged. At boot
+ * time all clocks are on, so earlier SPI transactions should succeed.
+ */
+static int msm_spi_clk_path_postponed_register(struct msm_spi *dd)
+{
+ dd->clk_path_vote.client_hdl = msm_bus_scale_register_client(
+ dd->clk_path_vote.pdata);
+
+ if (dd->clk_path_vote.client_hdl) {
+ if (dd->clk_path_vote.reg_err) {
+ /* log a success message if an error msg was logged */
+ dd->clk_path_vote.reg_err = false;
+ dev_info(dd->dev,
+ "msm_bus_scale_register_client(mstr-id:%d "
+ "actv-only:%d):0x%x",
+ dd->pdata->master_id, dd->pdata->active_only,
+ dd->clk_path_vote.client_hdl);
+ }
+
+ if (dd->pdata->active_only)
+ msm_spi_clk_path_vote(dd);
+ } else {
+ /* guard to log only one error on multiple failure */
+ if (!dd->clk_path_vote.reg_err) {
+ dd->clk_path_vote.reg_err = true;
+
+ dev_info(dd->dev,
+ "msm_bus_scale_register_client(mstr-id:%d "
+ "actv-only:%d):0",
+ dd->pdata->master_id, dd->pdata->active_only);
+ }
+ }
+
+ return dd->clk_path_vote.client_hdl ? 0 : -EAGAIN;
+}
+
+static void msm_spi_clk_path_init(struct msm_spi *dd)
+{
+ /*
+ * bail out if path voting is diabled (master_id == 0) or if it is
+ * already registered (client_hdl != 0)
+ */
+ if (!dd->pdata->master_id || dd->clk_path_vote.client_hdl)
+ return;
+
+ /* if fail once then try no more */
+ if (!dd->clk_path_vote.pdata && msm_spi_clk_path_init_structs(dd)) {
+ dd->pdata->master_id = 0;
+ return;
+ };
+
+ /* on failure try again later */
+ if (msm_spi_clk_path_postponed_register(dd))
+ return;
+
+ if (dd->pdata->active_only)
+ msm_spi_clk_path_vote(dd);
+}
+
static int msm_spi_calculate_size(int *fifo_size,
int *block_size,
int block,
@@ -476,6 +655,16 @@
else if (dd->mode == SPI_BAM_MODE)
spi_config |= SPI_CFG_INPUT_FIRST;
+ /*
+ * HS_MODE improves signal stability for spi-clk high rates
+ * but is invalid in LOOPBACK mode.
+ */
+ if ((dd->clock_speed >= SPI_HS_MIN_RATE) &&
+ !(dd->cur_msg->spi->mode & SPI_LOOP))
+ spi_config |= SPI_CFG_HS_MODE;
+ else
+ spi_config &= ~SPI_CFG_HS_MODE;
+
writel_relaxed(spi_config, dd->base + SPI_CONFIG);
}
@@ -859,6 +1048,10 @@
u32 op, ret = IRQ_NONE;
struct msm_spi *dd = dev_id;
+ if (pm_runtime_suspended(dd->dev)) {
+ dev_warn(dd->dev, "QUP: pm runtime suspend, irq:%d\n", irq);
+ return ret;
+ }
if (readl_relaxed(dd->base + SPI_ERROR_FLAGS) ||
readl_relaxed(dd->base + QUP_ERROR_FLAGS)) {
struct spi_master *master = dev_get_drvdata(dd->dev);
@@ -1265,7 +1458,7 @@
}
if (tr->cs_change &&
- ((bpw != 8) || (bpw != 16) || (bpw != 32)))
+ ((bpw != 8) && (bpw != 16) && (bpw != 32)))
return false;
}
@@ -1705,36 +1898,22 @@
container_of(work, struct msm_spi, work_data);
unsigned long flags;
u32 status_error = 0;
- int rc = 0;
+
+ pm_runtime_get_sync(dd->dev);
mutex_lock(&dd->core_lock);
- /* Don't allow power collapse until we release mutex */
- if (pm_qos_request_active(&qos_req_list))
- pm_qos_update_request(&qos_req_list,
- dd->pm_lat);
+ /*
+ * Counter-part of system-suspend when runtime-pm is not enabled.
+ * This way, resume can be left empty and device will be put in
+ * active mode only if client requests anything on the bus
+ */
+ if (!pm_runtime_enabled(dd->dev))
+ msm_spi_pm_resume_runtime(dd->dev);
+
if (dd->use_rlock)
remote_mutex_lock(&dd->r_lock);
- /* Configure the spi clk, miso, mosi and cs gpio */
- if (dd->pdata->gpio_config) {
- rc = dd->pdata->gpio_config();
- if (rc) {
- dev_err(dd->dev,
- "%s: error configuring GPIOs\n",
- __func__);
- status_error = 1;
- }
- }
-
- rc = msm_spi_request_gpios(dd);
- if (rc)
- status_error = 1;
-
- clk_prepare_enable(dd->clk);
- clk_prepare_enable(dd->pclk);
- msm_spi_enable_irqs(dd);
-
if (!msm_spi_is_valid_state(dd)) {
dev_err(dd->dev, "%s: SPI operational state not valid\n",
__func__);
@@ -1742,6 +1921,7 @@
}
spin_lock_irqsave(&dd->queue_lock, flags);
+ dd->transfer_pending = 1;
while (!list_empty(&dd->queue)) {
dd->cur_msg = list_entry(dd->queue.next,
struct spi_message, queue);
@@ -1758,24 +1938,14 @@
dd->transfer_pending = 0;
spin_unlock_irqrestore(&dd->queue_lock, flags);
- msm_spi_disable_irqs(dd);
- clk_disable_unprepare(dd->clk);
- clk_disable_unprepare(dd->pclk);
-
- /* Free the spi clk, miso, mosi, cs gpio */
- if (!rc && dd->pdata && dd->pdata->gpio_release)
- dd->pdata->gpio_release();
- if (!rc)
- msm_spi_free_gpios(dd);
-
if (dd->use_rlock)
remote_mutex_unlock(&dd->r_lock);
- if (pm_qos_request_active(&qos_req_list))
- pm_qos_update_request(&qos_req_list,
- PM_QOS_DEFAULT_VALUE);
-
mutex_unlock(&dd->core_lock);
+
+ pm_runtime_mark_last_busy(dd->dev);
+ pm_runtime_put_autosuspend(dd->dev);
+
/* If needed, this can be done after the current message is complete,
and work can be continued upon resume. No motivation for now. */
if (dd->suspended)
@@ -1789,8 +1959,6 @@
struct spi_transfer *tr;
dd = spi_master_get_devdata(spi->master);
- if (dd->suspended)
- return -EBUSY;
if (list_empty(&msg->transfers) || !msg->complete)
return -EINVAL;
@@ -1810,11 +1978,6 @@
}
spin_lock_irqsave(&dd->queue_lock, flags);
- if (dd->suspended) {
- spin_unlock_irqrestore(&dd->queue_lock, flags);
- return -EBUSY;
- }
- dd->transfer_pending = 1;
list_add_tail(&msg->queue, &dd->queue);
spin_unlock_irqrestore(&dd->queue_lock, flags);
queue_work(dd->workqueue, &dd->work_data);
@@ -1845,7 +2008,14 @@
dd = spi_master_get_devdata(spi->master);
+ pm_runtime_get_sync(dd->dev);
+
mutex_lock(&dd->core_lock);
+
+ /* Counter-part of system-suspend when runtime-pm is not enabled. */
+ if (!pm_runtime_enabled(dd->dev))
+ msm_spi_pm_resume_runtime(dd->dev);
+
if (dd->suspended) {
mutex_unlock(&dd->core_lock);
return -EBUSY;
@@ -1854,27 +2024,6 @@
if (dd->use_rlock)
remote_mutex_lock(&dd->r_lock);
- /* Configure the spi clk, miso, mosi, cs gpio */
- if (dd->pdata->gpio_config) {
- rc = dd->pdata->gpio_config();
- if (rc) {
- dev_err(&spi->dev,
- "%s: error configuring GPIOs\n",
- __func__);
- rc = -ENXIO;
- goto err_setup_gpio;
- }
- }
-
- rc = msm_spi_request_gpios(dd);
- if (rc) {
- rc = -ENXIO;
- goto err_setup_gpio;
- }
-
- clk_prepare_enable(dd->clk);
- clk_prepare_enable(dd->pclk);
-
spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select;
if (spi->mode & SPI_CS_HIGH)
@@ -1892,18 +2041,19 @@
/* Ensure previous write completed before disabling the clocks */
mb();
- clk_disable_unprepare(dd->clk);
- clk_disable_unprepare(dd->pclk);
- /* Free the spi clk, miso, mosi, cs gpio */
- if (dd->pdata && dd->pdata->gpio_release)
- dd->pdata->gpio_release();
- msm_spi_free_gpios(dd);
-
-err_setup_gpio:
if (dd->use_rlock)
remote_mutex_unlock(&dd->r_lock);
+
+ /* Counter-part of system-resume when runtime-pm is not enabled. */
+ if (!pm_runtime_enabled(dd->dev))
+ msm_spi_pm_suspend_runtime(dd->dev);
+
mutex_unlock(&dd->core_lock);
+
+ pm_runtime_mark_last_busy(dd->dev);
+ pm_runtime_put_autosuspend(dd->dev);
+
err_setup_exit:
return rc;
}
@@ -2280,7 +2430,7 @@
bam_props.phys_addr = dd->bam.phys_addr;
bam_props.virt_addr = dd->bam.base;
bam_props.irq = dd->bam.irq;
- bam_props.manage = SPS_BAM_MGR_LOCAL;
+ bam_props.manage = SPS_BAM_MGR_DEVICE_REMOTE;
bam_props.summing_threshold = 0x10;
rc = sps_register_bam_device(&bam_props, &bam_handle);
@@ -2383,49 +2533,148 @@
return 0;
}
-/**
- * msm_spi_dt_to_pdata: copy device-tree data to platfrom data struct
- */
-struct msm_spi_platform_data *
-__init msm_spi_dt_to_pdata(struct platform_device *pdev)
+enum msm_spi_dt_entry_status {
+ DT_REQ, /* Required: fail if missing */
+ DT_SGST, /* Suggested: warn if missing */
+ DT_OPT, /* Optional: don't warn if missing */
+};
+
+enum msm_spi_dt_entry_type {
+ DT_U32,
+ DT_GPIO,
+ DT_BOOL,
+};
+
+struct msm_spi_dt_to_pdata_map {
+ const char *dt_name;
+ void *ptr_data;
+ enum msm_spi_dt_entry_status status;
+ enum msm_spi_dt_entry_type type;
+ int default_val;
+};
+
+static int __init msm_spi_dt_to_pdata_populate(struct platform_device *pdev,
+ struct msm_spi_platform_data *pdata,
+ struct msm_spi_dt_to_pdata_map *itr)
{
+ int ret, err = 0;
struct device_node *node = pdev->dev.of_node;
+
+ for (; itr->dt_name ; ++itr) {
+ switch (itr->type) {
+ case DT_GPIO:
+ ret = of_get_named_gpio(node, itr->dt_name, 0);
+ if (ret >= 0) {
+ *((int *) itr->ptr_data) = ret;
+ ret = 0;
+ }
+ break;
+ case DT_U32:
+ ret = of_property_read_u32(node, itr->dt_name,
+ (u32 *) itr->ptr_data);
+ break;
+ case DT_BOOL:
+ *((bool *) itr->ptr_data) =
+ of_property_read_bool(node, itr->dt_name);
+ ret = 0;
+ break;
+ default:
+ dev_err(&pdev->dev, "%d is an unknown DT entry type\n",
+ itr->type);
+ ret = -EBADE;
+ }
+
+ dev_dbg(&pdev->dev, "DT entry ret:%d name:%s val:%d\n",
+ ret, itr->dt_name, *((int *)itr->ptr_data));
+
+ if (ret) {
+ *((int *)itr->ptr_data) = itr->default_val;
+
+ if (itr->status < DT_OPT) {
+ dev_err(&pdev->dev, "Missing '%s' DT entry\n",
+ itr->dt_name);
+
+ /* cont on err to dump all missing entries */
+ if (itr->status == DT_REQ && !err)
+ err = ret;
+ }
+ }
+ }
+
+ return err;
+}
+
+/**
+ * msm_spi_dt_to_pdata: create pdata and read gpio config from device tree
+ */
+struct msm_spi_platform_data * __init msm_spi_dt_to_pdata(
+ struct platform_device *pdev, struct msm_spi *dd)
+{
+ int i;
struct msm_spi_platform_data *pdata;
- int rc;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
pr_err("Unable to allocate platform data\n");
return NULL;
+ } else {
+ struct msm_spi_dt_to_pdata_map map[] = {
+ {"spi-max-frequency",
+ &pdata->max_clock_speed, DT_SGST, DT_U32, 0},
+ {"qcom,infinite-mode",
+ &pdata->infinite_mode, DT_OPT, DT_U32, 0},
+ {"qcom,active-only",
+ &pdata->active_only, DT_OPT, DT_BOOL, 0},
+ {"qcom,master-id",
+ &pdata->master_id, DT_SGST, DT_U32, 0},
+ {"qcom,ver-reg-exists",
+ &pdata->ver_reg_exists, DT_OPT, DT_BOOL, 0},
+ {"qcom,use-bam",
+ &pdata->use_bam, DT_OPT, DT_BOOL, 0},
+ {"qcom,bam-consumer-pipe-index",
+ &pdata->bam_consumer_pipe_index, DT_OPT, DT_U32, 0},
+ {"qcom,bam-producer-pipe-index",
+ &pdata->bam_producer_pipe_index, DT_OPT, DT_U32, 0},
+ {"qcom,gpio-clk",
+ &dd->spi_gpios[0], DT_OPT, DT_GPIO, -1},
+ {"qcom,gpio-miso",
+ &dd->spi_gpios[1], DT_OPT, DT_GPIO, -1},
+ {"qcom,gpio-mosi",
+ &dd->spi_gpios[2], DT_OPT, DT_GPIO, -1},
+ {"qcom,gpio-cs0",
+ &dd->cs_gpios[0].gpio_num, DT_OPT, DT_GPIO, -1},
+ {"qcom,gpio-cs1",
+ &dd->cs_gpios[1].gpio_num, DT_OPT, DT_GPIO, -1},
+ {"qcom,gpio-cs2",
+ &dd->cs_gpios[2].gpio_num, DT_OPT, DT_GPIO, -1},
+ {"qcom,gpio-cs3",
+ &dd->cs_gpios[3].gpio_num, DT_OPT, DT_GPIO, -1},
+ {NULL, NULL, 0, 0, 0},
+ };
+
+ if (msm_spi_dt_to_pdata_populate(pdev, pdata, map)) {
+ devm_kfree(&pdev->dev, pdata);
+ return NULL;
+ }
}
- of_property_read_u32(node, "spi-max-frequency",
- &pdata->max_clock_speed);
- of_property_read_u32(node, "qcom,infinite-mode",
- &pdata->infinite_mode);
-
- pdata->ver_reg_exists = of_property_read_bool(node
- , "qcom,ver-reg-exists");
-
- pdata->use_bam = of_property_read_bool(node, "qcom,use-bam");
-
if (pdata->use_bam) {
- rc = of_property_read_u32(node, "qcom,bam-consumer-pipe-index",
- &pdata->bam_consumer_pipe_index);
- if (rc) {
+ if (!pdata->bam_consumer_pipe_index) {
dev_warn(&pdev->dev,
"missing qcom,bam-consumer-pipe-index entry in device-tree\n");
pdata->use_bam = false;
}
- rc = of_property_read_u32(node, "qcom,bam-producer-pipe-index",
- &pdata->bam_producer_pipe_index);
- if (rc) {
+ if (pdata->bam_producer_pipe_index) {
dev_warn(&pdev->dev,
"missing qcom,bam-producer-pipe-index entry in device-tree\n");
pdata->use_bam = false;
}
}
+
+ for (i = 0; i < ARRAY_SIZE(spi_cs_rsrcs); ++i)
+ dd->cs_gpios[i].valid = (dd->cs_gpios[i].gpio_num >= 0);
+
return pdata;
}
@@ -2485,7 +2734,6 @@
int clk_enabled = 0;
int pclk_enabled = 0;
struct msm_spi_platform_data *pdata;
- enum of_gpio_flags flags;
master = spi_alloc_master(&pdev->dev, sizeof(struct msm_spi));
if (!master) {
@@ -2505,7 +2753,7 @@
if (pdev->dev.of_node) {
dd->qup_ver = SPI_QUP_VERSION_BFAM;
master->dev.of_node = pdev->dev.of_node;
- pdata = msm_spi_dt_to_pdata(pdev);
+ pdata = msm_spi_dt_to_pdata(pdev, dd);
if (!pdata) {
rc = -ENOMEM;
goto err_probe_exit;
@@ -2517,18 +2765,6 @@
"using default bus_num %d\n", pdev->id);
else
master->bus_num = pdev->id = rc;
-
- for (i = 0; i < ARRAY_SIZE(spi_rsrcs); ++i) {
- dd->spi_gpios[i] = of_get_gpio_flags(pdev->dev.of_node,
- i, &flags);
- }
-
- for (i = 0; i < ARRAY_SIZE(spi_cs_rsrcs); ++i) {
- dd->cs_gpios[i].gpio_num = of_get_named_gpio_flags(
- pdev->dev.of_node, "cs-gpios",
- i, &flags);
- dd->cs_gpios[i].valid = 0;
- }
} else {
pdata = pdev->dev.platform_data;
dd->qup_ver = SPI_QUP_VERSION_NONE;
@@ -2729,7 +2965,7 @@
clk_enabled = 0;
pclk_enabled = 0;
- dd->suspended = 0;
+ dd->suspended = 1;
dd->transfer_pending = 0;
dd->multi_xfr = 0;
dd->mode = SPI_MODE_NONE;
@@ -2745,6 +2981,10 @@
mutex_unlock(&dd->core_lock);
locked = 0;
+ pm_runtime_set_autosuspend_delay(&pdev->dev, MSEC_PER_SEC);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
rc = spi_register_master(master);
if (rc)
goto err_probe_reg_master;
@@ -2762,6 +3002,7 @@
err_attrs:
spi_unregister_master(master);
err_probe_reg_master:
+ pm_runtime_disable(&pdev->dev);
err_probe_irq:
err_probe_state:
if (dd->dma_teardown)
@@ -2795,48 +3036,136 @@
}
#ifdef CONFIG_PM
-static int msm_spi_suspend(struct platform_device *pdev, pm_message_t state)
+static int msm_spi_pm_suspend_runtime(struct device *device)
{
+ struct platform_device *pdev = to_platform_device(device);
struct spi_master *master = platform_get_drvdata(pdev);
- struct msm_spi *dd;
- unsigned long flags;
+ struct msm_spi *dd;
+ unsigned long flags;
+ dev_dbg(device, "pm_runtime: suspending...\n");
if (!master)
goto suspend_exit;
dd = spi_master_get_devdata(master);
if (!dd)
goto suspend_exit;
- /* Make sure nothing is added to the queue while we're suspending */
+ if (dd->suspended)
+ return 0;
+
+ /*
+ * Make sure nothing is added to the queue while we're
+ * suspending
+ */
spin_lock_irqsave(&dd->queue_lock, flags);
dd->suspended = 1;
spin_unlock_irqrestore(&dd->queue_lock, flags);
/* Wait for transactions to end, or time out */
- wait_event_interruptible(dd->continue_suspend, !dd->transfer_pending);
+ wait_event_interruptible(dd->continue_suspend,
+ !dd->transfer_pending);
+ msm_spi_disable_irqs(dd);
+ clk_disable_unprepare(dd->clk);
+ clk_disable_unprepare(dd->pclk);
+ if (!dd->pdata->active_only)
+ msm_spi_clk_path_unvote(dd);
+
+ /* Free the spi clk, miso, mosi, cs gpio */
+ if (dd->pdata && dd->pdata->gpio_release)
+ dd->pdata->gpio_release();
+
+ msm_spi_free_gpios(dd);
+
+ if (pm_qos_request_active(&qos_req_list))
+ pm_qos_update_request(&qos_req_list,
+ PM_QOS_DEFAULT_VALUE);
suspend_exit:
return 0;
}
-static int msm_spi_resume(struct platform_device *pdev)
+static int msm_spi_pm_resume_runtime(struct device *device)
{
+ struct platform_device *pdev = to_platform_device(device);
struct spi_master *master = platform_get_drvdata(pdev);
- struct msm_spi *dd;
+ struct msm_spi *dd;
+ int ret = 0;
+ dev_dbg(device, "pm_runtime: resuming...\n");
if (!master)
goto resume_exit;
dd = spi_master_get_devdata(master);
if (!dd)
goto resume_exit;
+ if (!dd->suspended)
+ return 0;
+
+ if (pm_qos_request_active(&qos_req_list))
+ pm_qos_update_request(&qos_req_list,
+ dd->pm_lat);
+
+ /* Configure the spi clk, miso, mosi and cs gpio */
+ if (dd->pdata->gpio_config) {
+ ret = dd->pdata->gpio_config();
+ if (ret) {
+ dev_err(dd->dev,
+ "%s: error configuring GPIOs\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ ret = msm_spi_request_gpios(dd);
+ if (ret)
+ return ret;
+
+ msm_spi_clk_path_init(dd);
+ if (!dd->pdata->active_only)
+ msm_spi_clk_path_vote(dd);
+ clk_prepare_enable(dd->clk);
+ clk_prepare_enable(dd->pclk);
+ msm_spi_enable_irqs(dd);
dd->suspended = 0;
+
resume_exit:
return 0;
}
+
+static int msm_spi_suspend(struct device *device)
+{
+ if (!pm_runtime_enabled(device) || !pm_runtime_suspended(device)) {
+ struct platform_device *pdev = to_platform_device(device);
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct msm_spi *dd;
+
+ dev_dbg(device, "system suspend");
+ if (!master)
+ goto suspend_exit;
+ dd = spi_master_get_devdata(master);
+ if (!dd)
+ goto suspend_exit;
+ msm_spi_pm_suspend_runtime(device);
+ }
+suspend_exit:
+ return 0;
+}
+
+static int msm_spi_resume(struct device *device)
+{
+ /*
+ * Rely on runtime-PM to call resume in case it is enabled
+ * Even if it's not enabled, rely on 1st client transaction to do
+ * clock ON and gpio configuration
+ */
+ dev_dbg(device, "system resume");
+ return 0;
+}
#else
#define msm_spi_suspend NULL
#define msm_spi_resume NULL
+#define msm_spi_pm_suspend_runtime NULL
+#define msm_spi_pm_resume_runtime NULL
#endif /* CONFIG_PM */
static int __devexit msm_spi_remove(struct platform_device *pdev)
@@ -2850,8 +3179,11 @@
if (dd->dma_teardown)
dd->dma_teardown(dd);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
clk_put(dd->clk);
clk_put(dd->pclk);
+ msm_spi_clk_path_teardown(dd);
destroy_workqueue(dd->workqueue);
platform_set_drvdata(pdev, 0);
spi_unregister_master(master);
@@ -2867,14 +3199,19 @@
{}
};
+static const struct dev_pm_ops msm_spi_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(msm_spi_suspend, msm_spi_resume)
+ SET_RUNTIME_PM_OPS(msm_spi_pm_suspend_runtime,
+ msm_spi_pm_resume_runtime, NULL)
+};
+
static struct platform_driver msm_spi_driver = {
.driver = {
.name = SPI_DRV_NAME,
.owner = THIS_MODULE,
+ .pm = &msm_spi_dev_pm_ops,
.of_match_table = msm_spi_dt_match,
},
- .suspend = msm_spi_suspend,
- .resume = msm_spi_resume,
.remove = __exit_p(msm_spi_remove),
};
diff --git a/drivers/spi/spi_qsd.h b/drivers/spi/spi_qsd.h
index b749cc0..90d7481 100644
--- a/drivers/spi/spi_qsd.h
+++ b/drivers/spi/spi_qsd.h
@@ -72,14 +72,17 @@
#define SPI_INPUT_FIFO QSD_REG(0x0200) QUP_REG(0x0218)
#define SPI_STATE QSD_REG(SPI_OPERATIONAL) QUP_REG(0x0004)
-/* SPI_CONFIG fields */
-#define SPI_CFG_INPUT_FIRST 0x00000200
+/* QUP_CONFIG fields */
+#define SPI_CFG_N 0x0000001F
#define SPI_NO_INPUT 0x00000080
#define SPI_NO_OUTPUT 0x00000040
-#define SPI_CFG_LOOPBACK 0x00000100
-#define SPI_CFG_N 0x0000001F
#define SPI_EN_EXT_OUT_FLAG 0x00010000
+/* SPI_CONFIG fields */
+#define SPI_CFG_LOOPBACK 0x00000100
+#define SPI_CFG_INPUT_FIRST 0x00000200
+#define SPI_CFG_HS_MODE 0x00000400
+
/* SPI_IO_CONTROL fields */
#define SPI_IO_C_FORCE_CS 0x00000800
#define SPI_IO_C_CLK_IDLE_HIGH 0x00000400
@@ -143,6 +146,9 @@
#define SPI_NUM_CHIPSELECTS 4
#define SPI_SUPPORTED_MODES (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP)
+/* high speed mode is when bus rate is greater then 26MHz */
+#define SPI_HS_MIN_RATE (26000000)
+
#define SPI_DELAY_THRESHOLD 1
/* Default timeout is 10 milliseconds */
#define SPI_DEFAULT_TIMEOUT 10
@@ -167,6 +173,13 @@
#define SPI_BAM_MAX_DESC_NUM 32
#define SPI_MAX_TRFR_BTWN_RESETS ((64 * 1024) - 16) /* 64KB - 16byte */
+enum msm_spi_clk_path_vec_idx {
+ MSM_SPI_CLK_PATH_SUSPEND_VEC = 0,
+ MSM_SPI_CLK_PATH_RESUME_VEC = 1,
+};
+#define MSM_SPI_CLK_PATH_AVRG_BW(dd) (dd->pdata->max_clock_speed * 8)
+#define MSM_SPI_CLK_PATH_BRST_BW(dd) (dd->pdata->max_clock_speed * 8)
+
static char const * const spi_rsrcs[] = {
"spi_clk",
"spi_miso",
@@ -247,6 +260,22 @@
};
#endif
+/**
+ * qup_i2c_clk_path_vote: data to use bus scaling driver for clock path vote
+ *
+ * @client_hdl when zero, client is not registered with the bus scaling driver,
+ * and bus scaling functionality should not be used. When non zero, it
+ * is a bus scaling client id and may be used to vote for clock path.
+ * @reg_err when true, registration error was detected and an error message was
+ * logged. i2c will attempt to re-register but will log error only once.
+ * once registration succeed, the flag is set to false.
+ */
+struct qup_i2c_clk_path_vote {
+ u32 client_hdl;
+ struct msm_bus_scale_pdata *pdata;
+ bool reg_err;
+};
+
struct msm_spi_bam_pipe {
struct sps_pipe *handle;
struct sps_connect config;
@@ -278,6 +307,7 @@
struct completion transfer_complete;
struct clk *clk; /* core clock */
struct clk *pclk; /* interface clock */
+ struct qup_i2c_clk_path_vote clk_path_vote;
unsigned long mem_phys_addr;
size_t mem_size;
int input_fifo_size;
diff --git a/drivers/spmi/spmi-dbgfs.c b/drivers/spmi/spmi-dbgfs.c
index b825ade..27df09333 100644
--- a/drivers/spmi/spmi-dbgfs.c
+++ b/drivers/spmi/spmi-dbgfs.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -24,7 +24,6 @@
* /spmi-#
*/
-#define DEBUG
#define pr_fmt(fmt) "%s:%d: " fmt, __func__, __LINE__
#include <linux/kernel.h>
@@ -582,7 +581,7 @@
{
struct dentry *root, *file;
- pr_debug("Creating SPMI debugfs file-system at\n");
+ pr_debug("Creating SPMI debugfs file-system\n");
root = debugfs_create_dir(DFS_ROOT_NAME, NULL);
if (IS_ERR(root)) {
pr_err("Error creating top level directory err:%ld",
@@ -697,6 +696,41 @@
}
/*
+ * spmi_dfs_del_controller: deletes spmi controller entry
+ * @return zero on success
+ */
+int spmi_dfs_del_controller(struct spmi_controller *ctrl)
+{
+ int rc;
+ struct list_head *pos, *tmp;
+ struct spmi_ctrl_data *ctrl_data;
+
+ pr_debug("Deleting controller %s\n", ctrl->dev.kobj.name);
+
+ rc = mutex_lock_interruptible(&dbgfs_data.lock);
+ if (rc)
+ return rc;
+
+ list_for_each_safe(pos, tmp, &dbgfs_data.ctrl) {
+ ctrl_data = list_entry(pos, struct spmi_ctrl_data, node);
+
+ if (ctrl_data->ctrl == ctrl) {
+ debugfs_remove_recursive(ctrl_data->dir);
+ list_del(pos);
+ kfree(ctrl_data);
+ rc = 0;
+ goto done;
+ }
+ }
+ rc = -EINVAL;
+ pr_debug("Unknown controller %s\n", ctrl->dev.kobj.name);
+
+done:
+ mutex_unlock(&dbgfs_data.lock);
+ return rc;
+}
+
+/*
* spmi_dfs_create_file: creates a new file in the SPMI debugfs
* @returns valid dentry pointer on success or NULL
*/
diff --git a/drivers/spmi/spmi-dbgfs.h b/drivers/spmi/spmi-dbgfs.h
index 2a0d815..10e98b9 100644
--- a/drivers/spmi/spmi-dbgfs.h
+++ b/drivers/spmi/spmi-dbgfs.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -16,8 +16,10 @@
#ifdef CONFIG_DEBUG_FS
int spmi_dfs_add_controller(struct spmi_controller *ctrl);
+int spmi_dfs_del_controller(struct spmi_controller *ctrl);
#else
static int spmi_dfs_add_controller(struct spmi_controller *ctrl) { return 0; }
+static int spmi_dfs_del_controller(struct spmi_controller *ctrl) { return 0; }
#endif
struct dentry *spmi_dfs_create_file(struct spmi_controller *ctrl,
diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c
index 49a27c7..fc21fbb 100644
--- a/drivers/spmi/spmi.c
+++ b/drivers/spmi/spmi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -31,11 +31,9 @@
static DEFINE_MUTEX(board_lock);
static LIST_HEAD(board_list);
-static LIST_HEAD(spmi_ctrl_list);
static DEFINE_IDR(ctrl_idr);
-static struct device_type spmi_ctrl_type = { 0 };
-
-#define to_spmi(dev) platform_get_drvdata(to_platform_device(dev))
+static struct device_type spmi_dev_type;
+static struct device_type spmi_ctrl_type;
/* Forward declarations */
struct bus_type spmi_bus_type;
@@ -51,14 +49,10 @@
struct spmi_controller *ctrl;
mutex_lock(&board_lock);
- list_for_each_entry(ctrl, &spmi_ctrl_list, list) {
- if (bus_num == ctrl->nr) {
- mutex_unlock(&board_lock);
- return ctrl;
- }
- }
+ ctrl = idr_find(&ctrl_idr, bus_num);
mutex_unlock(&board_lock);
- return NULL;
+
+ return ctrl;
}
EXPORT_SYMBOL_GPL(spmi_busnum_to_ctrl);
@@ -74,6 +68,9 @@
int id;
int status;
+ if (!ctrl)
+ return -EINVAL;
+
pr_debug("adding controller for bus %d (0x%p)\n", ctrl->nr, ctrl);
if (ctrl->nr & ~MAX_ID_MASK) {
@@ -90,7 +87,7 @@
mutex_lock(&board_lock);
status = idr_get_new_above(&ctrl_idr, ctrl, ctrl->nr, &id);
if (status == 0 && id != ctrl->nr) {
- status = -EAGAIN;
+ status = -EBUSY;
idr_remove(&ctrl_idr, id);
}
mutex_unlock(&board_lock);
@@ -103,18 +100,70 @@
}
EXPORT_SYMBOL_GPL(spmi_add_controller);
+/* Remove a device associated with a controller */
+static int spmi_ctrl_remove_device(struct device *dev, void *data)
+{
+ struct spmi_device *spmidev = to_spmi_device(dev);
+ struct spmi_controller *ctrl = data;
+
+ if (dev->type == &spmi_dev_type && spmidev->ctrl == ctrl)
+ spmi_remove_device(spmidev);
+
+ return 0;
+}
+
/**
* spmi_del_controller: Controller tear-down.
- * @ctrl: controller to which this device is to be added to.
+ * @ctrl: controller to be removed.
*
* Controller added with the above API is torn down using this API.
*/
int spmi_del_controller(struct spmi_controller *ctrl)
{
- return -ENXIO;
+ struct spmi_controller *found;
+
+ if (!ctrl)
+ return -EINVAL;
+
+ /* Check that the ctrl has been added */
+ mutex_lock(&board_lock);
+ found = idr_find(&ctrl_idr, ctrl->nr);
+ mutex_unlock(&board_lock);
+ if (found != ctrl)
+ return -EINVAL;
+
+ /* Remove all the clients associated with this controller */
+ mutex_lock(&board_lock);
+ bus_for_each_dev(&spmi_bus_type, NULL, ctrl, spmi_ctrl_remove_device);
+ mutex_unlock(&board_lock);
+
+ spmi_dfs_del_controller(ctrl);
+
+ mutex_lock(&board_lock);
+ idr_remove(&ctrl_idr, ctrl->nr);
+ mutex_unlock(&board_lock);
+
+ init_completion(&ctrl->dev_released);
+ device_unregister(&ctrl->dev);
+ wait_for_completion(&ctrl->dev_released);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(spmi_del_controller);
+#define spmi_ctrl_attr_gr NULL
+static void spmi_ctrl_release(struct device *dev)
+{
+ struct spmi_controller *ctrl = to_spmi_controller(dev);
+
+ complete(&ctrl->dev_released);
+}
+
+static struct device_type spmi_ctrl_type = {
+ .groups = spmi_ctrl_attr_gr,
+ .release = spmi_ctrl_release,
+};
+
#define spmi_device_attr_gr NULL
#define spmi_device_uevent NULL
static void spmi_dev_release(struct device *dev)
@@ -146,7 +195,7 @@
{
struct spmi_device *spmidev;
- if (!ctrl) {
+ if (!ctrl || !spmi_busnum_to_ctrl(ctrl->nr)) {
pr_err("Missing SPMI controller\n");
return NULL;
}
@@ -179,12 +228,15 @@
if (dev->bus != &spmi_bus_type || dev->type != &spmi_dev_type)
return NULL;
+ if (!spmidev->ctrl || !spmi_busnum_to_ctrl(spmidev->ctrl->nr))
+ return NULL;
+
return dev;
}
/**
* spmi_add_device: Add a new device without register board info.
- * @ctrl: controller to which this device is to be added to.
+ * @spmi_dev: spmi_device to be added (registered).
*
* Called when device doesn't have an explicit client-driver to be probed, or
* the client-driver is a module installed dynamically.
@@ -195,7 +247,7 @@
struct device *dev = get_valid_device(spmidev);
if (!dev) {
- pr_err("%s: invalid SPMI device\n", __func__);
+ pr_err("invalid SPMI device\n");
return -EINVAL;
}
@@ -259,7 +311,6 @@
}
EXPORT_SYMBOL_GPL(spmi_remove_device);
-/* If controller is not present, only add to boards list */
static void spmi_match_ctrl_to_boardinfo(struct spmi_controller *ctrl,
struct spmi_boardinfo *bi)
{
@@ -278,27 +329,29 @@
* @n: number of entries.
* API enumerates respective devices on corresponding controller.
* Called from board-init function.
+ * If controller is not present, only add to boards list
*/
int spmi_register_board_info(int busnum,
struct spmi_boardinfo const *info, unsigned n)
{
int i;
struct spmii_boardinfo *bi;
+ struct spmi_controller *ctrl;
bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);
if (!bi)
return -ENOMEM;
+ ctrl = spmi_busnum_to_ctrl(busnum);
+
for (i = 0; i < n; i++, bi++, info++) {
- struct spmi_controller *ctrl;
memcpy(&bi->board_info, info, sizeof(*info));
mutex_lock(&board_lock);
list_add_tail(&bi->list, &board_list);
- list_for_each_entry(ctrl, &spmi_ctrl_list, list)
- if (ctrl->nr == busnum)
- spmi_match_ctrl_to_boardinfo(ctrl,
- &bi->board_info);
+
+ if (ctrl)
+ spmi_match_ctrl_to_boardinfo(ctrl, &bi->board_info);
mutex_unlock(&board_lock);
}
return 0;
@@ -753,10 +806,6 @@
dev_dbg(&ctrl->dev, "Bus spmi-%d registered: dev:%x\n",
ctrl->nr, (u32)&ctrl->dev);
- mutex_lock(&board_lock);
- list_add_tail(&ctrl->list, &spmi_ctrl_list);
- mutex_unlock(&board_lock);
-
spmi_dfs_add_controller(ctrl);
return 0;
diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c
index 6cd30f1..3abc3b9 100644
--- a/drivers/staging/android/binder.c
+++ b/drivers/staging/android/binder.c
@@ -2525,14 +2525,38 @@
struct binder_transaction *t;
t = container_of(w, struct binder_transaction, work);
- if (t->buffer->target_node && !(t->flags & TF_ONE_WAY))
+ if (t->buffer->target_node &&
+ !(t->flags & TF_ONE_WAY)) {
binder_send_failed_reply(t, BR_DEAD_REPLY);
+ } else {
+ binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
+ "binder: undelivered transaction %d\n",
+ t->debug_id);
+ t->buffer->transaction = NULL;
+ kfree(t);
+ binder_stats_deleted(BINDER_STAT_TRANSACTION);
+ }
} break;
case BINDER_WORK_TRANSACTION_COMPLETE: {
+ binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
+ "binder: undelivered TRANSACTION_COMPLETE\n");
kfree(w);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
} break;
+ case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
+ case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
+ struct binder_ref_death *death;
+
+ death = container_of(w, struct binder_ref_death, work);
+ binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
+ "binder: undelivered death notification, %p\n",
+ death->cookie);
+ kfree(death);
+ binder_stats_deleted(BINDER_STAT_DEATH);
+ } break;
default:
+ pr_err("binder: unexpected work type, %d, not freed\n",
+ w->type);
break;
}
}
@@ -3009,6 +3033,7 @@
nodes++;
rb_erase(&node->rb_node, &proc->nodes);
list_del_init(&node->work.entry);
+ binder_release_work(&node->async_todo);
if (hlist_empty(&node->refs)) {
kfree(node);
binder_stats_deleted(BINDER_STAT_NODE);
@@ -3047,6 +3072,7 @@
binder_delete_ref(ref);
}
binder_release_work(&proc->todo);
+ binder_release_work(&proc->delivered_death);
buffers = 0;
while ((n = rb_first(&proc->allocated_buffers))) {
diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c
index eb3d4ca..e1e886c 100644
--- a/drivers/staging/android/logger.c
+++ b/drivers/staging/android/logger.c
@@ -728,10 +728,10 @@
.size = SIZE, \
};
-DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 256*1024)
-DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS, 256*1024)
-DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 256*1024)
-DEFINE_LOGGER_DEVICE(log_system, LOGGER_LOG_SYSTEM, 256*1024)
+DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024)
+DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS, 64*1024)
+DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 64*1024)
+DEFINE_LOGGER_DEVICE(log_system, LOGGER_LOG_SYSTEM, 64*1024)
static struct logger_log *get_log_from_minor(int minor)
{
diff --git a/drivers/staging/android/timed_output.c b/drivers/staging/android/timed_output.c
index f373422..4829b83 100644
--- a/drivers/staging/android/timed_output.c
+++ b/drivers/staging/android/timed_output.c
@@ -100,8 +100,8 @@
void timed_output_dev_unregister(struct timed_output_dev *tdev)
{
device_remove_file(tdev->dev, &dev_attr_enable);
- device_destroy(timed_output_class, MKDEV(0, tdev->index));
dev_set_drvdata(tdev->dev, NULL);
+ device_destroy(timed_output_class, MKDEV(0, tdev->index));
}
EXPORT_SYMBOL_GPL(timed_output_dev_unregister);
diff --git a/drivers/thermal/msm8974-tsens.c b/drivers/thermal/msm8974-tsens.c
index 95378c5..52608af 100644
--- a/drivers/thermal/msm8974-tsens.c
+++ b/drivers/thermal/msm8974-tsens.c
@@ -17,6 +17,7 @@
#include <linux/platform_device.h>
#include <linux/thermal.h>
#include <linux/interrupt.h>
+#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/io.h>
@@ -199,8 +200,8 @@
#define TSENS0_8X10_POINT1_SHIFT 16
#define TSENS0_8X10_POINT2_SHIFT 22
#define TSENS1_8X10_POINT2_SHIFT 6
-#define TSENS_8X10_BASE0_MASK 0xf
-#define TSENS_8X10_BASE1_MASK 0xf0
+#define TSENS_8X10_BASE0_MASK 0xff
+#define TSENS_8X10_BASE1_MASK 0xff00
#define TSENS0_8X10_POINT1_MASK 0x3f0000
#define TSENS0_8X10_POINT2_MASK 0xfc00000
#define TSENS_8X10_TSENS_CAL_SEL 0x70000000
@@ -258,6 +259,7 @@
struct tsens_tm_device {
struct platform_device *pdev;
+ struct workqueue_struct *tsens_wq;
bool prev_reading_avail;
bool calibration_less_mode;
bool tsens_local_init;
@@ -333,6 +335,8 @@
else
degc = num/den;
+ pr_debug("raw_code:0x%x, sensor_num:%d, degc:%d\n",
+ adc_code, idx, degc);
return degc;
}
@@ -345,6 +349,8 @@
code = TSENS_THRESHOLD_MAX_CODE;
else if (code < TSENS_THRESHOLD_MIN_CODE)
code = TSENS_THRESHOLD_MIN_CODE;
+ pr_debug("raw_code:0x%x, sensor_num:%d, degc:%d\n",
+ code, idx, degc);
return code;
}
@@ -651,7 +657,7 @@
}
if (upper_thr || lower_thr) {
/* Notify user space */
- schedule_work(&tm->sensor[i].work);
+ queue_work(tm->tsens_wq, &tm->sensor[i].work);
rc = tsens_get_sw_id_mapping(
tm->sensor[i].sensor_hw_num,
&sensor_sw_id);
@@ -669,7 +675,7 @@
static irqreturn_t tsens_isr(int irq, void *data)
{
- schedule_work(&tmdev->tsens_work);
+ queue_work(tmdev->tsens_wq, &tmdev->tsens_work);
return IRQ_HANDLED;
}
@@ -729,6 +735,7 @@
tsens_calibration_mode = (calib_data[0] & TSENS_8X10_TSENS_CAL_SEL)
>> TSENS_8X10_CAL_SEL_SHIFT;
+ pr_debug("calib mode scheme:%x\n", tsens_calibration_mode);
if ((tsens_calibration_mode == TSENS_TWO_POINT_CALIB) ||
(tsens_calibration_mode == TSENS_ONE_POINT_CALIB_OPTION_2)) {
@@ -783,6 +790,9 @@
int32_t num = 0, den = 0;
tmdev->sensor[i].calib_data_point2 = calib_tsens_point2_data[i];
tmdev->sensor[i].calib_data_point1 = calib_tsens_point1_data[i];
+ pr_debug("sensor:%d - calib_data_point1:0x%x, calib_data_point2:0x%x\n",
+ i, tmdev->sensor[i].calib_data_point1,
+ tmdev->sensor[i].calib_data_point2);
if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
/* slope (m) = adc_code2 - adc_code1 (y2 - y1)/
temp_120_degc - temp_30_degc (x2 - x1) */
@@ -827,6 +837,7 @@
tsens_calibration_mode = (calib_data[5] & TSENS_8X26_TSENS_CAL_SEL)
>> TSENS_8X26_CAL_SEL_SHIFT;
+ pr_debug("calib mode scheme:%x\n", tsens_calibration_mode);
if ((tsens_calibration_mode == TSENS_TWO_POINT_CALIB) ||
(tsens_calibration_mode == TSENS_ONE_POINT_CALIB_OPTION_2)) {
@@ -936,6 +947,9 @@
int32_t num = 0, den = 0;
tmdev->sensor[i].calib_data_point2 = calib_tsens_point2_data[i];
tmdev->sensor[i].calib_data_point1 = calib_tsens_point1_data[i];
+ pr_debug("sensor:%d - calib_data_point1:0x%x, calib_data_point2:0x%x\n",
+ i, tmdev->sensor[i].calib_data_point1,
+ tmdev->sensor[i].calib_data_point2);
if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
/* slope (m) = adc_code2 - adc_code1 (y2 - y1)/
temp_120_degc - temp_30_degc (x2 - x1) */
@@ -976,11 +990,14 @@
TSENS_EEPROM_REDUNDANCY_SEL(tmdev->tsens_calib_addr));
calib_redun_sel = calib_redun_sel & TSENS_QFPROM_BACKUP_REDUN_SEL;
calib_redun_sel >>= TSENS_QFPROM_BACKUP_REDUN_SHIFT;
+ pr_debug("calib_redun_sel:%x\n", calib_redun_sel);
- for (i = 0; i < TSENS_MAIN_CALIB_ADDR_RANGE; i++)
+ for (i = 0; i < TSENS_MAIN_CALIB_ADDR_RANGE; i++) {
calib_data[i] = readl_relaxed(
(TSENS_EEPROM(tmdev->tsens_calib_addr))
+ (i * TSENS_SN_ADDR_OFFSET));
+ pr_debug("calib raw data row%d:0x%x\n", i, calib_data[i]);
+ }
if (calib_redun_sel == TSENS_QFPROM_BACKUP_SEL) {
tsens_calibration_mode = (calib_data[4] & TSENS_CAL_SEL_0_1)
@@ -988,6 +1005,7 @@
temp = (calib_data[5] & TSENS_CAL_SEL_2)
>> TSENS_CAL_SEL_SHIFT_2;
tsens_calibration_mode |= temp;
+ pr_debug("backup calib mode:%x\n", calib_redun_sel);
for (i = 0; i < TSENS_BACKUP_CALIB_ADDR_RANGE; i++)
calib_data_backup[i] = readl_relaxed(
@@ -1073,6 +1091,7 @@
temp = (calib_data[3] & TSENS_CAL_SEL_2)
>> TSENS_CAL_SEL_SHIFT_2;
tsens_calibration_mode |= temp;
+ pr_debug("calib mode scheme:%x\n", tsens_calibration_mode);
if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB) ||
(tsens_calibration_mode ==
TSENS_ONE_POINT_CALIB_OPTION_2) ||
@@ -1184,6 +1203,7 @@
if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB_OPTION_2) ||
(tsens_calibration_mode == TSENS_TWO_POINT_CALIB)) {
+ pr_debug("one point calibration calculation\n");
calib_tsens_point1_data[0] =
((((tsens_base1_data) + tsens0_point1) << 2) |
TSENS_BIT_APPEND);
@@ -1261,6 +1281,9 @@
int32_t num = 0, den = 0;
tmdev->sensor[i].calib_data_point2 = calib_tsens_point2_data[i];
tmdev->sensor[i].calib_data_point1 = calib_tsens_point1_data[i];
+ pr_debug("sensor:%d - calib_data_point1:0x%x, calib_data_point2:0x%x\n",
+ i, tmdev->sensor[i].calib_data_point1,
+ tmdev->sensor[i].calib_data_point2);
if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
/* slope (m) = adc_code2 - adc_code1 (y2 - y1)/
temp_120_degc - temp_30_degc (x2 - x1) */
@@ -1273,6 +1296,7 @@
tmdev->sensor[i].offset = (tmdev->sensor[i].calib_data_point1 *
tmdev->tsens_factor) - (TSENS_CAL_DEGC_POINT1 *
tmdev->sensor[i].slope_mul_tsens_factor);
+ pr_debug("offset:%d\n", tmdev->sensor[i].offset);
INIT_WORK(&tmdev->sensor[i].work, notify_uspace_tsens_fn);
tmdev->prev_reading_avail = false;
}
@@ -1484,6 +1508,12 @@
return -ENODEV;
tmdev->pdev = pdev;
+ tmdev->tsens_wq = alloc_workqueue("tsens_wq", WQ_HIGHPRI, 0);
+ if (!tmdev->tsens_wq) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+
rc = tsens_calib_sensors();
if (rc < 0) {
pr_err("Calibration failed\n");
@@ -1498,6 +1528,8 @@
return 0;
fail:
+ if (tmdev->tsens_wq)
+ destroy_workqueue(tmdev->tsens_wq);
if (tmdev->tsens_calib_addr)
iounmap(tmdev->tsens_calib_addr);
if (tmdev->res_calib_mem)
@@ -1588,6 +1620,7 @@
release_mem_region(tmdev->res_tsens_mem->start,
tmdev->tsens_len);
free_irq(tmdev->tsens_irq, tmdev);
+ destroy_workqueue(tmdev->tsens_wq);
platform_set_drvdata(pdev, NULL);
return 0;
diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c
index 12ac3bc..03c58a2 100644
--- a/drivers/thermal/msm_thermal.c
+++ b/drivers/thermal/msm_thermal.c
@@ -28,6 +28,7 @@
#include <linux/of.h>
#include <linux/sysfs.h>
#include <linux/types.h>
+#include <linux/android_alarm.h>
#include <mach/cpufreq.h>
#include <mach/rpm-regulator.h>
#include <mach/rpm-regulator-smd.h>
@@ -41,6 +42,10 @@
static bool core_control_enabled;
static uint32_t cpus_offlined;
static DEFINE_MUTEX(core_control_mutex);
+static uint32_t wakeup_ms;
+static struct alarm thermal_rtc;
+static struct kobject *tt_kobj;
+static struct work_struct timer_work;
static int enabled;
static int rails_cnt;
@@ -58,6 +63,7 @@
static bool psm_enabled;
static bool psm_nodes_called;
static bool psm_probed;
+static int *tsens_id_map;
static DEFINE_MUTEX(vdd_rstr_mutex);
static DEFINE_MUTEX(psm_mutex);
@@ -66,7 +72,7 @@
uint32_t freq_req;
uint32_t min_level;
uint32_t num_levels;
- uint32_t curr_level;
+ int32_t curr_level;
uint32_t levels[3];
struct kobj_attribute value_attr;
struct kobj_attribute level_attr;
@@ -103,6 +109,7 @@
ko_attr.attr.mode = 444; \
ko_attr.show = vdd_rstr_reg_##_name##_show; \
ko_attr.store = NULL; \
+ sysfs_attr_init(&ko_attr.attr); \
_rail.attr_gp.attrs[j] = &ko_attr.attr;
#define VDD_RES_RW_ATTRIB(_rail, ko_attr, j, _name) \
@@ -110,6 +117,7 @@
ko_attr.attr.mode = 644; \
ko_attr.show = vdd_rstr_reg_##_name##_show; \
ko_attr.store = vdd_rstr_reg_##_name##_store; \
+ sysfs_attr_init(&ko_attr.attr); \
_rail.attr_gp.attrs[j] = &ko_attr.attr;
#define VDD_RSTR_ENABLE_FROM_ATTRIBS(attr) \
@@ -126,6 +134,7 @@
ko_attr.attr.mode = 644; \
ko_attr.show = psm_reg_##_name##_show; \
ko_attr.store = psm_reg_##_name##_store; \
+ sysfs_attr_init(&ko_attr.attr); \
_rail.attr_gp.attrs[j] = &ko_attr.attr;
#define PSM_REG_MODE_FROM_ATTRIBS(attr) \
@@ -150,6 +159,7 @@
{
int cpu = 0;
int ret = 0;
+ struct cpufreq_policy *policy = NULL;
if (!freq_table_get) {
ret = check_freq_table();
@@ -165,16 +175,20 @@
for_each_possible_cpu(cpu) {
ret = msm_cpufreq_set_freq_limits(cpu, min, limited_max_freq);
-
if (ret) {
pr_err("%s:Fail to set limits for cpu%d\n",
__func__, cpu);
return ret;
}
- if (cpufreq_update_policy(cpu))
- pr_debug("%s: Cannot update policy for cpu%d\n",
- __func__, cpu);
+ if (cpu_online(cpu)) {
+ policy = cpufreq_cpu_get(cpu);
+ if (!policy)
+ continue;
+ cpufreq_driver_target(policy, policy->cur,
+ CPUFREQ_RELATION_L);
+ cpufreq_cpu_put(policy);
+ }
}
return ret;
@@ -184,6 +198,9 @@
{
int ret = 0;
+ if (level == r->curr_level)
+ return ret;
+
/* level = -1: disable, level = 0,1,2..n: enable */
if (level == -1) {
ret = update_cpu_min_freq_all(r->min_level);
@@ -214,6 +231,9 @@
r->name);
return -EFAULT;
}
+ if (level == r->curr_level)
+ return ret;
+
/* level = -1: disable, level = 0,1,2..n: enable */
if (level == -1) {
ret = regulator_set_voltage(r->reg, r->min_level,
@@ -232,32 +252,6 @@
return ret;
}
-/* 1:enable, 0:disable */
-static int vdd_restriction_apply_all(int en)
-{
- int i = 0;
- int fail_cnt = 0;
- int ret = 0;
-
- for (i = 0; i < rails_cnt; i++) {
- if (rails[i].freq_req == 1 && freq_table_get)
- ret = vdd_restriction_apply_freq(&rails[i],
- en ? 0 : -1);
- else
- ret = vdd_restriction_apply_voltage(&rails[i],
- en ? 0 : -1);
- if (ret) {
- pr_err("Cannot set voltage for %s", rails[i].name);
- fail_cnt++;
- }
- }
- /* Check fail_cnt again to make sure all of the rails are applied
- * restriction successfully or not */
- if (fail_cnt)
- return -EFAULT;
-
- return ret;
-}
/* Setting all rails the same mode */
static int psm_set_mode_all(int mode)
@@ -319,8 +313,10 @@
ret = vdd_restriction_apply_voltage(&rails[i],
(val) ? 0 : -1);
- /* Even if fail to set one rail, still try to set the
- * others. Continue the loop */
+ /*
+ * Even if fail to set one rail, still try to set the
+ * others. Continue the loop
+ */
if (ret)
pr_err("Set vdd restriction for %s failed\n",
rails[i].name);
@@ -370,7 +366,7 @@
else
val = reg->levels[reg->curr_level];
- return snprintf(buf, PAGE_SIZE, "%d\n", reg->levels[reg->curr_level]);
+ return snprintf(buf, PAGE_SIZE, "%d\n", val);
}
static int vdd_rstr_reg_level_show(
@@ -392,7 +388,7 @@
if (vdd_rstr_en.enabled == 0)
goto done_store_level;
- ret = kstrtoint(buf, 10, &val);
+ ret = kstrtouint(buf, 10, &val);
if (ret) {
pr_err("Invalid input %s for level\n", buf);
goto done_store_level;
@@ -465,6 +461,103 @@
return count;
}
+static int check_sensor_id(int sensor_id)
+{
+ int i = 0;
+ bool hw_id_found;
+ int ret = 0;
+
+ for (i = 0; i < max_tsens_num; i++) {
+ if (sensor_id == tsens_id_map[i]) {
+ hw_id_found = true;
+ break;
+ }
+ }
+ if (!hw_id_found) {
+ pr_err("%s: Invalid sensor hw id :%d\n", __func__, sensor_id);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int create_sensor_id_map(void)
+{
+ int i = 0;
+ int ret = 0;
+
+ tsens_id_map = kzalloc(sizeof(int) * max_tsens_num,
+ GFP_KERNEL);
+ if (!tsens_id_map) {
+ pr_err("%s: Cannot allocate memory for tsens_id_map\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < max_tsens_num; i++) {
+ ret = tsens_get_hw_id_mapping(i, &tsens_id_map[i]);
+ /* If return -ENXIO, hw_id is default in sequence */
+ if (ret) {
+ if (ret == -ENXIO) {
+ tsens_id_map[i] = i;
+ ret = 0;
+ } else {
+ pr_err( \
+ "%s: Failed to get hw id for sw id %d\n",
+ __func__, i);
+ goto fail;
+ }
+ }
+ }
+
+ return ret;
+fail:
+ kfree(tsens_id_map);
+ return ret;
+}
+
+/* 1:enable, 0:disable */
+static int vdd_restriction_apply_all(int en)
+{
+ int i = 0;
+ int en_cnt = 0;
+ int dis_cnt = 0;
+ int fail_cnt = 0;
+ int ret = 0;
+
+ for (i = 0; i < rails_cnt; i++) {
+ if (rails[i].freq_req == 1 && freq_table_get)
+ ret = vdd_restriction_apply_freq(&rails[i],
+ en ? 0 : -1);
+ else
+ ret = vdd_restriction_apply_voltage(&rails[i],
+ en ? 0 : -1);
+ if (ret) {
+ pr_err("Cannot set voltage for %s", rails[i].name);
+ fail_cnt++;
+ } else {
+ if (en)
+ en_cnt++;
+ else
+ dis_cnt++;
+ }
+ }
+
+ /* As long as one rail is enabled, vdd rstr is enabled */
+ if (en && en_cnt)
+ vdd_rstr_en.enabled = 1;
+ else if (!en && (dis_cnt == rails_cnt))
+ vdd_rstr_en.enabled = 0;
+
+ /*
+ * Check fail_cnt again to make sure all of the rails are applied
+ * restriction successfully or not
+ */
+ if (fail_cnt)
+ return -EFAULT;
+ return ret;
+}
+
static int msm_thermal_get_freq_table(void)
{
int ret = 0;
@@ -503,12 +596,20 @@
pr_info("%s: Max frequency reset for cpu%d\n",
KBUILD_MODNAME, cpu);
- ret = cpufreq_update_policy(cpu);
+ if (cpu_online(cpu)) {
+ struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+ if (!policy)
+ return ret;
+ ret = cpufreq_driver_target(policy, policy->cur,
+ CPUFREQ_RELATION_H);
+ cpufreq_cpu_put(policy);
+ }
return ret;
}
-static void __cpuinit do_core_control(long temp)
+#ifdef CONFIG_SMP
+static void __ref do_core_control(long temp)
{
int i = 0;
int ret = 0;
@@ -542,7 +643,8 @@
cpus_offlined &= ~BIT(i);
pr_info("%s: Allow Online CPU%d Temp: %ld\n",
KBUILD_MODNAME, i, temp);
- /* If this core is already online, then bring up the
+ /*
+ * If this core is already online, then bring up the
* next offlined core.
*/
if (cpu_online(i))
@@ -556,6 +658,12 @@
}
mutex_unlock(&core_control_mutex);
}
+#else
+static void do_core_control(long temp)
+{
+ return;
+}
+#endif
static int do_vdd_restriction(void)
{
@@ -575,7 +683,7 @@
mutex_lock(&vdd_rstr_mutex);
for (i = 0; i < max_tsens_num; i++) {
- tsens_dev.sensor_num = i;
+ tsens_dev.sensor_num = tsens_id_map[i];
ret = tsens_get_temp(&tsens_dev, &temp);
if (ret) {
pr_debug("%s: Unable to read TSENS sensor %d\n",
@@ -583,18 +691,15 @@
dis_cnt++;
continue;
}
- if (temp <= msm_thermal_info.vdd_rstr_temp_hyst_degC &&
- vdd_rstr_en.enabled == 0) {
+ if (temp <= msm_thermal_info.vdd_rstr_temp_degC) {
ret = vdd_restriction_apply_all(1);
if (ret) {
pr_err( \
"Enable vdd rstr votlage for all failed\n");
goto exit;
}
- vdd_rstr_en.enabled = 1;
goto exit;
- } else if (temp > msm_thermal_info.vdd_rstr_temp_degC &&
- vdd_rstr_en.enabled == 1)
+ } else if (temp > msm_thermal_info.vdd_rstr_temp_hyst_degC)
dis_cnt++;
}
if (dis_cnt == max_tsens_num) {
@@ -603,7 +708,6 @@
pr_err("Disable vdd rstr votlage for all failed\n");
goto exit;
}
- vdd_rstr_en.enabled = 0;
}
exit:
mutex_unlock(&vdd_rstr_mutex);
@@ -620,7 +724,7 @@
mutex_lock(&psm_mutex);
for (i = 0; i < max_tsens_num; i++) {
- tsens_dev.sensor_num = i;
+ tsens_dev.sensor_num = tsens_id_map[i];
ret = tsens_get_temp(&tsens_dev, &temp);
if (ret) {
pr_debug("%s: Unable to read TSENS sensor %d\n",
@@ -629,9 +733,11 @@
continue;
}
- /* As long as one sensor is above the threshold, set PWM mode
+ /*
+ * As long as one sensor is above the threshold, set PWM mode
* on all rails, and loop stops. Set auto mode when all rails
- * are below thershold */
+ * are below thershold
+ */
if (temp > msm_thermal_info.psm_temp_degC) {
ret = psm_set_mode_all(PMIC_PWM_MODE);
if (ret) {
@@ -656,14 +762,56 @@
return ret;
}
-static void __cpuinit check_temp(struct work_struct *work)
+static void __ref do_freq_control(long temp)
+{
+ int ret = 0;
+ int cpu = 0;
+ uint32_t max_freq = limited_max_freq;
+
+ if (temp >= msm_thermal_info.limit_temp_degC) {
+ if (limit_idx == limit_idx_low)
+ return;
+
+ limit_idx -= msm_thermal_info.freq_step;
+ if (limit_idx < limit_idx_low)
+ limit_idx = limit_idx_low;
+ max_freq = table[limit_idx].frequency;
+ } else if (temp < msm_thermal_info.limit_temp_degC -
+ msm_thermal_info.temp_hysteresis_degC) {
+ if (limit_idx == limit_idx_high)
+ return;
+
+ limit_idx += msm_thermal_info.freq_step;
+ if (limit_idx >= limit_idx_high) {
+ limit_idx = limit_idx_high;
+ max_freq = MSM_CPUFREQ_NO_LIMIT;
+ } else
+ max_freq = table[limit_idx].frequency;
+ }
+
+ if (max_freq == limited_max_freq)
+ return;
+
+ /* Update new limits */
+ for_each_possible_cpu(cpu) {
+ if (!(msm_thermal_info.freq_control_mask & BIT(cpu)))
+ continue;
+ ret = update_cpu_max_freq(cpu, max_freq);
+ if (ret)
+ pr_debug(
+ "%s: Unable to limit cpu%d max freq to %d\n",
+ KBUILD_MODNAME, cpu, max_freq);
+ }
+
+}
+
+static void __ref check_temp(struct work_struct *work)
{
static int limit_init;
struct tsens_device tsens_dev;
long temp = 0;
- uint32_t max_freq = limited_max_freq;
- int cpu = 0;
int ret = 0;
+
tsens_dev.sensor_num = msm_thermal_info.sensor_id;
ret = tsens_get_temp(&tsens_dev, &temp);
if (ret) {
@@ -683,38 +831,7 @@
do_core_control(temp);
do_vdd_restriction();
do_psm();
-
- if (temp >= msm_thermal_info.limit_temp_degC) {
- if (limit_idx == limit_idx_low)
- goto reschedule;
-
- limit_idx -= msm_thermal_info.freq_step;
- if (limit_idx < limit_idx_low)
- limit_idx = limit_idx_low;
- max_freq = table[limit_idx].frequency;
- } else if (temp < msm_thermal_info.limit_temp_degC -
- msm_thermal_info.temp_hysteresis_degC) {
- if (limit_idx == limit_idx_high)
- goto reschedule;
-
- limit_idx += msm_thermal_info.freq_step;
- if (limit_idx >= limit_idx_high) {
- limit_idx = limit_idx_high;
- max_freq = MSM_CPUFREQ_NO_LIMIT;
- } else
- max_freq = table[limit_idx].frequency;
- }
- if (max_freq == limited_max_freq)
- goto reschedule;
-
- /* Update new limits */
- for_each_possible_cpu(cpu) {
- ret = update_cpu_max_freq(cpu, max_freq);
- if (ret)
- pr_debug(
- "%s: Unable to limit cpu%d max freq to %d\n",
- KBUILD_MODNAME, cpu, max_freq);
- }
+ do_freq_control(temp);
reschedule:
if (enabled)
@@ -722,7 +839,7 @@
msecs_to_jiffies(msm_thermal_info.poll_ms));
}
-static int __cpuinit msm_thermal_cpu_callback(struct notifier_block *nfb,
+static int __ref msm_thermal_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned long)hcpu;
@@ -746,12 +863,45 @@
.notifier_call = msm_thermal_cpu_callback,
};
-/**
+static void thermal_rtc_setup(void)
+{
+ ktime_t wakeup_time;
+ ktime_t curr_time;
+
+ curr_time = alarm_get_elapsed_realtime();
+ wakeup_time = ktime_add_us(curr_time,
+ (wakeup_ms * USEC_PER_MSEC));
+ alarm_start_range(&thermal_rtc, wakeup_time,
+ wakeup_time);
+ pr_debug("%s: Current Time: %ld %ld, Alarm set to: %ld %ld\n",
+ KBUILD_MODNAME,
+ ktime_to_timeval(curr_time).tv_sec,
+ ktime_to_timeval(curr_time).tv_usec,
+ ktime_to_timeval(wakeup_time).tv_sec,
+ ktime_to_timeval(wakeup_time).tv_usec);
+
+}
+
+static void timer_work_fn(struct work_struct *work)
+{
+ sysfs_notify(tt_kobj, NULL, "wakeup_ms");
+}
+
+static void thermal_rtc_callback(struct alarm *al)
+{
+ struct timeval ts;
+ ts = ktime_to_timeval(alarm_get_elapsed_realtime());
+ schedule_work(&timer_work);
+ pr_debug("%s: Time on alarm expiry: %ld %ld\n", KBUILD_MODNAME,
+ ts.tv_sec, ts.tv_usec);
+}
+
+/*
* We will reset the cpu frequencies limits here. The core online/offline
* status will be carried over to the process stopping the msm_thermal, as
* we dont want to online a core and bring in the thermal issues.
*/
-static void __cpuinit disable_msm_thermal(void)
+static void __ref disable_msm_thermal(void)
{
int cpu = 0;
@@ -767,7 +917,7 @@
}
}
-static int __cpuinit set_enabled(const char *val, const struct kernel_param *kp)
+static int __ref set_enabled(const char *val, const struct kernel_param *kp)
{
int ret = 0;
@@ -792,8 +942,9 @@
MODULE_PARM_DESC(enabled, "enforce thermal limit on cpu");
+#ifdef CONFIG_SMP
/* Call with core_control_mutex locked */
-static int __cpuinit update_offline_cores(int val)
+static int __ref update_offline_cores(int val)
{
int cpu = 0;
int ret = 0;
@@ -814,6 +965,12 @@
}
return ret;
}
+#else
+static int update_offline_cores(int val)
+{
+ return 0;
+}
+#endif
static ssize_t show_cc_enabled(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
@@ -821,7 +978,7 @@
return snprintf(buf, PAGE_SIZE, "%d\n", core_control_enabled);
}
-static ssize_t __cpuinit store_cc_enabled(struct kobject *kobj,
+static ssize_t __ref store_cc_enabled(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int ret = 0;
@@ -858,7 +1015,7 @@
return snprintf(buf, PAGE_SIZE, "%d\n", cpus_offlined);
}
-static ssize_t __cpuinit store_cpus_offlined(struct kobject *kobj,
+static ssize_t __ref store_cpus_offlined(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int ret = 0;
@@ -886,22 +1043,68 @@
return count;
}
-static __cpuinitdata struct kobj_attribute cc_enabled_attr =
+static __refdata struct kobj_attribute cc_enabled_attr =
__ATTR(enabled, 0644, show_cc_enabled, store_cc_enabled);
-static __cpuinitdata struct kobj_attribute cpus_offlined_attr =
+static __refdata struct kobj_attribute cpus_offlined_attr =
__ATTR(cpus_offlined, 0644, show_cpus_offlined, store_cpus_offlined);
-static __cpuinitdata struct attribute *cc_attrs[] = {
+static __refdata struct attribute *cc_attrs[] = {
&cc_enabled_attr.attr,
&cpus_offlined_attr.attr,
NULL,
};
-static __cpuinitdata struct attribute_group cc_attr_group = {
+static __refdata struct attribute_group cc_attr_group = {
.attrs = cc_attrs,
};
+static ssize_t show_wakeup_ms(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", wakeup_ms);
+}
+
+static ssize_t store_wakeup_ms(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ int ret;
+ ret = kstrtouint(buf, 10, &wakeup_ms);
+
+ if (ret) {
+ pr_err("%s: Trying to set invalid wakeup timer\n",
+ KBUILD_MODNAME);
+ return ret;
+ }
+
+ if (wakeup_ms > 0) {
+ thermal_rtc_setup();
+ pr_debug("%s: Timer started for %ums\n", KBUILD_MODNAME,
+ wakeup_ms);
+ } else {
+ ret = alarm_cancel(&thermal_rtc);
+ if (ret)
+ pr_debug("%s: Timer canceled\n", KBUILD_MODNAME);
+ else
+ pr_debug("%s: No active timer present to cancel\n",
+ KBUILD_MODNAME);
+
+ }
+ return count;
+}
+
+static __refdata struct kobj_attribute timer_attr =
+__ATTR(wakeup_ms, 0644, show_wakeup_ms, store_wakeup_ms);
+
+static __refdata struct attribute *tt_attrs[] = {
+ &timer_attr.attr,
+ NULL,
+};
+
+static __refdata struct attribute_group tt_attr_group = {
+ .attrs = tt_attrs,
+};
+
static __init int msm_thermal_add_cc_nodes(void)
{
struct kobject *module_kobj = NULL;
@@ -938,21 +1141,62 @@
return ret;
}
+static __init int msm_thermal_add_timer_nodes(void)
+{
+ struct kobject *module_kobj = NULL;
+ int ret = 0;
+
+ module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
+ if (!module_kobj) {
+ pr_err("%s: cannot find kobject for module\n",
+ KBUILD_MODNAME);
+ ret = -ENOENT;
+ goto failed;
+ }
+
+ tt_kobj = kobject_create_and_add("thermal_timer", module_kobj);
+ if (!tt_kobj) {
+ pr_err("%s: cannot create timer kobj\n",
+ KBUILD_MODNAME);
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ ret = sysfs_create_group(tt_kobj, &tt_attr_group);
+ if (ret) {
+ pr_err("%s: cannot create group\n", KBUILD_MODNAME);
+ goto failed;
+ }
+
+ return 0;
+
+failed:
+ if (tt_kobj)
+ kobject_del(tt_kobj);
+ return ret;
+}
+
int __devinit msm_thermal_init(struct msm_thermal_data *pdata)
{
int ret = 0;
BUG_ON(!pdata);
tsens_get_max_sensor_num(&max_tsens_num);
- BUG_ON(msm_thermal_info.sensor_id >= max_tsens_num);
memcpy(&msm_thermal_info, pdata, sizeof(struct msm_thermal_data));
+ if (create_sensor_id_map())
+ return -EINVAL;
+ if (check_sensor_id(msm_thermal_info.sensor_id))
+ return -EINVAL;
+
enabled = 1;
- core_control_enabled = 1;
+ if (num_possible_cpus() > 1)
+ core_control_enabled = 1;
INIT_DELAYED_WORK(&check_temp_work, check_temp);
schedule_delayed_work(&check_temp_work, 0);
- register_cpu_notifier(&msm_thermal_cpu_notifier);
+ if (num_possible_cpus() > 1)
+ register_cpu_notifier(&msm_thermal_cpu_notifier);
return ret;
}
@@ -966,8 +1210,10 @@
if (rails[i].freq_req == 1) {
usefreq |= BIT(i);
check_freq_table();
- /* Restrict frequency by default until we have made
- * our first temp reading */
+ /*
+ * Restrict frequency by default until we have made
+ * our first temp reading
+ */
if (freq_table_get)
ret = vdd_restriction_apply_freq(&rails[i], 0);
else
@@ -988,8 +1234,10 @@
}
return ret;
}
- /* Restrict votlage by default until we have made
- * our first temp reading */
+ /*
+ * Restrict votlage by default until we have made
+ * our first temp reading
+ */
ret = vdd_restriction_apply_voltage(&rails[i], 0);
}
}
@@ -1203,6 +1451,8 @@
char *key = NULL;
struct device_node *child_node = NULL;
+ rails = NULL;
+
key = "qcom,vdd-restriction-temp";
ret = of_property_read_u32(node, key, &data->vdd_rstr_temp_degC);
if (ret)
@@ -1252,17 +1502,18 @@
if (ret)
goto read_node_fail;
- key = "qcom,min-level";
- ret = of_property_read_u32(child_node, key,
- &rails[i].min_level);
- if (ret)
- goto read_node_fail;
-
key = "qcom,freq-req";
rails[i].freq_req = of_property_read_bool(child_node, key);
+ if (rails[i].freq_req)
+ rails[i].min_level = MSM_CPUFREQ_NO_LIMIT;
+ else {
+ key = "qcom,min-level";
+ ret = of_property_read_u32(child_node, key,
+ &rails[i].min_level);
+ if (ret)
+ goto read_node_fail;
+ }
- if (ret)
- goto read_node_fail;
rails[i].curr_level = 0;
rails[i].reg = NULL;
i++;
@@ -1298,6 +1549,8 @@
int j = 0;
char *key = NULL;
+ psm_rails = NULL;
+
key = "qcom,pmic-sw-mode-temp";
ret = of_property_read_u32(node, key, &data->psm_temp_degC);
if (ret)
@@ -1384,6 +1637,9 @@
if (ret)
goto fail;
+ key = "qcom,freq-control-mask";
+ ret = of_property_read_u32(node, key, &data.freq_control_mask);
+
key = "qcom,core-limit-temp";
ret = of_property_read_u32(node, key, &data.core_limit_temp_degC);
@@ -1393,9 +1649,11 @@
key = "qcom,core-control-mask";
ret = of_property_read_u32(node, key, &data.core_control_mask);
- /* Probe optional properties below. Call probe_psm before
+ /*
+ * Probe optional properties below. Call probe_psm before
* probe_vdd_rstr because rpm_regulator_get has to be called
- * before devm_regulator_get*/
+ * before devm_regulator_get
+ */
ret = probe_psm(node, &data, pdev);
if (ret == -EPROBE_DEFER)
goto fail;
@@ -1403,8 +1661,10 @@
if (ret == -EPROBE_DEFER)
goto fail;
- /* In case sysfs add nodes get called before probe function.
- * Need to make sure sysfs node is created again */
+ /*
+ * In case sysfs add nodes get called before probe function.
+ * Need to make sure sysfs node is created again
+ */
if (psm_nodes_called) {
msm_thermal_add_psm_nodes();
psm_nodes_called = false;
@@ -1446,9 +1706,14 @@
int __init msm_thermal_late_init(void)
{
- msm_thermal_add_cc_nodes();
+ if (num_possible_cpus() > 1)
+ msm_thermal_add_cc_nodes();
msm_thermal_add_psm_nodes();
msm_thermal_add_vdd_rstr_nodes();
+ alarm_init(&thermal_rtc, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
+ thermal_rtc_callback);
+ INIT_WORK(&timer_work, timer_work_fn);
+ msm_thermal_add_timer_nodes();
return 0;
}
diff --git a/drivers/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c
index d848a18..f3b29c9 100644
--- a/drivers/thermal/qpnp-adc-tm.c
+++ b/drivers/thermal/qpnp-adc-tm.c
@@ -91,8 +91,8 @@
#define QPNP_M2_ADC_CH_SEL_CTL 0x70
#define QPNP_M2_LOW_THR_LSB 0x71
#define QPNP_M2_LOW_THR_MSB 0x72
-#define QPNP_M2_HIGH_THR_LSB 0x7b
-#define QPNP_M2_HIGH_THR_MSB 0x7c
+#define QPNP_M2_HIGH_THR_LSB 0x73
+#define QPNP_M2_HIGH_THR_MSB 0x74
#define QPNP_M3_ADC_CH_SEL_CTL 0x78
#define QPNP_M3_LOW_THR_LSB 0x79
#define QPNP_M3_LOW_THR_MSB 0x7a
@@ -1200,22 +1200,28 @@
}
}
- rc = qpnp_adc_tm_reg_update(QPNP_ADC_TM_MULTI_MEAS_EN,
- sensor_mask, false);
- if (rc < 0) {
- pr_err("multi meas disable for channel failed\n");
- goto fail;
- }
+ if (adc_tm_high_enable || adc_tm_low_enable) {
+ rc = qpnp_adc_tm_reg_update(QPNP_ADC_TM_MULTI_MEAS_EN,
+ sensor_mask, false);
+ if (rc < 0) {
+ pr_err("multi meas disable for channel failed\n");
+ goto fail;
+ }
- rc = qpnp_adc_tm_enable_if_channel_meas();
- if (rc < 0) {
- pr_err("re-enabling measurement failed\n");
- return rc;
- }
+ rc = qpnp_adc_tm_enable_if_channel_meas();
+ if (rc < 0) {
+ pr_err("re-enabling measurement failed\n");
+ return rc;
+ }
+ } else
+ pr_debug("No threshold status enable %d for high/low??\n",
+ sensor_mask);
+
fail:
mutex_unlock(&adc_tm->adc->adc_lock);
- schedule_work(&adc_tm->sensor[sensor_num].work);
+ if (adc_tm_high_enable || adc_tm_low_enable)
+ schedule_work(&adc_tm->sensor[sensor_num].work);
return rc;
}
@@ -1528,6 +1534,7 @@
dev_err(&spmi->dev, "failed to read device tree\n");
goto fail;
}
+ mutex_init(&adc_tm->adc->adc_lock);
/* Register the ADC peripheral interrupt */
adc_tm->adc->adc_high_thr_irq = spmi_get_irq_byname(spmi,
diff --git a/drivers/tty/n_smux.c b/drivers/tty/n_smux.c
index 8760603..d1545dc 100644
--- a/drivers/tty/n_smux.c
+++ b/drivers/tty/n_smux.c
@@ -1,6 +1,6 @@
/* drivers/tty/n_smux.c
*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -259,6 +259,8 @@
unsigned powerdown_enabled;
unsigned power_ctl_remote_req_received;
struct list_head power_queue;
+ unsigned remote_initiated_wakeup_count;
+ unsigned local_initiated_wakeup_count;
};
@@ -279,6 +281,7 @@
[SMUX_CMD_CLOSE_LCH] = "CLOSE",
[SMUX_CMD_STATUS] = "STATUS",
[SMUX_CMD_PWR_CTL] = "PWR",
+ [SMUX_CMD_DELAY] = "DELAY",
[SMUX_CMD_BYTE] = "Raw Byte",
};
@@ -1908,6 +1911,7 @@
/* wakeup system */
SMUX_PWR("smux: %s: Power %d->%d\n", __func__,
smux.power_state, SMUX_PWR_ON);
+ smux.remote_initiated_wakeup_count++;
smux.power_state = SMUX_PWR_ON;
queue_work(smux_tx_wq, &smux_wakeup_work);
queue_work(smux_tx_wq, &smux_tx_work);
@@ -2163,6 +2167,62 @@
}
/**
+ * Sends a delay command to the remote side.
+ *
+ * @ms: Time in milliseconds for the remote side to delay
+ *
+ * This command defines the delay that the remote side will use
+ * to slow the response time for DATA commands.
+ */
+void smux_set_loopback_data_reply_delay(uint32_t ms)
+{
+ struct smux_lch_t *ch = &smux_lch[SMUX_TEST_LCID];
+ struct smux_pkt_t *pkt;
+
+ pkt = smux_alloc_pkt();
+ if (!pkt) {
+ pr_err("%s: unable to allocate packet\n", __func__);
+ return;
+ }
+
+ pkt->hdr.lcid = ch->lcid;
+ pkt->hdr.cmd = SMUX_CMD_DELAY;
+ pkt->hdr.flags = 0;
+ pkt->hdr.payload_len = sizeof(uint32_t);
+ pkt->hdr.pad_len = 0;
+
+ if (smux_alloc_pkt_payload(pkt)) {
+ pr_err("%s: unable to allocate payload\n", __func__);
+ smux_free_pkt(pkt);
+ return;
+ }
+ memcpy(pkt->payload, &ms, sizeof(uint32_t));
+
+ smux_tx_queue(pkt, ch, 1);
+}
+
+/**
+ * Retrieve wakeup counts.
+ *
+ * @local_cnt: Pointer to local wakeup count
+ * @remote_cnt: Pointer to remote wakeup count
+ */
+void smux_get_wakeup_counts(int *local_cnt, int *remote_cnt)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&smux.tx_lock_lha2, flags);
+
+ if (local_cnt)
+ *local_cnt = smux.local_initiated_wakeup_count;
+
+ if (remote_cnt)
+ *remote_cnt = smux.remote_initiated_wakeup_count;
+
+ spin_unlock_irqrestore(&smux.tx_lock_lha2, flags);
+}
+
+/**
* Add channel to transmit-ready list and trigger transmit worker.
*
* @ch Channel to add
@@ -2744,6 +2804,7 @@
SMUX_PWR("smux: %s: Power %d->%d\n", __func__,
smux.power_state,
SMUX_PWR_TURNING_ON);
+ smux.local_initiated_wakeup_count++;
smux.power_state = SMUX_PWR_TURNING_ON;
spin_unlock_irqrestore(&smux.tx_lock_lha2,
flags);
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index ad7c702..4d464c1 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -1974,7 +1974,8 @@
switch (msm_uport->clk_state) {
case MSM_HS_CLK_OFF:
wake_lock(&msm_uport->dma_wake_lock);
- disable_irq_nosync(msm_uport->wakeup.irq);
+ if (use_low_power_wakeup(msm_uport))
+ disable_irq_nosync(msm_uport->wakeup.irq);
spin_unlock_irqrestore(&uport->lock, flags);
/* Vote for PNOC BUS Scaling */
@@ -2340,9 +2341,7 @@
msm_hs_start_rx_locked(uport);
spin_unlock_irqrestore(&uport->lock, flags);
- ret = pm_runtime_set_active(uport->dev);
- if (ret)
- dev_err(uport->dev, "set active error:%d\n", ret);
+
pm_runtime_enable(uport->dev);
return 0;
@@ -2350,7 +2349,8 @@
free_uart_irq:
free_irq(uport->irq, msm_uport);
free_wake_irq:
- irq_set_irq_wake(msm_uport->wakeup.irq, 0);
+ if (use_low_power_wakeup(msm_uport))
+ irq_set_irq_wake(msm_uport->wakeup.irq, 0);
sps_disconnect_rx:
if (is_blsp_uart(msm_uport))
sps_disconnect(sps_pipe_handle_rx);
@@ -2741,7 +2741,7 @@
/* SPS driver wll handle the UART BAM IRQ */
bam.irq = (u32)msm_uport->bam_irq;
- bam.manage = SPS_BAM_MGR_LOCAL;
+ bam.manage = SPS_BAM_MGR_DEVICE_REMOTE;
pr_debug("msm_serial_hs: bam physical base=0x%x\n",
(u32)bam.phys_addr);
@@ -3180,7 +3180,6 @@
cancel_delayed_work_sync(&msm_uport->rx.flip_insert_work);
flush_workqueue(msm_uport->hsuart_wq);
pm_runtime_disable(uport->dev);
- pm_runtime_set_suspended(uport->dev);
/* Disable the transmitter */
msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK);
diff --git a/drivers/tty/serial/msm_serial_hs_lite.c b/drivers/tty/serial/msm_serial_hs_lite.c
index 7aa14de..d520253 100644
--- a/drivers/tty/serial/msm_serial_hs_lite.c
+++ b/drivers/tty/serial/msm_serial_hs_lite.c
@@ -364,6 +364,22 @@
return msm_hsl_port->uart.line;
}
+static int bus_vote(uint32_t client, int vector)
+{
+ int ret = 0;
+
+ if (!client)
+ return ret;
+
+ pr_debug("Voting for bus scaling:%d\n", vector);
+
+ ret = msm_bus_scale_client_update_request(client, vector);
+ if (ret)
+ pr_err("Failed to request bus bw vector %d\n", vector);
+
+ return ret;
+}
+
static int clk_en(struct uart_port *port, int enable)
{
struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
@@ -372,9 +388,13 @@
if (enable) {
msm_hsl_port->clk_enable_count++;
- ret = clk_prepare_enable(msm_hsl_port->clk);
+ ret = bus_vote(msm_hsl_port->bus_perf_client,
+ !!msm_hsl_port->clk_enable_count);
if (ret)
goto err;
+ ret = clk_prepare_enable(msm_hsl_port->clk);
+ if (ret)
+ goto err_bus;
if (msm_hsl_port->pclk) {
ret = clk_prepare_enable(msm_hsl_port->pclk);
if (ret)
@@ -386,23 +406,17 @@
clk_disable_unprepare(msm_hsl_port->clk);
if (msm_hsl_port->pclk)
clk_disable_unprepare(msm_hsl_port->pclk);
- }
-
- if (msm_hsl_port->bus_perf_client) {
- pr_debug("Voting for bus scaling:%d\n",
- !!msm_hsl_port->clk_enable_count);
- ret = msm_bus_scale_client_update_request(
- msm_hsl_port->bus_perf_client,
+ ret = bus_vote(msm_hsl_port->bus_perf_client,
!!msm_hsl_port->clk_enable_count);
- if (ret)
- pr_err("Failed to request bus bw vector %d\n",
- !!msm_hsl_port->clk_enable_count);
}
return ret;
err_clk_disable:
clk_disable_unprepare(msm_hsl_port->clk);
+err_bus:
+ bus_vote(msm_hsl_port->bus_perf_client,
+ !!(msm_hsl_port->clk_enable_count - 1));
err:
msm_hsl_port->clk_enable_count--;
return ret;
diff --git a/drivers/tty/smux_ctl.c b/drivers/tty/smux_ctl.c
index 1b3a7abe..dcbfe9a 100644
--- a/drivers/tty/smux_ctl.c
+++ b/drivers/tty/smux_ctl.c
@@ -50,6 +50,7 @@
static uint32_t smux_ctl_ch_id[] = {
SMUX_DATA_CTL_0,
+ SMUX_DATA_CTL_1,
};
#define SMUX_CTL_NUM_CHANNELS ARRAY_SIZE(smux_ctl_ch_id)
diff --git a/drivers/tty/smux_private.h b/drivers/tty/smux_private.h
index 8fdec86..b9a2e89 100644
--- a/drivers/tty/smux_private.h
+++ b/drivers/tty/smux_private.h
@@ -1,6 +1,6 @@
/* drivers/tty/smux_private.h
*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -122,6 +122,7 @@
SMUX_CMD_CLOSE_LCH = 0x2,
SMUX_CMD_STATUS = 0x3,
SMUX_CMD_PWR_CTL = 0x4,
+ SMUX_CMD_DELAY = 0x5,
SMUX_CMD_BYTE, /* for internal usage */
SMUX_NUM_COMMANDS
@@ -181,6 +182,8 @@
void smuxld_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count);
bool smux_remote_is_active(void);
+void smux_set_loopback_data_reply_delay(uint32_t ms);
+void smux_get_wakeup_counts(int *local_cnt, int *remote_cnt);
/* testing parameters */
extern int smux_byte_loopback;
diff --git a/drivers/tty/smux_test.c b/drivers/tty/smux_test.c
index e1d9975..8d17674 100644
--- a/drivers/tty/smux_test.c
+++ b/drivers/tty/smux_test.c
@@ -1,6 +1,6 @@
/* drivers/tty/smux_test.c
*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -20,11 +20,17 @@
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/termios.h>
+#include <linux/sched.h>
#include <linux/smux.h>
#include <mach/subsystem_restart.h>
#include "smux_private.h"
#define DEBUG_BUFMAX 4096
+#define RED_ZONE_SIZE 16
+#define RED_ZONE_PRE_CH 0xAB
+#define RED_ZONE_POS_CH 0xBA
+#define SMUX_REMOTE_INACTIVITY_TIME_MS 50
+#define SMUX_REMOTE_DELAY_TIME_MS 250
/**
* Unit test assertion for logging test cases.
@@ -138,7 +144,7 @@
/**
* Allocates a new buffer for SMUX for every call.
*/
-int get_rx_buffer(void *priv, void **pkt_priv, void **buffer, int size)
+static int get_rx_buffer(void *priv, void **pkt_priv, void **buffer, int size)
{
void *rx_buf;
@@ -216,7 +222,7 @@
*
* @cb Mock callback data
*/
-void mock_cb_data_init(struct smux_mock_callback *cb)
+static void mock_cb_data_init(struct smux_mock_callback *cb)
{
init_completion(&cb->cb_completion);
spin_lock_init(&cb->lock);
@@ -232,7 +238,7 @@
*
* All packets are freed and counters reset to zero.
*/
-void mock_cb_data_reset(struct smux_mock_callback *cb)
+static void mock_cb_data_reset(struct smux_mock_callback *cb)
{
cb->cb_count = 0;
INIT_COMPLETION(cb->cb_completion);
@@ -341,7 +347,7 @@
* Mock object event callback. Used to logs events for analysis in the unit
* tests.
*/
-void smux_mock_cb(void *priv, int event, const void *metadata)
+static void smux_mock_cb(void *priv, int event, const void *metadata)
{
struct smux_mock_callback *cb_data_ptr;
struct mock_write_event *write_event_meta;
@@ -518,12 +524,17 @@
for (; vectors->data != NULL; ++vectors) {
const char *test_data = vectors->data;
const unsigned test_len = vectors->len;
+ unsigned long long start_t;
+ unsigned long long end_t;
+ unsigned long long val;
+ unsigned long rem;
i += scnprintf(buf + i, max - i,
- "Writing vector %p len %d\n",
+ "Writing vector %p len %d: ",
test_data, test_len);
/* write data */
+ start_t = sched_clock();
msm_smux_write(SMUX_TEST_LCID, (void *)0xCAFEFACE,
test_data, test_len);
UT_ASSERT_INT(ret, ==, 0);
@@ -538,6 +549,7 @@
(int)wait_for_completion_timeout(
&cb_data.cb_completion, HZ),
>, 0);
+ end_t = sched_clock();
UT_ASSERT_INT(cb_data.cb_count, >=, 1);
UT_ASSERT_INT(cb_data.event_write_done, ==, 1);
@@ -569,16 +581,28 @@
hex_dump_to_buffer(test_data, test_len,
16, 1, linebuff, sizeof(linebuff), 1);
i += scnprintf(buf + i, max - i,
- "Expected:\n%s\n\n", linebuff);
+ "Failed\nExpected:\n%s\n\n", linebuff);
hex_dump_to_buffer(read_event->meta.buffer,
read_event->meta.len,
16, 1, linebuff, sizeof(linebuff), 1);
i += scnprintf(buf + i, max - i,
- "Actual:\n%s\n", linebuff);
+ "Failed\nActual:\n%s\n", linebuff);
failed = 1;
break;
}
+
+ /* calculate throughput stats */
+ val = end_t - start_t;
+ rem = do_div(val, 1000);
+ i += scnprintf(buf + i, max - i,
+ "OK - %u us",
+ (unsigned int)val);
+
+ val = 1000000000LL * 2 * test_len;
+ rem = do_div(val, end_t - start_t);
+ i += scnprintf(buf + i, max - i,
+ " (%u kB/sec)\n", (unsigned int)val);
mock_cb_data_reset(&cb_data);
}
@@ -682,7 +706,7 @@
* Run a basic loopback test followed by a subsystem restart and then another
* loopback test.
*/
-static int smux_ut_remote_ssr_basic(char *buf, int max)
+static int smux_ut_ssr_remote_basic(char *buf, int max)
{
const struct test_vector test_data[] = {
{"hello\0world\n", sizeof("hello\0world\n")},
@@ -723,7 +747,7 @@
/**
* Verify Subsystem Restart Support During Port Open
*/
-static int smux_ut_remote_ssr_open(char *buf, int max)
+static int smux_ut_ssr_remote_open(char *buf, int max)
{
static struct smux_mock_callback cb_data;
static int cb_initialized;
@@ -805,7 +829,7 @@
*
* @returns Number of bytes written to @buf
*/
-static int smux_ut_remote_ssr_rx_buff_retry(char *buf, int max)
+static int smux_ut_ssr_remote_rx_buff_retry(char *buf, int max)
{
static struct smux_mock_callback cb_data;
static int cb_initialized;
@@ -897,9 +921,10 @@
mock_cb_data_reset(&cb_data);
return i;
}
+
/**
* Fill test pattern into provided buffer including an optional
- * redzone 16 bytes before and 16 bytes after the buffer.
+ * redzone before and after the buffer.
*
* buf ---------
* redzone
@@ -909,70 +934,75 @@
* redzone
* ---------
*
- * @buf Pointer to the buffer of size len or len+32 (redzone)
- * @len Length of the *data* buffer (excluding 32-byte redzone)
+ * @buf Pointer to the buffer of size len or len+2*RED_ZONE_SIZE (redzone)
+ * @len Length of the *data* buffer (excluding the extra redzone buffers)
* @redzone If true, adds redzone data
*
- * @returns pointer to buffer (buf + 16 if redzone enabled)
+ * @returns pointer to buffer (buf + RED_ZONE_SIZE if redzone enabled)
*/
-uint8_t *test_pattern_fill(char *buf, int len, int redzone)
+static uint8_t *test_pattern_fill(char *buf, int len, int redzone)
{
- void *ret;
+ char *buf_ptr;
uint8_t ch;
- ret = buf;
if (redzone) {
- memset((char *)buf, 0xAB, 16);
- memset((char *)buf + len, 0xBA, 16);
- ret += 16;
+ memset(buf, RED_ZONE_PRE_CH, RED_ZONE_SIZE);
+ buf += RED_ZONE_SIZE;
+ memset(buf + len, RED_ZONE_POS_CH, RED_ZONE_SIZE);
}
- /* fill with test pattern */
- for (ch = 0; len > 0; --len, ++ch)
- *buf++ = (char)ch;
+ for (ch = 0, buf_ptr = buf; len > 0; --len, ++ch)
+ *buf_ptr++ = (char)ch;
- return ret;
+ return buf;
}
/**
* Verify test pattern generated by test_pattern_fill.
*
* @buf_ptr Pointer to buffer pointer
- * @len Length of the *data* buffer (excluding 32-byte redzone)
+ * @len Length of the *data* buffer (excluding redzone bytes)
* @redzone If true, verifies redzone and adjusts *buf_ptr
* @errmsg Buffer for error message
* @errmsg_max Size of error message buffer
*
* @returns 0 for success; length of error message otherwise
*/
-unsigned test_pattern_verify(char **buf_ptr, int len, int redzone,
+static unsigned test_pattern_verify(char **buf_ptr, int len, int redzone,
char *errmsg, int errmsg_max)
{
int n;
int i = 0;
char linebuff[80];
+ char *zone_ptr;
if (redzone) {
- *buf_ptr -= 16;
+ *buf_ptr -= RED_ZONE_SIZE;
+ zone_ptr = *buf_ptr;
/* verify prefix redzone */
- for (n = 0; n < 16; ++n) {
- if (*buf_ptr[n] != 0xAB) {
- hex_dump_to_buffer(*buf_ptr, 16,
- 16, 1, linebuff, sizeof(linebuff), 1);
+ for (n = 0; n < RED_ZONE_SIZE; ++n) {
+ if (zone_ptr[n] != RED_ZONE_PRE_CH) {
+ hex_dump_to_buffer(zone_ptr, RED_ZONE_SIZE,
+ RED_ZONE_SIZE, 1, linebuff,
+ sizeof(linebuff), 1);
i += scnprintf(errmsg + i, errmsg_max - i,
- "Redzone violation: %s\n", linebuff);
+ "Pre-redzone violation: %s\n",
+ linebuff);
break;
}
}
/* verify postfix redzone */
- for (n = 0; n < 16; ++n) {
- if (*buf_ptr[len + n] != 0xBA) {
- hex_dump_to_buffer(&(*buf_ptr)[len], 16,
- 16, 1, linebuff, sizeof(linebuff), 1);
+ zone_ptr = *buf_ptr + RED_ZONE_SIZE + len;
+ for (n = 0; n < RED_ZONE_SIZE; ++n) {
+ if (zone_ptr[n] != RED_ZONE_POS_CH) {
+ hex_dump_to_buffer(zone_ptr, RED_ZONE_SIZE,
+ RED_ZONE_SIZE, 1, linebuff,
+ sizeof(linebuff), 1);
i += scnprintf(errmsg + i, errmsg_max - i,
- "Redzone violation: %s\n", linebuff);
+ "Post-redzone violation: %s\n",
+ linebuff);
break;
}
}
@@ -1001,6 +1031,7 @@
{0, 256},
{0, 512},
{0, 1024},
+ {0, 1500},
{0, 2048},
{0, 4096},
{0, 0},
@@ -1011,9 +1042,7 @@
/* generate test data */
for (tv = test_data; tv->len > 0; ++tv) {
- tv->data = kmalloc(tv->len + 32, GFP_KERNEL);
- pr_err("%s: allocating %p len %d\n",
- __func__, tv->data, tv->len);
+ tv->data = kmalloc(tv->len + 2 * RED_ZONE_SIZE, GFP_KERNEL);
if (!tv->data) {
i += scnprintf(buf + i, max - i,
"%s: Unable to allocate %d bytes\n",
@@ -1021,7 +1050,7 @@
failed = 1;
goto out;
}
- test_pattern_fill((uint8_t *)tv->data, tv->len, 1);
+ tv->data = test_pattern_fill((uint8_t *)tv->data, tv->len, 1);
}
/* run test */
@@ -1038,11 +1067,9 @@
}
for (tv = test_data; tv->len > 0; ++tv) {
- if (!tv->data) {
+ if (tv->data) {
i += test_pattern_verify((char **)&tv->data,
tv->len, 1, buf + i, max - i);
- pr_err("%s: freeing %p len %d\n", __func__,
- tv->data, tv->len);
kfree(tv->data);
}
}
@@ -1112,6 +1139,59 @@
}
/**
+ * Run a large packet test for throughput metrics.
+ *
+ * Repeatedly send a packet for 100 iterations to get throughput metrics.
+ */
+static int smux_ut_remote_throughput(char *buf, int max)
+{
+ struct test_vector test_data[] = {
+ {0, 1500},
+ {0, 0},
+ };
+ int failed = 0;
+ int i = 0;
+ int loop = 0;
+ struct test_vector *tv;
+ int ret;
+
+ /* generate test data */
+ for (tv = test_data; tv->len > 0; ++tv) {
+ tv->data = kmalloc(tv->len, GFP_KERNEL);
+ if (!tv->data) {
+ i += scnprintf(buf + i, max - i,
+ "%s: Unable to allocate %d bytes\n",
+ __func__, tv->len);
+ failed = 1;
+ goto out;
+ }
+ test_pattern_fill((uint8_t *)tv->data, tv->len, 0);
+ }
+
+ /* run test */
+ i += scnprintf(buf + i, max - i, "Running %s\n", __func__);
+ while (!failed && loop < 100) {
+ ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
+ SMUX_CH_OPTION_REMOTE_LOOPBACK, 0);
+ UT_ASSERT_INT(ret, ==, 0);
+
+ i += smux_ut_basic_core(buf + i, max - i, test_data, __func__);
+ ++loop;
+ }
+
+out:
+ if (failed) {
+ pr_err("%s: Failed\n", __func__);
+ i += scnprintf(buf + i, max - i, "\tFailed\n");
+ }
+
+ for (tv = test_data; tv->len > 0; ++tv)
+ kfree(tv->data);
+
+ return i;
+}
+
+/**
* Verify set and get operations for each TIOCM bit.
*
* @buf Buffer for status message
@@ -2083,6 +2163,126 @@
return i;
}
+/**
+ * Verify Remote-initiated wakeup test case.
+ *
+ * @buf Output buffer for failure/status messages
+ * @max Size of @buf
+ */
+static int smux_ut_remote_initiated_wakeup(char *buf, int max)
+{
+ int i = 0;
+ int failed = 0;
+ static struct smux_mock_callback cb_data;
+ static int cb_initialized;
+ int ret;
+
+ if (!cb_initialized)
+ mock_cb_data_init(&cb_data);
+
+ smux_set_loopback_data_reply_delay(SMUX_REMOTE_DELAY_TIME_MS);
+ mock_cb_data_reset(&cb_data);
+ do {
+ unsigned long start_j;
+ unsigned transfer_time;
+ unsigned lwakeups_start;
+ unsigned rwakeups_start;
+ unsigned lwakeups_end;
+ unsigned rwakeups_end;
+ unsigned lwakeup_delta;
+ unsigned rwakeup_delta;
+
+ /* open port */
+ ret = msm_smux_open(SMUX_TEST_LCID, &cb_data, smux_mock_cb,
+ get_rx_buffer);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_data.cb_completion, HZ), >, 0);
+ UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+ UT_ASSERT_INT(cb_data.event_connected, ==, 1);
+ mock_cb_data_reset(&cb_data);
+
+ /* do local wakeup test and send echo packet */
+ msleep(SMUX_REMOTE_INACTIVITY_TIME_MS);
+ smux_get_wakeup_counts(&lwakeups_start, &rwakeups_start);
+ msm_smux_write(SMUX_TEST_LCID, (void *)0x12345678,
+ "Hello", 5);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_data.cb_completion, HZ), >, 0);
+ UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+ UT_ASSERT_INT(cb_data.event_write_done, ==, 1);
+ mock_cb_data_reset(&cb_data);
+
+ /* verify local initiated wakeup */
+ smux_get_wakeup_counts(&lwakeups_end, &rwakeups_end);
+ if (lwakeups_end > lwakeups_start)
+ i += scnprintf(buf + i, max - i,
+ "\tGood - have Apps-initiated wakeup\n");
+ else
+ i += scnprintf(buf + i, max - i,
+ "\tBad - no Apps-initiated wakeup\n");
+
+ /* verify remote wakeup and echo response */
+ smux_get_wakeup_counts(&lwakeups_start, &rwakeups_start);
+ start_j = jiffies;
+ INIT_COMPLETION(cb_data.cb_completion);
+ if (!cb_data.event_read_done)
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_data.cb_completion,
+ SMUX_REMOTE_DELAY_TIME_MS * 2),
+ >, 0);
+ transfer_time = (unsigned)jiffies_to_msecs(jiffies - start_j);
+ UT_ASSERT_INT(cb_data.event_read_done, ==, 1);
+ UT_ASSERT_INT_IN_RANGE(transfer_time,
+ SMUX_REMOTE_DELAY_TIME_MS -
+ SMUX_REMOTE_INACTIVITY_TIME_MS,
+ SMUX_REMOTE_DELAY_TIME_MS +
+ SMUX_REMOTE_INACTIVITY_TIME_MS);
+ smux_get_wakeup_counts(&lwakeups_end, &rwakeups_end);
+
+ lwakeup_delta = lwakeups_end - lwakeups_end;
+ rwakeup_delta = rwakeups_end - rwakeups_end;
+ if (rwakeup_delta && lwakeup_delta) {
+ i += scnprintf(buf + i, max - i,
+ "\tBoth local and remote wakeup - re-run test (transfer time %d ms)\n",
+ transfer_time);
+ failed = 1;
+ break;
+ } else if (lwakeup_delta) {
+ i += scnprintf(buf + i, max - i,
+ "\tLocal wakeup only (transfer time %d ms) - FAIL\n",
+ transfer_time);
+ failed = 1;
+ break;
+ } else {
+ i += scnprintf(buf + i, max - i,
+ "\tRemote wakeup verified (transfer time %d ms) - OK\n",
+ transfer_time);
+ }
+ } while (0);
+
+ if (!failed) {
+ i += scnprintf(buf + i, max - i, "\tOK\n");
+ } else {
+ pr_err("%s: Failed\n", __func__);
+ i += scnprintf(buf + i, max - i, "\tFailed\n");
+ i += mock_cb_data_print(&cb_data, buf + i, max - i);
+ }
+
+ mock_cb_data_reset(&cb_data);
+ msm_smux_close(SMUX_TEST_LCID);
+ wait_for_completion_timeout(&cb_data.cb_completion, HZ);
+
+ mock_cb_data_reset(&cb_data);
+ smux_set_loopback_data_reply_delay(0);
+
+ return i;
+}
+
static char debug_buffer[DEBUG_BUFMAX];
static ssize_t debug_read(struct file *file, char __user *buf,
@@ -2148,15 +2348,18 @@
smux_ut_local_get_rx_buff_retry);
debug_create("ut_local_get_rx_buff_retry_auto", 0444, dent,
smux_ut_local_get_rx_buff_retry_auto);
- debug_create("ut_remote_ssr_basic", 0444, dent,
- smux_ut_remote_ssr_basic);
- debug_create("ut_remote_ssr_open", 0444, dent,
- smux_ut_remote_ssr_open);
- debug_create("ut_remote_ssr_rx_buff_retry", 0444, dent,
- smux_ut_remote_ssr_rx_buff_retry);
+ debug_create("ut_ssr_remote_basic", 0444, dent,
+ smux_ut_ssr_remote_basic);
+ debug_create("ut_ssr_remote_open", 0444, dent,
+ smux_ut_ssr_remote_open);
+ debug_create("ut_ssr_remote_rx_buff_retry", 0444, dent,
+ smux_ut_ssr_remote_rx_buff_retry);
debug_create("ut_remote_tx_stop", 0444, dent,
smux_ut_remote_tx_stop);
-
+ debug_create("ut_remote_throughput", 0444, dent,
+ smux_ut_remote_throughput);
+ debug_create("ut_remote_initiated_wakeup", 0444, dent,
+ smux_ut_remote_initiated_wakeup);
return 0;
}
diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
index ede2ef1..1d02e32 100644
--- a/drivers/tty/vt/vt_ioctl.c
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -110,6 +110,34 @@
wake_up_interruptible(&vt_event_waitqueue);
}
+static void __vt_event_queue(struct vt_event_wait *vw)
+{
+ unsigned long flags;
+ /* Prepare the event */
+ INIT_LIST_HEAD(&vw->list);
+ vw->done = 0;
+ /* Queue our event */
+ spin_lock_irqsave(&vt_event_lock, flags);
+ list_add(&vw->list, &vt_events);
+ spin_unlock_irqrestore(&vt_event_lock, flags);
+}
+
+static void __vt_event_wait(struct vt_event_wait *vw)
+{
+ /* Wait for it to pass */
+ wait_event_interruptible(vt_event_waitqueue, vw->done);
+}
+
+static void __vt_event_dequeue(struct vt_event_wait *vw)
+{
+ unsigned long flags;
+
+ /* Dequeue it */
+ spin_lock_irqsave(&vt_event_lock, flags);
+ list_del(&vw->list);
+ spin_unlock_irqrestore(&vt_event_lock, flags);
+}
+
/**
* vt_event_wait - wait for an event
* @vw: our event
@@ -121,20 +149,9 @@
static void vt_event_wait(struct vt_event_wait *vw)
{
- unsigned long flags;
- /* Prepare the event */
- INIT_LIST_HEAD(&vw->list);
- vw->done = 0;
- /* Queue our event */
- spin_lock_irqsave(&vt_event_lock, flags);
- list_add(&vw->list, &vt_events);
- spin_unlock_irqrestore(&vt_event_lock, flags);
- /* Wait for it to pass */
- wait_event_interruptible(vt_event_waitqueue, vw->done);
- /* Dequeue it */
- spin_lock_irqsave(&vt_event_lock, flags);
- list_del(&vw->list);
- spin_unlock_irqrestore(&vt_event_lock, flags);
+ __vt_event_queue(vw);
+ __vt_event_wait(vw);
+ __vt_event_dequeue(vw);
}
/**
@@ -177,10 +194,14 @@
{
struct vt_event_wait vw;
do {
- if (n == fg_console + 1)
- break;
vw.event.event = VT_EVENT_SWITCH;
- vt_event_wait(&vw);
+ __vt_event_queue(&vw);
+ if (n == fg_console + 1) {
+ __vt_event_dequeue(&vw);
+ break;
+ }
+ __vt_event_wait(&vw);
+ __vt_event_dequeue(&vw);
if (vw.done == 0)
return -EINTR;
} while (vw.event.newev != n);
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index fc8f4b3..bb2879b 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -241,7 +241,13 @@
for (i = 0; i < num; i++) {
struct dwc3_event_buffer *evt;
- evt = dwc3_alloc_one_event_buffer(dwc, length);
+ /*
+ * As SW workaround, allocate 8 bytes more than size of event
+ * buffer given to USB Controller to avoid possible memory
+ * corruption caused by event buffer overflow when Hw writes
+ * Vendor Device test event which could be of 12 bytes.
+ */
+ evt = dwc3_alloc_one_event_buffer(dwc, (length + 8));
if (IS_ERR(evt)) {
dev_err(dwc->dev, "can't allocate event buffer\n");
return PTR_ERR(evt);
@@ -276,7 +282,7 @@
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n),
upper_32_bits(evt->dma));
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n),
- evt->length & 0xffff);
+ (evt->length - 8) & 0xffff);
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0);
}
@@ -421,6 +427,14 @@
reg &= ~DWC3_GUSB3PIPECTL_DIS_RXDET_U3_RXDET;
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
}
+ /*
+ * clear Elastic buffer mode in GUSBPIPE_CTRL(0) register, otherwise
+ * it results in high link errors and could cause SS mode transfer
+ * failure.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+ reg &= ~DWC3_GUSB3PIPECTL_ELASTIC_BUF_MODE;
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
if (!dwc->ev_buffs) {
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
@@ -488,7 +502,7 @@
if (!dev->dma_mask)
dev->dma_mask = &dwc3_dma_mask;
if (!dev->coherent_dma_mask)
- dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ dev->coherent_dma_mask = DMA_BIT_MASK(64);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 3fb89cd..5db7420 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -195,6 +195,7 @@
#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
#define DWC3_GUSB3PIPECTL_DELAY_P1P2P3 (7 << 19)
#define DWC3_GUSB3PIPECTL_DIS_RXDET_U3_RXDET (1 << 22)
+#define DWC3_GUSB3PIPECTL_ELASTIC_BUF_MODE (1 << 0)
/* Global TX Fifo Size Register */
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 93504eb..df95646 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -693,9 +693,10 @@
list_for_each(ptr, &dep->request_list) {
req = list_entry(ptr, struct dwc3_request, list);
- seq_printf(s, "req:0x%p len: %d sts: %d dma:0x%x num_sgs: %d\n",
+ seq_printf(s,
+ "req:0x%p len: %d sts: %d dma:0x%pa num_sgs: %d\n",
req, req->request.length, req->request.status,
- req->request.dma, req->request.num_sgs);
+ &req->request.dma, req->request.num_sgs);
}
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -731,9 +732,10 @@
list_for_each(ptr, &dep->req_queued) {
req = list_entry(ptr, struct dwc3_request, list);
- seq_printf(s, "req:0x%p len:%d sts:%d dma:%x nsg:%d trb:0x%p\n",
+ seq_printf(s,
+ "req:0x%p len:%d sts:%d dma:%pa nsg:%d trb:0x%p\n",
req, req->request.length, req->request.status,
- req->request.dma, req->request.num_sgs, req->trb);
+ &req->request.dma, req->request.num_sgs, req->trb);
}
spin_unlock_irqrestore(&dwc->lock, flags);
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 924e8f4..6aeb26e 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -37,6 +37,8 @@
#include <linux/regulator/consumer.h>
#include <linux/power_supply.h>
#include <linux/qpnp/qpnp-adc.h>
+#include <linux/cdev.h>
+#include <linux/completion.h>
#include <mach/rpm-regulator.h>
#include <mach/rpm-regulator-smd.h>
@@ -134,6 +136,7 @@
*
*/
#define QSCRATCH_REG_OFFSET (0x000F8800)
+#define QSCRATCH_CTRL_REG (QSCRATCH_REG_OFFSET + 0x04)
#define QSCRATCH_GENERAL_CFG (QSCRATCH_REG_OFFSET + 0x08)
#define HS_PHY_CTRL_REG (QSCRATCH_REG_OFFSET + 0x10)
#define PARAMETER_OVERRIDE_X_REG (QSCRATCH_REG_OFFSET + 0x14)
@@ -218,6 +221,15 @@
unsigned long lpm_flags;
#define MDWC3_CORECLK_OFF BIT(0)
#define MDWC3_TCXO_SHUTDOWN BIT(1)
+
+ u32 qscratch_ctl_val;
+ dev_t ext_chg_dev;
+ struct cdev ext_chg_cdev;
+ struct class *ext_chg_class;
+ struct device *ext_chg_device;
+ bool ext_chg_opened;
+ bool ext_chg_active;
+ struct completion ext_chg_wait;
};
#define USB_HSPHY_3P3_VOL_MIN 3050000 /* uV */
@@ -1309,8 +1321,14 @@
data |= (0x7F | (1 << 14));
dwc3_msm_ssusb_write_phycreg(msm->base, 0x1002, data);
- /* Set LOS_BIAS to 0x5 */
- dwc3_msm_write_readback(msm->base, SS_PHY_PARAM_CTRL_1, 0x07, 0x5);
+ /*
+ * Set the QSCRATCH SS_PHY_PARAM_CTRL1 parameters as follows
+ * TX_FULL_SWING [26:20] amplitude to 127
+ * TX_DEEMPH_3_5DB [13:8] to 22
+ * LOS_BIAS [2:0] to 0x5
+ */
+ dwc3_msm_write_readback(msm->base, SS_PHY_PARAM_CTRL_1,
+ 0x07f03f07, 0x07f01605);
}
/* Initialize QSCRATCH registers for HSPHY and SSPHY operation */
@@ -1356,6 +1374,11 @@
dwc3_msm_read_reg(msm->base, CGCTL_REG) | 0x18);
dwc3_msm_ss_phy_reg_init(msm);
+ /*
+ * This is required to restore the POR value after userspace
+ * is done with charger detection.
+ */
+ msm->qscratch_ctl_val = dwc3_msm_read_reg(msm->base, QSCRATCH_CTRL_REG);
}
static void dwc3_msm_block_reset(bool core_reset)
@@ -1463,6 +1486,8 @@
{
u32 chg_ctrl;
+ dwc3_msm_write_reg(mdwc->base, QSCRATCH_CTRL_REG,
+ mdwc->qscratch_ctl_val);
/* Clear charger detecting control bits */
dwc3_msm_write_reg(mdwc->base, CHARGING_DET_CTRL_REG, 0x0);
@@ -1556,9 +1581,14 @@
case USB_CHG_STATE_DETECTED:
dwc3_chg_block_reset(mdwc);
/* Enable VDP_SRC */
- if (mdwc->charger.chg_type == DWC3_DCP_CHARGER)
+ if (mdwc->charger.chg_type == DWC3_DCP_CHARGER) {
dwc3_msm_write_readback(mdwc->base,
CHARGING_DET_CTRL_REG, 0x1F, 0x10);
+ if (mdwc->ext_chg_opened) {
+ init_completion(&mdwc->ext_chg_wait);
+ mdwc->ext_chg_active = true;
+ }
+ }
dev_dbg(mdwc->dev, "chg_type = %s\n",
chg_to_string(mdwc->charger.chg_type));
mdwc->charger.notify_detection_complete(mdwc->otg_xceiv->otg,
@@ -1620,6 +1650,10 @@
(mdwc->charger.chg_type == DWC3_PROPRIETARY_CHARGER));
host_bus_suspend = mdwc->host_mode == 1;
+ if (!dcp && !host_bus_suspend)
+ dwc3_msm_write_reg(mdwc->base, QSCRATCH_CTRL_REG,
+ mdwc->qscratch_ctl_val);
+
/* Sequence to put SSPHY in low power state:
* 1. Clear REF_SS_PHY_EN in SS_PHY_CTRL_REG
* 2. Clear REF_USE_PAD in SS_PHY_CTRL_REG
@@ -1844,6 +1878,30 @@
return 0;
}
+static void dwc3_wait_for_ext_chg_done(struct dwc3_msm *mdwc)
+{
+ unsigned long t;
+
+ /*
+ * Defer next cable connect event till external charger
+ * detection is completed.
+ */
+
+ if (mdwc->ext_chg_active && (mdwc->ext_xceiv.bsv ||
+ !mdwc->ext_xceiv.id)) {
+
+ dev_dbg(mdwc->dev, "before ext chg wait\n");
+
+ t = wait_for_completion_timeout(&mdwc->ext_chg_wait,
+ msecs_to_jiffies(3000));
+ if (!t)
+ dev_err(mdwc->dev, "ext chg wait timeout\n");
+ else
+ dev_dbg(mdwc->dev, "ext chg wait done\n");
+ }
+
+}
+
static void dwc3_resume_work(struct work_struct *w)
{
struct dwc3_msm *mdwc = container_of(w, struct dwc3_msm,
@@ -1853,9 +1911,11 @@
/* handle any event that was queued while work was already running */
if (!atomic_read(&mdwc->in_lpm)) {
dev_dbg(mdwc->dev, "%s: notifying xceiv event\n", __func__);
- if (mdwc->otg_xceiv)
+ if (mdwc->otg_xceiv) {
+ dwc3_wait_for_ext_chg_done(mdwc);
mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg,
DWC3_EVENT_XCEIV_STATE);
+ }
return;
}
@@ -1868,9 +1928,11 @@
mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg,
DWC3_EVENT_PHY_RESUME);
pm_runtime_put_noidle(mdwc->dev);
- if (mdwc->otg_xceiv && (mdwc->ext_xceiv.otg_capability))
+ if (mdwc->otg_xceiv && (mdwc->ext_xceiv.otg_capability)) {
+ dwc3_wait_for_ext_chg_done(mdwc);
mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg,
DWC3_EVENT_XCEIV_STATE);
+ }
}
}
@@ -2083,6 +2145,7 @@
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_TYPE,
POWER_SUPPLY_PROP_SCOPE,
};
@@ -2255,6 +2318,131 @@
static DEVICE_ATTR(adc_enable, S_IRUGO | S_IWUSR, adc_enable_show,
adc_enable_store);
+static int dwc3_msm_ext_chg_open(struct inode *inode, struct file *file)
+{
+ struct dwc3_msm *mdwc = context;
+
+ pr_debug("dwc3-msm ext chg open\n");
+
+ mdwc->ext_chg_opened = true;
+ return 0;
+}
+
+static ssize_t
+dwc3_msm_ext_chg_write(struct file *file, const char __user *ubuf,
+ size_t size, loff_t *pos)
+{
+ struct dwc3_msm *mdwc = context;
+ char kbuf[16];
+
+ memset(kbuf, 0x00, sizeof(kbuf));
+ if (copy_from_user(&kbuf, ubuf, min_t(size_t, sizeof(kbuf) - 1, size)))
+ return -EFAULT;
+
+ pr_debug("%s: buf = %s\n", __func__, kbuf);
+
+ if (!strncmp(kbuf, "enable", 6)) {
+ pr_info("%s: on\n", __func__);
+ if (mdwc->charger.chg_type == DWC3_DCP_CHARGER) {
+ pm_runtime_get_sync(mdwc->dev);
+ } else {
+ mdwc->ext_chg_active = false;
+ complete(&mdwc->ext_chg_wait);
+ return -ENODEV;
+ }
+ } else if (!strncmp(kbuf, "disable", 7)) {
+ pr_info("%s: off\n", __func__);
+ mdwc->ext_chg_active = false;
+ complete(&mdwc->ext_chg_wait);
+ pm_runtime_put(mdwc->dev);
+ } else {
+ return -EINVAL;
+ }
+
+ return size;
+}
+
+static int dwc3_msm_ext_chg_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long vsize = vma->vm_end - vma->vm_start;
+ int ret;
+
+ pr_debug("%s: size = %lu %x\n", __func__, vsize, (int) vma->vm_pgoff);
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vsize, vma->vm_page_prot);
+ if (ret < 0)
+ pr_err("%s: failed with return val %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int dwc3_msm_ext_chg_release(struct inode *inode, struct file *file)
+{
+ struct dwc3_msm *mdwc = context;
+
+ pr_debug("dwc3-msm ext chg release\n");
+
+ mdwc->ext_chg_opened = false;
+
+ return 0;
+}
+
+static const struct file_operations dwc3_msm_ext_chg_fops = {
+ .owner = THIS_MODULE,
+ .open = dwc3_msm_ext_chg_open,
+ .write = dwc3_msm_ext_chg_write,
+ .mmap = dwc3_msm_ext_chg_mmap,
+ .release = dwc3_msm_ext_chg_release,
+};
+
+static int dwc3_msm_setup_cdev(struct dwc3_msm *mdwc)
+{
+ int ret;
+
+ ret = alloc_chrdev_region(&mdwc->ext_chg_dev, 0, 1, "usb_ext_chg");
+ if (ret < 0) {
+ pr_err("Fail to allocate usb ext char dev region\n");
+ return ret;
+ }
+ mdwc->ext_chg_class = class_create(THIS_MODULE, "dwc_ext_chg");
+ if (ret < 0) {
+ pr_err("Fail to create usb ext chg class\n");
+ goto unreg_chrdev;
+ }
+ cdev_init(&mdwc->ext_chg_cdev, &dwc3_msm_ext_chg_fops);
+ mdwc->ext_chg_cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&mdwc->ext_chg_cdev, mdwc->ext_chg_dev, 1);
+ if (ret < 0) {
+ pr_err("Fail to add usb ext chg cdev\n");
+ goto destroy_class;
+ }
+ mdwc->ext_chg_device = device_create(mdwc->ext_chg_class,
+ NULL, mdwc->ext_chg_dev, NULL,
+ "usb_ext_chg");
+ if (IS_ERR(mdwc->ext_chg_device)) {
+ pr_err("Fail to create usb ext chg device\n");
+ ret = PTR_ERR(mdwc->ext_chg_device);
+ mdwc->ext_chg_device = NULL;
+ goto del_cdev;
+ }
+
+ pr_debug("dwc3 msm ext chg cdev setup success\n");
+ return 0;
+
+del_cdev:
+ cdev_del(&mdwc->ext_chg_cdev);
+destroy_class:
+ class_destroy(mdwc->ext_chg_class);
+unreg_chrdev:
+ unregister_chrdev_region(mdwc->ext_chg_dev, 1);
+
+ return ret;
+}
+
static int __devinit dwc3_msm_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -2282,6 +2470,7 @@
INIT_WORK(&msm->restart_usb_work, dwc3_restart_usb_work);
INIT_WORK(&msm->id_work, dwc3_id_work);
INIT_DELAYED_WORK(&msm->init_adc_work, dwc3_init_adc_work);
+ init_completion(&msm->ext_chg_wait);
msm->xo_clk = clk_get(&pdev->dev, "xo");
if (IS_ERR(msm->xo_clk)) {
@@ -2433,6 +2622,8 @@
msm->charger.charging_disabled = of_property_read_bool(node,
"qcom,charging-disabled");
+ msm->charger.skip_chg_detect = of_property_read_bool(node,
+ "qcom,skip-charger-detection");
/*
* DWC3 has separate IRQ line for OTG events (ID/BSV) and for
* DP and DM linestate transitions during low power mode.
@@ -2606,12 +2797,17 @@
msm->otg_xceiv = usb_get_transceiver();
/* Register with OTG if present, ignore USB2 OTG using other PHY */
if (msm->otg_xceiv && !(msm->otg_xceiv->flags & ENABLE_SECONDARY_PHY)) {
- msm->charger.start_detection = dwc3_start_chg_det;
- ret = dwc3_set_charger(msm->otg_xceiv->otg, &msm->charger);
- if (ret || !msm->charger.notify_detection_complete) {
- dev_err(&pdev->dev, "failed to register charger: %d\n",
- ret);
- goto put_xcvr;
+ /* Skip charger detection for simulator targets */
+ if (!msm->charger.skip_chg_detect) {
+ msm->charger.start_detection = dwc3_start_chg_det;
+ ret = dwc3_set_charger(msm->otg_xceiv->otg,
+ &msm->charger);
+ if (ret || !msm->charger.notify_detection_complete) {
+ dev_err(&pdev->dev,
+ "failed to register charger: %d\n",
+ ret);
+ goto put_xcvr;
+ }
}
if (msm->ext_xceiv.otg_capability)
@@ -2638,6 +2834,11 @@
}
msm->otg_xceiv = NULL;
}
+ if (msm->ext_xceiv.otg_capability && msm->charger.start_detection) {
+ ret = dwc3_msm_setup_cdev(msm);
+ if (ret)
+ dev_err(&pdev->dev, "Fail to setup dwc3 setup cdev\n");
+ }
wake_lock_init(&msm->wlock, WAKE_LOCK_SUSPEND, "msm_dwc3");
wake_lock(&msm->wlock);
@@ -2690,6 +2891,13 @@
{
struct dwc3_msm *msm = platform_get_drvdata(pdev);
+ if (!msm->ext_chg_device) {
+ device_destroy(msm->ext_chg_class, msm->ext_chg_dev);
+ cdev_del(&msm->ext_chg_cdev);
+ class_destroy(msm->ext_chg_class);
+ unregister_chrdev_region(msm->ext_chg_dev, 1);
+ }
+
if (msm->id_adc_detect)
qpnp_adc_tm_usbid_end();
if (dwc3_debugfs_root)
@@ -2778,8 +2986,27 @@
static int dwc3_msm_runtime_idle(struct device *dev)
{
+ struct dwc3_msm *mdwc = dev_get_drvdata(dev);
+
dev_dbg(dev, "DWC3-msm runtime idle\n");
+ if (mdwc->ext_chg_active) {
+ dev_dbg(dev, "Deferring LPM\n");
+ /*
+ * Charger detection may happen in user space.
+ * Delay entering LPM by 3 sec. Otherwise we
+ * have to exit LPM when user space begins
+ * charger detection.
+ *
+ * This timer will be canceled when user space
+ * votes against LPM by incrementing PM usage
+ * counter. We enter low power mode when
+ * PM usage counter is decremented.
+ */
+ pm_schedule_suspend(dev, 3000);
+ return -EAGAIN;
+ }
+
return 0;
}
diff --git a/drivers/usb/dwc3/dwc3_otg.c b/drivers/usb/dwc3/dwc3_otg.c
index a3b2617..be9a4b5 100644
--- a/drivers/usb/dwc3/dwc3_otg.c
+++ b/drivers/usb/dwc3/dwc3_otg.c
@@ -498,7 +498,7 @@
power_supply_set_supply_type(dotg->psy, power_supply_type);
- if ((dotg->charger->chg_type == DWC3_CDP_CHARGER) && mA > 2)
+ if (dotg->charger->chg_type == DWC3_CDP_CHARGER)
mA = DWC3_IDEV_CHG_MAX;
if (dotg->charger->max_power == mA)
diff --git a/drivers/usb/dwc3/dwc3_otg.h b/drivers/usb/dwc3/dwc3_otg.h
index c2fab53..90e26cf 100644
--- a/drivers/usb/dwc3/dwc3_otg.h
+++ b/drivers/usb/dwc3/dwc3_otg.h
@@ -77,6 +77,8 @@
unsigned max_power;
bool charging_disabled;
+ bool skip_chg_detect;
+
/* start/stop charger detection, provided by external charger module */
void (*start_detection)(struct dwc3_charger *charger, bool start);
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 8d2ec97..0cfd515 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -393,6 +393,7 @@
u32 recip;
u32 wValue;
u32 wIndex;
+ u32 reg;
int ret;
wValue = le16_to_cpu(ctrl->wValue);
@@ -413,6 +414,13 @@
return -EINVAL;
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
return -EINVAL;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (set)
+ reg |= DWC3_DCTL_INITU1ENA;
+ else
+ reg &= ~DWC3_DCTL_INITU1ENA;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
break;
case USB_DEVICE_U2_ENABLE:
@@ -420,6 +428,13 @@
return -EINVAL;
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
return -EINVAL;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (set)
+ reg |= DWC3_DCTL_INITU2ENA;
+ else
+ reg &= ~DWC3_DCTL_INITU2ENA;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
break;
case USB_DEVICE_LTM_ENABLE:
@@ -524,6 +539,7 @@
{
u32 cfg;
int ret;
+ u32 reg;
dwc->start_config_issued = false;
cfg = le16_to_cpu(ctrl->wValue);
@@ -538,6 +554,14 @@
/* if the cfg matches and the cfg is non zero */
if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
dwc->dev_state = DWC3_CONFIGURED_STATE;
+ /*
+ * Enable transition to U1/U2 state when
+ * nothing is pending from application.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
dwc->resize_fifos = true;
dev_dbg(dwc->dev, "resize fifos flag SET\n");
}
@@ -751,6 +775,9 @@
dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS;
r = next_request(&ep0->request_list);
+ if (r == NULL)
+ return;
+
ur = &r->request;
if ((epnum & 1) && ur->zero &&
(ur->length % ep0->endpoint.maxpacket == 0)) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index c08a259..1059a4d 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1706,6 +1706,7 @@
int ret = 0;
u32 reg;
+ pm_runtime_get_sync(dwc->dev);
spin_lock_irqsave(&dwc->lock, flags);
if (dwc->gadget_driver) {
@@ -1765,6 +1766,7 @@
dwc3_ep0_out_start(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put(dwc->dev);
return 0;
@@ -1773,6 +1775,7 @@
err0:
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put(dwc->dev);
return ret;
}
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 705600d..36a43c3 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -91,6 +91,7 @@
#include "u_uac1.c"
#include "f_uac1.c"
#endif
+#include "f_ncm.c"
MODULE_AUTHOR("Mike Lockwood");
MODULE_DESCRIPTION("Android Composite USB Driver");
@@ -426,6 +427,7 @@
struct adb_data {
bool opened;
bool enabled;
+ struct android_dev *dev;
};
static int
@@ -459,6 +461,7 @@
data->enabled = true;
+
/* Disable the gadget until adbd is ready */
if (!data->opened)
android_disable(dev);
@@ -490,27 +493,45 @@
struct android_dev *dev = adb_function.android_dev;
struct adb_data *data = adb_function.config;
+ /* dev is null in case ADB is not in the composition */
+ if (dev)
+ mutex_lock(&dev->mutex);
+
+ /* Save dev in case the adb function will get disabled */
+ data->dev = dev;
data->opened = true;
- if (data->enabled && dev) {
- mutex_lock(&dev->mutex);
+ if (data->enabled && dev)
android_enable(dev);
+
+ if (dev)
mutex_unlock(&dev->mutex);
- }
}
static void adb_closed_callback(void)
{
- struct android_dev *dev = adb_function.android_dev;
struct adb_data *data = adb_function.config;
+ struct android_dev *dev = adb_function.android_dev;
+
+ /* In case new composition is without ADB, use saved one */
+ if (!dev)
+ dev = data->dev;
+
+ if (!dev)
+ pr_err("adb_closed_callback: data->dev is NULL");
+
+ if (dev)
+ mutex_lock(&dev->mutex);
data->opened = false;
- if (data->enabled) {
- mutex_lock(&dev->mutex);
+ if (data->enabled && dev)
android_disable(dev);
+
+ data->dev = NULL;
+
+ if (dev)
mutex_unlock(&dev->mutex);
- }
}
@@ -772,7 +793,103 @@
.bind_config = gps_function_bind_config,
};
+/* ncm */
+struct ncm_function_config {
+ u8 ethaddr[ETH_ALEN];
+};
+static int
+ncm_function_init(struct android_usb_function *f, struct usb_composite_dev *c)
+{
+ f->config = kzalloc(sizeof(struct ncm_function_config), GFP_KERNEL);
+ if (!f->config)
+ return -ENOMEM;
+ return 0;
+}
+
+static void ncm_function_cleanup(struct android_usb_function *f)
+{
+ kfree(f->config);
+ f->config = NULL;
+}
+
+static int
+ncm_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ struct ncm_function_config *ncm = f->config;
+ int ret;
+
+ if (!ncm) {
+ pr_err("%s: ncm config is null\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_info("%s MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", __func__,
+ ncm->ethaddr[0], ncm->ethaddr[1], ncm->ethaddr[2],
+ ncm->ethaddr[3], ncm->ethaddr[4], ncm->ethaddr[5]);
+
+ ret = gether_setup_name(c->cdev->gadget, ncm->ethaddr, "ncm");
+ if (ret) {
+ pr_err("%s: gether setup failed err:%d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = ncm_bind_config(c, ncm->ethaddr);
+ if (ret) {
+ pr_err("%s: ncm bind config failed err:%d", __func__, ret);
+ gether_cleanup();
+ return ret;
+ }
+
+ return ret;
+}
+
+static void ncm_function_unbind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ gether_cleanup();
+}
+
+static ssize_t ncm_ethaddr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct ncm_function_config *ncm = f->config;
+ return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ ncm->ethaddr[0], ncm->ethaddr[1], ncm->ethaddr[2],
+ ncm->ethaddr[3], ncm->ethaddr[4], ncm->ethaddr[5]);
+}
+
+static ssize_t ncm_ethaddr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct ncm_function_config *ncm = f->config;
+
+ if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ (int *)&ncm->ethaddr[0], (int *)&ncm->ethaddr[1],
+ (int *)&ncm->ethaddr[2], (int *)&ncm->ethaddr[3],
+ (int *)&ncm->ethaddr[4], (int *)&ncm->ethaddr[5]) == 6)
+ return size;
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(ncm_ethaddr, S_IRUGO | S_IWUSR, ncm_ethaddr_show,
+ ncm_ethaddr_store);
+static struct device_attribute *ncm_function_attributes[] = {
+ &dev_attr_ncm_ethaddr,
+ NULL
+};
+
+static struct android_usb_function ncm_function = {
+ .name = "ncm",
+ .init = ncm_function_init,
+ .cleanup = ncm_function_cleanup,
+ .bind_config = ncm_function_bind_config,
+ .unbind_config = ncm_function_unbind_config,
+ .attributes = ncm_function_attributes,
+};
/* ecm transport string */
static char ecm_transports[MAX_XPORT_STR_LEN];
@@ -1837,6 +1954,7 @@
&rndis_function,
&rndis_qc_function,
&ecm_function,
+ &ncm_function,
&mass_storage_function,
&accessory_function,
#ifdef CONFIG_SND_PCM
@@ -2689,6 +2807,8 @@
of_property_read_u32(pdev->dev.of_node,
"qcom,android-usb-swfi-latency",
&pdata->swfi_latency);
+ pdata->cdrom = of_property_read_bool(pdev->dev.of_node,
+ "qcom,android-usb-cdrom");
} else {
pdata = pdev->dev.platform_data;
}
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
index 3cad3ce..8d8c30e 100644
--- a/drivers/usb/gadget/ci13xxx_msm.c
+++ b/drivers/usb/gadget/ci13xxx_msm.c
@@ -16,6 +16,8 @@
#define MSM_USB_BASE (udc->regs)
+#define CI13XXX_MSM_MAX_ITC_LEVEL 6
+
struct ci13xxx_udc_context {
int irq;
void __iomem *regs;
@@ -177,7 +179,7 @@
CI13XXX_ZERO_ITC |
CI13XXX_DISABLE_STREAMING |
CI13XXX_IS_OTG,
-
+ .nz_itc = 0,
.notify_event = ci13xxx_msm_notify_event,
};
@@ -230,10 +232,21 @@
static int ci13xxx_msm_probe(struct platform_device *pdev)
{
struct resource *res;
- int ret;
+ int ret, rc;
+ int itc_level = 0;
dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n");
+ if (pdev->dev.of_node) {
+ rc = of_property_read_u32(pdev->dev.of_node, "qcom,itc-level",
+ &itc_level);
+ /* Acceptable values for nz_itc are: 0,1,2,4,8,16,32,64 */
+ if (itc_level > CI13XXX_MSM_MAX_ITC_LEVEL || rc)
+ ci13xxx_msm_udc_driver.nz_itc = 0;
+ else
+ ci13xxx_msm_udc_driver.nz_itc = 1 << itc_level;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get platform resource mem\n");
@@ -313,9 +326,18 @@
writel_relaxed(val, USB_GENCONFIG);
}
+static const struct of_device_id ci13xx_msm_dt_match[] = {
+ { .compatible = "qcom,ci13xxx_msm",
+ },
+ {}
+};
+
static struct platform_driver ci13xxx_msm_driver = {
.probe = ci13xxx_msm_probe,
- .driver = { .name = "msm_hsusb", },
+ .driver = {
+ .name = "msm_hsusb",
+ .of_match_table = ci13xx_msm_dt_match,
+ },
.remove = ci13xxx_msm_remove,
};
MODULE_ALIAS("platform:msm_hsusb");
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 6fca910..e7074a2 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -79,7 +79,7 @@
*****************************************************************************/
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
-#define USB_MAX_TIMEOUT 100 /* 100msec timeout */
+#define USB_MAX_TIMEOUT 25 /* 25msec timeout */
#define EP_PRIME_CHECK_DELAY (jiffies + msecs_to_jiffies(1000))
#define MAX_PRIME_CHECK_RETRY 3 /*Wait for 3sec for EP prime failure */
@@ -338,14 +338,17 @@
*/
static int hw_device_reset(struct ci13xxx *udc)
{
+ int delay_count = 25; /* 250 usec */
+
/* should flush & stop before reset */
hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0);
hw_cwrite(CAP_USBCMD, USBCMD_RS, 0);
hw_cwrite(CAP_USBCMD, USBCMD_RST, USBCMD_RST);
- while (hw_cread(CAP_USBCMD, USBCMD_RST))
- udelay(10); /* not RTOS friendly */
-
+ while (delay_count-- && hw_cread(CAP_USBCMD, USBCMD_RST))
+ udelay(10);
+ if (delay_count < 0)
+ pr_err("USB controller reset failed\n");
if (udc->udc_driver->notify_event)
udc->udc_driver->notify_event(udc,
@@ -364,7 +367,10 @@
* 8 micro frames. If CPU can handle interrupts at faster rate, ITC
* can be set to lesser value to gain performance.
*/
- if (udc->udc_driver->flags & CI13XXX_ZERO_ITC)
+ if (udc->udc_driver->nz_itc)
+ hw_cwrite(CAP_USBCMD, USBCMD_ITC_MASK,
+ USBCMD_ITC(udc->udc_driver->nz_itc));
+ else if (udc->udc_driver->flags & CI13XXX_ZERO_ITC)
hw_cwrite(CAP_USBCMD, USBCMD_ITC_MASK, USBCMD_ITC(0));
if (hw_cread(CAP_USBMODE, USBMODE_CM) != USBMODE_CM_DEVICE) {
@@ -445,7 +451,8 @@
int n = hw_ep_bit(num, dir);
struct ci13xxx_ep *mEp = &_udc->ci13xxx_ep[n];
- if (_udc->skip_flush || list_empty(&mEp->qh.queue))
+ /* Flush ep0 even when queue is empty */
+ if (_udc->skip_flush || (num && list_empty(&mEp->qh.queue)))
return 0;
start = ktime_get();
@@ -813,6 +820,8 @@
*/
static int hw_usb_reset(void)
{
+ int delay_count = 10; /* 100 usec delay */
+
hw_usb_set_address(0);
/* ESS flushes only at end?!? */
@@ -825,8 +834,10 @@
hw_cwrite(CAP_ENDPTCOMPLETE, 0, 0); /* writes its content */
/* wait until all bits cleared */
- while (hw_cread(CAP_ENDPTPRIME, ~0))
- udelay(10); /* not RTOS friendly */
+ while (delay_count-- && hw_cread(CAP_ENDPTPRIME, ~0))
+ udelay(10);
+ if (delay_count < 0)
+ pr_err("ENDPTPRIME is not cleared during bus reset\n");
/* reset all endpoints ? */
@@ -1971,6 +1982,7 @@
for (i = 1; i < 5; i++)
mReq->ptr->page[i] = (mReq->req.dma + i * CI13XXX_PAGE_SIZE) &
~TD_RESERVED_MASK;
+ wmb();
/* Remote Wakeup */
if (udc->suspended) {
@@ -2107,7 +2119,18 @@
if (mReq->zptr) {
if ((TD_STATUS_ACTIVE & mReq->zptr->token) != 0)
return -EBUSY;
- dma_pool_free(mEp->td_pool, mReq->zptr, mReq->zdma);
+
+ /* The controller may access this dTD one more time.
+ * Defer freeing this to next zero length dTD completion.
+ * It is safe to assume that controller will no longer
+ * access the previous dTD after next dTD completion.
+ */
+ if (mEp->last_zptr)
+ dma_pool_free(mEp->td_pool, mEp->last_zptr,
+ mEp->last_zdma);
+ mEp->last_zptr = mReq->zptr;
+ mEp->last_zdma = mReq->zdma;
+
mReq->zptr = NULL;
}
@@ -2256,12 +2279,16 @@
gadget->otg_srp_reqd = 0;
udc->driver->disconnect(gadget);
- usb_ep_fifo_flush(&udc->ep0out.ep);
- usb_ep_fifo_flush(&udc->ep0in.ep);
- if (udc->status != NULL) {
- usb_ep_free_request(&udc->ep0in.ep, udc->status);
- udc->status = NULL;
+ spin_lock_irqsave(udc->lock, flags);
+ _ep_nuke(&udc->ep0out);
+ _ep_nuke(&udc->ep0in);
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ if (udc->ep0in.last_zptr) {
+ dma_pool_free(udc->ep0in.td_pool, udc->ep0in.last_zptr,
+ udc->ep0in.last_zdma);
+ udc->ep0in.last_zptr = NULL;
}
return 0;
@@ -2305,7 +2332,7 @@
/*stop charging upon reset */
if (udc->transceiver)
- usb_phy_set_power(udc->transceiver, 0);
+ usb_phy_set_power(udc->transceiver, 100);
retval = _gadget_stop_activity(&udc->gadget);
if (retval)
@@ -2316,10 +2343,6 @@
if (retval)
goto done;
- udc->status = usb_ep_alloc_request(&udc->ep0in.ep, GFP_ATOMIC);
- if (udc->status == NULL)
- retval = -ENOMEM;
-
spin_lock(udc->lock);
done:
@@ -2388,8 +2411,8 @@
return;
}
- kfree(req->buf);
- usb_ep_free_request(ep, req);
+ if (req->status)
+ err("GET_STATUS failed");
}
/**
@@ -2405,8 +2428,7 @@
__acquires(mEp->lock)
{
struct ci13xxx_ep *mEp = &udc->ep0in;
- struct usb_request *req = NULL;
- gfp_t gfp_flags = GFP_ATOMIC;
+ struct usb_request *req = udc->status;
int dir, num, retval;
trace("%p, %p", mEp, setup);
@@ -2414,19 +2436,9 @@
if (mEp == NULL || setup == NULL)
return -EINVAL;
- spin_unlock(mEp->lock);
- req = usb_ep_alloc_request(&mEp->ep, gfp_flags);
- spin_lock(mEp->lock);
- if (req == NULL)
- return -ENOMEM;
-
req->complete = isr_get_status_complete;
req->length = 2;
- req->buf = kzalloc(req->length, gfp_flags);
- if (req->buf == NULL) {
- retval = -ENOMEM;
- goto err_free_req;
- }
+ req->buf = udc->status_buf;
if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
if (setup->wIndex == OTG_STATUS_SELECTOR) {
@@ -2449,18 +2461,7 @@
/* else do nothing; reserved for future use */
spin_unlock(mEp->lock);
- retval = usb_ep_queue(&mEp->ep, req, gfp_flags);
- spin_lock(mEp->lock);
- if (retval)
- goto err_free_buf;
-
- return 0;
-
- err_free_buf:
- kfree(req->buf);
- err_free_req:
- spin_unlock(mEp->lock);
- usb_ep_free_request(&mEp->ep, req);
+ retval = usb_ep_queue(&mEp->ep, req, GFP_ATOMIC);
spin_lock(mEp->lock);
return retval;
}
@@ -2503,11 +2504,9 @@
trace("%p", udc);
mEp = (udc->ep0_dir == TX) ? &udc->ep0out : &udc->ep0in;
- if (udc->status) {
- udc->status->context = udc;
- udc->status->complete = isr_setup_status_complete;
- } else
- return -EINVAL;
+ udc->status->context = udc;
+ udc->status->complete = isr_setup_status_complete;
+ udc->status->length = 0;
spin_unlock(mEp->lock);
retval = usb_ep_queue(&mEp->ep, udc->status, GFP_ATOMIC);
@@ -2941,6 +2940,12 @@
} while (mEp->dir != direction);
+ if (mEp->last_zptr) {
+ dma_pool_free(mEp->td_pool, mEp->last_zptr,
+ mEp->last_zdma);
+ mEp->last_zptr = NULL;
+ }
+
mEp->desc = NULL;
mEp->ep.desc = NULL;
mEp->ep.maxpacket = USHRT_MAX;
@@ -3032,21 +3037,24 @@
trace("%p, %p, %X", ep, req, gfp_flags);
- if (ep == NULL || req == NULL || mEp->desc == NULL)
- return -EINVAL;
-
- if (!udc->softconnect)
- return -ENODEV;
-
spin_lock_irqsave(mEp->lock, flags);
+ if (ep == NULL || req == NULL || mEp->desc == NULL) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ if (!udc->softconnect) {
+ retval = -ENODEV;
+ goto done;
+ }
if (!udc->configured && mEp->type !=
USB_ENDPOINT_XFER_CONTROL) {
- spin_unlock_irqrestore(mEp->lock, flags);
trace("usb is not configured"
"ept #%d, ept name#%s\n",
mEp->num, mEp->ep.name);
- return -ESHUTDOWN;
+ retval = -ESHUTDOWN;
+ goto done;
}
if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
@@ -3126,12 +3134,19 @@
trace("%p, %p", ep, req);
+ spin_lock_irqsave(mEp->lock, flags);
+ /*
+ * Only ep0 IN is exposed to composite. When a req is dequeued
+ * on ep0, check both ep0 IN and ep0 OUT queues.
+ */
if (ep == NULL || req == NULL || mReq->req.status != -EALREADY ||
mEp->desc == NULL || list_empty(&mReq->queue) ||
- list_empty(&mEp->qh.queue))
+ (list_empty(&mEp->qh.queue) && ((mEp->type !=
+ USB_ENDPOINT_XFER_CONTROL) ||
+ list_empty(&_udc->ep0out.qh.queue)))) {
+ spin_unlock_irqrestore(mEp->lock, flags);
return -EINVAL;
-
- spin_lock_irqsave(mEp->lock, flags);
+ }
dbg_event(_usb_addr(mEp), "DEQUEUE", 0);
@@ -3270,7 +3285,13 @@
del_timer(&mEp->prime_timer);
mEp->prime_timer_count = 0;
dbg_event(_usb_addr(mEp), "FFLUSH", 0);
- hw_ep_flush(mEp->num, mEp->dir);
+ /*
+ * _ep_nuke() takes care of flushing the endpoint.
+ * some function drivers expect udc to retire all
+ * pending requests upon flushing an endpoint. There
+ * is no harm in doing it.
+ */
+ _ep_nuke(mEp);
spin_unlock_irqrestore(mEp->lock, flags);
}
@@ -3475,6 +3496,14 @@
retval = usb_ep_enable(&udc->ep0in.ep);
if (retval)
return retval;
+ udc->status = usb_ep_alloc_request(&udc->ep0in.ep, GFP_KERNEL);
+ if (!udc->status)
+ return -ENOMEM;
+ udc->status_buf = kzalloc(2, GFP_KERNEL); /* for GET_STATUS */
+ if (!udc->status_buf) {
+ usb_ep_free_request(&udc->ep0in.ep, udc->status);
+ return -ENOMEM;
+ }
spin_lock_irqsave(udc->lock, flags);
udc->gadget.ep0 = &udc->ep0in.ep;
@@ -3558,6 +3587,9 @@
driver->unbind(&udc->gadget); /* MAY SLEEP */
spin_lock_irqsave(udc->lock, flags);
+ usb_ep_free_request(&udc->ep0in.ep, udc->status);
+ kfree(udc->status_buf);
+
udc->gadget.dev.driver = NULL;
/* free resources */
@@ -3774,9 +3806,7 @@
pm_runtime_no_callbacks(&udc->gadget.dev);
pm_runtime_enable(&udc->gadget.dev);
- retval = register_trace_usb_daytona_invalid_access(dump_usb_info,
- NULL);
- if (retval)
+ if (register_trace_usb_daytona_invalid_access(dump_usb_info, NULL))
pr_err("Registering trace failed\n");
_udc = udc;
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h
index 3145418..09404c8 100644
--- a/drivers/usb/gadget/ci13xxx_udc.h
+++ b/drivers/usb/gadget/ci13xxx_udc.h
@@ -57,7 +57,7 @@
#define TD_CURR_OFFSET (0x0FFFUL << 0)
#define TD_FRAME_NUM (0x07FFUL << 0)
#define TD_RESERVED_MASK (0x0FFFUL << 0)
-} __attribute__ ((packed));
+} __attribute__ ((packed, aligned(4)));
/* DMA layout of queue heads */
struct ci13xxx_qh {
@@ -75,7 +75,7 @@
/* 9 */
u32 RESERVED;
struct usb_ctrlrequest setup;
-} __attribute__ ((packed));
+} __attribute__ ((packed, aligned(4)));
/* cache of larger request's original attributes */
struct ci13xxx_multi_req {
@@ -115,6 +115,8 @@
spinlock_t *lock;
struct device *device;
struct dma_pool *td_pool;
+ struct ci13xxx_td *last_zptr;
+ dma_addr_t last_zdma;
unsigned long dTD_update_fail_count;
unsigned long prime_fail_count;
int prime_timer_count;
@@ -127,6 +129,7 @@
struct ci13xxx_udc_driver {
const char *name;
unsigned long flags;
+ unsigned int nz_itc;
#define CI13XXX_REGS_SHARED BIT(0)
#define CI13XXX_REQUIRE_TRANSCEIVER BIT(1)
#define CI13XXX_PULLUP_ON_VBUS BIT(2)
@@ -153,6 +156,7 @@
struct dma_pool *qh_pool; /* DMA pool for queue heads */
struct dma_pool *td_pool; /* DMA pool for transfer descs */
struct usb_request *status; /* ep0 status request */
+ void *status_buf;/* GET_STATUS buffer */
struct usb_gadget gadget; /* USB slave device */
struct ci13xxx_ep ci13xxx_ep[ENDPT_MAX]; /* extended endpts */
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index bac8b68..2f35315 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -728,12 +728,18 @@
rc = -EINTR;
break;
}
- if (common->thread_wakeup_needed)
+ spin_lock_irq(&common->lock);
+ if (common->thread_wakeup_needed) {
+ spin_unlock_irq(&common->lock);
break;
+ }
+ spin_unlock_irq(&common->lock);
schedule();
}
__set_current_state(TASK_RUNNING);
+ spin_lock_irq(&common->lock);
common->thread_wakeup_needed = 0;
+ spin_unlock_irq(&common->lock);
return rc;
}
@@ -796,12 +802,17 @@
curlun->file_length - file_offset);
/* Wait for the next buffer to become available */
+ spin_lock_irq(&common->lock);
bh = common->next_buffhd_to_fill;
while (bh->state != BUF_STATE_EMPTY) {
+ spin_unlock_irq(&common->lock);
rc = sleep_thread(common);
if (rc)
return rc;
+
+ spin_lock_irq(&common->lock);
}
+ spin_unlock_irq(&common->lock);
/*
* If we were asked to read past the end of file,
@@ -813,8 +824,10 @@
curlun->sense_data_info =
file_offset >> curlun->blkbits;
curlun->info_valid = 1;
+ spin_lock_irq(&common->lock);
bh->inreq->length = 0;
bh->state = BUF_STATE_FULL;
+ spin_unlock_irq(&common->lock);
break;
}
@@ -854,8 +867,10 @@
* equal to the buffer size, which is divisible by the
* bulk-in maxpacket size.
*/
+ spin_lock_irq(&common->lock);
bh->inreq->length = nread;
bh->state = BUF_STATE_FULL;
+ spin_unlock_irq(&common->lock);
/* If an error occurred, report it and its position */
if (nread < amount) {
@@ -1814,12 +1829,17 @@
u32 sd, sdinfo = 0;
/* Wait for the next buffer to become available */
+ spin_lock_irq(&common->lock);
bh = common->next_buffhd_to_fill;
while (bh->state != BUF_STATE_EMPTY) {
+ spin_unlock_irq(&common->lock);
rc = sleep_thread(common);
if (rc)
return rc;
+
+ spin_lock_irq(&common->lock);
}
+ spin_unlock_irq(&common->lock);
if (curlun) {
sd = curlun->sense_data;
@@ -2029,13 +2049,19 @@
dump_cdb(common);
/* Wait for the next buffer to become available for data or status */
+ spin_lock_irq(&common->lock);
bh = common->next_buffhd_to_fill;
common->next_buffhd_to_drain = bh;
while (bh->state != BUF_STATE_EMPTY) {
+ spin_unlock_irq(&common->lock);
rc = sleep_thread(common);
if (rc)
return rc;
+
+ spin_lock_irq(&common->lock);
}
+ spin_unlock_irq(&common->lock);
+
common->phase_error = 0;
common->short_packet_received = 0;
@@ -2376,12 +2402,17 @@
int rc = 0;
/* Wait for the next buffer to become available */
+ spin_lock_irq(&common->lock);
bh = common->next_buffhd_to_fill;
while (bh->state != BUF_STATE_EMPTY) {
+ spin_unlock_irq(&common->lock);
rc = sleep_thread(common);
if (rc)
return rc;
+
+ spin_lock_irq(&common->lock);
}
+ spin_unlock_irq(&common->lock);
/* Queue a request to read a Bulk-only CBW */
set_bulk_out_req_length(common, bh, US_BULK_CB_WRAP_LEN);
@@ -2396,14 +2427,23 @@
*/
/* Wait for the CBW to arrive */
+ spin_lock_irq(&common->lock);
while (bh->state != BUF_STATE_FULL) {
+ spin_unlock_irq(&common->lock);
rc = sleep_thread(common);
if (rc)
return rc;
+
+ spin_lock_irq(&common->lock);
}
+ spin_unlock_irq(&common->lock);
+
smp_rmb();
rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO;
+
+ spin_lock_irq(&common->lock);
bh->state = BUF_STATE_EMPTY;
+ spin_unlock_irq(&common->lock);
return rc;
}
@@ -2510,7 +2550,8 @@
goto reset_bulk_int;
fsg->bulk_out->driver_data = common;
fsg->bulk_out_enabled = 1;
- common->bulk_out_maxpacket = le16_to_cpu(fsg->bulk_in->desc->wMaxPacketSize);
+ common->bulk_out_maxpacket =
+ le16_to_cpu(fsg->bulk_out->desc->wMaxPacketSize);
clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
csw_hack_sent = 0;
write_error_after_csw_sent = 0;
@@ -2585,10 +2626,13 @@
/* Wait until everything is idle */
for (;;) {
int num_active = 0;
+ spin_lock_irq(&common->lock);
for (i = 0; i < fsg_num_buffers; ++i) {
bh = &common->buffhds[i];
num_active += bh->inreq_busy + bh->outreq_busy;
}
+ spin_unlock_irq(&common->lock);
+
if (num_active == 0)
break;
if (sleep_thread(common))
diff --git a/drivers/usb/gadget/f_mbim.c b/drivers/usb/gadget/f_mbim.c
index 22f8dc9..15f20ca 100644
--- a/drivers/usb/gadget/f_mbim.c
+++ b/drivers/usb/gadget/f_mbim.c
@@ -63,10 +63,10 @@
};
enum mbim_notify_state {
- NCM_NOTIFY_NONE,
- NCM_NOTIFY_CONNECT,
- NCM_NOTIFY_SPEED,
- NCM_NOTIFY_RESPONSE_AVAILABLE,
+ MBIM_NOTIFY_NONE,
+ MBIM_NOTIFY_CONNECT,
+ MBIM_NOTIFY_SPEED,
+ MBIM_NOTIFY_RESPONSE_AVAILABLE,
};
struct f_mbim {
@@ -74,7 +74,6 @@
struct usb_composite_dev *cdev;
atomic_t online;
- bool is_open;
atomic_t open_excl;
atomic_t ioctl_excl;
@@ -95,7 +94,7 @@
u8 ctrl_id, data_id;
u8 data_alt_int;
- struct ndp_parser_opts *parser_opts;
+ struct mbim_ndp_parser_opts *parser_opts;
spinlock_t lock;
@@ -140,24 +139,24 @@
/*-------------------------------------------------------------------------*/
-#define NTB_DEFAULT_IN_SIZE (0x4000)
-#define NTB_OUT_SIZE (0x1000)
-#define NDP_IN_DIVISOR (0x4)
+#define MBIM_NTB_DEFAULT_IN_SIZE (0x4000)
+#define MBIM_NTB_OUT_SIZE (0x1000)
+#define MBIM_NDP_IN_DIVISOR (0x4)
#define NTB_DEFAULT_IN_SIZE_IPA (0x2000)
-#define NTB_OUT_SIZE_IPA (0x2000)
+#define MBIM_NTB_OUT_SIZE_IPA (0x2000)
-#define FORMATS_SUPPORTED USB_CDC_NCM_NTB16_SUPPORTED
+#define MBIM_FORMATS_SUPPORTED USB_CDC_NCM_NTB16_SUPPORTED
-static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
- .wLength = sizeof ntb_parameters,
- .bmNtbFormatsSupported = cpu_to_le16(FORMATS_SUPPORTED),
- .dwNtbInMaxSize = cpu_to_le32(NTB_DEFAULT_IN_SIZE),
- .wNdpInDivisor = cpu_to_le16(NDP_IN_DIVISOR),
+static struct usb_cdc_ncm_ntb_parameters mbim_ntb_parameters = {
+ .wLength = sizeof mbim_ntb_parameters,
+ .bmNtbFormatsSupported = cpu_to_le16(MBIM_FORMATS_SUPPORTED),
+ .dwNtbInMaxSize = cpu_to_le32(MBIM_NTB_DEFAULT_IN_SIZE),
+ .wNdpInDivisor = cpu_to_le16(MBIM_NDP_IN_DIVISOR),
.wNdpInPayloadRemainder = cpu_to_le16(0),
.wNdpInAlignment = cpu_to_le16(4),
- .dwNtbOutMaxSize = cpu_to_le32(NTB_OUT_SIZE),
+ .dwNtbOutMaxSize = cpu_to_le32(MBIM_NTB_OUT_SIZE),
.wNdpOutDivisor = cpu_to_le16(4),
.wNdpOutPayloadRemainder = cpu_to_le16(0),
.wNdpOutAlignment = cpu_to_le16(4),
@@ -444,7 +443,7 @@
* and switch pointers to the structures when the format is changed.
*/
-struct ndp_parser_opts {
+struct mbim_ndp_parser_opts {
u32 nth_sign;
u32 ndp_sign;
unsigned nth_size;
@@ -487,8 +486,8 @@
.next_fp_index = 2, \
}
-static struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
-static struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;
+static struct mbim_ndp_parser_opts mbim_ndp16_opts = INIT_NDP16_OPTS;
+static struct mbim_ndp_parser_opts mbim_ndp32_opts = INIT_NDP32_OPTS;
static inline int mbim_lock(atomic_t *excl)
{
@@ -630,7 +629,7 @@
return 0;
}
- if (dev->not_port.notify_state != NCM_NOTIFY_RESPONSE_AVAILABLE) {
+ if (dev->not_port.notify_state != MBIM_NOTIFY_RESPONSE_AVAILABLE) {
pr_err("dev:%p state=%d, recover!!\n", dev,
dev->not_port.notify_state);
mbim_free_ctrl_pkt(cpkt);
@@ -670,12 +669,14 @@
int ret = 0;
aggr_params.dl.aggr_prot = TETH_AGGR_PROTOCOL_MBIM;
- aggr_params.dl.max_datagrams = ntb_parameters.wNtbOutMaxDatagrams;
- aggr_params.dl.max_transfer_size_byte = ntb_parameters.dwNtbInMaxSize;
+ aggr_params.dl.max_datagrams = mbim_ntb_parameters.wNtbOutMaxDatagrams;
+ aggr_params.dl.max_transfer_size_byte =
+ mbim_ntb_parameters.dwNtbInMaxSize;
aggr_params.ul.aggr_prot = TETH_AGGR_PROTOCOL_MBIM;
- aggr_params.ul.max_datagrams = ntb_parameters.wNtbOutMaxDatagrams;
- aggr_params.ul.max_transfer_size_byte = ntb_parameters.dwNtbOutMaxSize;
+ aggr_params.ul.max_datagrams = mbim_ntb_parameters.wNtbOutMaxDatagrams;
+ aggr_params.ul.max_transfer_size_byte =
+ mbim_ntb_parameters.dwNtbOutMaxSize;
ret = teth_bridge_set_aggr_params(&aggr_params);
if (ret)
@@ -731,9 +732,9 @@
static inline void mbim_reset_values(struct f_mbim *mbim)
{
- mbim->parser_opts = &ndp16_opts;
+ mbim->parser_opts = &mbim_ndp16_opts;
- mbim->ntb_input_size = NTB_DEFAULT_IN_SIZE;
+ mbim->ntb_input_size = MBIM_NTB_DEFAULT_IN_SIZE;
atomic_set(&mbim->online, 0);
}
@@ -745,11 +746,6 @@
pr_debug("Queue empty packet for QBI");
spin_lock(&dev->lock);
- if (!dev->is_open) {
- pr_err("%s: mbim file handler %p is not open", __func__, dev);
- spin_unlock(&dev->lock);
- return;
- }
cpkt = mbim_alloc_ctrl_pkt(0, GFP_ATOMIC);
if (!cpkt) {
@@ -798,8 +794,6 @@
{
struct usb_request *req = mbim->not_port.notify_req;
struct usb_cdc_notification *event;
- struct usb_composite_dev *cdev = mbim->cdev;
- __le32 *data;
int status;
pr_debug("notify_state: %d", mbim->not_port.notify_state);
@@ -811,15 +805,15 @@
switch (mbim->not_port.notify_state) {
- case NCM_NOTIFY_NONE:
+ case MBIM_NOTIFY_NONE:
if (atomic_read(&mbim->not_port.notify_count) > 0)
- pr_err("Pending notifications in NCM_NOTIFY_NONE\n");
+ pr_err("Pending notifications in MBIM_NOTIFY_NONE\n");
else
pr_debug("No pending notifications\n");
return;
- case NCM_NOTIFY_RESPONSE_AVAILABLE:
+ case MBIM_NOTIFY_RESPONSE_AVAILABLE:
pr_debug("Notification %02x sent\n", event->bNotificationType);
if (atomic_read(&mbim->not_port.notify_count) <= 0) {
@@ -836,36 +830,6 @@
}
return;
-
- case NCM_NOTIFY_CONNECT:
- event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
- if (mbim->is_open)
- event->wValue = cpu_to_le16(1);
- else
- event->wValue = cpu_to_le16(0);
- event->wLength = 0;
- req->length = sizeof *event;
-
- pr_info("notify connect %s\n",
- mbim->is_open ? "true" : "false");
- mbim->not_port.notify_state = NCM_NOTIFY_RESPONSE_AVAILABLE;
- break;
-
- case NCM_NOTIFY_SPEED:
- event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
- event->wValue = cpu_to_le16(0);
- event->wLength = cpu_to_le16(8);
- req->length = NCM_STATUS_BYTECOUNT;
-
- /* SPEED_CHANGE data is up/down speeds in bits/sec */
- data = req->buf + sizeof *event;
- data[0] = cpu_to_le32(mbim_bitrate(cdev->gadget));
- data[1] = data[0];
-
- pr_info("notify speed %d\n",
- mbim_bitrate(cdev->gadget));
- mbim->not_port.notify_state = NCM_NOTIFY_CONNECT;
- break;
}
event->bmRequestType = 0xA1;
@@ -888,22 +852,6 @@
}
}
-/*
- * Context: mbim->lock held
- */
-static void mbim_notify(struct f_mbim *mbim)
-{
- /*
- * If mbim_notify() is called before the second (CONNECT)
- * notification is sent, then it will reset to send the SPEED
- * notificaion again (and again, and again), but it's not a problem
- */
- pr_debug("dev:%p\n", mbim);
-
- mbim->not_port.notify_state = NCM_NOTIFY_RESPONSE_AVAILABLE;
- mbim_do_notify(mbim);
-}
-
static void mbim_notify_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_mbim *mbim = req->context;
@@ -922,7 +870,7 @@
case -ECONNRESET:
case -ESHUTDOWN:
/* connection gone */
- mbim->not_port.notify_state = NCM_NOTIFY_NONE;
+ mbim->not_port.notify_state = MBIM_NOTIFY_NONE;
atomic_set(&mbim->not_port.notify_count, 0);
pr_info("ESHUTDOWN/ECONNRESET, connection gone");
spin_unlock(&mbim->lock);
@@ -961,7 +909,7 @@
if (req->length == 4) {
in_size = get_unaligned_le32(req->buf);
if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
- in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) {
+ in_size > le32_to_cpu(mbim_ntb_parameters.dwNtbInMaxSize)) {
pr_err("Illegal INPUT SIZE (%d) from host\n", in_size);
goto invalid;
}
@@ -969,7 +917,7 @@
ntb = (struct mbim_ntb_input_size *)req->buf;
in_size = get_unaligned_le32(&(ntb->ntb_input_size));
if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
- in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) {
+ in_size > le32_to_cpu(mbim_ntb_parameters.dwNtbInMaxSize)) {
pr_err("Illegal INPUT SIZE (%d) from host\n", in_size);
goto invalid;
}
@@ -1022,12 +970,6 @@
memcpy(cpkt->buf, req->buf, len);
spin_lock(&dev->lock);
- if (!dev->is_open) {
- pr_err("mbim file handler %p is not open", dev);
- spin_unlock(&dev->lock);
- mbim_free_ctrl_pkt(cpkt);
- return;
- }
list_add_tail(&cpkt->list, &dev->cpkt_req_q);
spin_unlock(&dev->lock);
@@ -1128,9 +1070,9 @@
if (w_length == 0 || w_value != 0 || w_index != mbim->ctrl_id)
break;
- value = w_length > sizeof ntb_parameters ?
- sizeof ntb_parameters : w_length;
- memcpy(req->buf, &ntb_parameters, value);
+ value = w_length > sizeof mbim_ntb_parameters ?
+ sizeof mbim_ntb_parameters : w_length;
+ memcpy(req->buf, &mbim_ntb_parameters, value);
break;
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
@@ -1177,7 +1119,7 @@
if (w_length < 2 || w_value != 0 || w_index != mbim->ctrl_id)
break;
- format = (mbim->parser_opts == &ndp16_opts) ? 0x0000 : 0x0001;
+ format = (mbim->parser_opts == &mbim_ndp16_opts) ? 0 : 1;
put_unaligned_le16(format, req->buf);
value = 2;
pr_debug("NTB FORMAT: sending %d\n", format);
@@ -1193,11 +1135,11 @@
break;
switch (w_value) {
case 0x0000:
- mbim->parser_opts = &ndp16_opts;
+ mbim->parser_opts = &mbim_ndp16_opts;
pr_debug("NCM16 selected\n");
break;
case 0x0001:
- mbim->parser_opts = &ndp32_opts;
+ mbim->parser_opts = &mbim_ndp32_opts;
pr_debug("NCM32 selected\n");
break;
default:
@@ -1391,7 +1333,7 @@
mbim->data_alt_int = alt;
spin_lock(&mbim->lock);
- mbim_notify(mbim);
+ mbim->not_port.notify_state = MBIM_NOTIFY_RESPONSE_AVAILABLE;
spin_unlock(&mbim->lock);
} else {
goto fail;
@@ -1435,7 +1377,7 @@
pr_info("SET DEVICE OFFLINE");
atomic_set(&mbim->online, 0);
- mbim->not_port.notify_state = NCM_NOTIFY_NONE;
+ mbim->not_port.notify_state = MBIM_NOTIFY_NONE;
mbim_clear_queues(mbim);
mbim_reset_function_queue(mbim);
@@ -1501,6 +1443,7 @@
mbim_union_desc.bSlaveInterface0 = status;
mbim->bam_port.cdev = cdev;
+ mbim->bam_port.func = &mbim->function;
status = -ENODEV;
@@ -1545,6 +1488,11 @@
mbim->not_port.notify_req->context = mbim;
mbim->not_port.notify_req->complete = mbim_notify_complete;
+ if (mbim->xport == USB_GADGET_XPORT_BAM2BAM_IPA)
+ mbb_desc.wMaxSegmentSize = cpu_to_le16(0x800);
+ else
+ mbb_desc.wMaxSegmentSize = cpu_to_le16(0xfe0);
+
/* copy descriptors, and track endpoint copies */
f->descriptors = usb_copy_descriptors(mbim_fs_function);
if (!f->descriptors)
@@ -1615,6 +1563,7 @@
{
struct f_mbim *mbim = func_to_mbim(f);
+ bam_data_destroy(mbim->port_num);
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->descriptors);
@@ -1698,12 +1647,13 @@
mbim->xport = USB_GADGET_XPORT_BAM2BAM;
} else {
/* For IPA we use limit of 16 */
- ntb_parameters.wNtbOutMaxDatagrams = 16;
+ mbim_ntb_parameters.wNtbOutMaxDatagrams = 16;
/* For IPA this is proven to give maximum throughput */
- ntb_parameters.dwNtbInMaxSize =
+ mbim_ntb_parameters.dwNtbInMaxSize =
cpu_to_le32(NTB_DEFAULT_IN_SIZE_IPA);
- ntb_parameters.dwNtbOutMaxSize = cpu_to_le32(NTB_OUT_SIZE_IPA);
- ntb_parameters.wNdpInDivisor = 1;
+ mbim_ntb_parameters.dwNtbOutMaxSize =
+ cpu_to_le32(MBIM_NTB_OUT_SIZE_IPA);
+ mbim_ntb_parameters.wNdpInDivisor = 1;
}
INIT_LIST_HEAD(&mbim->cpkt_req_q);
@@ -1884,10 +1834,6 @@
atomic_set(&_mbim_dev->error, 0);
- spin_lock(&_mbim_dev->lock);
- _mbim_dev->is_open = true;
- spin_unlock(&_mbim_dev->lock);
-
pr_info("Exit, mbim file opened\n");
return 0;
@@ -1895,14 +1841,8 @@
static int mbim_release(struct inode *ip, struct file *fp)
{
- struct f_mbim *mbim = fp->private_data;
-
pr_info("Close mbim file");
- spin_lock(&mbim->lock);
- mbim->is_open = false;
- spin_unlock(&mbim->lock);
-
mbim_unlock(&_mbim_dev->open_excl);
return 0;
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
index 0c0d58a..4586d80 100644
--- a/drivers/usb/gadget/f_mtp.c
+++ b/drivers/usb/gadget/f_mtp.c
@@ -50,7 +50,7 @@
#define STATE_ERROR 4 /* error from completion routine */
/* number of tx and rx requests to allocate */
-#define TX_REQ_MAX 4
+#define MTP_TX_REQ_MAX 8
#define RX_REQ_MAX 2
#define INTR_REQ_MAX 5
@@ -70,6 +70,12 @@
unsigned int mtp_rx_req_len = MTP_BULK_BUFFER_SIZE;
module_param(mtp_rx_req_len, uint, S_IRUGO | S_IWUSR);
+unsigned int mtp_tx_req_len = MTP_BULK_BUFFER_SIZE;
+module_param(mtp_tx_req_len, uint, S_IRUGO | S_IWUSR);
+
+unsigned int mtp_tx_reqs = MTP_TX_REQ_MAX;
+module_param(mtp_tx_reqs, uint, S_IRUGO | S_IWUSR);
+
static const char mtp_shortname[] = "mtp_usb";
struct mtp_dev {
@@ -488,11 +494,22 @@
ep->driver_data = dev; /* claim the endpoint */
dev->ep_intr = ep;
+retry_tx_alloc:
+ if (mtp_tx_req_len > MTP_BULK_BUFFER_SIZE)
+ mtp_tx_reqs = 4;
+
/* now allocate requests for our endpoints */
- for (i = 0; i < TX_REQ_MAX; i++) {
- req = mtp_request_new(dev->ep_in, MTP_BULK_BUFFER_SIZE);
- if (!req)
- goto fail;
+ for (i = 0; i < mtp_tx_reqs; i++) {
+ req = mtp_request_new(dev->ep_in, mtp_tx_req_len);
+ if (!req) {
+ if (mtp_tx_req_len <= MTP_BULK_BUFFER_SIZE)
+ goto fail;
+ while ((req = mtp_req_get(dev, &dev->tx_idle)))
+ mtp_request_free(req, dev->ep_in);
+ mtp_tx_req_len = MTP_BULK_BUFFER_SIZE;
+ mtp_tx_reqs = MTP_TX_REQ_MAX;
+ goto retry_tx_alloc;
+ }
req->complete = mtp_complete_in;
mtp_req_put(dev, &dev->tx_idle, req);
}
@@ -679,8 +696,8 @@
break;
}
- if (count > MTP_BULK_BUFFER_SIZE)
- xfer = MTP_BULK_BUFFER_SIZE;
+ if (count > mtp_tx_req_len)
+ xfer = mtp_tx_req_len;
else
xfer = count;
if (xfer && copy_from_user(req->buf, buf, xfer)) {
@@ -772,8 +789,8 @@
break;
}
- if (count > MTP_BULK_BUFFER_SIZE)
- xfer = MTP_BULK_BUFFER_SIZE;
+ if (count > mtp_tx_req_len)
+ xfer = mtp_tx_req_len;
else
xfer = count;
diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c
index aab8ede..cdf86fd 100644
--- a/drivers/usb/gadget/f_ncm.c
+++ b/drivers/usb/gadget/f_ncm.c
@@ -24,6 +24,21 @@
#include "u_ether.h"
+#undef DBG
+#undef VDBG
+#undef ERROR
+#undef INFO
+
+#define DBG(d, fmt, args...) \
+ dev_dbg(&(d)->gadget->dev , fmt , ## args)
+#define VDBG(d, fmt, args...) \
+ dev_vdbg(&(d)->gadget->dev , fmt , ## args)
+#define ERROR(d, fmt, args...) \
+ dev_err(&(d)->gadget->dev , fmt , ## args)
+#define WARNING(d, fmt, args...) \
+ dev_warn(&(d)->gadget->dev , fmt , ## args)
+#define INFO(d, fmt, args...) \
+ dev_info(&(d)->gadget->dev , fmt , ## args)
/*
* This function is a "CDC Network Control Model" (CDC NCM) Ethernet link.
* NCM is intended to be used with high-speed network attachments.
@@ -124,7 +139,7 @@
#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
#define NCM_STATUS_BYTECOUNT 16 /* 8 byte header + data */
-static struct usb_interface_assoc_descriptor ncm_iad_desc __initdata = {
+static struct usb_interface_assoc_descriptor ncm_iad_desc = {
.bLength = sizeof ncm_iad_desc,
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
@@ -138,7 +153,7 @@
/* interface descriptor: */
-static struct usb_interface_descriptor ncm_control_intf __initdata = {
+static struct usb_interface_descriptor ncm_control_intf = {
.bLength = sizeof ncm_control_intf,
.bDescriptorType = USB_DT_INTERFACE,
@@ -150,7 +165,7 @@
/* .iInterface = DYNAMIC */
};
-static struct usb_cdc_header_desc ncm_header_desc __initdata = {
+static struct usb_cdc_header_desc ncm_header_desc = {
.bLength = sizeof ncm_header_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
@@ -158,7 +173,7 @@
.bcdCDC = cpu_to_le16(0x0110),
};
-static struct usb_cdc_union_desc ncm_union_desc __initdata = {
+static struct usb_cdc_union_desc ncm_union_desc = {
.bLength = sizeof(ncm_union_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
@@ -166,8 +181,8 @@
/* .bSlaveInterface0 = DYNAMIC */
};
-static struct usb_cdc_ether_desc ecm_desc __initdata = {
- .bLength = sizeof ecm_desc,
+static struct usb_cdc_ether_desc necm_desc = {
+ .bLength = sizeof necm_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ETHERNET_TYPE,
@@ -181,7 +196,7 @@
#define NCAPS (USB_CDC_NCM_NCAP_ETH_FILTER | USB_CDC_NCM_NCAP_CRC_MODE)
-static struct usb_cdc_ncm_desc ncm_desc __initdata = {
+static struct usb_cdc_ncm_desc ncm_desc = {
.bLength = sizeof ncm_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_NCM_TYPE,
@@ -193,7 +208,7 @@
/* the default data interface has no endpoints ... */
-static struct usb_interface_descriptor ncm_data_nop_intf __initdata = {
+static struct usb_interface_descriptor ncm_data_nop_intf = {
.bLength = sizeof ncm_data_nop_intf,
.bDescriptorType = USB_DT_INTERFACE,
@@ -208,7 +223,7 @@
/* ... but the "real" data interface has two bulk endpoints */
-static struct usb_interface_descriptor ncm_data_intf __initdata = {
+static struct usb_interface_descriptor ncm_data_intf = {
.bLength = sizeof ncm_data_intf,
.bDescriptorType = USB_DT_INTERFACE,
@@ -223,7 +238,7 @@
/* full speed support: */
-static struct usb_endpoint_descriptor fs_ncm_notify_desc __initdata = {
+static struct usb_endpoint_descriptor fs_ncm_notify_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -233,7 +248,7 @@
.bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
};
-static struct usb_endpoint_descriptor fs_ncm_in_desc __initdata = {
+static struct usb_endpoint_descriptor fs_ncm_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -241,7 +256,7 @@
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_endpoint_descriptor fs_ncm_out_desc __initdata = {
+static struct usb_endpoint_descriptor fs_ncm_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -249,13 +264,13 @@
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_descriptor_header *ncm_fs_function[] __initdata = {
+static struct usb_descriptor_header *ncm_fs_function[] = {
(struct usb_descriptor_header *) &ncm_iad_desc,
/* CDC NCM control descriptors */
(struct usb_descriptor_header *) &ncm_control_intf,
(struct usb_descriptor_header *) &ncm_header_desc,
(struct usb_descriptor_header *) &ncm_union_desc,
- (struct usb_descriptor_header *) &ecm_desc,
+ (struct usb_descriptor_header *) &necm_desc,
(struct usb_descriptor_header *) &ncm_desc,
(struct usb_descriptor_header *) &fs_ncm_notify_desc,
/* data interface, altsettings 0 and 1 */
@@ -268,7 +283,7 @@
/* high speed support: */
-static struct usb_endpoint_descriptor hs_ncm_notify_desc __initdata = {
+static struct usb_endpoint_descriptor hs_ncm_notify_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -277,7 +292,7 @@
.wMaxPacketSize = cpu_to_le16(NCM_STATUS_BYTECOUNT),
.bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
};
-static struct usb_endpoint_descriptor hs_ncm_in_desc __initdata = {
+static struct usb_endpoint_descriptor hs_ncm_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -286,7 +301,7 @@
.wMaxPacketSize = cpu_to_le16(512),
};
-static struct usb_endpoint_descriptor hs_ncm_out_desc __initdata = {
+static struct usb_endpoint_descriptor hs_ncm_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -295,13 +310,13 @@
.wMaxPacketSize = cpu_to_le16(512),
};
-static struct usb_descriptor_header *ncm_hs_function[] __initdata = {
+static struct usb_descriptor_header *ncm_hs_function[] = {
(struct usb_descriptor_header *) &ncm_iad_desc,
/* CDC NCM control descriptors */
(struct usb_descriptor_header *) &ncm_control_intf,
(struct usb_descriptor_header *) &ncm_header_desc,
(struct usb_descriptor_header *) &ncm_union_desc,
- (struct usb_descriptor_header *) &ecm_desc,
+ (struct usb_descriptor_header *) &necm_desc,
(struct usb_descriptor_header *) &ncm_desc,
(struct usb_descriptor_header *) &hs_ncm_notify_desc,
/* data interface, altsettings 0 and 1 */
@@ -316,13 +331,13 @@
#define STRING_CTRL_IDX 0
#define STRING_MAC_IDX 1
-#define STRING_DATA_IDX 2
+#define NCM_STRING_DATA_IDX 2
#define STRING_IAD_IDX 3
static struct usb_string ncm_string_defs[] = {
[STRING_CTRL_IDX].s = "CDC Network Control Model (NCM)",
[STRING_MAC_IDX].s = NULL /* DYNAMIC */,
- [STRING_DATA_IDX].s = "CDC Network Data",
+ [NCM_STRING_DATA_IDX].s = "CDC Network Data",
[STRING_IAD_IDX].s = "CDC NCM",
{ } /* end of list */
};
@@ -1148,7 +1163,7 @@
/* ethernet function driver setup/binding */
-static int __init
+static int
ncm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
@@ -1299,7 +1314,7 @@
* Caller must have called @gether_setup(). Caller is also responsible
* for calling @gether_cleanup() before module unload.
*/
-int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
+int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
{
struct f_ncm *ncm;
int status;
@@ -1321,7 +1336,7 @@
status = usb_string_id(c->cdev);
if (status < 0)
return status;
- ncm_string_defs[STRING_DATA_IDX].id = status;
+ ncm_string_defs[NCM_STRING_DATA_IDX].id = status;
ncm_data_nop_intf.iInterface = status;
ncm_data_intf.iInterface = status;
@@ -1330,7 +1345,7 @@
if (status < 0)
return status;
ncm_string_defs[STRING_MAC_IDX].id = status;
- ecm_desc.iMACAddress = status;
+ necm_desc.iMACAddress = status;
/* IAD */
status = usb_string_id(c->cdev);
diff --git a/drivers/usb/gadget/f_qc_ecm.c b/drivers/usb/gadget/f_qc_ecm.c
index 46a6bba..3db5b51 100644
--- a/drivers/usb/gadget/f_qc_ecm.c
+++ b/drivers/usb/gadget/f_qc_ecm.c
@@ -75,17 +75,10 @@
struct usb_request *notify_req;
u8 notify_state;
bool is_open;
+ struct data_port bam_port;
};
-struct f_ecm_qc_ipa_params {
- u8 dev_mac[ETH_ALEN];
- u8 host_mac[ETH_ALEN];
- ecm_ipa_callback ipa_rx_cb;
- ecm_ipa_callback ipa_tx_cb;
- void *ipa_priv;
-};
-
-static struct f_ecm_qc_ipa_params ipa_params;
+static struct ecm_ipa_params ipa_params;
static inline struct f_ecm_qc *func_to_ecm_qc(struct usb_function *f)
{
@@ -119,8 +112,9 @@
#define ECM_QC_LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
#define ECM_QC_STATUS_BYTECOUNT 16 /* 8 byte header + data */
-/* currently only one std ecm instance is supported */
+/* Currently only one std ecm instance is supported - port index 0. */
#define ECM_QC_NO_PORTS 1
+#define ECM_QC_ACTIVE_PORT 0
/* interface descriptor: */
@@ -304,8 +298,6 @@
NULL,
};
-static struct data_port ecm_qc_bam_port;
-
static void ecm_qc_do_notify(struct f_ecm_qc *ecm)
{
struct usb_request *req = ecm->notify_req;
@@ -396,9 +388,10 @@
enum peer_bam peer_bam = (dev->xport == USB_GADGET_XPORT_BAM2BAM_IPA) ?
IPA_P_BAM : A2_P_BAM;
- ecm_qc_bam_port.cdev = cdev;
- ecm_qc_bam_port.in = dev->port.in_ep;
- ecm_qc_bam_port.out = dev->port.out_ep;
+ dev->bam_port.cdev = cdev;
+ dev->bam_port.func = &dev->port.func;
+ dev->bam_port.in = dev->port.in_ep;
+ dev->bam_port.out = dev->port.out_ep;
/* currently we use the first connection */
src_connection_idx = usb_bam_get_connection_idx(gadget->name, peer_bam,
@@ -409,7 +402,7 @@
pr_err("%s: usb_bam_get_connection_idx failed\n", __func__);
return ret;
}
- ret = bam_data_connect(&ecm_qc_bam_port, 0, dev->xport,
+ ret = bam_data_connect(&dev->bam_port, 0, dev->xport,
src_connection_idx, dst_connection_idx, USB_FUNC_ECM);
if (ret) {
pr_err("bam_data_connect failed: err:%d\n", ret);
@@ -428,26 +421,24 @@
{
pr_debug("dev:%p. Disconnect BAM.\n", dev);
- bam_data_disconnect(&ecm_qc_bam_port, 0);
-
- ecm_ipa_cleanup(ipa_params.ipa_priv);
+ bam_data_disconnect(&dev->bam_port, 0);
return 0;
}
void *ecm_qc_get_ipa_rx_cb(void)
{
- return ipa_params.ipa_rx_cb;
+ return ipa_params.ecm_ipa_rx_dp_notify;
}
void *ecm_qc_get_ipa_tx_cb(void)
{
- return ipa_params.ipa_tx_cb;
+ return ipa_params.ecm_ipa_tx_dp_notify;
}
void *ecm_qc_get_ipa_priv(void)
{
- return ipa_params.ipa_priv;
+ return ipa_params.private;
}
/*-------------------------------------------------------------------------*/
@@ -609,7 +600,7 @@
DBG(cdev, "activate ecm\n");
if (ecm->xport != USB_GADGET_XPORT_BAM2BAM_IPA) {
net = gether_qc_connect_name(&ecm->port,
- "ecm0");
+ "ecm0", true);
if (IS_ERR(net))
return PTR_ERR(net);
}
@@ -665,6 +656,20 @@
}
}
+static void ecm_qc_suspend(struct usb_function *f)
+{
+ pr_debug("ecm suspended\n");
+
+ bam_data_suspend(ECM_QC_ACTIVE_PORT);
+}
+
+static void ecm_qc_resume(struct usb_function *f)
+{
+ pr_debug("ecm resumed\n");
+
+ bam_data_resume(ECM_QC_ACTIVE_PORT);
+}
+
/*-------------------------------------------------------------------------*/
/*
@@ -841,6 +846,7 @@
DBG(c->cdev, "ecm unbind\n");
+ bam_data_destroy(0);
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->descriptors);
@@ -851,7 +857,7 @@
ecm_qc_string_defs[1].s = NULL;
if (ecm->xport == USB_GADGET_XPORT_BAM2BAM_IPA)
- ecm_ipa_cleanup(ipa_params.ipa_priv);
+ ecm_ipa_cleanup(ipa_params.private);
kfree(ecm);
}
@@ -922,12 +928,13 @@
/* export host's Ethernet address in CDC format */
if (ecm->xport == USB_GADGET_XPORT_BAM2BAM_IPA) {
- gether_qc_get_macs(ipa_params.dev_mac, ipa_params.host_mac);
+ gether_qc_get_macs(ipa_params.device_ethaddr,
+ ipa_params.host_ethaddr);
snprintf(ecm->ethaddr, sizeof ecm->ethaddr,
"%02X%02X%02X%02X%02X%02X",
- ipa_params.host_mac[0], ipa_params.host_mac[1],
- ipa_params.host_mac[2], ipa_params.host_mac[3],
- ipa_params.host_mac[4], ipa_params.host_mac[5]);
+ ipa_params.host_ethaddr[0], ipa_params.host_ethaddr[1],
+ ipa_params.host_ethaddr[2], ipa_params.host_ethaddr[3],
+ ipa_params.host_ethaddr[4], ipa_params.host_ethaddr[5]);
} else
snprintf(ecm->ethaddr, sizeof ecm->ethaddr,
"%02X%02X%02X%02X%02X%02X",
@@ -947,6 +954,8 @@
ecm->port.func.get_alt = ecm_qc_get_alt;
ecm->port.func.setup = ecm_qc_setup;
ecm->port.func.disable = ecm_qc_disable;
+ ecm->port.func.suspend = ecm_qc_suspend;
+ ecm->port.func.resume = ecm_qc_resume;
status = usb_add_function(c, &ecm->port.func);
if (status) {
@@ -959,21 +968,15 @@
if (ecm->xport != USB_GADGET_XPORT_BAM2BAM_IPA)
return status;
- status = ecm_ipa_init(&ipa_params.ipa_rx_cb, &ipa_params.ipa_tx_cb,
- &ipa_params.ipa_priv);
+ pr_debug("setting ecm_ipa, host_ethaddr=%pM, device_ethaddr=%pM",
+ ipa_params.host_ethaddr, ipa_params.device_ethaddr);
+ status = ecm_ipa_init(&ipa_params);
if (status) {
- pr_err("failed to initialize ECM IPA Driver");
+ pr_err("failed to initialize ecm_ipa");
ecm_qc_string_defs[1].s = NULL;
kfree(ecm);
- return status;
- }
-
- status = ecm_ipa_configure(ipa_params.host_mac, ipa_params.dev_mac,
- ipa_params.ipa_priv);
- if (status) {
- pr_err("failed to configure ECM IPA Driver");
- ecm_qc_string_defs[1].s = NULL;
- kfree(ecm);
+ } else {
+ pr_debug("ecm_ipa successful created");
}
return status;
diff --git a/drivers/usb/gadget/f_qc_rndis.c b/drivers/usb/gadget/f_qc_rndis.c
index 8b01176..c1ab552 100644
--- a/drivers/usb/gadget/f_qc_rndis.c
+++ b/drivers/usb/gadget/f_qc_rndis.c
@@ -426,6 +426,7 @@
struct usb_gadget *gadget = cdev->gadget;
dev->bam_port.cdev = cdev;
+ dev->bam_port.func = &dev->port.func;
dev->bam_port.in = dev->port.in_ep;
dev->bam_port.out = dev->port.out_ep;
@@ -724,14 +725,14 @@
*/
rndis->port.cdc_filter = 0;
- DBG(cdev, "RNDIS RX/TX early activation ...\n");
- net = gether_qc_connect_name(&rndis->port, "rndis0");
- if (IS_ERR(net))
- return PTR_ERR(net);
-
if (rndis_qc_bam_connect(rndis))
goto fail;
+ DBG(cdev, "RNDIS RX/TX early activation ...\n");
+ net = gether_qc_connect_name(&rndis->port, "rndis0", false);
+ if (IS_ERR(net))
+ return PTR_ERR(net);
+
rndis_set_param_dev(rndis->config, net,
&rndis->port.cdc_filter);
} else
@@ -975,6 +976,8 @@
{
struct f_rndis_qc *rndis = func_to_rndis_qc(f);
+ pr_debug("rndis_qc_unbind: free");
+ bam_data_destroy(0);
rndis_deregister(rndis->config);
rndis_exit();
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
index f3c6481..7903764 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/f_rndis.c
@@ -25,11 +25,6 @@
#include "u_ether.h"
#include "rndis.h"
-static bool rndis_multipacket_dl_disable;
-module_param(rndis_multipacket_dl_disable, bool, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(rndis_multipacket_dl_disable,
- "Disable RNDIS Multi-packet support in DownLink");
-
/*
* This function is an RNDIS Ethernet port -- a Microsoft protocol that's
* been promoted instead of the standard CDC Ethernet. The published RNDIS
@@ -71,6 +66,16 @@
* - MS-Windows drivers sometimes emit undocumented requests.
*/
+static unsigned int rndis_dl_max_pkt_per_xfer = 3;
+module_param(rndis_dl_max_pkt_per_xfer, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(rndis_dl_max_pkt_per_xfer,
+ "Maximum packets per transfer for DL aggregation");
+
+static unsigned int rndis_ul_max_pkt_per_xfer = 3;
+module_param(rndis_ul_max_pkt_per_xfer, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(rndis_ul_max_pkt_per_xfer,
+ "Maximum packets per transfer for UL aggregation");
+
struct f_rndis {
struct gether port;
u8 ctrl_id, data_id;
@@ -483,7 +488,7 @@
__func__, buf->MaxTransferSize,
rndis->port.multi_pkt_xfer ? "enabled" :
"disabled");
- if (rndis_multipacket_dl_disable)
+ if (rndis_dl_max_pkt_per_xfer <= 1)
rndis->port.multi_pkt_xfer = 0;
}
// spin_unlock(&dev->lock);
@@ -799,6 +804,7 @@
rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0);
rndis_set_host_mac(rndis->config, rndis->ethaddr);
+ rndis_set_max_pkt_xfer(rndis->config, rndis_ul_max_pkt_per_xfer);
if (rndis->manufacturer && rndis->vendorID &&
rndis_set_param_vendor(rndis->config, rndis->vendorID,
@@ -945,6 +951,8 @@
rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
rndis->port.wrap = rndis_add_header;
rndis->port.unwrap = rndis_rm_hdr;
+ rndis->port.ul_max_pkts_per_xfer = rndis_ul_max_pkt_per_xfer;
+ rndis->port.dl_max_pkts_per_xfer = rndis_dl_max_pkt_per_xfer;
rndis->port.func.name = "rndis";
rndis->port.func.strings = rndis_strings;
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
index 7ac5b64..e662bfc 100644
--- a/drivers/usb/gadget/rndis.c
+++ b/drivers/usb/gadget/rndis.c
@@ -59,6 +59,16 @@
#define RNDIS_MAX_CONFIGS 1
+int rndis_ul_max_pkt_per_xfer_rcvd;
+module_param(rndis_ul_max_pkt_per_xfer_rcvd, int, S_IRUGO);
+MODULE_PARM_DESC(rndis_ul_max_pkt_per_xfer_rcvd,
+ "Max num of REMOTE_NDIS_PACKET_MSGs received in a single transfer");
+
+int rndis_ul_max_xfer_size_rcvd;
+module_param(rndis_ul_max_xfer_size_rcvd, int, S_IRUGO);
+MODULE_PARM_DESC(rndis_ul_max_xfer_size_rcvd,
+ "Max size of bus transfer received");
+
static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS];
@@ -590,7 +600,6 @@
(params->dev->mtu
+ sizeof(struct ethhdr)
+ sizeof(struct rndis_packet_msg_type)
-
+ 22));
resp->PacketAlignmentFactor = cpu_to_le32(params->pkt_alignment_factor);
resp->AFListOffset = cpu_to_le32(0);
@@ -938,6 +947,10 @@
rndis_per_dev_params[configNr].dev = dev;
rndis_per_dev_params[configNr].filter = cdc_filter;
+ /* reset aggregation stats for every set_alt */
+ rndis_ul_max_xfer_size_rcvd = 0;
+ rndis_ul_max_pkt_per_xfer_rcvd = 0;
+
return 0;
}
@@ -1051,25 +1064,72 @@
struct sk_buff *skb,
struct sk_buff_head *list)
{
- /* tmp points to a struct rndis_packet_msg_type */
- __le32 *tmp = (void *)skb->data;
+ int num_pkts = 0;
- /* MessageType, MessageLength */
- if (cpu_to_le32(REMOTE_NDIS_PACKET_MSG)
- != get_unaligned(tmp++)) {
- dev_kfree_skb_any(skb);
- return -EINVAL;
- }
- tmp++;
+ if (skb->len > rndis_ul_max_xfer_size_rcvd)
+ rndis_ul_max_xfer_size_rcvd = skb->len;
- /* DataOffset, DataLength */
- if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) {
- dev_kfree_skb_any(skb);
- return -EOVERFLOW;
+ while (skb->len) {
+ struct rndis_packet_msg_type *hdr;
+ struct sk_buff *skb2;
+ u32 msg_len, data_offset, data_len;
+
+ if (skb->len < sizeof *hdr) {
+ pr_err("invalid rndis pkt: skblen:%u hdr_len:%u",
+ skb->len, sizeof *hdr);
+ dev_kfree_skb_any(skb);
+ return -EINVAL;
+ }
+
+ hdr = (void *)skb->data;
+ msg_len = le32_to_cpu(hdr->MessageLength);
+ data_offset = le32_to_cpu(hdr->DataOffset);
+ data_len = le32_to_cpu(hdr->DataLength);
+
+ if (skb->len < msg_len ||
+ ((data_offset + data_len + 8) > msg_len)) {
+ pr_err("invalid rndis message: %d/%d/%d/%d, len:%d\n",
+ le32_to_cpu(hdr->MessageType),
+ msg_len, data_offset, data_len, skb->len);
+ dev_kfree_skb_any(skb);
+ return -EOVERFLOW;
+ }
+
+ if (le32_to_cpu(hdr->MessageType) != REMOTE_NDIS_PACKET_MSG) {
+ pr_err("invalid rndis message: %d/%d/%d/%d, len:%d\n",
+ le32_to_cpu(hdr->MessageType),
+ msg_len, data_offset, data_len, skb->len);
+ dev_kfree_skb_any(skb);
+ return -EINVAL;
+ }
+
+ num_pkts++;
+
+ skb_pull(skb, data_offset + 8);
+
+ if (data_len == skb->len ||
+ data_len == (skb->len - 1)) {
+ skb_trim(skb, data_len);
+ break;
+ }
+
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (!skb2) {
+ pr_err("%s:skb clone failed\n", __func__);
+ dev_kfree_skb_any(skb);
+ return -ENOMEM;
+ }
+
+ skb_pull(skb, msg_len - sizeof *hdr);
+ skb_trim(skb2, data_len);
+ skb_queue_tail(list, skb2);
}
- skb_trim(skb, get_unaligned_le32(tmp++));
+
+ if (num_pkts > rndis_ul_max_pkt_per_xfer_rcvd)
+ rndis_ul_max_pkt_per_xfer_rcvd = num_pkts;
skb_queue_tail(list, skb);
+
return 0;
}
@@ -1087,7 +1147,9 @@
"speed : %d\n"
"cable : %s\n"
"vendor ID : 0x%08X\n"
- "vendor : %s\n",
+ "vendor : %s\n"
+ "ul-max-xfer-size:%d max-xfer-size-rcvd: %d\n"
+ "ul-max-pkts-per-xfer:%d max-pkts-per-xfer-rcvd:%d\n",
param->confignr, (param->used) ? "y" : "n",
({ char *s = "?";
switch (param->state) {
@@ -1101,7 +1163,13 @@
param->medium,
(param->media_state) ? 0 : param->speed*100,
(param->media_state) ? "disconnected" : "connected",
- param->vendorID, param->vendorDescr);
+ param->vendorID, param->vendorDescr,
+ param->max_pkt_per_xfer *
+ (param->dev->mtu + sizeof(struct ethhdr) +
+ sizeof(struct rndis_packet_msg_type) + 22),
+ rndis_ul_max_xfer_size_rcvd,
+ param->max_pkt_per_xfer,
+ rndis_ul_max_pkt_per_xfer_rcvd);
return 0;
}
diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h
index 8a6a630..d00f81f 100644
--- a/drivers/usb/gadget/rndis.h
+++ b/drivers/usb/gadget/rndis.h
@@ -252,6 +252,7 @@
int rndis_set_param_vendor (u8 configNr, u32 vendorID,
const char *vendorDescr);
int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
+void rndis_set_max_pkt_xfer(u8 configNr, u8 max_pkt_per_xfer);
void rndis_add_hdr (struct sk_buff *skb);
int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
struct sk_buff_head *list);
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index 67c9a1a..edcafcc 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -539,9 +539,12 @@
struct bam_ch_info *d = &port->data_ch;
int status;
- if (!port->port_usb)
+ if (!port->port_usb) {
+ pr_err("%s: port->port_usb is NULL", __func__);
return;
+ }
+ pr_debug("%s: enqueue\n", __func__);
status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC);
if (status)
pr_err("%s: error enqueuing transfer, %d\n", __func__, status);
@@ -552,14 +555,69 @@
struct bam_ch_info *d = &port->data_ch;
int status;
- if (!port->port_usb)
+ if (!port->port_usb) {
+ pr_err("%s: port->port_usb is NULL", __func__);
return;
+ }
+ pr_debug("%s: enqueue\n", __func__);
status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC);
if (status)
pr_err("%s: error enqueuing transfer, %d\n", __func__, status);
}
+static void gbam_stop_endless_rx(struct gbam_port *port)
+{
+ struct bam_ch_info *d = &port->data_ch;
+ int status;
+
+ if (!port->port_usb) {
+ pr_err("%s: port->port_usb is NULL", __func__);
+ return;
+ }
+ pr_debug("%s: dequeue\n", __func__);
+
+ status = usb_ep_dequeue(port->port_usb->out, d->rx_req);
+ if (status)
+ pr_err("%s: error dequeuing transfer, %d\n", __func__, status);
+
+}
+static void gbam_stop_endless_tx(struct gbam_port *port)
+{
+ struct bam_ch_info *d = &port->data_ch;
+ int status;
+
+ if (!port->port_usb) {
+ pr_err("%s: port->port_usb is NULL", __func__);
+ return;
+ }
+
+ pr_debug("%s: dequeue\n", __func__);
+ status = usb_ep_dequeue(port->port_usb->in, d->tx_req);
+ if (status)
+ pr_err("%s: error dequeuing transfer, %d\n", __func__, status);
+}
+
+static void gbam_start(void *param, enum usb_bam_pipe_dir dir)
+{
+ struct gbam_port *port = param;
+
+ if (dir == USB_TO_PEER_PERIPHERAL)
+ gbam_start_endless_rx(port);
+ else
+ gbam_start_endless_tx(port);
+}
+
+static void gbam_stop(void *param, enum usb_bam_pipe_dir dir)
+{
+ struct gbam_port *port = param;
+
+ if (dir == USB_TO_PEER_PERIPHERAL)
+ gbam_stop_endless_rx(port);
+ else
+ gbam_stop_endless_tx(port);
+}
+
static void gbam_start_io(struct gbam_port *port)
{
unsigned long flags;
@@ -667,11 +725,11 @@
int ret;
if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+ teth_bridge_disconnect();
ret = usb_bam_disconnect_ipa(&d->ipa_params);
if (ret)
pr_err("%s: usb_bam_disconnect_ipa failed: err:%d\n",
__func__, ret);
- teth_bridge_disconnect();
}
}
@@ -717,8 +775,10 @@
ipa_notify_cb usb_notify_cb;
void *priv;
int ret;
+ unsigned long flags;
if (d->trans == USB_GADGET_XPORT_BAM2BAM) {
+ usb_bam_reset_complete();
ret = usb_bam_connect(d->src_connection_idx, &d->src_pipe_idx);
if (ret) {
pr_err("%s: usb_bam_connect (src) failed: err:%d\n",
@@ -741,8 +801,8 @@
d->ipa_params.priv = priv;
d->ipa_params.ipa_ep_cfg.mode.mode = IPA_BASIC;
- d->ipa_params.client = IPA_CLIENT_USB_CONS;
- d->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
+ d->ipa_params.client = IPA_CLIENT_USB_PROD;
+ d->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
ret = usb_bam_connect_ipa(&d->ipa_params);
if (ret) {
pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
@@ -750,8 +810,8 @@
return;
}
- d->ipa_params.client = IPA_CLIENT_USB_PROD;
- d->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
+ d->ipa_params.client = IPA_CLIENT_USB_CONS;
+ d->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
ret = usb_bam_connect_ipa(&d->ipa_params);
if (ret) {
pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
@@ -769,23 +829,42 @@
}
}
- d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
- if (!d->rx_req)
+ spin_lock_irqsave(&port->port_lock_ul, flags);
+ spin_lock(&port->port_lock_dl);
+ if (!port->port_usb) {
+ pr_debug("%s: usb cable is disconnected, exiting\n", __func__);
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
return;
+ }
+ d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_ATOMIC);
+ if (!d->rx_req) {
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+ pr_err("%s: out of memory\n", __func__);
+ return;
+ }
d->rx_req->context = port;
d->rx_req->complete = gbam_endless_rx_complete;
d->rx_req->length = 0;
+ d->rx_req->no_interrupt = 1;
sps_params = (MSM_SPS_MODE | d->src_pipe_idx |
MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
d->rx_req->udc_priv = sps_params;
- d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_KERNEL);
- if (!d->tx_req)
+
+ d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_ATOMIC);
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+ if (!d->tx_req) {
+ pr_err("%s: out of memory\n", __func__);
return;
+ }
d->tx_req->context = port;
d->tx_req->complete = gbam_endless_tx_complete;
d->tx_req->length = 0;
+ d->tx_req->no_interrupt = 1;
sps_params = (MSM_SPS_MODE | d->dst_pipe_idx |
MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
d->tx_req->udc_priv = sps_params;
@@ -840,7 +919,7 @@
msm_hw_bam_disable(1);
/* Reset BAM */
- ret = usb_bam_a2_reset();
+ ret = usb_bam_a2_reset(0);
if (ret) {
pr_err("%s: BAM reset failed %d\n", __func__, ret);
goto reenable_eps;
@@ -1391,6 +1470,10 @@
pr_debug("%s: suspended port %d\n", __func__, port_num);
usb_bam_register_wake_cb(d->dst_connection_idx, gbam_wake_cb, port);
+ if (trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+ usb_bam_register_start_stop_cbs(gbam_start, gbam_stop, port);
+ usb_bam_suspend(&d->ipa_params);
+ }
}
void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans)
@@ -1408,4 +1491,6 @@
pr_debug("%s: resumed port %d\n", __func__, port_num);
usb_bam_register_wake_cb(d->dst_connection_idx, NULL, NULL);
+ if (trans == USB_GADGET_XPORT_BAM2BAM_IPA)
+ usb_bam_resume(&d->ipa_params);
}
diff --git a/drivers/usb/gadget/u_bam_data.c b/drivers/usb/gadget/u_bam_data.c
index eec9e37..d470edf 100644
--- a/drivers/usb/gadget/u_bam_data.c
+++ b/drivers/usb/gadget/u_bam_data.c
@@ -92,6 +92,7 @@
if (!port->port_usb)
return;
+ pr_debug("%s: enqueue\n", __func__);
status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC);
if (status)
pr_err("error enqueuing transfer, %d\n", status);
@@ -105,70 +106,63 @@
if (!port->port_usb)
return;
+ pr_debug("%s: enqueue\n", __func__);
status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC);
if (status)
pr_err("error enqueuing transfer, %d\n", status);
}
+static void bam_data_stop_endless_rx(struct bam_data_port *port)
+{
+ struct bam_data_ch_info *d = &port->data_ch;
+ int status;
+
+ if (!port->port_usb)
+ return;
+
+ pr_debug("%s: dequeue\n", __func__);
+ status = usb_ep_dequeue(port->port_usb->out, d->rx_req);
+ if (status)
+ pr_err("%s: error dequeuing transfer, %d\n", __func__, status);
+
+}
+static void bam_data_stop_endless_tx(struct bam_data_port *port)
+{
+ struct bam_data_ch_info *d = &port->data_ch;
+ int status;
+
+ if (!port->port_usb)
+ return;
+
+ pr_debug("%s: dequeue\n", __func__);
+ status = usb_ep_dequeue(port->port_usb->in, d->tx_req);
+ if (status)
+ pr_err("%s: error dequeuing transfer, %d\n", __func__, status);
+}
+
static int bam_data_peer_reset_cb(void *param)
{
struct bam_data_port *port = (struct bam_data_port *)param;
struct bam_data_ch_info *d;
int ret;
- bool reenable_eps = false;
d = &port->data_ch;
pr_debug("%s: reset by peer\n", __func__);
- /* Disable the relevant EPs if currently EPs are enabled */
- if (port->port_usb && port->port_usb->in &&
- port->port_usb->in->driver_data) {
- usb_ep_disable(port->port_usb->out);
- usb_ep_disable(port->port_usb->in);
-
- port->port_usb->in->driver_data = NULL;
- port->port_usb->out->driver_data = NULL;
- reenable_eps = true;
- }
-
/* Disable BAM */
msm_hw_bam_disable(1);
/* Reset BAM */
- ret = usb_bam_a2_reset();
+ ret = usb_bam_a2_reset(0);
if (ret) {
pr_err("%s: BAM reset failed %d\n", __func__, ret);
- goto reenable_eps;
+ return ret;
}
/* Enable BAM */
msm_hw_bam_disable(0);
-reenable_eps:
- /* Re-Enable the relevant EPs, if EPs were originally enabled */
- if (reenable_eps) {
- ret = usb_ep_enable(port->port_usb->in);
- if (ret) {
- pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
- __func__, port->port_usb->in);
- return ret;
- }
- port->port_usb->in->driver_data = port;
-
- ret = usb_ep_enable(port->port_usb->out);
- if (ret) {
- pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
- __func__, port->port_usb->out);
- port->port_usb->in->driver_data = 0;
- return ret;
- }
- port->port_usb->out->driver_data = port;
-
- bam_data_start_endless_rx(port);
- bam_data_start_endless_tx(port);
- }
-
/* Unregister the peer reset callback */
usb_bam_register_peer_reset_cb(NULL, NULL);
@@ -205,6 +199,7 @@
int ret;
pr_debug("%s: Connect workqueue started", __func__);
+ usb_bam_reset_complete();
if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
if (d->func_type == USB_FUNC_MBIM) {
@@ -219,10 +214,10 @@
d->ipa_params.ipa_ep_cfg.mode.mode = IPA_BASIC;
}
- d->ipa_params.client = IPA_CLIENT_USB_CONS;
- d->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
+ d->ipa_params.client = IPA_CLIENT_USB_PROD;
+ d->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
if (d->func_type == USB_FUNC_ECM) {
- d->ipa_params.notify = ecm_qc_get_ipa_tx_cb();
+ d->ipa_params.notify = ecm_qc_get_ipa_rx_cb();
d->ipa_params.priv = ecm_qc_get_ipa_priv();
}
ret = usb_bam_connect_ipa(&d->ipa_params);
@@ -232,10 +227,10 @@
return;
}
- d->ipa_params.client = IPA_CLIENT_USB_PROD;
- d->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
+ d->ipa_params.client = IPA_CLIENT_USB_CONS;
+ d->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
if (d->func_type == USB_FUNC_ECM) {
- d->ipa_params.notify = ecm_qc_get_ipa_rx_cb();
+ d->ipa_params.notify = ecm_qc_get_ipa_tx_cb();
d->ipa_params.priv = ecm_qc_get_ipa_priv();
}
ret = usb_bam_connect_ipa(&d->ipa_params);
@@ -301,6 +296,7 @@
d->rx_req->context = port;
d->rx_req->complete = bam_data_endless_rx_complete;
d->rx_req->length = 0;
+ d->rx_req->no_interrupt = 1;
sps_params = (SPS_PARAMS_SPS_MODE | d->src_pipe_idx |
MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
d->rx_req->udc_priv = sps_params;
@@ -311,6 +307,7 @@
d->tx_req->context = port;
d->tx_req->complete = bam_data_endless_tx_complete;
d->tx_req->length = 0;
+ d->tx_req->no_interrupt = 1;
sps_params = (SPS_PARAMS_SPS_MODE | d->dst_pipe_idx |
MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
d->tx_req->udc_priv = sps_params;
@@ -465,6 +462,23 @@
return 0;
}
+int bam_data_destroy(unsigned int no_bam2bam_port)
+{
+ struct bam_data_ch_info *d;
+ struct bam_data_port *port;
+
+ port = bam2bam_data_ports[no_bam2bam_port];
+ d = &port->data_ch;
+
+ pr_debug("bam_data_destroy: Freeing ports\n");
+ bam2bam_data_port_free(no_bam2bam_port);
+ if (bam_data_wq)
+ destroy_workqueue(bam_data_wq);
+ bam_data_wq = NULL;
+
+ return 0;
+}
+
int bam_data_setup(unsigned int no_bam2bam_port)
{
int i;
@@ -477,6 +491,11 @@
return -EINVAL;
}
+ if (bam_data_wq) {
+ pr_debug("bam_data is already setup");
+ return 0;
+ }
+
bam_data_wq = alloc_workqueue("k_bam_data",
WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
if (!bam_data_wq) {
@@ -529,9 +548,28 @@
return usb_gadget_wakeup(d_port->cdev->gadget);
}
+static void bam_data_start(void *param, enum usb_bam_pipe_dir dir)
+{
+ struct bam_data_port *port = param;
+
+ if (dir == USB_TO_PEER_PERIPHERAL)
+ bam_data_start_endless_rx(port);
+ else
+ bam_data_start_endless_tx(port);
+}
+
+static void bam_data_stop(void *param, enum usb_bam_pipe_dir dir)
+{
+ struct bam_data_port *port = param;
+
+ if (dir == USB_TO_PEER_PERIPHERAL)
+ bam_data_stop_endless_rx(port);
+ else
+ bam_data_stop_endless_tx(port);
+}
+
void bam_data_suspend(u8 port_num)
{
-
struct bam_data_port *port;
struct bam_data_ch_info *d;
@@ -540,6 +578,11 @@
pr_debug("%s: suspended port %d\n", __func__, port_num);
usb_bam_register_wake_cb(d->dst_connection_idx, bam_data_wake_cb, port);
+ if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+ usb_bam_register_start_stop_cbs(bam_data_start, bam_data_stop,
+ port);
+ usb_bam_suspend(&d->ipa_params);
+ }
}
void bam_data_resume(u8 port_num)
@@ -553,5 +596,6 @@
pr_debug("%s: resumed port %d\n", __func__, port_num);
usb_bam_register_wake_cb(d->dst_connection_idx, NULL, NULL);
+ if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA)
+ usb_bam_resume(&d->ipa_params);
}
-
diff --git a/drivers/usb/gadget/u_bam_data.h b/drivers/usb/gadget/u_bam_data.h
index 486191b5..61f653a 100644
--- a/drivers/usb/gadget/u_bam_data.h
+++ b/drivers/usb/gadget/u_bam_data.h
@@ -23,6 +23,7 @@
struct data_port {
struct usb_composite_dev *cdev;
+ struct usb_function *func;
struct usb_ep *in;
struct usb_ep *out;
};
@@ -35,6 +36,8 @@
int bam_data_setup(unsigned int no_bam2bam_port);
+int bam_data_destroy(unsigned int no_bam2bam_port);
+
void bam_data_suspend(u8 port_num);
void bam_data_resume(u8 port_num);
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 2d974ab..9961d00 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -70,6 +70,8 @@
struct sk_buff_head rx_frames;
unsigned header_len;
+ unsigned int ul_max_pkts_per_xfer;
+ unsigned int dl_max_pkts_per_xfer;
struct sk_buff *(*wrap)(struct gether *, struct sk_buff *skb);
int (*unwrap)(struct gether *,
struct sk_buff *skb,
@@ -233,9 +235,13 @@
size += out->maxpacket - 1;
size -= size % out->maxpacket;
+ if (dev->ul_max_pkts_per_xfer)
+ size *= dev->ul_max_pkts_per_xfer;
+
if (dev->port_usb->is_fixed)
size = max_t(size_t, size, dev->port_usb->fixed_out_len);
+ pr_debug("%s: size: %d", __func__, size);
skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags);
if (skb == NULL) {
DBG(dev, "no rx skb\n");
@@ -580,12 +586,12 @@
return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS;
}
-static void alloc_tx_buffer(struct eth_dev *dev)
+static int alloc_tx_buffer(struct eth_dev *dev)
{
struct list_head *act;
struct usb_request *req;
- dev->tx_req_bufsize = (TX_SKB_HOLD_THRESHOLD *
+ dev->tx_req_bufsize = (dev->dl_max_pkts_per_xfer *
(dev->net->mtu
+ sizeof(struct ethhdr)
/* size of rndis_packet_msg_type */
@@ -597,7 +603,19 @@
if (!req->buf)
req->buf = kmalloc(dev->tx_req_bufsize,
GFP_ATOMIC);
+ if (!req->buf)
+ goto free_buf;
}
+ return 0;
+
+free_buf:
+ /* tx_req_bufsize = 0 retries mem alloc on next eth_start_xmit */
+ dev->tx_req_bufsize = 0;
+ list_for_each(act, &dev->tx_reqs) {
+ req = container_of(act, struct usb_request, list);
+ kfree(req->buf);
+ }
+ return -ENOMEM;
}
static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
@@ -629,8 +647,11 @@
}
/* Allocate memory for tx_reqs to support multi packet transfer */
- if (multi_pkt_xfer && !dev->tx_req_bufsize)
- alloc_tx_buffer(dev);
+ if (multi_pkt_xfer && !dev->tx_req_bufsize) {
+ retval = alloc_tx_buffer(dev);
+ if (retval < 0)
+ return -ENOMEM;
+ }
/* apply outgoing CDC or RNDIS filters */
if (!is_promisc(cdc_filter)) {
@@ -699,7 +720,7 @@
dev_kfree_skb_any(skb);
spin_lock_irqsave(&dev->req_lock, flags);
- if (dev->tx_skb_hold_count < TX_SKB_HOLD_THRESHOLD) {
+ if (dev->tx_skb_hold_count < dev->dl_max_pkts_per_xfer) {
if (dev->no_tx_req_used > TX_REQ_THRESHOLD) {
list_add(&req->list, &dev->tx_reqs);
spin_unlock_irqrestore(&dev->req_lock, flags);
@@ -1076,6 +1097,8 @@
dev->header_len = link->header_len;
dev->unwrap = link->unwrap;
dev->wrap = link->wrap;
+ dev->ul_max_pkts_per_xfer = link->ul_max_pkts_per_xfer;
+ dev->dl_max_pkts_per_xfer = link->dl_max_pkts_per_xfer;
spin_lock(&dev->lock);
dev->tx_skb_hold_count = 0;
diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h
index faa9a3b..05984d8 100644
--- a/drivers/usb/gadget/u_ether.h
+++ b/drivers/usb/gadget/u_ether.h
@@ -53,8 +53,9 @@
bool is_fixed;
u32 fixed_out_len;
u32 fixed_in_len;
-/* Max number of SKB packets to be used to create Multi Packet RNDIS */
-#define TX_SKB_HOLD_THRESHOLD 3
+
+ unsigned ul_max_pkts_per_xfer;
+ unsigned dl_max_pkts_per_xfer;
bool multi_pkt_xfer;
struct sk_buff *(*wrap)(struct gether *port,
struct sk_buff *skb);
diff --git a/drivers/usb/gadget/u_qc_ether.c b/drivers/usb/gadget/u_qc_ether.c
index e10ec25..044da47 100644
--- a/drivers/usb/gadget/u_qc_ether.c
+++ b/drivers/usb/gadget/u_qc_ether.c
@@ -354,12 +354,13 @@
* current device speed, and any framing wrapper(s) set up.
* @netname: name for network device (for example, "usb")
* Context: irqs blocked
+ * @netif_enable: if true, net interface will be turned on
*
* This is called to let the network layer know the connection
* is active ("carrier detect").
*/
struct net_device *gether_qc_connect_name(struct qc_gether *link,
- const char *netname)
+ const char *netname, bool netif_enable)
{
struct net_device *net_dev;
struct eth_qc_dev *dev;
@@ -390,9 +391,11 @@
}
spin_unlock(&dev->lock);
- netif_carrier_on(dev->net);
- if (netif_running(dev->net))
- netif_wake_queue(dev->net);
+ if (netif_enable) {
+ netif_carrier_on(dev->net);
+ if (netif_running(dev->net))
+ netif_wake_queue(dev->net);
+ }
return dev->net;
}
diff --git a/drivers/usb/gadget/u_qc_ether.h b/drivers/usb/gadget/u_qc_ether.h
index 25562da..5d9f738 100644
--- a/drivers/usb/gadget/u_qc_ether.h
+++ b/drivers/usb/gadget/u_qc_ether.h
@@ -82,7 +82,7 @@
/* connect/disconnect is handled by individual functions */
struct net_device *gether_qc_connect_name(struct qc_gether *link,
- const char *netname);
+ const char *netname, bool netif_enable);
void gether_qc_disconnect_name(struct qc_gether *link, const char *netname);
/* each configuration may bind one instance of an ethernet link */
diff --git a/drivers/usb/gadget/u_rmnet_ctrl_qti.c b/drivers/usb/gadget/u_rmnet_ctrl_qti.c
index e92978f..182cd40 100644
--- a/drivers/usb/gadget/u_rmnet_ctrl_qti.c
+++ b/drivers/usb/gadget/u_rmnet_ctrl_qti.c
@@ -259,20 +259,6 @@
return -EBUSY;
}
- /* block until online */
- while (!(atomic_read(&port->connected))) {
- pr_debug("Not connected. Wait.\n");
- ret = wait_event_interruptible(port->read_wq,
- atomic_read(&port->connected));
- if (ret < 0) {
- rmnet_ctrl_unlock(&port->read_excl);
- if (ret == -ERESTARTSYS)
- return -ERESTARTSYS;
- else
- return -EINTR;
- }
- }
-
/* block until a new packet is available */
do {
spin_lock_irqsave(&port->lock, flags);
diff --git a/drivers/usb/gadget/u_rmnet_ctrl_smd.c b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
index caea4ef..5767ba3 100644
--- a/drivers/usb/gadget/u_rmnet_ctrl_smd.c
+++ b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
@@ -44,6 +44,7 @@
char *name;
unsigned long flags;
wait_queue_head_t wait;
+ wait_queue_head_t smd_wait_q;
unsigned dtr;
struct list_head tx_q;
@@ -111,8 +112,8 @@
{
struct smd_ch_info *c = container_of(w, struct smd_ch_info, read_w);
struct rmnet_ctrl_port *port = c->port;
- int sz;
- size_t len;
+ int sz, total_received, read_avail;
+ int len;
void *buf;
unsigned long flags;
@@ -122,22 +123,47 @@
if (sz <= 0)
break;
- if (smd_read_avail(c->ch) < sz)
- break;
-
spin_unlock_irqrestore(&port->port_lock, flags);
buf = kmalloc(sz, GFP_KERNEL);
if (!buf)
return;
- len = smd_read(c->ch, buf, sz);
+ total_received = 0;
+ while (total_received < sz) {
+ wait_event(c->smd_wait_q,
+ ((read_avail = smd_read_avail(c->ch)) ||
+ (c->ch == 0)));
+
+ if (read_avail < 0 || c->ch == 0) {
+ pr_err("%s:smd read_avail failure:%d or channel closed ch=%p",
+ __func__, read_avail, c->ch);
+ kfree(buf);
+ return;
+ }
+
+ if (read_avail + total_received > sz) {
+ pr_err("%s: SMD sending incorrect pkt\n",
+ __func__);
+ kfree(buf);
+ return;
+ }
+
+ len = smd_read(c->ch, buf + total_received, read_avail);
+ if (len <= 0) {
+ pr_err("%s: smd read failure %d\n",
+ __func__, len);
+ kfree(buf);
+ return;
+ }
+ total_received += len;
+ }
/* send it to USB here */
spin_lock_irqsave(&port->port_lock, flags);
if (port->port_usb && port->port_usb->send_cpkt_response) {
port->port_usb->send_cpkt_response(port->port_usb,
- buf, len);
+ buf, sz);
c->to_host++;
}
kfree(buf);
@@ -304,7 +330,7 @@
switch (event) {
case SMD_EVENT_DATA:
- if (smd_read_avail(c->ch))
+ if (smd_read_avail(c->ch) && !waitqueue_active(&c->smd_wait_q))
queue_work(grmnet_ctrl_wq, &c->read_w);
if (smd_write_avail(c->ch))
queue_work(grmnet_ctrl_wq, &c->write_w);
@@ -334,6 +360,7 @@
break;
}
+ wake_up(&c->smd_wait_q);
}
/*------------------------------------------------------------ */
@@ -579,6 +606,7 @@
[portno % MAX_CTRL_PER_CLIENT];
c->port = port;
init_waitqueue_head(&c->wait);
+ init_waitqueue_head(&c->smd_wait_q);
INIT_LIST_HEAD(&c->tx_q);
INIT_WORK(&c->read_w, grmnet_ctrl_smd_read_w);
INIT_WORK(&c->write_w, grmnet_ctrl_smd_write_w);
@@ -604,6 +632,11 @@
pr_debug("%s: requested ports:%d\n", __func__, count);
+ if (client_num >= NR_CTRL_CLIENTS) {
+ pr_err("%s: Invalid client:%d\n", __func__, client_num);
+ return -EINVAL;
+ }
+
if (!count || count > MAX_CTRL_PER_CLIENT) {
pr_err("%s: Invalid num of ports count:%d\n",
__func__, count);
diff --git a/drivers/usb/gadget/u_sdio.c b/drivers/usb/gadget/u_sdio.c
index a0cdde2..52b707e 100644
--- a/drivers/usb/gadget/u_sdio.c
+++ b/drivers/usb/gadget/u_sdio.c
@@ -232,7 +232,7 @@
{
unsigned avail;
char *packet;
- unsigned size = req->actual;
+ unsigned size;
unsigned n;
int ret = 0;
@@ -271,6 +271,7 @@
return -ENODEV;
}
+ size = req->actual;
packet = req->buf;
n = port->n_read;
if (n) {
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 4a085aa..f16a0b6 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -78,6 +78,7 @@
struct clk *alt_core_clk;
struct clk *phy_clk;
struct clk *cal_clk;
+ struct clk *inactivity_clk;
struct regulator *hsic_vddcx;
struct regulator *hsic_gdsc;
atomic_t async_int;
@@ -575,6 +576,8 @@
clk_disable_unprepare(mehci->phy_clk);
clk_disable_unprepare(mehci->cal_clk);
clk_disable_unprepare(mehci->ahb_clk);
+ if (!IS_ERR(mehci->inactivity_clk))
+ clk_disable_unprepare(mehci->inactivity_clk);
ret = clk_reset(mehci->core_clk, CLK_RESET_ASSERT);
if (ret) {
@@ -596,6 +599,8 @@
clk_prepare_enable(mehci->phy_clk);
clk_prepare_enable(mehci->cal_clk);
clk_prepare_enable(mehci->ahb_clk);
+ if (!IS_ERR(mehci->inactivity_clk))
+ clk_prepare_enable(mehci->inactivity_clk);
}
}
@@ -747,6 +752,19 @@
return -EBUSY;
}
+ if (pdata->consider_ipa_handshake) {
+ dev_dbg(mehci->dev, "%s:Wait for resources release\n",
+ __func__);
+ if (!msm_bam_hsic_lpm_ok()) {
+ dev_dbg(mehci->dev, "%s:Prod+Cons not released\n",
+ __func__);
+ enable_irq(hcd->irq);
+ return -EBUSY;
+ }
+ dev_dbg(mehci->dev, "%s:Prod+Cons resources released\n",
+ __func__);
+ }
+
/*
* PHY may take some time or even fail to enter into low power
* mode (LPM). Hence poll for 500 msec and reset the PHY and link
@@ -794,6 +812,8 @@
clk_disable_unprepare(mehci->phy_clk);
clk_disable_unprepare(mehci->cal_clk);
clk_disable_unprepare(mehci->ahb_clk);
+ if (!IS_ERR(mehci->inactivity_clk))
+ clk_disable_unprepare(mehci->inactivity_clk);
none_vol = vdd_val[mehci->vdd_type][VDD_NONE];
max_vol = vdd_val[mehci->vdd_type][VDD_MAX];
@@ -822,7 +842,7 @@
wake_unlock(&mehci->wlock);
- dev_dbg(mehci->dev, "HSIC-USB in low power mode\n");
+ dev_info(mehci->dev, "HSIC-USB in low power mode\n");
return 0;
}
@@ -841,6 +861,14 @@
return 0;
}
+ if (pdata->consider_ipa_handshake) {
+ dev_dbg(mehci->dev, "%s:Wait for producer resource\n",
+ __func__);
+ msm_bam_wait_for_hsic_prod_granted();
+ dev_dbg(mehci->dev, "%s:Producer resource obtained\n",
+ __func__);
+ }
+
/* Handles race with Async interrupt */
disable_irq(hcd->irq);
@@ -876,6 +904,8 @@
clk_prepare_enable(mehci->phy_clk);
clk_prepare_enable(mehci->cal_clk);
clk_prepare_enable(mehci->ahb_clk);
+ if (!IS_ERR(mehci->inactivity_clk))
+ clk_prepare_enable(mehci->inactivity_clk);
temp = readl_relaxed(USB_USBCMD);
temp &= ~ASYNC_INTR_CTRL;
@@ -924,7 +954,13 @@
}
enable_irq(hcd->irq);
- dev_dbg(mehci->dev, "HSIC-USB exited from low power mode\n");
+ dev_info(mehci->dev, "HSIC-USB exited from low power mode\n");
+
+ if (pdata->consider_ipa_handshake) {
+ dev_dbg(mehci->dev, "%s:Notify usb bam on resume complete\n",
+ __func__);
+ msm_bam_hsic_notify_on_resume();
+ }
return 0;
}
@@ -1026,6 +1062,7 @@
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+ struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data;
int retval;
mehci->timer = USB_HS_GPTIMER_BASE;
@@ -1056,8 +1093,14 @@
/* bursts of unspecified length. */
writel_relaxed(0, USB_AHBBURST);
- /* Use the AHB transactor */
- writel_relaxed(0x08, USB_AHBMODE);
+
+ /* Use the AHB transactor and configure async bridge bypass */
+#define MSM_USB_ASYNC_BRIDGE_BYPASS BIT(31)
+ if (pdata->ahb_async_bridge_bypass)
+ writel_relaxed(0x08 | MSM_USB_ASYNC_BRIDGE_BYPASS, USB_AHBMODE);
+ else
+ writel_relaxed(0x08, USB_AHBMODE);
+
/* Disable streaming mode and select host mode */
writel_relaxed(0x13, USB_USBMODE);
@@ -1507,10 +1550,21 @@
goto put_cal_clk;
}
+ /*
+ * Inactivity_clk is required for hsic bam inactivity timer.
+ * This clock is not compulsory and is defined in clock lookup
+ * only for targets that need to use the inactivity timer feature.
+ */
+ mehci->inactivity_clk = clk_get(mehci->dev, "inactivity_clk");
+ if (IS_ERR(mehci->inactivity_clk))
+ dev_dbg(mehci->dev, "failed to get inactivity_clk\n");
+
clk_prepare_enable(mehci->core_clk);
clk_prepare_enable(mehci->phy_clk);
clk_prepare_enable(mehci->cal_clk);
clk_prepare_enable(mehci->ahb_clk);
+ if (!IS_ERR(mehci->inactivity_clk))
+ clk_prepare_enable(mehci->inactivity_clk);
return 0;
@@ -1520,7 +1574,11 @@
clk_disable_unprepare(mehci->phy_clk);
clk_disable_unprepare(mehci->cal_clk);
clk_disable_unprepare(mehci->ahb_clk);
+ if (!IS_ERR(mehci->inactivity_clk))
+ clk_disable_unprepare(mehci->inactivity_clk);
}
+ if (!IS_ERR(mehci->inactivity_clk))
+ clk_put(mehci->inactivity_clk);
clk_put(mehci->ahb_clk);
put_cal_clk:
clk_put(mehci->cal_clk);
@@ -1827,6 +1885,12 @@
"qcom,pool-64-bit-align");
pdata->enable_hbm = of_property_read_bool(node,
"qcom,enable-hbm");
+ pdata->disable_park_mode = (of_property_read_bool(node,
+ "qcom,disable-park-mode"));
+ pdata->consider_ipa_handshake = (of_property_read_bool(node,
+ "hsic,consider-ipa-handshake"));
+ pdata->ahb_async_bridge_bypass = of_property_read_bool(node,
+ "qcom,ahb-async-bridge-bypass");
return pdata;
}
@@ -2064,7 +2128,10 @@
pm_runtime_put_sync(pdev->dev.parent);
if (mehci->enable_hbm)
- hbm_init(hcd);
+ hbm_init(hcd, pdata->disable_park_mode);
+
+ if (pdata && pdata->consider_ipa_handshake)
+ msm_bam_set_hsic_host_dev(&pdev->dev);
return 0;
@@ -2089,6 +2156,9 @@
struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data;
+ if (pdata && pdata->consider_ipa_handshake)
+ msm_bam_set_hsic_host_dev(NULL);
+
/* If the device was removed no need to call pm_runtime_disable */
if (pdev->dev.power.power_state.event != PM_EVENT_INVALID)
pm_runtime_disable(&pdev->dev);
@@ -2154,7 +2224,6 @@
#ifdef CONFIG_PM_SLEEP
static int msm_hsic_pm_suspend(struct device *dev)
{
- int ret;
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
@@ -2162,15 +2231,16 @@
dbg_log_event(NULL, "PM Suspend", 0);
+ if (!atomic_read(&mehci->in_lpm)) {
+ dev_info(dev, "abort suspend\n");
+ dbg_log_event(NULL, "PM Suspend abort", 0);
+ return -EBUSY;
+ }
+
if (device_may_wakeup(dev) && !mehci->async_irq)
enable_irq_wake(hcd->irq);
- ret = msm_hsic_suspend(mehci);
-
- if (ret && device_may_wakeup(dev) && !mehci->async_irq)
- disable_irq_wake(hcd->irq);
-
- return ret;
+ return 0;
}
static int msm_hsic_pm_suspend_noirq(struct device *dev)
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index 521ace0..c2e8943 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -103,6 +103,7 @@
.bus_resume = ehci_bus_resume,
};
+static u64 msm_ehci_dma_mask = DMA_BIT_MASK(64);
static int ehci_msm_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd;
@@ -111,6 +112,11 @@
dev_dbg(&pdev->dev, "ehci_msm proble\n");
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &msm_ehci_dma_mask;
+ if (!pdev->dev.coherent_dma_mask)
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
hcd = usb_create_hcd(&msm_hc_driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
dev_err(&pdev->dev, "Unable to create HCD\n");
diff --git a/drivers/usb/host/hbm.c b/drivers/usb/host/hbm.c
index 1a0c0aa..d34301d 100644
--- a/drivers/usb/host/hbm.c
+++ b/drivers/usb/host/hbm.c
@@ -44,6 +44,7 @@
struct hbm_msm {
u32 *base;
struct usb_hcd *hcd;
+ bool disable_park_mode;
};
static struct hbm_msm *hbm_ctx;
@@ -173,8 +174,8 @@
USB_OTG_HS_HBM_PIPE_PRODUCER, 1 << pipe_num,
(is_consumer ? 0 : 1));
- /* disable park mode as default */
- set_disable_park_mode(pipe_num, true);
+ /* set park mode */
+ set_disable_park_mode(pipe_num, hbm_ctx->disable_park_mode);
/* enable zlt as default*/
set_disable_zlt(pipe_num, false);
@@ -186,7 +187,7 @@
return 0;
}
-void hbm_init(struct usb_hcd *hcd)
+void hbm_init(struct usb_hcd *hcd, bool disable_park_mode)
{
pr_info("%s\n", __func__);
@@ -198,6 +199,7 @@
hbm_ctx->base = hcd->regs;
hbm_ctx->hcd = hcd;
+ hbm_ctx->disable_park_mode = disable_park_mode;
/* reset hbm */
hbm_reset(true);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 4865b03..5055dcf 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -861,7 +861,7 @@
struct usb_bus *bus = phy->otg->host;
struct msm_otg_platform_data *pdata = motg->pdata;
int cnt = 0;
- bool host_bus_suspend, device_bus_suspend, dcp;
+ bool host_bus_suspend, device_bus_suspend, dcp, prop_charger;
u32 phy_ctrl_val = 0, cmd_val;
unsigned ret;
u32 portsc;
@@ -869,6 +869,9 @@
if (atomic_read(&motg->in_lpm))
return 0;
+ if (motg->pdata->delay_lpm_hndshk_on_disconnect && !msm_bam_lpm_ok())
+ return -EBUSY;
+
disable_irq(motg->irq);
host_bus_suspend = !test_bit(MHL, &motg->inputs) && phy->otg->host &&
!test_bit(ID, &motg->inputs);
@@ -876,6 +879,7 @@
test_bit(A_BUS_SUSPEND, &motg->inputs) &&
motg->caps & ALLOW_LPM_ON_DEV_SUSPEND;
dcp = motg->chg_type == USB_DCP_CHARGER;
+ prop_charger = motg->chg_type == USB_PROPRIETARY_CHARGER;
/*
* Abort suspend when,
@@ -884,7 +888,7 @@
*/
if ((test_bit(B_SESS_VLD, &motg->inputs) && !device_bus_suspend &&
- !dcp) || test_bit(A_BUS_REQ, &motg->inputs)) {
+ !dcp && !prop_charger) || test_bit(A_BUS_REQ, &motg->inputs)) {
enable_irq(motg->irq);
return -EBUSY;
}
@@ -952,7 +956,7 @@
*/
cmd_val = readl_relaxed(USB_USBCMD);
if (host_bus_suspend || device_bus_suspend ||
- (motg->pdata->otg_control == OTG_PHY_CONTROL && dcp))
+ (motg->pdata->otg_control == OTG_PHY_CONTROL))
cmd_val |= ASYNC_INTR_CTRL | ULPI_STP_CTRL;
else
cmd_val |= ULPI_STP_CTRL;
@@ -1255,7 +1259,14 @@
/* Set max current limit */
if (power_supply_set_current_limit(psy, 0))
goto psy_error;
+ } else {
+ if (power_supply_set_online(psy, true))
+ goto psy_error;
+ /* Current has changed (100/2 --> 500) */
+ if (power_supply_set_current_limit(psy, 1000*mA))
+ goto psy_error;
}
+
power_supply_changed(psy);
return 0;
@@ -1494,7 +1505,7 @@
vbus_otg = devm_regulator_get(motg->phy.dev, "vbus_otg");
if (IS_ERR(vbus_otg)) {
pr_err("Unable to get vbus_otg\n");
- return -ENODEV;
+ return PTR_ERR(vbus_otg);
}
}
@@ -3802,6 +3813,8 @@
"qcom,hsusb-otg-clk-always-on-workaround");
pdata->delay_lpm_on_disconnect = of_property_read_bool(node,
"qcom,hsusb-otg-delay-lpm");
+ pdata->delay_lpm_hndshk_on_disconnect = of_property_read_bool(node,
+ "qcom,hsusb-otg-delay-lpm-hndshk-on-disconnect");
pdata->dp_manual_pullup = of_property_read_bool(node,
"qcom,dp-manual-pullup");
pdata->enable_sec_phy = of_property_read_bool(node,
diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile
index 2c58e49..43eda51 100644
--- a/drivers/video/msm/mdss/Makefile
+++ b/drivers/video/msm/mdss/Makefile
@@ -1,4 +1,5 @@
mdss-mdp3-objs = mdp3.o mdp3_dma.o mdp3_ctrl.o
+mdss-mdp3-objs += mdp3_ppp.o mdp3_ppp_hwio.o mdp3_ppp_data.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp3.o
mdss-mdp-objs := mdss_mdp.o mdss_mdp_ctl.o mdss_mdp_pipe.o mdss_mdp_util.o
diff --git a/drivers/video/msm/mdss/dsi_host_v2.c b/drivers/video/msm/mdss/dsi_host_v2.c
index 887dde7..96f0f8c 100644
--- a/drivers/video/msm/mdss/dsi_host_v2.c
+++ b/drivers/video/msm/mdss/dsi_host_v2.c
@@ -35,8 +35,7 @@
struct completion dma_comp;
int irq_enabled;
spinlock_t irq_lock;
- spinlock_t mdp_lock;
- int mdp_busy;
+
int irq_no;
unsigned char *dsi_base;
struct device dis_dev;
@@ -57,7 +56,6 @@
init_completion(&dsi_host_private->dma_comp);
spin_lock_init(&dsi_host_private->irq_lock);
- spin_lock_init(&dsi_host_private->mdp_lock);
return 0;
}
@@ -140,14 +138,10 @@
unsigned long flags;
spin_lock_irqsave(&dsi_host_private->irq_lock, flags);
- if (dsi_host_private->irq_enabled) {
- pr_debug("%s: IRQ aleady enabled\n", __func__);
- spin_unlock_irqrestore(&dsi_host_private->irq_lock, flags);
- return;
- }
+ dsi_host_private->irq_enabled++;
+ if (dsi_host_private->irq_enabled == 1)
+ enable_irq(dsi_host_private->irq_no);
- enable_irq(dsi_host_private->irq_no);
- dsi_host_private->irq_enabled = 1;
spin_unlock_irqrestore(&dsi_host_private->irq_lock, flags);
}
@@ -156,26 +150,19 @@
unsigned long flags;
spin_lock_irqsave(&dsi_host_private->irq_lock, flags);
- if (dsi_host_private->irq_enabled == 0) {
- pr_debug("%s: IRQ already disabled\n", __func__);
- spin_unlock_irqrestore(&dsi_host_private->irq_lock, flags);
- return;
- }
- disable_irq(dsi_host_private->irq_no);
- dsi_host_private->irq_enabled = 0;
+ dsi_host_private->irq_enabled--;
+ if (dsi_host_private->irq_enabled == 0)
+ disable_irq(dsi_host_private->irq_no);
+
spin_unlock_irqrestore(&dsi_host_private->irq_lock, flags);
}
void msm_dsi_disable_irq_nosync(void)
{
spin_lock(&dsi_host_private->irq_lock);
- if (dsi_host_private->irq_enabled == 0) {
- pr_debug("%s: IRQ cannot be disabled\n", __func__);
- spin_unlock(&dsi_host_private->irq_lock);
- return;
- }
- disable_irq_nosync(dsi_host_private->irq_no);
- dsi_host_private->irq_enabled = 0;
+ dsi_host_private->irq_enabled--;
+ if (dsi_host_private->irq_enabled == 0)
+ disable_irq_nosync(dsi_host_private->irq_no);
spin_unlock(&dsi_host_private->irq_lock);
}
@@ -192,13 +179,6 @@
if (isr & DSI_INTR_CMD_DMA_DONE)
complete(&dsi_host_private->dma_comp);
- if (isr & DSI_INTR_CMD_MDP_DONE) {
- spin_lock(&dsi_host_private->mdp_lock);
- dsi_host_private->mdp_busy = false;
- msm_dsi_disable_irq_nosync();
- spin_unlock(&dsi_host_private->mdp_lock);
- }
-
return IRQ_HANDLED;
}
@@ -448,16 +428,6 @@
wmb();
}
-void msm_dsi_cmd_mdp_start(void)
-{
- unsigned long flag;
-
- spin_lock_irqsave(&dsi_host_private->mdp_lock, flag);
- msm_dsi_enable_irq();
- dsi_host_private->mdp_busy = true;
- spin_unlock_irqrestore(&dsi_host_private->mdp_lock, flag);
-}
-
int msm_dsi_cmd_reg_tx(u32 data)
{
unsigned char *ctrl_base = dsi_host_private->dsi_base;
@@ -551,7 +521,6 @@
struct dsi_cmd_desc *cm;
u32 dsi_ctrl, ctrl;
int i, video_mode;
- unsigned long flag;
unsigned char *ctrl_base = dsi_host_private->dsi_base;
/* turn on cmd mode
@@ -566,13 +535,9 @@
MIPI_OUTP(ctrl_base + DSI_CTRL, ctrl);
}
- spin_lock_irqsave(&dsi_host_private->mdp_lock, flag);
msm_dsi_enable_irq();
- dsi_host_private->mdp_busy = true;
- spin_unlock_irqrestore(&dsi_host_private->mdp_lock, flag);
cm = cmds;
- dsi_buf_init(tp);
for (i = 0; i < cnt; i++) {
dsi_buf_init(tp);
dsi_cmd_dma_add(tp, cm);
@@ -582,10 +547,7 @@
cm++;
}
- spin_lock_irqsave(&dsi_host_private->mdp_lock, flag);
- dsi_host_private->mdp_busy = false;
msm_dsi_disable_irq();
- spin_unlock_irqrestore(&dsi_host_private->mdp_lock, flag);
if (video_mode)
MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl);
@@ -617,7 +579,6 @@
struct dsi_cmd_desc *cmds, int rlen)
{
int cnt, len, diff, pkt_size;
- unsigned long flag;
char cmd;
if (pdata->panel_info.mipi.no_max_pkt_size)
@@ -645,10 +606,7 @@
cnt = len + 6; /* 4 bytes header + 2 bytes crc */
}
- spin_lock_irqsave(&dsi_host_private->mdp_lock, flag);
msm_dsi_enable_irq();
- dsi_host_private->mdp_busy = true;
- spin_unlock_irqrestore(&dsi_host_private->mdp_lock, flag);
if (!pdata->panel_info.mipi.no_max_pkt_size) {
/* packet size need to be set at every read */
@@ -681,10 +639,7 @@
msm_dsi_cmd_dma_rx(rp, cnt);
- spin_lock_irqsave(&dsi_host_private->mdp_lock, flag);
- dsi_host_private->mdp_busy = false;
msm_dsi_disable_irq();
- spin_unlock_irqrestore(&dsi_host_private->mdp_lock, flag);
if (pdata->panel_info.mipi.no_max_pkt_size) {
/*
@@ -890,7 +845,7 @@
msm_dsi_clk_set_rate(DSI_ESC_CLK_RATE, 0, 0, 0);
msm_dsi_clk_disable();
msm_dsi_unprepare_clocks();
-
+ msm_dsi_phy_off(dsi_host_private->dsi_base);
msm_dsi_ahb_ctrl(0);
ret = msm_dsi_regulator_disable();
diff --git a/drivers/video/msm/mdss/dsi_io_v2.c b/drivers/video/msm/mdss/dsi_io_v2.c
index 93f2c76..273fb54 100644
--- a/drivers/video/msm/mdss/dsi_io_v2.c
+++ b/drivers/video/msm/mdss/dsi_io_v2.c
@@ -435,15 +435,11 @@
wmb();
}
-void msm_dsi_phy_enable(unsigned char *ctrl_base, int on)
+void msm_dsi_phy_off(unsigned char *ctrl_base)
{
- if (on) {
- MIPI_OUTP(ctrl_base + DSI_DSIPHY_PLL_CTRL_5, 0x050);
- } else {
- MIPI_OUTP(ctrl_base + DSI_DSIPHY_PLL_CTRL_5, 0x05f);
- MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_0, 0x02);
- MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_0, 0x00);
- MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_1, 0x7f);
- MIPI_OUTP(ctrl_base + DSI_CLK_CTRL, 0);
- }
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_PLL_CTRL_5, 0x05f);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_0, 0x02);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_0, 0x00);
+ MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_1, 0x7f);
+ MIPI_OUTP(ctrl_base + DSI_CLK_CTRL, 0);
}
diff --git a/drivers/video/msm/mdss/dsi_io_v2.h b/drivers/video/msm/mdss/dsi_io_v2.h
index 285bf30..a7d43a0 100644
--- a/drivers/video/msm/mdss/dsi_io_v2.h
+++ b/drivers/video/msm/mdss/dsi_io_v2.h
@@ -51,4 +51,5 @@
void msm_dsi_phy_sw_reset(unsigned char *ctrl_base);
+void msm_dsi_phy_off(unsigned char *ctrl_base);
#endif /* DSI_IO_V2_H */
diff --git a/drivers/video/msm/mdss/dsi_panel_v2.c b/drivers/video/msm/mdss/dsi_panel_v2.c
index e46ea3b..bb5d3ca 100644
--- a/drivers/video/msm/mdss/dsi_panel_v2.c
+++ b/drivers/video/msm/mdss/dsi_panel_v2.c
@@ -104,8 +104,14 @@
kfree(panel_private->on_cmds);
kfree(panel_private->off_cmds);
+
kfree(panel_private);
panel_private = NULL;
+
+ if (bl_led_trigger) {
+ led_trigger_unregister_simple(bl_led_trigger);
+ bl_led_trigger = NULL;
+ }
}
int dsi_panel_power(int enable)
{
diff --git a/drivers/video/msm/mdss/dsi_v2.h b/drivers/video/msm/mdss/dsi_v2.h
index f68527c..54b772b 100644
--- a/drivers/video/msm/mdss/dsi_v2.h
+++ b/drivers/video/msm/mdss/dsi_v2.h
@@ -181,7 +181,7 @@
struct dsi_panel_cmds_list {
struct dsi_cmd_desc *buf;
- char size;
+ int size;
char ctrl_state;
};
diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c
index 52243eb..e5b6603 100644
--- a/drivers/video/msm/mdss/mdp3.c
+++ b/drivers/video/msm/mdss/mdp3.c
@@ -32,6 +32,8 @@
#include <linux/spinlock.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
+#include <linux/file.h>
+#include <linux/msm_kgsl.h>
#include <mach/board.h>
#include <mach/clk.h>
@@ -46,11 +48,12 @@
#include "mdss_fb.h"
#include "mdp3_hwio.h"
#include "mdp3_ctrl.h"
+#include "mdp3_ppp.h"
#define MDP_CORE_HW_VERSION 0x03040310
struct mdp3_hw_resource *mdp3_res;
-#define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val) \
+#define MDP_BUS_VECTOR_ENTRY_DMA(ab_val, ib_val) \
{ \
.src = MSM_BUS_MASTER_MDP_PORT0, \
.dst = MSM_BUS_SLAVE_EBI_CH0, \
@@ -58,18 +61,57 @@
.ib = (ib_val), \
}
-static struct msm_bus_vectors mdp_bus_vectors[] = {
- MDP_BUS_VECTOR_ENTRY(0, 0),
- MDP_BUS_VECTOR_ENTRY(SZ_128M, SZ_256M),
- MDP_BUS_VECTOR_ENTRY(SZ_256M, SZ_512M),
+static struct msm_bus_vectors mdp_bus_dma_vectors[] = {
+ MDP_BUS_VECTOR_ENTRY_DMA(0, 0),
+ MDP_BUS_VECTOR_ENTRY_DMA(SZ_128M, SZ_256M),
+ MDP_BUS_VECTOR_ENTRY_DMA(SZ_256M, SZ_512M),
+};
+static struct msm_bus_paths
+ mdp_bus_dma_usecases[ARRAY_SIZE(mdp_bus_dma_vectors)];
+static struct msm_bus_scale_pdata mdp_bus_dma_scale_table = {
+ .usecase = mdp_bus_dma_usecases,
+ .num_usecases = ARRAY_SIZE(mdp_bus_dma_usecases),
+ .name = "mdp3",
};
-static struct msm_bus_paths mdp_bus_usecases[ARRAY_SIZE(mdp_bus_vectors)];
+#define MDP_BUS_VECTOR_ENTRY_PPP(ab_val, ib_val) \
+ { \
+ .src = MSM_BUS_MASTER_MDPE, \
+ .dst = MSM_BUS_SLAVE_EBI_CH0, \
+ .ab = (ab_val), \
+ .ib = (ib_val), \
+ }
-static struct msm_bus_scale_pdata mdp_bus_scale_table = {
- .usecase = mdp_bus_usecases,
- .num_usecases = ARRAY_SIZE(mdp_bus_usecases),
- .name = "mdp3",
+static struct msm_bus_vectors mdp_bus_ppp_vectors[] = {
+ MDP_BUS_VECTOR_ENTRY_PPP(0, 0),
+ MDP_BUS_VECTOR_ENTRY_PPP(SZ_128M, SZ_256M),
+ MDP_BUS_VECTOR_ENTRY_PPP(SZ_256M, SZ_512M),
+};
+
+static struct msm_bus_paths
+ mdp_bus_ppp_usecases[ARRAY_SIZE(mdp_bus_ppp_vectors)];
+
+static struct msm_bus_scale_pdata mdp_bus_ppp_scale_table = {
+ .usecase = mdp_bus_ppp_usecases,
+ .num_usecases = ARRAY_SIZE(mdp_bus_ppp_usecases),
+ .name = "mdp3_ppp",
+};
+
+struct mdp3_bus_handle_map mdp3_bus_handle[MDP3_BUS_HANDLE_MAX] = {
+ [MDP3_BUS_HANDLE_DMA] = {
+ .bus_vector = mdp_bus_dma_vectors,
+ .usecases = mdp_bus_dma_usecases,
+ .scale_pdata = &mdp_bus_dma_scale_table,
+ .current_bus_idx = 0,
+ .handle = 0,
+ },
+ [MDP3_BUS_HANDLE_PPP] = {
+ .bus_vector = mdp_bus_ppp_vectors,
+ .usecases = mdp_bus_ppp_usecases,
+ .scale_pdata = &mdp_bus_ppp_scale_table,
+ .current_bus_idx = 0,
+ .handle = 0,
+ },
};
struct mdp3_iommu_domain_map mdp3_iommu_domains[MDP3_IOMMU_DOMAIN_MAX] = {
@@ -125,7 +167,7 @@
pr_debug("mdp3_irq_handler irq=%d\n", mdp_interrupt);
spin_lock(&mdata->irq_lock);
- mdp_interrupt &= mdata->irqMask;
+ mdp_interrupt &= mdata->irq_mask;
while (mdp_interrupt && i < MDP3_MAX_INTR) {
if ((mdp_interrupt & 0x1) && mdata->callbacks[i].cb)
@@ -145,14 +187,15 @@
pr_debug("mdp3_irq_enable type=%d\n", type);
spin_lock_irqsave(&mdp3_res->irq_lock, flag);
- if (mdp3_res->irqMask & BIT(type)) {
+ mdp3_res->irq_ref_count[type] += 1;
+ if (mdp3_res->irq_ref_count[type] > 1) {
pr_debug("interrupt %d already enabled\n", type);
spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
return;
}
- irqEnabled = mdp3_res->irqMask;
- mdp3_res->irqMask |= BIT(type);
- MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irqMask);
+ irqEnabled = mdp3_res->irq_mask;
+ mdp3_res->irq_mask |= BIT(type);
+ MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irq_mask);
if (!irqEnabled)
enable_irq(mdp3_res->irq);
spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
@@ -163,26 +206,22 @@
unsigned long flag;
spin_lock_irqsave(&mdp3_res->irq_lock, flag);
- if (mdp3_res->irqMask & BIT(type)) {
- mdp3_res->irqMask &= ~BIT(type);
- MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irqMask);
- if (!mdp3_res->irqMask)
- disable_irq(mdp3_res->irq);
- } else {
- pr_debug("interrupt %d not enabled\n", type);
- }
+ mdp3_irq_disable_nosync(type);
spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
}
void mdp3_irq_disable_nosync(int type)
{
- if (mdp3_res->irqMask & BIT(type)) {
- mdp3_res->irqMask &= ~BIT(type);
- MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irqMask);
- if (!mdp3_res->irqMask)
- disable_irq_nosync(mdp3_res->irq);
- } else {
+ if (mdp3_res->irq_ref_count[type] <= 0) {
pr_debug("interrupt %d not enabled\n", type);
+ return;
+ }
+ mdp3_res->irq_ref_count[type] -= 1;
+ if (mdp3_res->irq_ref_count[type] == 0) {
+ mdp3_res->irq_mask &= ~BIT(type);
+ MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irq_mask);
+ if (!mdp3_res->irq_mask)
+ disable_irq_nosync(mdp3_res->irq);
}
}
@@ -203,71 +242,110 @@
static int mdp3_bus_scale_register(void)
{
+ int i;
+
if (!mdp3_res->bus_handle) {
- struct msm_bus_scale_pdata *bus_pdata = &mdp_bus_scale_table;
- int i;
+ pr_err("No bus handle\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < MDP3_BUS_HANDLE_MAX; i++) {
+ struct mdp3_bus_handle_map *bus_handle =
+ &mdp3_res->bus_handle[i];
- for (i = 0; i < bus_pdata->num_usecases; i++) {
- mdp_bus_usecases[i].num_paths = 1;
- mdp_bus_usecases[i].vectors = &mdp_bus_vectors[i];
- }
+ if (!bus_handle->handle) {
+ int j;
+ struct msm_bus_scale_pdata *bus_pdata =
+ bus_handle->scale_pdata;
- mdp3_res->bus_handle = msm_bus_scale_register_client(bus_pdata);
- if (!mdp3_res->bus_handle) {
- pr_err("not able to get bus scale\n");
- return -ENOMEM;
+ for (j = 0; j < bus_pdata->num_usecases; j++) {
+ bus_handle->usecases[j].num_paths = 1;
+ bus_handle->usecases[j].vectors =
+ &bus_handle->bus_vector[j];
+ }
+
+ bus_handle->handle =
+ msm_bus_scale_register_client(bus_pdata);
+ if (!bus_handle->handle) {
+ pr_err("not able to get bus scale i=%d\n", i);
+ return -ENOMEM;
+ }
+ pr_debug("register bus_hdl=%x\n",
+ bus_handle->handle);
}
- pr_debug("register bus_hdl=%x\n", mdp3_res->bus_handle);
}
return 0;
}
static void mdp3_bus_scale_unregister(void)
{
- pr_debug("unregister bus_handle=%x\n", mdp3_res->bus_handle);
+ int i;
- if (mdp3_res->bus_handle)
- msm_bus_scale_unregister_client(mdp3_res->bus_handle);
+ if (!mdp3_res->bus_handle)
+ return;
+
+ for (i = 0; i < MDP3_BUS_HANDLE_MAX; i++) {
+ pr_debug("unregister index=%d bus_handle=%x\n",
+ i, mdp3_res->bus_handle[i].handle);
+ if (mdp3_res->bus_handle[i].handle) {
+ msm_bus_scale_unregister_client(
+ mdp3_res->bus_handle[i].handle);
+ mdp3_res->bus_handle[i].handle = 0;
+ }
+ }
}
int mdp3_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota)
{
- static int current_bus_idx;
+ struct mdp3_bus_handle_map *bus_handle;
+ int cur_bus_idx;
int bus_idx;
+ int client_idx;
int rc;
- if (mdp3_res->bus_handle < 1) {
- pr_err("invalid bus handle %d\n", mdp3_res->bus_handle);
+ if (client == MDP3_CLIENT_DMA_P) {
+ client_idx = MDP3_BUS_HANDLE_DMA;
+ } else if (client == MDP3_CLIENT_PPP) {
+ client_idx = MDP3_BUS_HANDLE_PPP;
+ } else {
+ pr_err("invalid client %d\n", client);
+ return -EINVAL;
+ }
+
+ bus_handle = &mdp3_res->bus_handle[client_idx];
+ cur_bus_idx = bus_handle->current_bus_idx;
+
+ if (bus_handle->handle < 1) {
+ pr_err("invalid bus handle %d\n", bus_handle->handle);
return -EINVAL;
}
if ((ab_quota | ib_quota) == 0) {
bus_idx = 0;
} else {
- int num_cases = mdp_bus_scale_table.num_usecases;
+ int num_cases = bus_handle->scale_pdata->num_usecases;
struct msm_bus_vectors *vect = NULL;
- bus_idx = (current_bus_idx % (num_cases - 1)) + 1;
+ bus_idx = (cur_bus_idx % (num_cases - 1)) + 1;
/* aligning to avoid performing updates for small changes */
ab_quota = ALIGN(ab_quota, SZ_64M);
ib_quota = ALIGN(ib_quota, SZ_64M);
- vect = mdp_bus_scale_table.usecase[current_bus_idx].vectors;
+ vect = bus_handle->scale_pdata->usecase[cur_bus_idx].vectors;
if ((ab_quota == vect->ab) && (ib_quota == vect->ib)) {
pr_debug("skip bus scaling, no change in vectors\n");
return 0;
}
- vect = mdp_bus_scale_table.usecase[bus_idx].vectors;
+ vect = bus_handle->scale_pdata->usecase[bus_idx].vectors;
vect->ab = ab_quota;
vect->ib = ib_quota;
pr_debug("bus scale idx=%d ab=%llu ib=%llu\n", bus_idx,
vect->ab, vect->ib);
}
- current_bus_idx = bus_idx;
- rc = msm_bus_scale_client_update_request(mdp3_res->bus_handle, bus_idx);
+ bus_handle->current_bus_idx = bus_idx;
+ rc = msm_bus_scale_client_update_request(bus_handle->handle, bus_idx);
return rc;
}
@@ -288,7 +366,7 @@
mdp3_res->clock_ref_count[clk_idx]--;
count = mdp3_res->clock_ref_count[clk_idx];
- if (count == 1) {
+ if (count == 1 && enable) {
pr_debug("clk=%d en=%d\n", clk_idx, enable);
ret = clk_prepare_enable(clk);
} else if (count == 0) {
@@ -304,7 +382,8 @@
-int mdp3_clk_set_rate(int clk_type, unsigned long clk_rate)
+int mdp3_clk_set_rate(int clk_type, unsigned long clk_rate,
+ int client)
{
int ret = 0;
unsigned long rounded_rate;
@@ -318,6 +397,19 @@
mutex_unlock(&mdp3_res->res_mutex);
return -EINVAL;
}
+ if (clk_type == MDP3_CLK_CORE) {
+ if (client == MDP3_CLIENT_DMA_P) {
+ mdp3_res->dma_core_clk_request = rounded_rate;
+ } else if (client == MDP3_CLIENT_PPP) {
+ mdp3_res->ppp_core_clk_request = rounded_rate;
+ } else {
+ pr_err("unrecognized client=%d\n", client);
+ mutex_unlock(&mdp3_res->res_mutex);
+ return -EINVAL;
+ }
+ rounded_rate = max(mdp3_res->dma_core_clk_request,
+ mdp3_res->ppp_core_clk_request);
+ }
if (rounded_rate != clk_get_rate(clk)) {
ret = clk_set_rate(clk, rounded_rate);
if (ret)
@@ -399,11 +491,20 @@
static void mdp3_clk_remove(void)
{
- clk_put(mdp3_res->clocks[MDP3_CLK_AHB]);
- clk_put(mdp3_res->clocks[MDP3_CLK_CORE]);
- clk_put(mdp3_res->clocks[MDP3_CLK_VSYNC]);
- clk_put(mdp3_res->clocks[MDP3_CLK_LCDC]);
- clk_put(mdp3_res->clocks[MDP3_CLK_DSI]);
+ if (!IS_ERR_OR_NULL(mdp3_res->clocks[MDP3_CLK_AHB]))
+ clk_put(mdp3_res->clocks[MDP3_CLK_AHB]);
+
+ if (!IS_ERR_OR_NULL(mdp3_res->clocks[MDP3_CLK_CORE]))
+ clk_put(mdp3_res->clocks[MDP3_CLK_CORE]);
+
+ if (!IS_ERR_OR_NULL(mdp3_res->clocks[MDP3_CLK_VSYNC]))
+ clk_put(mdp3_res->clocks[MDP3_CLK_VSYNC]);
+
+ if (!IS_ERR_OR_NULL(mdp3_res->clocks[MDP3_CLK_LCDC]))
+ clk_put(mdp3_res->clocks[MDP3_CLK_LCDC]);
+
+ if (!IS_ERR_OR_NULL(mdp3_res->clocks[MDP3_CLK_DSI]))
+ clk_put(mdp3_res->clocks[MDP3_CLK_DSI]);
}
int mdp3_clk_enable(int enable)
@@ -434,6 +535,7 @@
return ret;
}
disable_irq(mdp3_res->irq);
+ mdp3_res->irq_registered = true;
return 0;
}
@@ -471,7 +573,8 @@
struct mdp3_iommu_ctx_map *context_map;
struct mdp3_iommu_domain_map *domain_map;
- if (context >= MDP3_IOMMU_CTX_MAX)
+ if (!mdp3_res->iommu_contexts ||
+ context >= MDP3_IOMMU_CTX_MAX)
return -EINVAL;
context_map = mdp3_res->iommu_contexts + context;
@@ -510,10 +613,13 @@
mdp3_iommu_domains[i].domain_idx = domain_idx;
mdp3_iommu_domains[i].domain = msm_get_iommu_domain(domain_idx);
- if (!mdp3_iommu_domains[i].domain) {
+ if (IS_ERR_OR_NULL(mdp3_iommu_domains[i].domain)) {
pr_err("unable to get iommu domain(%d)\n",
domain_idx);
- return -EINVAL;
+ if (!mdp3_iommu_domains[i].domain)
+ return -EINVAL;
+ else
+ return PTR_ERR(mdp3_iommu_domains[i].domain);
}
iommu_set_fault_handler(mdp3_iommu_domains[i].domain,
mdp3_iommu_fault_handler,
@@ -538,10 +644,13 @@
mdp3_iommu_contexts[i].ctx =
msm_iommu_get_ctx(mdp3_iommu_contexts[i].ctx_name);
- if (!mdp3_iommu_contexts[i].ctx) {
+ if (IS_ERR_OR_NULL(mdp3_iommu_contexts[i].ctx)) {
pr_warn("unable to get iommu ctx(%s)\n",
mdp3_iommu_contexts[i].ctx_name);
- return -EINVAL;
+ if (!mdp3_iommu_contexts[i].ctx)
+ return -EINVAL;
+ else
+ return PTR_ERR(mdp3_iommu_contexts[i].ctx);
}
}
@@ -568,6 +677,19 @@
return ret;
}
+void mdp3_iommu_deinit(void)
+{
+ int i;
+
+ if (!mdp3_res->domains)
+ return;
+
+ for (i = 0; i < MDP3_IOMMU_DOMAIN_MAX; i++) {
+ if (!IS_ERR_OR_NULL(mdp3_res->domains[i].domain))
+ msm_unregister_domain(mdp3_res->domains[i].domain);
+ }
+}
+
static int mdp3_check_version(void)
{
int rc;
@@ -640,11 +762,7 @@
if (rc)
return rc;
- rc = mdp3_iommu_attach(MDP3_IOMMU_CTX_DMA_0);
- if (rc) {
- pr_err("fail to attach DMA-P context 0\n");
- return rc;
- }
+ mdp3_res->bus_handle = mdp3_bus_handle;
rc = mdp3_bus_scale_register();
if (rc) {
pr_err("unable to register bus scaling\n");
@@ -656,6 +774,21 @@
return rc;
}
+static void mdp3_res_deinit(void)
+{
+ mdp3_bus_scale_unregister();
+ mdp3_iommu_dettach(MDP3_IOMMU_CTX_DMA_0);
+ mdp3_iommu_deinit();
+
+ if (!IS_ERR_OR_NULL(mdp3_res->ion_client))
+ ion_client_destroy(mdp3_res->ion_client);
+
+ mdp3_clk_remove();
+
+ if (mdp3_res->irq_registered)
+ devm_free_irq(&mdp3_res->pdev->dev, mdp3_res->irq, mdp3_res);
+}
+
static int mdp3_parse_dt(struct platform_device *pdev)
{
struct resource *res;
@@ -688,9 +821,145 @@
return 0;
}
+int mdp3_put_img(struct mdp3_img_data *data)
+{
+ struct ion_client *iclient = mdp3_res->ion_client;
+ int dom = (mdp3_res->domains + MDP3_IOMMU_DOMAIN)->domain_idx;
+
+ if (!data->srcp_file) {
+ pr_debug("No img to put\n");
+ return 0;
+ }
+ if (data->flags & MDP_BLIT_SRC_GEM) {
+ pr_debug("memory source MDP_BLIT_SRC_GEM\n");
+ } else if (data->flags & MDP_MEMORY_ID_TYPE_FB) {
+ pr_debug("fb mem buf=0x%x\n", data->addr);
+ fput_light(data->srcp_file, data->p_need);
+ data->srcp_file = NULL;
+ } else {
+ ion_unmap_iommu(iclient, data->srcp_ihdl, dom, 0);
+ ion_free(iclient, data->srcp_ihdl);
+ data->srcp_ihdl = NULL;
+ }
+ return 0;
+}
+
+int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data)
+{
+ struct file *file;
+ int ret = -EINVAL;
+ int fb_num;
+ unsigned long *start, *len;
+ struct ion_client *iclient = mdp3_res->ion_client;
+ int dom = (mdp3_res->domains + MDP3_IOMMU_DOMAIN)->domain_idx;
+
+ start = (unsigned long *) &data->addr;
+ len = (unsigned long *) &data->len;
+ data->flags |= img->flags;
+ data->p_need = 0;
+
+ if (img->flags & MDP_BLIT_SRC_GEM) {
+ data->srcp_file = NULL;
+ ret = kgsl_gem_obj_addr(img->memory_id, (int) img->priv,
+ &data->addr, &data->len);
+ if (!ret)
+ goto done;
+ }
+ if (img->flags & MDP_MEMORY_ID_TYPE_FB) {
+ file = fget_light(img->memory_id, &data->p_need);
+ if (file == NULL) {
+ pr_err("invalid framebuffer file (%d)\n",
+ img->memory_id);
+ return -EINVAL;
+ }
+ if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
+ fb_num = MINOR(file->f_dentry->d_inode->i_rdev);
+ ret = mdss_fb_get_phys_info(start, len, fb_num);
+ if (ret) {
+ pr_err("mdss_fb_get_phys_info() failed\n");
+ fput_light(file, data->p_need);
+ file = NULL;
+ }
+ } else {
+ pr_err("invalid FB_MAJOR\n");
+ fput_light(file, data->p_need);
+ file = NULL;
+ ret = -EINVAL;
+ }
+ data->srcp_file = file;
+ if (!ret)
+ goto done;
+ }
+ if (iclient) {
+ data->srcp_ihdl = ion_import_dma_buf(iclient, img->memory_id);
+ if (IS_ERR_OR_NULL(data->srcp_ihdl)) {
+ pr_err("error on ion_import_fd\n");
+ if (!data->srcp_ihdl)
+ ret = -EINVAL;
+ else
+ ret = PTR_ERR(data->srcp_ihdl);
+ data->srcp_ihdl = NULL;
+ return ret;
+ }
+
+ ret = ion_map_iommu(iclient, data->srcp_ihdl, dom,
+ 0, SZ_4K, 0, start, len, 0, 0);
+
+ if (IS_ERR_VALUE(ret)) {
+ ion_free(iclient, data->srcp_ihdl);
+ pr_err("failed to map ion handle (%d)\n", ret);
+ return ret;
+ }
+ }
+done:
+ if (!ret && (img->offset < data->len)) {
+ data->addr += img->offset;
+ data->len -= img->offset;
+
+ pr_debug("mem=%d ihdl=%p buf=0x%x len=0x%x\n", img->memory_id,
+ data->srcp_ihdl, data->addr, data->len);
+ } else {
+ mdp3_put_img(data);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+int mdp3_iommu_enable(int client)
+{
+ int rc;
+
+ if (client == MDP3_CLIENT_DMA_P) {
+ rc = mdp3_iommu_attach(MDP3_IOMMU_CTX_DMA_0);
+ } else {
+ rc = mdp3_iommu_attach(MDP3_IOMMU_CTX_PPP_0);
+ rc |= mdp3_iommu_attach(MDP3_IOMMU_CTX_PPP_1);
+ }
+
+ return rc;
+}
+
+int mdp3_iommu_disable(int client)
+{
+ int rc;
+
+ if (client == MDP3_CLIENT_DMA_P) {
+ rc = mdp3_iommu_dettach(MDP3_IOMMU_CTX_DMA_0);
+ } else {
+ rc = mdp3_iommu_dettach(MDP3_IOMMU_CTX_PPP_0);
+ rc |= mdp3_iommu_dettach(MDP3_IOMMU_CTX_PPP_1);
+ }
+
+ return rc;
+}
+
static int mdp3_init(struct msm_fb_data_type *mfd)
{
- return mdp3_ctrl_init(mfd);
+ int rc;
+ rc = mdp3_ctrl_init(mfd);
+ rc |= mdp3_ppp_res_init(mfd);
+ return rc;
}
u32 mdp3_fb_stride(u32 fb_index, u32 xres, int bpp)
@@ -708,45 +977,6 @@
return xres * bpp;
}
-/*
- * physical contiguous memory should be allocated in mdss_fb, and SMMU
- * virtual address mapping can be done in the MDP h/w specific code. It
- * should have a reference count, if none is current mapped, the SMMU context
- * can bedetached, thus allowing power saving in SMMU.
- */
-static int mdp3_fbmem_alloc(struct msm_fb_data_type *mfd)
-{
- int dom;
- void *virt = NULL;
- unsigned long phys = 0;
- size_t size;
- u32 yres = mfd->fbi->var.yres_virtual;
-
- size = PAGE_ALIGN(mfd->fbi->fix.line_length * yres);
-
- if (mfd->index == 0) {
- virt = allocate_contiguous_memory(size, MEMTYPE_EBI1, SZ_1M, 0);
- if (!virt) {
- pr_err("unable to alloc fbmem size=%u\n", size);
- return -ENOMEM;
- }
- phys = memory_pool_node_paddr(virt);
- dom = (mdp3_res->domains + MDP3_IOMMU_DOMAIN)->domain_idx;
- msm_iommu_map_contig_buffer(phys, dom, 0, size, SZ_4K, 0,
- &mfd->iova);
-
- pr_debug("allocating %u bytes at %p (%lx phys) for fb %d\n",
- size, virt, phys, mfd->index);
- } else {
- size = 0;
- }
-
- mfd->fbi->screen_base = virt;
- mfd->fbi->fix.smem_start = phys;
- mfd->fbi->fix.smem_len = size;
- return 0;
-}
-
struct mdp3_dma *mdp3_get_dma_pipe(int capability)
{
int i;
@@ -775,12 +1005,19 @@
return NULL;
}
+static int mdp3_fb_mem_get_iommu_domain(void)
+{
+ if (!mdp3_res)
+ return -ENODEV;
+ return mdp3_res->domains[MDP3_IOMMU_DOMAIN].domain_idx;
+}
+
static int mdp3_probe(struct platform_device *pdev)
{
int rc;
static struct msm_mdp_interface mdp3_interface = {
.init_fnc = mdp3_init,
- .fb_mem_alloc_fnc = mdp3_fbmem_alloc,
+ .fb_mem_get_iommu_domain = mdp3_fb_mem_get_iommu_domain,
.fb_stride = mdp3_fb_stride,
};
@@ -827,6 +1064,11 @@
probe_done:
if (IS_ERR_VALUE(rc)) {
+ mdp3_res_deinit();
+
+ if (mdp3_res->mdp_base)
+ devm_iounmap(&pdev->dev, mdp3_res->mdp_base);
+
devm_kfree(&pdev->dev, mdp3_res);
mdp3_res = NULL;
}
diff --git a/drivers/video/msm/mdss/mdp3.h b/drivers/video/msm/mdss/mdp3.h
index 5774e5a..1afae01 100644
--- a/drivers/video/msm/mdss/mdp3.h
+++ b/drivers/video/msm/mdss/mdp3.h
@@ -23,6 +23,7 @@
#include <mach/iommu_domains.h>
#include "mdp3_dma.h"
+#include "mdss_fb.h"
enum {
MDP3_CLK_AHB,
@@ -34,6 +35,12 @@
};
enum {
+ MDP3_BUS_HANDLE_DMA,
+ MDP3_BUS_HANDLE_PPP,
+ MDP3_BUS_HANDLE_MAX,
+};
+
+enum {
MDP3_IOMMU_DOMAIN,
MDP3_IOMMU_DOMAIN_MAX
};
@@ -47,10 +54,16 @@
};
enum {
- MDP3_BW_CLIENT_DMA_P,
- MDP3_BW_CLIENT_DMA_S,
- MDP3_BW_CLIENT_DMA_E,
- MDP3_BW_CLIENT_PPP,
+ MDP3_CLIENT_DMA_P,
+ MDP3_CLIENT_PPP,
+};
+
+struct mdp3_bus_handle_map {
+ struct msm_bus_vectors *bus_vector;
+ struct msm_bus_paths *usecases;
+ struct msm_bus_scale_pdata *scale_pdata;
+ int current_bus_idx;
+ u32 handle;
};
struct mdp3_iommu_domain_map {
@@ -85,12 +98,14 @@
struct clk *clocks[MDP3_MAX_CLK];
int clock_ref_count[MDP3_MAX_CLK];
+ unsigned long dma_core_clk_request;
+ unsigned long ppp_core_clk_request;
char __iomem *mdp_base;
size_t mdp_reg_size;
u32 irq;
- u32 bus_handle;
+ struct mdp3_bus_handle_map *bus_handle;
struct ion_client *ion_client;
struct mdp3_iommu_domain_map *domains;
@@ -100,12 +115,24 @@
struct mdp3_intf intf[MDP3_DMA_OUTPUT_SEL_MAX];
spinlock_t irq_lock;
- u32 irqMask;
+ u32 irq_ref_count[MDP3_MAX_INTR];
+ u32 irq_mask;
struct mdp3_intr_cb callbacks[MDP3_MAX_INTR];
+ int irq_registered;
+
struct early_suspend suspend_handler;
};
+struct mdp3_img_data {
+ u32 addr;
+ u32 len;
+ u32 flags;
+ int p_need;
+ struct file *srcp_file;
+ struct ion_handle *srcp_ihdl;
+};
+
extern struct mdp3_hw_resource *mdp3_res;
struct mdp3_dma *mdp3_get_dma_pipe(int capability);
@@ -114,9 +141,13 @@
void mdp3_irq_disable(int type);
void mdp3_irq_disable_nosync(int type);
int mdp3_set_intr_callback(u32 type, struct mdp3_intr_cb *cb);
-int mdp3_clk_set_rate(int clk_type, unsigned long clk_rate);
+int mdp3_clk_set_rate(int clk_type, unsigned long clk_rate, int client);
int mdp3_clk_enable(int enable);
int mdp3_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota);
+int mdp3_put_img(struct mdp3_img_data *data);
+int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data);
+int mdp3_iommu_enable(int client);
+int mdp3_iommu_disable(int client);
#define MDP3_REG_WRITE(addr, val) writel_relaxed(val, mdp3_res->mdp_base + addr)
#define MDP3_REG_READ(addr) readl_relaxed(mdp3_res->mdp_base + addr)
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c
index 99b7604..98e7e29 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.c
+++ b/drivers/video/msm/mdss/mdp3_ctrl.c
@@ -22,41 +22,151 @@
#include "mdp3_ctrl.h"
#include "mdp3.h"
+#include "mdp3_ppp.h"
+#define MDP_CORE_CLK_RATE 100000000
#define MDP_VSYNC_CLK_RATE 19200000
#define VSYNC_PERIOD 16
+static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd);
+static int mdp3_overlay_unset(struct msm_fb_data_type *mfd, int ndx);
+
+static void mdp3_bufq_init(struct mdp3_buffer_queue *bufq)
+{
+ bufq->count = 0;
+ bufq->push_idx = 0;
+ bufq->pop_idx = 0;
+}
+
+static void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq)
+{
+ int count = bufq->count;
+
+ if (!count)
+ return;
+
+ while (count--) {
+ struct mdp3_img_data *data = &bufq->img_data[bufq->pop_idx];
+ bufq->pop_idx = (bufq->pop_idx + 1) % MDP3_MAX_BUF_QUEUE;
+ mdp3_put_img(data);
+ }
+ bufq->count = 0;
+ bufq->push_idx = 0;
+ bufq->pop_idx = 0;
+}
+
+static int mdp3_bufq_push(struct mdp3_buffer_queue *bufq,
+ struct mdp3_img_data *data)
+{
+ if (bufq->count >= MDP3_MAX_BUF_QUEUE) {
+ pr_err("bufq full\n");
+ return -EPERM;
+ }
+
+ bufq->img_data[bufq->push_idx] = *data;
+ bufq->push_idx = (bufq->push_idx + 1) % MDP3_MAX_BUF_QUEUE;
+ bufq->count++;
+ return 0;
+}
+
+static struct mdp3_img_data *mdp3_bufq_pop(struct mdp3_buffer_queue *bufq)
+{
+ struct mdp3_img_data *data;
+ if (bufq->count == 0)
+ return NULL;
+
+ data = &bufq->img_data[bufq->pop_idx];
+ bufq->count--;
+ bufq->pop_idx = (bufq->pop_idx + 1) % MDP3_MAX_BUF_QUEUE;
+ return data;
+}
+
+static int mdp3_bufq_count(struct mdp3_buffer_queue *bufq)
+{
+ return bufq->count;
+}
+
void vsync_notify_handler(void *arg)
{
struct mdp3_session_data *session = (struct mdp3_session_data *)arg;
+ spin_lock(&session->vsync_lock);
+ session->vsync_time = ktime_get();
complete(&session->vsync_comp);
+ spin_unlock(&session->vsync_lock);
}
static int mdp3_ctrl_vsync_enable(struct msm_fb_data_type *mfd, int enable)
{
struct mdp3_session_data *mdp3_session;
struct mdp3_vsync_notification vsync_client;
+ struct mdp3_vsync_notification *arg = NULL;
+ unsigned long flag;
+ pr_debug("mdp3_ctrl_vsync_enable =%d\n", enable);
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma ||
!mdp3_session->intf)
return -ENODEV;
- vsync_client.handler = vsync_notify_handler;
- vsync_client.arg = mdp3_session;
-
- mutex_lock(&mdp3_session->lock);
if (!mdp3_session->status) {
pr_debug("fb%d is not on yet", mfd->index);
- mutex_unlock(&mdp3_session->lock);
return -EINVAL;
}
+ if (enable) {
+ vsync_client.handler = vsync_notify_handler;
+ vsync_client.arg = mdp3_session;
+ arg = &vsync_client;
+ }
- mdp3_session->dma->vsync_enable(mdp3_session->dma, &vsync_client);
+ mutex_lock(&mdp3_session->lock);
+ mdp3_session->dma->vsync_enable(mdp3_session->dma, arg);
mutex_unlock(&mdp3_session->lock);
+ spin_lock_irqsave(&mdp3_session->vsync_lock, flag);
+ if (enable)
+ INIT_COMPLETION(mdp3_session->vsync_comp);
+ else
+ complete(&mdp3_session->vsync_comp);
+ spin_unlock_irqrestore(&mdp3_session->vsync_lock, flag);
return 0;
}
+static int mdp3_ctrl_async_blit_req(struct msm_fb_data_type *mfd,
+ void __user *p)
+{
+ struct mdp_async_blit_req_list req_list_header;
+ int rc, count;
+ void __user *p_req;
+
+ if (copy_from_user(&req_list_header, p, sizeof(req_list_header)))
+ return -EFAULT;
+ p_req = p + sizeof(req_list_header);
+ count = req_list_header.count;
+ if (count < 0 || count >= MAX_BLIT_REQ)
+ return -EINVAL;
+ rc = mdp3_ppp_parse_req(p_req, &req_list_header, 1);
+ if (!rc)
+ rc = copy_to_user(p, &req_list_header, sizeof(req_list_header));
+ return rc;
+}
+
+static int mdp3_ctrl_blit_req(struct msm_fb_data_type *mfd, void __user *p)
+{
+ struct mdp_async_blit_req_list req_list_header;
+ int rc, count;
+ void __user *p_req;
+
+ if (copy_from_user(&(req_list_header.count), p,
+ sizeof(struct mdp_blit_req_list)))
+ return -EFAULT;
+ p_req = p + sizeof(struct mdp_blit_req_list);
+ count = req_list_header.count;
+ if (count < 0 || count >= MAX_BLIT_REQ)
+ return -EINVAL;
+ req_list_header.sync.acq_fen_fd_cnt = 0;
+ rc = mdp3_ppp_parse_req(p_req, &req_list_header, 0);
+ return rc;
+}
+
static ssize_t mdp3_vsync_show_event(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -64,25 +174,21 @@
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdp3_session_data *mdp3_session = NULL;
u64 vsync_ticks;
- ktime_t vsync_time;
int rc;
+ unsigned long flag;
if (!mfd || !mfd->mdp.private1)
return 0;
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
- rc = wait_for_completion_interruptible_timeout(
- &mdp3_session->vsync_comp,
- msecs_to_jiffies(VSYNC_PERIOD * 5));
- if (rc <= 0) {
- pr_warn("vsync wait on fb%d interrupted (%d)\n",
- mfd->index, rc);
- return -EBUSY;
- }
+ rc = wait_for_completion_interruptible(&mdp3_session->vsync_comp);
+ if (rc < 0)
+ return rc;
- vsync_time = mdp3_session->dma->get_vsync_time(mdp3_session->dma);
- vsync_ticks = ktime_to_ns(vsync_time);
+ spin_lock_irqsave(&mdp3_session->vsync_lock, flag);
+ vsync_ticks = ktime_to_ns(mdp3_session->vsync_time);
+ spin_unlock_irqrestore(&mdp3_session->vsync_lock, flag);
pr_debug("fb%d vsync=%llu", mfd->index, vsync_ticks);
rc = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_ticks);
@@ -110,9 +216,9 @@
ab = panel_info->xres * panel_info->yres * 4;
ab *= panel_info->mipi.frame_rate;
ib = (ab * 3) / 2;
- rc = mdp3_bus_scale_set_quota(MDP3_BW_CLIENT_DMA_P, ab, ib);
+ rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib);
} else {
- rc = mdp3_bus_scale_set_quota(MDP3_BW_CLIENT_DMA_P, 0, 0);
+ rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, 0, 0);
}
return rc;
}
@@ -121,18 +227,11 @@
{
int rc = 0;
if (status) {
- struct mdss_panel_info *panel_info = mfd->panel_info;
- unsigned long core_clk;
- int vtotal;
- vtotal = panel_info->lcdc.v_back_porch +
- panel_info->lcdc.v_front_porch +
- panel_info->lcdc.v_pulse_width +
- panel_info->yres;
- core_clk = panel_info->xres * panel_info->yres;
- core_clk *= panel_info->mipi.frame_rate;
- core_clk = (core_clk / panel_info->yres) * vtotal;
- mdp3_clk_set_rate(MDP3_CLK_CORE, core_clk);
- mdp3_clk_set_rate(MDP3_CLK_VSYNC, MDP_VSYNC_CLK_RATE);
+
+ mdp3_clk_set_rate(MDP3_CLK_CORE, MDP_CORE_CLK_RATE,
+ MDP3_CLIENT_DMA_P);
+ mdp3_clk_set_rate(MDP3_CLK_VSYNC, MDP_VSYNC_CLK_RATE,
+ MDP3_CLIENT_DMA_P);
rc = mdp3_clk_enable(true);
if (rc)
@@ -299,10 +398,18 @@
goto on_error;
}
+ rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P);
+ if (rc) {
+ pr_err("fail to attach MDP DMA SMMU\n");
+ goto on_error;
+ }
+
/* request bus bandwidth before DSI DMA traffic */
rc = mdp3_ctrl_res_req_bus(mfd, 1);
- if (rc)
+ if (rc) {
pr_err("fail to request bus resource\n");
+ goto on_error;
+ }
panel = mdp3_session->panel;
if (panel->event_handler)
@@ -323,12 +430,15 @@
goto on_error;
}
+ rc = mdp3_ppp_init();
+ if (rc) {
+ pr_err("ppp init failed\n");
+ goto on_error;
+ }
+
rc = mdp3_ctrl_intf_init(mfd, mdp3_session->intf);
if (rc) {
pr_err("display interface init failed\n");
-
-
-
goto on_error;
}
@@ -388,13 +498,161 @@
rc = mdp3_ctrl_res_req_clk(mfd, 0);
if (rc)
pr_err("mdp clock resource release failed\n");
+
+ rc = mdp3_iommu_disable(MDP3_CLIENT_DMA_P);
+ if (rc)
+ pr_err("fail to dettach MDP DMA SMMU\n");
+
off_error:
mdp3_session->status = 0;
+ mutex_unlock(&mdp3_session->lock);
+ if (mdp3_session->overlay.id != MSMFB_NEW_REQUEST)
+ mdp3_overlay_unset(mfd, mdp3_session->overlay.id);
+ return 0;
+}
+
+static int mdp3_overlay_get(struct msm_fb_data_type *mfd,
+ struct mdp_overlay *req)
+{
+ int rc = 0;
+ struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
+
+ mutex_lock(&mdp3_session->lock);
+
+ if (mdp3_session->overlay.id == req->id)
+ *req = mdp3_session->overlay;
+ else
+ rc = -EINVAL;
mutex_unlock(&mdp3_session->lock);
+
+ return rc;
+}
+
+static int mdp3_overlay_set(struct msm_fb_data_type *mfd,
+ struct mdp_overlay *req)
+{
+ int rc = 0;
+ struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
+
+ mutex_lock(&mdp3_session->lock);
+
+ if (mdp3_session->overlay.id == req->id) {
+ mdp3_session->overlay = *req;
+ if (req->id == MSMFB_NEW_REQUEST) {
+ mdp3_session->overlay.id = 1;
+ req->id = 1;
+ }
+ } else {
+ rc = -EINVAL;
+ }
+ mutex_unlock(&mdp3_session->lock);
+
+ return rc;
+}
+
+static int mdp3_overlay_unset(struct msm_fb_data_type *mfd, int ndx)
+{
+ int rc = 0;
+ struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
+
+ mdp3_ctrl_pan_display(mfd);
+
+ mutex_lock(&mdp3_session->lock);
+
+ if (mdp3_session->overlay.id == ndx && ndx == 1) {
+ mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
+ mdp3_bufq_deinit(&mdp3_session->bufq_in);
+ mdp3_bufq_deinit(&mdp3_session->bufq_out);
+ } else {
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&mdp3_session->lock);
+
+ return rc;
+}
+
+static int mdp3_overlay_queue_buffer(struct msm_fb_data_type *mfd,
+ struct msmfb_overlay_data *req)
+{
+ int rc;
+ struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
+ struct msmfb_data *img = &req->data;
+ struct mdp3_img_data data;
+
+ rc = mdp3_get_img(img, &data);
+ if (rc) {
+ pr_err("fail to get overlay buffer\n");
+ return rc;
+ }
+
+ rc = mdp3_bufq_push(&mdp3_session->bufq_in, &data);
+ if (rc) {
+ pr_err("fail to queue the overlay buffer, buffer drop\n");
+ mdp3_put_img(&data);
+ return rc;
+ }
return 0;
}
+static int mdp3_overlay_play(struct msm_fb_data_type *mfd,
+ struct msmfb_overlay_data *req)
+{
+ struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
+ int rc = 0;
+
+ pr_debug("mdp3_overlay_play req id=%x mem_id=%d\n",
+ req->id, req->data.memory_id);
+
+ mutex_lock(&mdp3_session->lock);
+
+ if (mfd->panel_power_on)
+ rc = mdp3_overlay_queue_buffer(mfd, req);
+ else
+ rc = -EPERM;
+
+ mutex_unlock(&mdp3_session->lock);
+
+ return rc;
+}
+
+static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd)
+{
+ struct mdp3_session_data *mdp3_session;
+ struct mdp3_img_data *data;
+ int rc = 0;
+
+ if (!mfd || !mfd->mdp.private1)
+ return -EINVAL;
+
+ mdp3_session = mfd->mdp.private1;
+ if (!mdp3_session || !mdp3_session->dma)
+ return -EINVAL;
+
+ if (!mdp3_session->status) {
+ pr_err("%s, display off!\n", __func__);
+ return -EPERM;
+ }
+
+ mutex_lock(&mdp3_session->lock);
+
+ data = mdp3_bufq_pop(&mdp3_session->bufq_in);
+ if (data) {
+ mdp3_session->dma->update(mdp3_session->dma,
+ (void *)data->addr);
+ mdp3_bufq_push(&mdp3_session->bufq_out, data);
+ }
+
+ if (mdp3_bufq_count(&mdp3_session->bufq_out) > 1) {
+ data = mdp3_bufq_pop(&mdp3_session->bufq_out);
+ mdp3_put_img(data);
+ }
+
+ mutex_unlock(&mdp3_session->lock);
+ return rc;
+}
+
static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd)
{
struct fb_info *fbi;
@@ -434,11 +692,37 @@
mutex_unlock(&mdp3_session->lock);
}
+static int mdp3_get_metadata(struct msm_fb_data_type *mfd,
+ struct msmfb_metadata *metadata)
+{
+ int ret = 0;
+ switch (metadata->op) {
+ case metadata_op_frame_rate:
+ metadata->data.panel_frame_rate =
+ mfd->panel_info->mipi.frame_rate;
+ break;
+ case metadata_op_get_caps:
+ metadata->data.caps.mdp_rev = 304;
+ metadata->data.caps.rgb_pipes = 0;
+ metadata->data.caps.vig_pipes = 0;
+ metadata->data.caps.dma_pipes = 1;
+ break;
+ default:
+ pr_warn("Unsupported request to MDP META IOCTL.\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
static int mdp3_ctrl_ioctl_handler(struct msm_fb_data_type *mfd,
u32 cmd, void __user *argp)
{
int rc = -EINVAL;
struct mdp3_session_data *mdp3_session;
+ struct msmfb_metadata metadata;
+ struct mdp_overlay req;
+ struct msmfb_overlay_data ov_data;
int val;
pr_debug("mdp3_ctrl_ioctl_handler\n");
@@ -457,17 +741,61 @@
case MSMFB_OVERLAY_VSYNC_CTRL:
if (!copy_from_user(&val, argp, sizeof(val))) {
rc = mdp3_ctrl_vsync_enable(mfd, val);
- if (!val)
- init_completion(&mdp3_session->vsync_comp);
} else {
pr_err("MSMFB_OVERLAY_VSYNC_CTRL failed\n");
rc = -EFAULT;
}
break;
+ case MSMFB_ASYNC_BLIT:
+ rc = mdp3_ctrl_async_blit_req(mfd, argp);
+ break;
+ case MSMFB_BLIT:
+ rc = mdp3_ctrl_blit_req(mfd, argp);
+ break;
+ case MSMFB_METADATA_GET:
+ rc = copy_from_user(&metadata, argp, sizeof(metadata));
+ if (rc)
+ return rc;
+ rc = mdp3_get_metadata(mfd, &metadata);
+ if (!rc)
+ rc = copy_to_user(argp, &metadata, sizeof(metadata));
+ break;
+ case MSMFB_OVERLAY_GET:
+ rc = copy_from_user(&req, argp, sizeof(req));
+ if (!rc) {
+ rc = mdp3_overlay_get(mfd, &req);
+
+ if (!IS_ERR_VALUE(rc))
+ rc = copy_to_user(argp, &req, sizeof(req));
+ }
+ if (rc)
+ pr_err("OVERLAY_GET failed (%d)\n", rc);
+ break;
+ case MSMFB_OVERLAY_SET:
+ rc = copy_from_user(&req, argp, sizeof(req));
+ if (!rc) {
+ rc = mdp3_overlay_set(mfd, &req);
+
+ if (!IS_ERR_VALUE(rc))
+ rc = copy_to_user(argp, &req, sizeof(req));
+ }
+ if (rc)
+ pr_err("OVERLAY_SET failed (%d)\n", rc);
+ break;
+ case MSMFB_OVERLAY_UNSET:
+ if (!IS_ERR_VALUE(copy_from_user(&val, argp, sizeof(val))))
+ rc = mdp3_overlay_unset(mfd, val);
+ break;
+ case MSMFB_OVERLAY_PLAY:
+ rc = copy_from_user(&ov_data, argp, sizeof(ov_data));
+ if (!rc)
+ rc = mdp3_overlay_play(mfd, &ov_data);
+ if (rc)
+ pr_err("OVERLAY_PLAY failed (%d)\n", rc);
+ break;
default:
break;
}
-
return rc;
}
@@ -486,7 +814,7 @@
mdp3_interface->cursor_update = NULL;
mdp3_interface->dma_fnc = mdp3_ctrl_pan_display;
mdp3_interface->ioctl_handler = mdp3_ctrl_ioctl_handler;
- mdp3_interface->kickoff_fnc = NULL;
+ mdp3_interface->kickoff_fnc = mdp3_ctrl_display_commit_kickoff;
mdp3_session = kmalloc(sizeof(struct mdp3_session_data), GFP_KERNEL);
if (!mdp3_session) {
@@ -496,6 +824,7 @@
memset(mdp3_session, 0, sizeof(struct mdp3_session_data));
mutex_init(&mdp3_session->lock);
init_completion(&mdp3_session->vsync_comp);
+ spin_lock_init(&mdp3_session->vsync_lock);
mdp3_session->dma = mdp3_get_dma_pipe(MDP3_DMA_CAP_ALL);
if (!mdp3_session->dma) {
rc = -ENODEV;
@@ -511,6 +840,9 @@
mdp3_session->panel = dev_get_platdata(&mfd->pdev->dev);
mdp3_session->status = 0;
+ mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
+ mdp3_bufq_init(&mdp3_session->bufq_in);
+ mdp3_bufq_init(&mdp3_session->bufq_out);
mfd->mdp.private1 = mdp3_session;
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.h b/drivers/video/msm/mdss/mdp3_ctrl.h
index d42ece7..fb3bd36 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.h
+++ b/drivers/video/msm/mdss/mdp3_ctrl.h
@@ -18,10 +18,20 @@
#include <linux/mutex.h>
#include <linux/completion.h>
+#include "mdp3.h"
#include "mdp3_dma.h"
#include "mdss_fb.h"
#include "mdss_panel.h"
+#define MDP3_MAX_BUF_QUEUE 8
+
+struct mdp3_buffer_queue {
+ struct mdp3_img_data img_data[MDP3_MAX_BUF_QUEUE];
+ int count;
+ int push_idx;
+ int pop_idx;
+};
+
struct mdp3_session_data {
struct mutex lock;
int status;
@@ -29,7 +39,12 @@
struct mdss_panel_data *panel;
struct mdp3_intf *intf;
struct msm_fb_data_type *mfd;
+ ktime_t vsync_time;
+ spinlock_t vsync_lock;
struct completion vsync_comp;
+ struct mdp_overlay overlay;
+ struct mdp3_buffer_queue bufq_in;
+ struct mdp3_buffer_queue bufq_out;
};
int mdp3_ctrl_init(struct msm_fb_data_type *mfd);
diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c
index 69e3d7e..94ad9be 100644
--- a/drivers/video/msm/mdss/mdp3_dma.c
+++ b/drivers/video/msm/mdss/mdp3_dma.c
@@ -20,17 +20,6 @@
#define DMA_STOP_POLL_SLEEP_US 1000
#define DMA_STOP_POLL_TIMEOUT_US 16000
-static ktime_t mdp3_get_vsync_time(struct mdp3_dma *dma)
-{
- unsigned long flag;
- ktime_t time;
-
- spin_lock_irqsave(&dma->dma_lock, flag);
- time = dma->vsync_time;
- spin_unlock_irqrestore(&dma->dma_lock, flag);
- return time;
-}
-
static void mdp3_vsync_intr_handler(int type, void *arg)
{
struct mdp3_dma *dma = (struct mdp3_dma *)arg;
@@ -39,15 +28,11 @@
pr_debug("mdp3_vsync_intr_handler\n");
spin_lock(&dma->dma_lock);
vsync_client = dma->vsync_client;
- if (!vsync_client.handler)
- dma->cb_type &= ~MDP3_DMA_CALLBACK_TYPE_VSYNC;
- dma->vsync_time = ktime_get();
complete(&dma->vsync_comp);
+ spin_unlock(&dma->dma_lock);
if (vsync_client.handler)
vsync_client.handler(vsync_client.arg);
- spin_unlock(&dma->dma_lock);
-
- if (!vsync_client.handler)
+ else
mdp3_irq_disable_nosync(type);
}
@@ -56,10 +41,6 @@
struct mdp3_dma *dma = (struct mdp3_dma *)arg;
pr_debug("mdp3_dma_done_intr_handler\n");
- spin_lock(&dma->dma_lock);
- dma->busy = false;
- dma->cb_type &= ~MDP3_DMA_CALLBACK_TYPE_DMA_DONE;
- spin_unlock(&dma->dma_lock);
complete(&dma->dma_comp);
mdp3_irq_disable_nosync(type);
}
@@ -67,19 +48,9 @@
void mdp3_dma_callback_enable(struct mdp3_dma *dma, int type)
{
int irq_bit;
- unsigned long flag;
pr_debug("mdp3_dma_callback_enable type=%d\n", type);
- spin_lock_irqsave(&dma->dma_lock, flag);
- if (dma->cb_type & type) {
- spin_unlock_irqrestore(&dma->dma_lock, flag);
- return;
- } else {
- dma->cb_type |= type;
- spin_unlock_irqrestore(&dma->dma_lock, flag);
- }
-
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) {
if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC)
@@ -105,19 +76,9 @@
void mdp3_dma_callback_disable(struct mdp3_dma *dma, int type)
{
int irq_bit;
- unsigned long flag;
pr_debug("mdp3_dma_callback_disable type=%d\n", type);
- spin_lock_irqsave(&dma->dma_lock, flag);
- if ((dma->cb_type & type) == 0) {
- spin_unlock_irqrestore(&dma->dma_lock, flag);
- return;
- } else {
- dma->cb_type &= ~type;
- spin_unlock_irqrestore(&dma->dma_lock, flag);
- }
-
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) {
if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC)
@@ -186,7 +147,7 @@
updated = 1;
}
} else {
- if (!dma->vsync_client.handler) {
+ if (dma->vsync_client.handler) {
dma->vsync_client.handler = NULL;
dma->vsync_client.arg = NULL;
updated = 1;
@@ -231,7 +192,7 @@
* NOTE: MDP_DMA_P_FETCH_CFG: max_burst_size need to use value 4, not
* the default 16 for MDP hang issue workaround
*/
- MDP3_REG_WRITE(MDP3_REG_DMA_P_FETCH_CFG, 0x10);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_FETCH_CFG, 0x20);
MDP3_REG_WRITE(MDP3_REG_PRIMARY_RD_PTR_IRQ, 0x10);
dma->source_config = *source_config;
@@ -456,7 +417,6 @@
static int mdp3_dmap_update(struct mdp3_dma *dma, void *buf)
{
- int wait_for_dma_done = 0;
unsigned long flag;
int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC;
@@ -464,22 +424,15 @@
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
cb_type |= MDP3_DMA_CALLBACK_TYPE_DMA_DONE;
- spin_lock_irqsave(&dma->dma_lock, flag);
- if (dma->busy)
- wait_for_dma_done = 1;
- spin_unlock_irqrestore(&dma->dma_lock, flag);
-
- if (wait_for_dma_done)
- wait_for_completion_killable(&dma->dma_comp);
+ wait_for_completion_killable(&dma->dma_comp);
}
spin_lock_irqsave(&dma->dma_lock, flag);
MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_ADDR, (u32)buf);
dma->source_config.buf = buf;
- if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
+ if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD)
MDP3_REG_WRITE(MDP3_REG_DMA_P_START, 1);
- dma->busy = true;
- }
+
wmb();
init_completion(&dma->vsync_comp);
spin_unlock_irqrestore(&dma->dma_lock, flag);
@@ -493,28 +446,19 @@
static int mdp3_dmas_update(struct mdp3_dma *dma, void *buf)
{
- int wait_for_dma_done = 0;
unsigned long flag;
int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC;
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
cb_type |= MDP3_DMA_CALLBACK_TYPE_DMA_DONE;
- spin_lock_irqsave(&dma->dma_lock, flag);
- if (dma->busy)
- wait_for_dma_done = 1;
- spin_unlock_irqrestore(&dma->dma_lock, flag);
-
- if (wait_for_dma_done)
- wait_for_completion_killable(&dma->dma_comp);
+ wait_for_completion_killable(&dma->dma_comp);
}
spin_lock_irqsave(&dma->dma_lock, flag);
MDP3_REG_WRITE(MDP3_REG_DMA_S_IBUF_ADDR, (u32)buf);
dma->source_config.buf = buf;
- if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
+ if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD)
MDP3_REG_WRITE(MDP3_REG_DMA_S_START, 1);
- dma->busy = true;
- }
wmb();
init_completion(&dma->vsync_comp);
spin_unlock_irqrestore(&dma->dma_lock, flag);
@@ -626,7 +570,6 @@
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
cb_type |= MDP3_DMA_CALLBACK_TYPE_DMA_DONE;
MDP3_REG_WRITE(dma_start_offset, 1);
- dma->busy = true;
}
intf->start(intf);
@@ -666,7 +609,6 @@
mdp3_dma_callback_disable(dma, MDP3_DMA_CALLBACK_TYPE_VSYNC |
MDP3_DMA_CALLBACK_TYPE_DMA_DONE);
- dma->busy = false;
return ret;
}
@@ -679,8 +621,6 @@
pr_debug("mdp3_dma_init\n");
switch (dma->dma_sel) {
case MDP3_DMA_P:
- dma->busy = 0;
-
ret = mdp3_dmap_config(dma, source_config, output_config);
if (ret < 0)
return ret;
@@ -696,12 +636,10 @@
dma->histo_intr_enable = mdp3_dmap_histo_intr_enable;
dma->histo_intr_clear = mdp3_dmap_histo_intr_clear;
dma->vsync_enable = mdp3_dma_vsync_enable;
- dma->get_vsync_time = mdp3_get_vsync_time;
dma->start = mdp3_dma_start;
dma->stop = mdp3_dma_stop;
break;
case MDP3_DMA_S:
- dma->busy = 0;
ret = mdp3_dmas_config(dma, source_config, output_config);
if (ret < 0)
return ret;
@@ -717,7 +655,6 @@
dma->histo_intr_enable = NULL;
dma->histo_intr_clear = NULL;
dma->vsync_enable = mdp3_dma_vsync_enable;
- dma->get_vsync_time = mdp3_get_vsync_time;
dma->start = mdp3_dma_start;
dma->stop = mdp3_dma_stop;
break;
@@ -730,7 +667,6 @@
spin_lock_init(&dma->dma_lock);
init_completion(&dma->vsync_comp);
init_completion(&dma->dma_comp);
- dma->cb_type = 0;
dma->vsync_client.handler = NULL;
dma->vsync_client.arg = NULL;
@@ -824,6 +760,7 @@
temp |= BIT(2);
MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_CTL_POLARITY, temp);
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_UNDERFLOW_CTL, 0x800000ff);
return 0;
}
diff --git a/drivers/video/msm/mdss/mdp3_dma.h b/drivers/video/msm/mdss/mdp3_dma.h
index 2fb8427..f86bea9 100644
--- a/drivers/video/msm/mdss/mdp3_dma.h
+++ b/drivers/video/msm/mdss/mdp3_dma.h
@@ -226,14 +226,11 @@
u32 capability;
int in_use;
int available;
- int busy;
spinlock_t dma_lock;
struct completion vsync_comp;
struct completion dma_comp;
- ktime_t vsync_time;
struct mdp3_vsync_notification vsync_client;
- u32 cb_type;
struct mdp3_dma_output_config output_config;
struct mdp3_dma_source source_config;
@@ -275,9 +272,6 @@
void (*vsync_enable)(struct mdp3_dma *dma,
struct mdp3_vsync_notification *vsync_client);
-
- ktime_t (*get_vsync_time)(struct mdp3_dma *dma);
-
};
struct mdp3_video_intf_cfg {
diff --git a/drivers/video/msm/mdss/mdp3_hwio.h b/drivers/video/msm/mdss/mdp3_hwio.h
index 2763f46..1c5bf46 100644
--- a/drivers/video/msm/mdss/mdp3_hwio.h
+++ b/drivers/video/msm/mdss/mdp3_hwio.h
@@ -148,6 +148,70 @@
#define MDP3_REG_DSI_CMD_MODE_ID_MAP 0xF1000
#define MDP3_REG_DSI_CMD_MODE_TRIGGER_EN 0xF1004
+#define MDP3_PPP_CSC_PFMVn(n) (0x40400 + (4 * (n)))
+#define MDP3_PPP_CSC_PRMVn(n) (0x40440 + (4 * (n)))
+#define MDP3_PPP_CSC_PBVn(n) (0x40500 + (4 * (n)))
+#define MDP3_PPP_CSC_PLVn(n) (0x40580 + (4 * (n)))
+
+#define MDP3_PPP_CSC_SFMVn(n) (0x40480 + (4 * (n)))
+#define MDP3_PPP_CSC_SRMVn(n) (0x404C0 + (4 * (n)))
+#define MDP3_PPP_CSC_SBVn(n) (0x40540 + (4 * (n)))
+#define MDP3_PPP_CSC_SLVn(n) (0x405C0 + (4 * (n)))
+
+#define MDP3_PPP_SCALE_PHASEX_INIT 0x1013C
+#define MDP3_PPP_SCALE_PHASEY_INIT 0x10140
+#define MDP3_PPP_SCALE_PHASEX_STEP 0x10144
+#define MDP3_PPP_SCALE_PHASEY_STEP 0x10148
+
+#define MDP3_PPP_OP_MODE 0x10138
+
+#define MDP3_PPP_PRE_LUT 0x40800
+#define MDP3_PPP_POST_LUT 0x40C00
+#define MDP3_PPP_LUTn(n) ((4 * (n)))
+
+#define MDP3_PPP_BG_EDGE_REP 0x101BC
+#define MDP3_PPP_SRC_EDGE_REP 0x101B8
+
+#define MDP3_PPP_STRIDE_MASK 0x3FFF
+#define MDP3_PPP_STRIDE1_OFFSET 16
+
+#define MDP3_PPP_XY_MASK 0x0FFF
+#define MDP3_PPP_XY_OFFSET 16
+
+#define MDP3_PPP_SRC_SIZE 0x10108
+#define MDP3_PPP_SRCP0_ADDR 0x1010C
+#define MDP3_PPP_SRCP1_ADDR 0x10110
+#define MDP3_PPP_SRCP3_ADDR 0x10118
+#define MDP3_PPP_SRC_YSTRIDE1_ADDR 0x1011C
+#define MDP3_PPP_SRC_YSTRIDE2_ADDR 0x10120
+#define MDP3_PPP_SRC_FORMAT 0x10124
+#define MDP3_PPP_SRC_UNPACK_PATTERN1 0x10128
+#define MDP3_PPP_SRC_UNPACK_PATTERN2 0x1012C
+
+#define MDP3_PPP_OUT_FORMAT 0x10150
+#define MDP3_PPP_OUT_PACK_PATTERN1 0x10154
+#define MDP3_PPP_OUT_PACK_PATTERN2 0x10158
+#define MDP3_PPP_OUT_SIZE 0x10164
+#define MDP3_PPP_OUTP0_ADDR 0x10168
+#define MDP3_PPP_OUTP1_ADDR 0x1016C
+#define MDP3_PPP_OUTP3_ADDR 0x10174
+#define MDP3_PPP_OUT_YSTRIDE1_ADDR 0x10178
+#define MDP3_PPP_OUT_YSTRIDE2_ADDR 0x1017C
+#define MDP3_PPP_OUT_XY 0x1019C
+
+#define MDP3_PPP_BGP0_ADDR 0x101C0
+#define MDP3_PPP_BGP1_ADDR 0x101C4
+#define MDP3_PPP_BGP3_ADDR 0x101C8
+#define MDP3_PPP_BG_YSTRIDE1_ADDR 0x101CC
+#define MDP3_PPP_BG_YSTRIDE2_ADDR 0x101D0
+#define MDP3_PPP_BG_FORMAT 0x101D4
+#define MDP3_PPP_BG_UNPACK_PATTERN1 0x101D8
+#define MDP3_PPP_BG_UNPACK_PATTERN2 0x101DC
+
+#define MDP3_PPP_BLEND_PARAM 0x1014C
+
+#define MDP3_PPP_BLEND_BG_ALPHA_SEL 0x70010
+
/*interrupt mask*/
#define MDP3_INTR_DP0_ROI_DONE_BIT BIT(0)
@@ -212,5 +276,6 @@
#define MDP3_DMA_P_HIST_INTR_RESET_DONE_BIT BIT(0)
#define MDP3_DMA_P_HIST_INTR_HIST_DONE_BIT BIT(1)
+#define MDP3_PPP_DONE MDP3_INTR_DP0_ROI_DONE
#endif /* MDP3_HWIO_H */
diff --git a/drivers/video/msm/mdss/mdp3_ppp.c b/drivers/video/msm/mdss/mdp3_ppp.c
new file mode 100644
index 0000000..af316d3
--- /dev/null
+++ b/drivers/video/msm/mdss/mdp3_ppp.c
@@ -0,0 +1,1065 @@
+/* Copyright (c) 2007, 2013 The Linux Foundation. All rights reserved.
+ * Copyright (C) 2007 Google Incorporated
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/file.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/sync.h>
+#include <linux/sw_sync.h>
+#include "linux/proc_fs.h"
+
+#include "mdss_fb.h"
+#include "mdp3_ppp.h"
+#include "mdp3_hwio.h"
+#include "mdp3.h"
+
+#define MDP_IS_IMGTYPE_BAD(x) ((x) >= MDP_IMGTYPE_LIMIT)
+#define MDP_BLIT_CLK_RATE 200000000
+#define MDP_PPP_MAX_BPP 4
+#define MDP_PPP_DYNAMIC_FACTOR 3
+#define MDP_PPP_MAX_READ_WRITE 3
+
+static const bool valid_fmt[MDP_IMGTYPE_LIMIT] = {
+ [MDP_RGB_565] = true,
+ [MDP_BGR_565] = true,
+ [MDP_RGB_888] = true,
+ [MDP_BGR_888] = true,
+ [MDP_BGRA_8888] = true,
+ [MDP_RGBA_8888] = true,
+ [MDP_ARGB_8888] = true,
+ [MDP_XRGB_8888] = true,
+ [MDP_RGBX_8888] = true,
+ [MDP_Y_CRCB_H2V2] = true,
+ [MDP_Y_CBCR_H2V2] = true,
+ [MDP_Y_CBCR_H2V2_ADRENO] = true,
+ [MDP_YCRYCB_H2V1] = true,
+ [MDP_Y_CBCR_H2V1] = true,
+ [MDP_Y_CRCB_H2V1] = true,
+};
+
+#define MAX_LIST_WINDOW 16
+#define MDP3_PPP_MAX_LIST_REQ 8
+
+struct blit_req_list {
+ int count;
+ struct mdp_blit_req req_list[MAX_LIST_WINDOW];
+ struct mdp3_img_data src_data[MAX_LIST_WINDOW];
+ struct mdp3_img_data dst_data[MAX_LIST_WINDOW];
+ struct sync_fence *acq_fen[MDP_MAX_FENCE_FD];
+ u32 acq_fen_cnt;
+ int cur_rel_fen_fd;
+ struct sync_pt *cur_rel_sync_pt;
+ struct sync_fence *cur_rel_fence;
+ struct sync_fence *last_rel_fence;
+};
+
+struct blit_req_queue {
+ struct blit_req_list req[MDP3_PPP_MAX_LIST_REQ];
+ int count;
+ int push_idx;
+ int pop_idx;
+};
+
+struct ppp_status {
+ int busy;
+ bool wait_for_pop;
+ spinlock_t ppp_lock;
+ struct completion ppp_comp;
+ struct completion pop_q_comp;
+ struct mutex req_mutex; /* Protect request queue */
+ struct mutex config_ppp_mutex; /* Only one client configure register */
+ struct msm_fb_data_type *mfd;
+
+ struct work_struct blit_work;
+ struct blit_req_queue req_q;
+
+ struct sw_sync_timeline *timeline;
+ int timeline_value;
+
+};
+
+static struct ppp_status *ppp_stat;
+
+
+int ppp_get_bpp(uint32_t format, uint32_t fb_format)
+{
+ int bpp = -EINVAL;
+ if (format == MDP_FB_FORMAT)
+ format = fb_format;
+
+ bpp = ppp_bpp(format);
+ if (bpp <= 0)
+ pr_err("%s incorrect format %d\n", __func__, format);
+ return bpp;
+}
+
+int mdp3_ppp_get_img(struct mdp_img *img, struct mdp_blit_req *req,
+ struct mdp3_img_data *data)
+{
+ struct msmfb_data fb_data;
+ fb_data.flags = img->priv;
+ fb_data.memory_id = img->memory_id;
+ fb_data.offset = 0;
+
+ return mdp3_get_img(&fb_data, data);
+}
+
+/* Check format */
+int mdp3_ppp_verify_fmt(struct mdp_blit_req *req)
+{
+ if (MDP_IS_IMGTYPE_BAD(req->src.format) ||
+ MDP_IS_IMGTYPE_BAD(req->dst.format)) {
+ pr_err("%s: Color format out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!valid_fmt[req->src.format] ||
+ !valid_fmt[req->dst.format]) {
+ pr_err("%s: Color format not supported\n", __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* Check resolution */
+int mdp3_ppp_verify_res(struct mdp_blit_req *req)
+{
+ if ((req->src.width == 0) || (req->src.height == 0) ||
+ (req->src_rect.w == 0) || (req->src_rect.h == 0) ||
+ (req->dst.width == 0) || (req->dst.height == 0) ||
+ (req->dst_rect.w == 0) || (req->dst_rect.h == 0)) {
+ pr_err("%s: Height/width can't be 0\n", __func__);
+ return -EINVAL;
+ }
+
+ if (((req->src_rect.x + req->src_rect.w) > req->src.width) ||
+ ((req->src_rect.y + req->src_rect.h) > req->src.height)) {
+ pr_err("%s: src roi larger than boundary\n", __func__);
+ return -EINVAL;
+ }
+
+ if (((req->dst_rect.x + req->dst_rect.w) > req->dst.width) ||
+ ((req->dst_rect.y + req->dst_rect.h) > req->dst.height)) {
+ pr_err("%s: dst roi larger than boundary\n", __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* scaling range check */
+int mdp3_ppp_verify_scale(struct mdp_blit_req *req)
+{
+ u32 src_width, src_height, dst_width, dst_height;
+
+ src_width = req->src_rect.w;
+ src_height = req->src_rect.h;
+
+ if (req->flags & MDP_ROT_90) {
+ dst_width = req->dst_rect.h;
+ dst_height = req->dst_rect.w;
+ } else {
+ dst_width = req->dst_rect.w;
+ dst_height = req->dst_rect.h;
+ }
+
+ switch (req->dst.format) {
+ case MDP_Y_CRCB_H2V2:
+ case MDP_Y_CBCR_H2V2:
+ src_width = (src_width / 2) * 2;
+ src_height = (src_height / 2) * 2;
+ dst_width = (dst_width / 2) * 2;
+ dst_height = (dst_height / 2) * 2;
+ break;
+
+ case MDP_Y_CRCB_H2V1:
+ case MDP_Y_CBCR_H2V1:
+ case MDP_YCRYCB_H2V1:
+ src_width = (src_width / 2) * 2;
+ dst_width = (dst_width / 2) * 2;
+ break;
+
+ default:
+ break;
+ }
+
+ if (((MDP_SCALE_Q_FACTOR * dst_width) / src_width >
+ MDP_MAX_X_SCALE_FACTOR)
+ || ((MDP_SCALE_Q_FACTOR * dst_width) / src_width <
+ MDP_MIN_X_SCALE_FACTOR)) {
+ pr_err("%s: x req scale factor beyond capability\n", __func__);
+ return -EINVAL;
+ }
+
+ if (((MDP_SCALE_Q_FACTOR * dst_height) / src_height >
+ MDP_MAX_Y_SCALE_FACTOR)
+ || ((MDP_SCALE_Q_FACTOR * dst_height) / src_height <
+ MDP_MIN_Y_SCALE_FACTOR)) {
+ pr_err("%s: y req scale factor beyond capability\n", __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* operation check */
+int mdp3_ppp_verify_op(struct mdp_blit_req *req)
+{
+ if (req->flags & MDP_DEINTERLACE) {
+ pr_err("\n%s(): deinterlace not supported", __func__);
+ return -EINVAL;
+ }
+
+ if (req->flags & MDP_SHARPENING) {
+ pr_err("\n%s(): sharpening not supported", __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int mdp3_ppp_verify_req(struct mdp_blit_req *req)
+{
+ int rc;
+
+ if (req == NULL) {
+ pr_err("%s: req == null\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = mdp3_ppp_verify_fmt(req);
+ rc |= mdp3_ppp_verify_res(req);
+ rc |= mdp3_ppp_verify_scale(req);
+ rc |= mdp3_ppp_verify_op(req);
+
+ return rc;
+}
+
+int mdp3_ppp_pipe_wait(void)
+{
+ int ret = 1;
+ int wait;
+ unsigned long flag;
+
+ /*
+ * wait 5 secs for operation to complete before declaring
+ * the MDP hung
+ */
+ spin_lock_irqsave(&ppp_stat->ppp_lock, flag);
+ wait = ppp_stat->busy;
+ spin_unlock_irqrestore(&ppp_stat->ppp_lock, flag);
+
+ if (wait) {
+ ret = wait_for_completion_interruptible_timeout(
+ &ppp_stat->ppp_comp, 5 * HZ);
+ if (!ret)
+ pr_err("%s: Timed out waiting for the MDP.\n",
+ __func__);
+ }
+
+ return ret;
+}
+
+uint32_t mdp3_calc_tpval(struct ppp_img_desc *img, uint32_t old_tp)
+{
+ uint32_t tpVal;
+ uint8_t plane_tp;
+
+ tpVal = 0;
+ if ((img->color_fmt == MDP_RGB_565)
+ || (img->color_fmt == MDP_BGR_565)) {
+ /* transparent color conversion into 24 bpp */
+ plane_tp = (uint8_t) ((old_tp & 0xF800) >> 11);
+ tpVal |= ((plane_tp << 3) | ((plane_tp & 0x1C) >> 2)) << 16;
+ plane_tp = (uint8_t) (old_tp & 0x1F);
+ tpVal |= ((plane_tp << 3) | ((plane_tp & 0x1C) >> 2)) << 8;
+
+ plane_tp = (uint8_t) ((old_tp & 0x7E0) >> 5);
+ tpVal |= ((plane_tp << 2) | ((plane_tp & 0x30) >> 4));
+ } else {
+ /* 24bit RGB to RBG conversion */
+ tpVal = (old_tp & 0xFF00) >> 8;
+ tpVal |= (old_tp & 0xFF) << 8;
+ tpVal |= (old_tp & 0xFF0000);
+ }
+
+ return tpVal;
+}
+
+static void mdp3_ppp_intr_handler(int type, void *arg)
+{
+ spin_lock(&ppp_stat->ppp_lock);
+ ppp_stat->busy = false;
+ spin_unlock(&ppp_stat->ppp_lock);
+ complete(&ppp_stat->ppp_comp);
+ mdp3_irq_disable_nosync(type);
+}
+
+static int mdp3_ppp_callback_setup(void)
+{
+ int rc;
+ struct mdp3_intr_cb ppp_done_cb = {
+ .cb = mdp3_ppp_intr_handler,
+ .data = NULL,
+ };
+
+ rc = mdp3_set_intr_callback(MDP3_PPP_DONE, &ppp_done_cb);
+ return rc;
+}
+
+void mdp3_ppp_kickoff(void)
+{
+ unsigned long flag;
+ mdp3_irq_enable(MDP3_PPP_DONE);
+
+ init_completion(&ppp_stat->ppp_comp);
+
+ spin_lock_irqsave(&ppp_stat->ppp_lock, flag);
+ ppp_stat->busy = true;
+ spin_unlock_irqrestore(&ppp_stat->ppp_lock, flag);
+ ppp_enable();
+
+ mdp3_ppp_pipe_wait();
+}
+
+int mdp3_ppp_turnon(struct msm_fb_data_type *mfd, int on_off)
+{
+ struct mdss_panel_info *panel_info = mfd->panel_info;
+ int ab = 0, ib = 0;
+ int rate = 0;
+
+ if (on_off) {
+ rate = MDP_BLIT_CLK_RATE;
+ ab = panel_info->xres * panel_info->yres *
+ panel_info->mipi.frame_rate *
+ MDP_PPP_MAX_BPP *
+ MDP_PPP_DYNAMIC_FACTOR *
+ MDP_PPP_MAX_READ_WRITE;
+ ib = (ab * 3) / 2;
+ }
+ mdp3_clk_set_rate(MDP3_CLK_CORE, rate, MDP3_CLIENT_PPP);
+ mdp3_clk_enable(on_off);
+ mdp3_bus_scale_set_quota(MDP3_CLIENT_PPP, ab, ib);
+ return 0;
+}
+
+void mdp3_start_ppp(struct ppp_blit_op *blit_op)
+{
+ /* Wait for the pipe to clear */
+ do { } while (mdp3_ppp_pipe_wait() <= 0);
+ config_ppp_op_mode(blit_op);
+ mdp3_ppp_kickoff();
+}
+
+static void mdp3_ppp_process_req(struct ppp_blit_op *blit_op,
+ struct mdp_blit_req *req, struct mdp3_img_data *src_data,
+ struct mdp3_img_data *dst_data)
+{
+ unsigned long srcp0_start, srcp0_len, dst_start, dst_len;
+ uint32_t dst_width, dst_height;
+
+ srcp0_start = (unsigned long) src_data->addr;
+ srcp0_len = (unsigned long) src_data->len;
+ dst_start = (unsigned long) dst_data->addr;
+ dst_len = (unsigned long) dst_data->len;
+
+ blit_op->dst.prop.width = req->dst.width;
+ blit_op->dst.prop.height = req->dst.height;
+
+ blit_op->dst.color_fmt = req->dst.format;
+ blit_op->dst.p0 = (void *) dst_start;
+ blit_op->dst.p0 += req->dst.offset;
+
+ blit_op->dst.roi.x = req->dst_rect.x;
+ blit_op->dst.roi.y = req->dst_rect.y;
+ blit_op->dst.roi.width = req->dst_rect.w;
+ blit_op->dst.roi.height = req->dst_rect.h;
+
+ blit_op->src.roi.x = req->src_rect.x;
+ blit_op->src.roi.y = req->src_rect.y;
+ blit_op->src.roi.width = req->src_rect.w;
+ blit_op->src.roi.height = req->src_rect.h;
+
+ blit_op->src.prop.width = req->src.width;
+ blit_op->src.color_fmt = req->src.format;
+
+
+ blit_op->src.p0 = (void *) (srcp0_start + req->src.offset);
+ if (blit_op->src.color_fmt == MDP_Y_CBCR_H2V2_ADRENO)
+ blit_op->src.p1 =
+ (void *) ((uint32_t) blit_op->src.p0 +
+ ALIGN((ALIGN(req->src.width, 32) *
+ ALIGN(req->src.height, 32)), 4096));
+ else
+ blit_op->src.p1 = (void *) ((uint32_t) blit_op->src.p0 +
+ req->src.width * req->src.height);
+
+ if (req->flags & MDP_IS_FG)
+ blit_op->mdp_op |= MDPOP_LAYER_IS_FG;
+
+ /* blending check */
+ if (req->transp_mask != MDP_TRANSP_NOP) {
+ blit_op->mdp_op |= MDPOP_TRANSP;
+ blit_op->blend.trans_color =
+ mdp3_calc_tpval(&blit_op->src, req->transp_mask);
+ } else {
+ blit_op->blend.trans_color = 0;
+ }
+
+ req->alpha &= 0xff;
+ if (req->alpha < MDP_ALPHA_NOP) {
+ blit_op->mdp_op |= MDPOP_ALPHAB;
+ blit_op->blend.const_alpha = req->alpha;
+ } else {
+ blit_op->blend.const_alpha = 0xff;
+ }
+
+ /* rotation check */
+ if (req->flags & MDP_FLIP_LR)
+ blit_op->mdp_op |= MDPOP_LR;
+ if (req->flags & MDP_FLIP_UD)
+ blit_op->mdp_op |= MDPOP_UD;
+ if (req->flags & MDP_ROT_90)
+ blit_op->mdp_op |= MDPOP_ROT90;
+ if (req->flags & MDP_DITHER)
+ blit_op->mdp_op |= MDPOP_DITHER;
+
+ if (req->flags & MDP_BLEND_FG_PREMULT)
+ blit_op->mdp_op |= MDPOP_FG_PM_ALPHA;
+
+ /* scale check */
+ if (req->flags & MDP_ROT_90) {
+ dst_width = req->dst_rect.h;
+ dst_height = req->dst_rect.w;
+ } else {
+ dst_width = req->dst_rect.w;
+ dst_height = req->dst_rect.h;
+ }
+
+ if ((blit_op->src.roi.width != dst_width) ||
+ (blit_op->src.roi.height != dst_height))
+ blit_op->mdp_op |= MDPOP_ASCALE;
+
+ if (req->flags & MDP_BLUR)
+ blit_op->mdp_op |= MDPOP_ASCALE | MDPOP_BLUR;
+}
+
+static void mdp3_ppp_tile_workaround(struct ppp_blit_op *blit_op,
+ struct mdp_blit_req *req)
+{
+ int dst_h, src_w, i;
+ uint32_t mdp_op = blit_op->mdp_op;
+
+ src_w = req->src_rect.w;
+ dst_h = blit_op->dst.roi.height;
+ /* bg tile fetching HW workaround */
+ for (i = 0; i < (req->dst_rect.h / 16); i++) {
+ /* this tile size */
+ blit_op->dst.roi.height = 16;
+ blit_op->src.roi.width =
+ (16 * req->src_rect.w) / req->dst_rect.h;
+
+ /* if it's out of scale range... */
+ if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
+ blit_op->src.roi.width) > MDP_MAX_X_SCALE_FACTOR)
+ blit_op->src.roi.width =
+ (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
+ MDP_MAX_X_SCALE_FACTOR;
+ else if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
+ blit_op->src.roi.width) < MDP_MIN_X_SCALE_FACTOR)
+ blit_op->src.roi.width =
+ (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
+ MDP_MIN_X_SCALE_FACTOR;
+
+ mdp3_start_ppp(blit_op);
+
+ /* next tile location */
+ blit_op->dst.roi.y += 16;
+ blit_op->src.roi.x += blit_op->src.roi.width;
+
+ /* this is for a remainder update */
+ dst_h -= 16;
+ src_w -= blit_op->src.roi.width;
+ /* restore mdp_op since MDPOP_ASCALE have been cleared */
+ blit_op->mdp_op = mdp_op;
+ }
+
+ if ((dst_h < 0) || (src_w < 0))
+ pr_err
+ ("msm_fb: mdp_blt_ex() unexpected result! line:%d\n",
+ __LINE__);
+
+ /* remainder update */
+ if ((dst_h > 0) && (src_w > 0)) {
+ u32 tmp_v;
+
+ blit_op->dst.roi.height = dst_h;
+ blit_op->src.roi.width = src_w;
+
+ if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
+ blit_op->src.roi.width) > MDP_MAX_X_SCALE_FACTOR) {
+ tmp_v =
+ (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
+ MDP_MAX_X_SCALE_FACTOR +
+ (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) %
+ MDP_MAX_X_SCALE_FACTOR ? 1 : 0;
+
+ /* move x location as roi width gets bigger */
+ blit_op->src.roi.x -= tmp_v - blit_op->src.roi.width;
+ blit_op->src.roi.width = tmp_v;
+ } else if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
+ blit_op->src.roi.width) < MDP_MIN_X_SCALE_FACTOR) {
+ tmp_v =
+ (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
+ MDP_MIN_X_SCALE_FACTOR +
+ (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) %
+ MDP_MIN_X_SCALE_FACTOR ? 1 : 0;
+
+ /*
+ * we don't move x location for continuity of
+ * source image
+ */
+ blit_op->src.roi.width = tmp_v;
+ }
+
+
+ mdp3_start_ppp(blit_op);
+ }
+}
+
+static int mdp3_ppp_blit(struct msm_fb_data_type *mfd,
+ struct mdp_blit_req *req, struct mdp3_img_data *src_data,
+ struct mdp3_img_data *dst_data)
+{
+ struct ppp_blit_op blit_op;
+
+ memset(&blit_op, 0, sizeof(blit_op));
+
+ if (req->dst.format == MDP_FB_FORMAT)
+ req->dst.format = mfd->fb_imgType;
+ if (req->src.format == MDP_FB_FORMAT)
+ req->src.format = mfd->fb_imgType;
+
+ if (mdp3_ppp_verify_req(req)) {
+ pr_err("%s: invalid image!\n", __func__);
+ return -EINVAL;
+ }
+
+ mdp3_ppp_process_req(&blit_op, req, src_data, dst_data);
+
+ if (((blit_op.mdp_op & (MDPOP_TRANSP | MDPOP_ALPHAB)) ||
+ (req->src.format == MDP_ARGB_8888) ||
+ (req->src.format == MDP_BGRA_8888) ||
+ (req->src.format == MDP_RGBA_8888)) &&
+ (blit_op.mdp_op & MDPOP_ROT90) && (req->dst_rect.w <= 16)) {
+ mdp3_ppp_tile_workaround(&blit_op, req);
+ } else {
+ mdp3_start_ppp(&blit_op);
+ }
+
+ return 0;
+}
+
+static int mdp3_ppp_blit_workaround(struct msm_fb_data_type *mfd,
+ struct mdp_blit_req *req, unsigned int remainder,
+ struct mdp3_img_data *src_data,
+ struct mdp3_img_data *dst_data)
+{
+ int ret;
+ struct mdp_blit_req splitreq;
+ int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1;
+ int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1;
+
+ /* make new request as provide by user */
+ splitreq = *req;
+
+ /* break dest roi at width*/
+ d_y_0 = d_y_1 = req->dst_rect.y;
+ d_h_0 = d_h_1 = req->dst_rect.h;
+ d_x_0 = req->dst_rect.x;
+
+ if (remainder == 14 || remainder == 6)
+ d_w_1 = req->dst_rect.w / 2;
+ else
+ d_w_1 = (req->dst_rect.w - 1) / 2 - 1;
+
+ d_w_0 = req->dst_rect.w - d_w_1;
+ d_x_1 = d_x_0 + d_w_0;
+ /* blit first region */
+ if (((splitreq.flags & 0x07) == 0x07) ||
+ ((splitreq.flags & 0x07) == 0x05) ||
+ ((splitreq.flags & 0x07) == 0x02) ||
+ ((splitreq.flags & 0x07) == 0x0)) {
+
+ if (splitreq.flags & MDP_ROT_90) {
+ s_x_0 = s_x_1 = req->src_rect.x;
+ s_w_0 = s_w_1 = req->src_rect.w;
+ s_y_0 = req->src_rect.y;
+ s_h_1 = (req->src_rect.h * d_w_1) /
+ req->dst_rect.w;
+ s_h_0 = req->src_rect.h - s_h_1;
+ s_y_1 = s_y_0 + s_h_0;
+ if (d_w_1 >= 8 * s_h_1) {
+ s_h_1++;
+ s_y_1--;
+ }
+ } else {
+ s_y_0 = s_y_1 = req->src_rect.y;
+ s_h_0 = s_h_1 = req->src_rect.h;
+ s_x_0 = req->src_rect.x;
+ s_w_1 = (req->src_rect.w * d_w_1) /
+ req->dst_rect.w;
+ s_w_0 = req->src_rect.w - s_w_1;
+ s_x_1 = s_x_0 + s_w_0;
+ if (d_w_1 >= 8 * s_w_1) {
+ s_w_1++;
+ s_x_1--;
+ }
+ }
+
+ splitreq.src_rect.h = s_h_0;
+ splitreq.src_rect.y = s_y_0;
+ splitreq.dst_rect.h = d_h_0;
+ splitreq.dst_rect.y = d_y_0;
+ splitreq.src_rect.x = s_x_0;
+ splitreq.src_rect.w = s_w_0;
+ splitreq.dst_rect.x = d_x_0;
+ splitreq.dst_rect.w = d_w_0;
+ } else {
+ if (splitreq.flags & MDP_ROT_90) {
+ s_x_0 = s_x_1 = req->src_rect.x;
+ s_w_0 = s_w_1 = req->src_rect.w;
+ s_y_0 = req->src_rect.y;
+ s_h_1 = (req->src_rect.h * d_w_0) /
+ req->dst_rect.w;
+ s_h_0 = req->src_rect.h - s_h_1;
+ s_y_1 = s_y_0 + s_h_0;
+ if (d_w_0 >= 8 * s_h_1) {
+ s_h_1++;
+ s_y_1--;
+ }
+ } else {
+ s_y_0 = s_y_1 = req->src_rect.y;
+ s_h_0 = s_h_1 = req->src_rect.h;
+ s_x_0 = req->src_rect.x;
+ s_w_1 = (req->src_rect.w * d_w_0) /
+ req->dst_rect.w;
+ s_w_0 = req->src_rect.w - s_w_1;
+ s_x_1 = s_x_0 + s_w_0;
+ if (d_w_0 >= 8 * s_w_1) {
+ s_w_1++;
+ s_x_1--;
+ }
+ }
+ splitreq.src_rect.h = s_h_0;
+ splitreq.src_rect.y = s_y_0;
+ splitreq.dst_rect.h = d_h_1;
+ splitreq.dst_rect.y = d_y_1;
+ splitreq.src_rect.x = s_x_0;
+ splitreq.src_rect.w = s_w_0;
+ splitreq.dst_rect.x = d_x_1;
+ splitreq.dst_rect.w = d_w_1;
+ }
+
+ /* No need to split in height */
+ ret = mdp3_ppp_blit(mfd, &splitreq, src_data, dst_data);
+
+ if (ret)
+ return ret;
+ /* blit second region */
+ if (((splitreq.flags & 0x07) == 0x07) ||
+ ((splitreq.flags & 0x07) == 0x05) ||
+ ((splitreq.flags & 0x07) == 0x02) ||
+ ((splitreq.flags & 0x07) == 0x0)) {
+ splitreq.src_rect.h = s_h_1;
+ splitreq.src_rect.y = s_y_1;
+ splitreq.dst_rect.h = d_h_1;
+ splitreq.dst_rect.y = d_y_1;
+ splitreq.src_rect.x = s_x_1;
+ splitreq.src_rect.w = s_w_1;
+ splitreq.dst_rect.x = d_x_1;
+ splitreq.dst_rect.w = d_w_1;
+ } else {
+ splitreq.src_rect.h = s_h_1;
+ splitreq.src_rect.y = s_y_1;
+ splitreq.dst_rect.h = d_h_0;
+ splitreq.dst_rect.y = d_y_0;
+ splitreq.src_rect.x = s_x_1;
+ splitreq.src_rect.w = s_w_1;
+ splitreq.dst_rect.x = d_x_0;
+ splitreq.dst_rect.w = d_w_0;
+ }
+
+ /* No need to split in height ... just width */
+ return mdp3_ppp_blit(mfd, &splitreq, src_data, dst_data);
+}
+
+int mdp3_ppp_start_blit(struct msm_fb_data_type *mfd,
+ struct mdp_blit_req *req,
+ struct mdp3_img_data *src_data,
+ struct mdp3_img_data *dst_data)
+{
+ int ret;
+ unsigned int remainder = 0, is_bpp_4 = 0;
+
+ if (unlikely(req->src_rect.h == 0 || req->src_rect.w == 0)) {
+ pr_err("mdp_ppp: src img of zero size!\n");
+ return -EINVAL;
+ }
+ if (unlikely(req->dst_rect.h == 0 || req->dst_rect.w == 0))
+ return 0;
+
+ if (req->flags & MDP_ROT_90) {
+ if (((req->dst_rect.h == 1) && ((req->src_rect.w != 1) ||
+ (req->dst_rect.w != req->src_rect.h))) ||
+ ((req->dst_rect.w == 1) && ((req->src_rect.h != 1) ||
+ (req->dst_rect.h != req->src_rect.w)))) {
+ pr_err("mdp_ppp: error scaling when size is 1!\n");
+ return -EINVAL;
+ }
+ } else {
+ if (((req->dst_rect.w == 1) && ((req->src_rect.w != 1) ||
+ (req->dst_rect.h != req->src_rect.h))) ||
+ ((req->dst_rect.h == 1) && ((req->src_rect.h != 1) ||
+ (req->dst_rect.w != req->src_rect.w)))) {
+ pr_err("mdp_ppp: error scaling when size is 1!\n");
+ return -EINVAL;
+ }
+ }
+
+ /* MDP width split workaround */
+ remainder = (req->dst_rect.w) % 16;
+ ret = ppp_get_bpp(req->dst.format, mfd->fb_imgType);
+ if (ret <= 0) {
+ pr_err("mdp_ppp: incorrect bpp!\n");
+ return -EINVAL;
+ }
+ is_bpp_4 = (ret == 4) ? 1 : 0;
+
+ if ((is_bpp_4 && (remainder == 6 || remainder == 14)))
+ ret = mdp3_ppp_blit_workaround(mfd, req, remainder,
+ src_data, dst_data);
+ else
+ ret = mdp3_ppp_blit(mfd, req, src_data, dst_data);
+
+ mdp3_put_img(src_data);
+ mdp3_put_img(dst_data);
+ return ret;
+}
+
+void mdp3_ppp_wait_for_fence(struct blit_req_list *req)
+{
+ int i, ret = 0;
+ /* buf sync */
+ for (i = 0; i < req->acq_fen_cnt; i++) {
+ ret = sync_fence_wait(req->acq_fen[i],
+ WAIT_FENCE_FINAL_TIMEOUT);
+ if (ret < 0) {
+ pr_err("%s: sync_fence_wait failed! ret = %x\n",
+ __func__, ret);
+ break;
+ }
+ sync_fence_put(req->acq_fen[i]);
+ }
+
+ if (ret < 0) {
+ while (i < req->acq_fen_cnt) {
+ sync_fence_put(req->acq_fen[i]);
+ i++;
+ }
+ }
+ req->acq_fen_cnt = 0;
+}
+
+void mdp3_ppp_signal_timeline(struct blit_req_list *req)
+{
+ sw_sync_timeline_inc(ppp_stat->timeline, 1);
+ req->last_rel_fence = req->cur_rel_fence;
+ req->cur_rel_fence = 0;
+}
+
+
+static void mdp3_ppp_deinit_buf_sync(struct blit_req_list *req)
+{
+ int i;
+
+ put_unused_fd(req->cur_rel_fen_fd);
+ sync_fence_put(req->cur_rel_fence);
+ req->cur_rel_fence = NULL;
+ req->cur_rel_fen_fd = 0;
+ ppp_stat->timeline_value--;
+ for (i = 0; i < req->acq_fen_cnt; i++)
+ sync_fence_put(req->acq_fen[i]);
+ req->acq_fen_cnt = 0;
+}
+
+static int mdp3_ppp_handle_buf_sync(struct blit_req_list *req,
+ struct mdp_buf_sync *buf_sync)
+{
+ int i, fence_cnt = 0, ret = 0;
+ int acq_fen_fd[MDP_MAX_FENCE_FD];
+ struct sync_fence *fence;
+
+ if ((buf_sync->acq_fen_fd_cnt > MDP_MAX_FENCE_FD) ||
+ (ppp_stat->timeline == NULL))
+ return -EINVAL;
+
+ if (buf_sync->acq_fen_fd_cnt)
+ ret = copy_from_user(acq_fen_fd, buf_sync->acq_fen_fd,
+ buf_sync->acq_fen_fd_cnt * sizeof(int));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ for (i = 0; i < buf_sync->acq_fen_fd_cnt; i++) {
+ fence = sync_fence_fdget(acq_fen_fd[i]);
+ if (fence == NULL) {
+ pr_info("%s: null fence! i=%d fd=%d\n", __func__, i,
+ acq_fen_fd[i]);
+ ret = -EINVAL;
+ break;
+ }
+ req->acq_fen[i] = fence;
+ }
+ fence_cnt = i;
+ if (ret)
+ goto buf_sync_err_1;
+ req->acq_fen_cnt = fence_cnt;
+ if (buf_sync->flags & MDP_BUF_SYNC_FLAG_WAIT)
+ mdp3_ppp_wait_for_fence(req);
+
+ req->cur_rel_sync_pt = sw_sync_pt_create(ppp_stat->timeline,
+ ppp_stat->timeline_value++);
+ if (req->cur_rel_sync_pt == NULL) {
+ pr_err("%s: cannot create sync point\n", __func__);
+ ret = -ENOMEM;
+ goto buf_sync_err_2;
+ }
+ /* create fence */
+ req->cur_rel_fence = sync_fence_create("ppp-fence",
+ req->cur_rel_sync_pt);
+ if (req->cur_rel_fence == NULL) {
+ sync_pt_free(req->cur_rel_sync_pt);
+ req->cur_rel_sync_pt = NULL;
+ pr_err("%s: cannot create fence\n", __func__);
+ ret = -ENOMEM;
+ goto buf_sync_err_2;
+ }
+ /* create fd */
+ return ret;
+buf_sync_err_2:
+ ppp_stat->timeline_value--;
+buf_sync_err_1:
+ for (i = 0; i < fence_cnt; i++)
+ sync_fence_put(req->acq_fen[i]);
+ req->acq_fen_cnt = 0;
+ return ret;
+}
+
+void mdp3_ppp_req_push(struct blit_req_queue *req_q, struct blit_req_list *req)
+{
+ int idx = req_q->push_idx;
+ req_q->req[idx] = *req;
+ req_q->count++;
+ req_q->push_idx = (req_q->push_idx + 1) % MDP3_PPP_MAX_LIST_REQ;
+}
+
+struct blit_req_list *mdp3_ppp_next_req(struct blit_req_queue *req_q)
+{
+ struct blit_req_list *req;
+ if (req_q->count == 0)
+ return NULL;
+ req = &req_q->req[req_q->pop_idx];
+ return req;
+}
+
+void mdp3_ppp_req_pop(struct blit_req_queue *req_q)
+{
+ req_q->count--;
+ req_q->pop_idx = (req_q->pop_idx + 1) % MDP3_PPP_MAX_LIST_REQ;
+}
+
+static void mdp3_ppp_blit_wq_handler(struct work_struct *work)
+{
+ struct msm_fb_data_type *mfd = ppp_stat->mfd;
+ struct blit_req_list *req;
+ int i, rc;
+
+ req = mdp3_ppp_next_req(&ppp_stat->req_q);
+ mutex_lock(&ppp_stat->config_ppp_mutex);
+
+ mdp3_iommu_enable(MDP3_CLIENT_PPP);
+ mdp3_ppp_turnon(mfd, 1);
+ while (req) {
+ mdp3_ppp_wait_for_fence(req);
+ for (i = 0; i < req->count; i++) {
+ if (!(req->req_list[i].flags & MDP_NO_BLIT)) {
+ /* Do the actual blit. */
+ rc = mdp3_ppp_start_blit(mfd,
+ &(req->req_list[i]),
+ &req->src_data[i],
+ &req->dst_data[i]);
+ if (rc)
+ break;
+ }
+ }
+ /* Signal to release fence */
+ mutex_lock(&ppp_stat->req_mutex);
+ mdp3_ppp_signal_timeline(req);
+ mdp3_ppp_req_pop(&ppp_stat->req_q);
+ req = mdp3_ppp_next_req(&ppp_stat->req_q);
+ if (ppp_stat->wait_for_pop)
+ complete(&ppp_stat->pop_q_comp);
+ mutex_unlock(&ppp_stat->req_mutex);
+ }
+ mdp3_ppp_turnon(mfd, 0);
+ mdp3_iommu_disable(MDP3_CLIENT_PPP);
+ mutex_unlock(&ppp_stat->config_ppp_mutex);
+}
+
+int mdp3_ppp_parse_req(void __user *p,
+ struct mdp_async_blit_req_list *req_list_header,
+ int async)
+{
+ struct blit_req_list *req;
+ struct blit_req_queue *req_q = &ppp_stat->req_q;
+ struct sync_fence *fence = NULL;
+ int count, rc, idx, i;
+ count = req_list_header->count;
+
+ mutex_lock(&ppp_stat->req_mutex);
+ while (req_q->count >= MDP3_PPP_MAX_LIST_REQ) {
+ ppp_stat->wait_for_pop = true;
+ mutex_unlock(&ppp_stat->req_mutex);
+ rc = wait_for_completion_interruptible_timeout(
+ &ppp_stat->pop_q_comp, 5 * HZ);
+ if (rc == 0) {
+ /* This will only occur if there is serious problem */
+ pr_err("%s: timeout exiting queuing request\n",
+ __func__);
+ return -EBUSY;
+ }
+ mutex_lock(&ppp_stat->req_mutex);
+ ppp_stat->wait_for_pop = false;
+ }
+ idx = req_q->push_idx;
+ req = &req_q->req[idx];
+
+ if (copy_from_user(&req->req_list, p,
+ sizeof(struct mdp_blit_req) * count))
+ return -EFAULT;
+
+ rc = mdp3_ppp_handle_buf_sync(req, &req_list_header->sync);
+ if (rc < 0) {
+ pr_err("%s: Failed create sync point\n", __func__);
+ return rc;
+ }
+ req->count = count;
+
+ /* We need to grab ion handle while client thread */
+ for (i = 0; i < count; i++) {
+ rc = mdp3_ppp_get_img(&req->req_list[i].src,
+ &req->req_list[i], &req->src_data[i]);
+ if (rc < 0 || req->src_data[i].len == 0) {
+ pr_err("mdp_ppp: couldn't retrieve src img from mem\n");
+ goto parse_err_1;
+ }
+
+ rc = mdp3_ppp_get_img(&req->req_list[i].dst,
+ &req->req_list[i], &req->dst_data[i]);
+ if (rc < 0 || req->dst_data[i].len == 0) {
+ mdp3_put_img(&req->src_data[i]);
+ pr_err("mdp_ppp: couldn't retrieve dest img from mem\n");
+ goto parse_err_1;
+ }
+ }
+
+ if (async) {
+ req->cur_rel_fen_fd = get_unused_fd_flags(0);
+ if (req->cur_rel_fen_fd < 0) {
+ pr_err("%s: get_unused_fd_flags failed\n", __func__);
+ rc = -ENOMEM;
+ goto parse_err_2;
+ }
+ sync_fence_install(req->cur_rel_fence, req->cur_rel_fen_fd);
+ rc = copy_to_user(req_list_header->sync.rel_fen_fd,
+ &req->cur_rel_fen_fd, sizeof(int));
+ if (rc) {
+ pr_err("%s:copy_to_user failed\n", __func__);
+ goto parse_err_3;
+ }
+ } else {
+ fence = req->cur_rel_fence;
+ }
+
+ mdp3_ppp_req_push(req_q, req);
+ mutex_unlock(&ppp_stat->req_mutex);
+ schedule_work(&ppp_stat->blit_work);
+ if (!async) {
+ /* wait for release fence */
+ rc = sync_fence_wait(fence,
+ 5 * MSEC_PER_SEC);
+ if (rc < 0)
+ pr_err("%s: sync blit! rc = %x\n", __func__, rc);
+
+ sync_fence_put(fence);
+ fence = NULL;
+ }
+ return 0;
+
+parse_err_3:
+ put_unused_fd(req->cur_rel_fen_fd);
+parse_err_2:
+ sync_fence_put(req->cur_rel_fence);
+ req->cur_rel_fence = NULL;
+ req->cur_rel_fen_fd = 0;
+parse_err_1:
+ for (i--; i >= 0; i--) {
+ mdp3_put_img(&req->src_data[i]);
+ mdp3_put_img(&req->dst_data[i]);
+ }
+ mdp3_ppp_deinit_buf_sync(req);
+ return rc;
+}
+
+int mdp3_ppp_res_init(struct msm_fb_data_type *mfd)
+{
+ const char timeline_name[] = "mdp3_ppp";
+ ppp_stat = kzalloc(sizeof(struct ppp_status), GFP_KERNEL);
+ if (!ppp_stat) {
+ pr_err("%s: kmalloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ /*Setup sync_pt timeline for ppp*/
+ ppp_stat->timeline = sw_sync_timeline_create(timeline_name);
+ if (ppp_stat->timeline == NULL) {
+ pr_err("%s: cannot create time line\n", __func__);
+ return -ENOMEM;
+ } else {
+ ppp_stat->timeline_value = 1;
+ }
+
+ INIT_WORK(&ppp_stat->blit_work, mdp3_ppp_blit_wq_handler);
+ init_completion(&ppp_stat->pop_q_comp);
+ spin_lock_init(&ppp_stat->ppp_lock);
+ mutex_init(&ppp_stat->req_mutex);
+ mutex_init(&ppp_stat->config_ppp_mutex);
+ ppp_stat->busy = false;
+ ppp_stat->mfd = mfd;
+ mdp3_ppp_callback_setup();
+ return 0;
+}
diff --git a/drivers/video/msm/mdss/mdp3_ppp.h b/drivers/video/msm/mdss/mdp3_ppp.h
new file mode 100644
index 0000000..b4252ca
--- /dev/null
+++ b/drivers/video/msm/mdss/mdp3_ppp.h
@@ -0,0 +1,414 @@
+/* Copyright (c) 2007, 2013 The Linux Foundation. All rights reserved.
+ * Copyright (C) 2007 Google Incorporated
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef MDP3_PPP_H
+#define MDP3_PPP_H
+#include "mdp3.h"
+#include "mdss_fb.h"
+
+#define PPP_WRITEL(val, off) MDP3_REG_WRITE(off, val)
+
+#define MAX_BLIT_REQ 16
+#define PPP_UPSCALE_MAX 64
+#define PPP_BLUR_SCALE_MAX 128
+#define PPP_LUT_MAX 256
+
+/* MDP PPP Operations */
+#define MDPOP_NOP 0
+#define MDPOP_LR BIT(0) /* left to right flip */
+#define MDPOP_UD BIT(1) /* up and down flip */
+#define MDPOP_ROT90 BIT(2) /* rotate image to 90 degree */
+#define MDPOP_ROT180 (MDPOP_UD|MDPOP_LR)
+#define MDPOP_ROT270 (MDPOP_ROT90|MDPOP_UD|MDPOP_LR)
+#define MDPOP_ASCALE BIT(7)
+#define MDPOP_ALPHAB BIT(8) /* enable alpha blending */
+#define MDPOP_TRANSP BIT(9) /* enable transparency */
+#define MDPOP_DITHER BIT(10) /* enable dither */
+#define MDPOP_SHARPENING BIT(11) /* enable sharpening */
+#define MDPOP_BLUR BIT(12) /* enable blur */
+#define MDPOP_FG_PM_ALPHA BIT(13)
+#define MDPOP_LAYER_IS_FG BIT(14)
+
+#define MDPOP_ROTATION (MDPOP_ROT90|MDPOP_LR|MDPOP_UD)
+
+#define PPP_OP_CONVERT_YCBCR2RGB BIT(2)
+#define PPP_OP_CONVERT_ON BIT(3)
+#define PPP_OP_SCALE_X_ON BIT(0)
+#define PPP_OP_SCALE_Y_ON BIT(1)
+#define PPP_OP_ROT_ON BIT(8)
+#define PPP_OP_ROT_90 BIT(9)
+#define PPP_OP_FLIP_LR BIT(10)
+#define PPP_OP_FLIP_UD BIT(11)
+#define PPP_OP_BLEND_ON BIT(12)
+#define PPP_OP_BLEND_CONSTANT_ALPHA BIT(14)
+#define PPP_OP_DITHER_EN BIT(16)
+#define PPP_BLEND_CALPHA_TRNASP BIT(24)
+
+#define PPP_OP_BLEND_SRCPIXEL_ALPHA 0
+#define PPP_OP_BLEND_ALPHA_BLEND_NORMAL 0
+#define PPP_OP_BLEND_ALPHA_BLEND_REVERSE BIT(15)
+
+#define PPP_BLEND_BG_USE_ALPHA_SEL (1 << 0)
+#define PPP_BLEND_BG_ALPHA_REVERSE (1 << 3)
+#define PPP_BLEND_BG_SRCPIXEL_ALPHA (0 << 1)
+#define PPP_BLEND_BG_DSTPIXEL_ALPHA (1 << 1)
+#define PPP_BLEND_BG_CONSTANT_ALPHA (2 << 1)
+#define PPP_BLEND_BG_CONST_ALPHA_VAL(x) ((x) << 24)
+#define PPP_OP_BG_CHROMA_H2V1 BIT(25)
+
+#define CLR_G 0x0
+#define CLR_B 0x1
+#define CLR_R 0x2
+#define CLR_ALPHA 0x3
+
+#define CLR_Y CLR_G
+#define CLR_CB CLR_B
+#define CLR_CR CLR_R
+
+/* from lsb to msb */
+#define PPP_GET_PACK_PATTERN(a, x, y, z, bit) \
+ (((a)<<(bit*3))|((x)<<(bit*2))|((y)<<bit)|(z))
+
+/* Frame unpacking */
+#define PPP_C0G_8BITS (BIT(1)|BIT(0))
+#define PPP_C1B_8BITS (BIT(3)|BIT(2))
+#define PPP_C2R_8BITS (BIT(5)|BIT(4))
+#define PPP_C3A_8BITS (BIT(7)|BIT(6))
+
+#define PPP_C0G_6BITS BIT(1)
+#define PPP_C1B_6BITS BIT(3)
+#define PPP_C2R_6BITS BIT(5)
+
+#define PPP_C0G_5BITS BIT(0)
+#define PPP_C1B_5BITS BIT(2)
+#define PPP_C2R_5BITS BIT(4)
+
+#define PPP_SRC_C3_ALPHA_EN BIT(8)
+
+#define PPP_SRC_BPP_INTERLVD_1BYTES 0
+#define PPP_SRC_BPP_INTERLVD_2BYTES BIT(9)
+#define PPP_SRC_BPP_INTERLVD_3BYTES BIT(10)
+#define PPP_SRC_BPP_INTERLVD_4BYTES (BIT(10)|BIT(9))
+
+#define PPP_SRC_BPP_ROI_ODD_X BIT(11)
+#define PPP_SRC_BPP_ROI_ODD_Y BIT(12)
+#define PPP_SRC_INTERLVD_2COMPONENTS BIT(13)
+#define PPP_SRC_INTERLVD_3COMPONENTS BIT(14)
+#define PPP_SRC_INTERLVD_4COMPONENTS (BIT(14)|BIT(13))
+
+#define PPP_SRC_UNPACK_TIGHT BIT(17)
+#define PPP_SRC_UNPACK_LOOSE 0
+#define PPP_SRC_UNPACK_ALIGN_LSB 0
+#define PPP_SRC_UNPACK_ALIGN_MSB BIT(18)
+
+#define PPP_SRC_FETCH_PLANES_INTERLVD 0
+#define PPP_SRC_FETCH_PLANES_PSEUDOPLNR BIT(20)
+
+#define PPP_OP_SRC_CHROMA_H2V1 BIT(18)
+#define PPP_OP_SRC_CHROMA_H1V2 BIT(19)
+#define PPP_OP_SRC_CHROMA_420 (BIT(18)|BIT(19))
+#define PPP_OP_SRC_CHROMA_OFFSITE BIT(20)
+
+#define PPP_DST_PACKET_CNT_INTERLVD_2ELEM BIT(9)
+#define PPP_DST_PACKET_CNT_INTERLVD_3ELEM BIT(10)
+#define PPP_DST_PACKET_CNT_INTERLVD_4ELEM (BIT(10)|BIT(9))
+#define PPP_DST_PACKET_CNT_INTERLVD_6ELEM (BIT(11)|BIT(9))
+
+#define PPP_DST_C3A_8BIT (BIT(7)|BIT(6))
+#define PPP_DST_C3ALPHA_EN BIT(8)
+
+#define PPP_DST_PACK_LOOSE 0
+#define PPP_DST_PACK_TIGHT BIT(13)
+#define PPP_DST_PACK_ALIGN_LSB 0
+#define PPP_DST_PACK_ALIGN_MSB BIT(14)
+
+#define PPP_DST_OUT_SEL_AXI 0
+#define PPP_DST_OUT_SEL_MDDI BIT(15)
+
+#define PPP_DST_BPP_2BYTES BIT(16)
+#define PPP_DST_BPP_3BYTES BIT(17)
+#define PPP_DST_BPP_4BYTES (BIT(17)|BIT(16))
+
+#define PPP_DST_PLANE_INTERLVD 0
+#define PPP_DST_PLANE_PLANAR BIT(18)
+#define PPP_DST_PLANE_PSEUDOPLN BIT(19)
+
+#define PPP_OP_DST_CHROMA_H2V1 BIT(21)
+#define PPP_OP_DST_CHROMA_420 (BIT(21)|BIT(22))
+#define PPP_OP_COLOR_SPACE_YCBCR BIT(17)
+
+#define MDP_SCALE_Q_FACTOR 512
+#define MDP_MAX_X_SCALE_FACTOR (MDP_SCALE_Q_FACTOR*4)
+#define MDP_MIN_X_SCALE_FACTOR (MDP_SCALE_Q_FACTOR/4)
+#define MDP_MAX_Y_SCALE_FACTOR (MDP_SCALE_Q_FACTOR*4)
+#define MDP_MIN_Y_SCALE_FACTOR (MDP_SCALE_Q_FACTOR/4)
+
+#define MDP_TOP_LUMA 16
+#define MDP_TOP_CHROMA 0
+#define MDP_BOTTOM_LUMA 19
+#define MDP_BOTTOM_CHROMA 3
+#define MDP_LEFT_LUMA 22
+#define MDP_LEFT_CHROMA 6
+#define MDP_RIGHT_LUMA 25
+#define MDP_RIGHT_CHROMA 9
+
+#define MDP_RGB_565_SRC_REG (PPP_C2R_5BITS | PPP_C0G_6BITS | \
+ PPP_C1B_5BITS | PPP_SRC_BPP_INTERLVD_2BYTES | \
+ PPP_SRC_INTERLVD_3COMPONENTS | PPP_SRC_UNPACK_TIGHT | \
+ PPP_SRC_UNPACK_ALIGN_LSB | \
+ PPP_SRC_FETCH_PLANES_INTERLVD)
+
+#define MDP_RGB_888_SRC_REG (PPP_C2R_8BITS | PPP_C0G_8BITS | \
+ PPP_C1B_8BITS | PPP_SRC_BPP_INTERLVD_3BYTES | \
+ PPP_SRC_INTERLVD_3COMPONENTS | PPP_SRC_UNPACK_TIGHT | \
+ PPP_SRC_UNPACK_ALIGN_LSB | PPP_SRC_FETCH_PLANES_INTERLVD)
+
+#define MDP_RGBX_8888_SRC_REG (PPP_C2R_8BITS | PPP_C0G_8BITS | \
+ PPP_C1B_8BITS | PPP_C3A_8BITS | \
+ PPP_SRC_C3_ALPHA_EN | PPP_SRC_BPP_INTERLVD_4BYTES | \
+ PPP_SRC_INTERLVD_4COMPONENTS | PPP_SRC_UNPACK_TIGHT | \
+ PPP_SRC_UNPACK_ALIGN_LSB | \
+ PPP_SRC_FETCH_PLANES_INTERLVD)
+
+#define MDP_Y_CBCR_H2V2_SRC_REG (PPP_C2R_8BITS | PPP_C0G_8BITS | \
+ PPP_C1B_8BITS | PPP_SRC_BPP_INTERLVD_2BYTES | \
+ PPP_SRC_INTERLVD_2COMPONENTS | PPP_SRC_UNPACK_TIGHT | \
+ PPP_SRC_UNPACK_ALIGN_LSB | \
+ PPP_SRC_FETCH_PLANES_PSEUDOPLNR)
+
+#define MDP_YCRYCB_H2V1_SRC_REG (PPP_C2R_8BITS | \
+ PPP_C0G_8BITS | PPP_C1B_8BITS | \
+ PPP_C3A_8BITS | PPP_SRC_BPP_INTERLVD_2BYTES | \
+ PPP_SRC_INTERLVD_4COMPONENTS | \
+ PPP_SRC_UNPACK_TIGHT | PPP_SRC_UNPACK_ALIGN_LSB)
+
+#define MDP_Y_CRCB_H2V1_SRC_REG (PPP_C2R_8BITS | \
+ PPP_C0G_8BITS | PPP_C1B_8BITS | \
+ PPP_C3A_8BITS | PPP_SRC_BPP_INTERLVD_2BYTES | \
+ PPP_SRC_INTERLVD_2COMPONENTS | PPP_SRC_UNPACK_TIGHT | \
+ PPP_SRC_UNPACK_ALIGN_LSB | PPP_SRC_FETCH_PLANES_PSEUDOPLNR)
+
+#define MDP_RGB_565_DST_REG (PPP_C0G_6BITS | \
+ PPP_C1B_5BITS | PPP_C2R_5BITS | \
+ PPP_DST_PACKET_CNT_INTERLVD_3ELEM | \
+ PPP_DST_PACK_TIGHT | PPP_DST_PACK_ALIGN_LSB | \
+ PPP_DST_OUT_SEL_AXI | PPP_DST_BPP_2BYTES | \
+ PPP_DST_PLANE_INTERLVD)
+
+#define MDP_RGB_888_DST_REG (PPP_C0G_8BITS | \
+ PPP_C1B_8BITS | PPP_C2R_8BITS | \
+ PPP_DST_PACKET_CNT_INTERLVD_3ELEM | PPP_DST_PACK_TIGHT | \
+ PPP_DST_PACK_ALIGN_LSB | PPP_DST_OUT_SEL_AXI | \
+ PPP_DST_BPP_3BYTES | PPP_DST_PLANE_INTERLVD)
+
+#define MDP_RGBX_8888_DST_REG (PPP_C0G_8BITS | \
+ PPP_C1B_8BITS | PPP_C2R_8BITS | PPP_C3A_8BITS | \
+ PPP_DST_C3ALPHA_EN | PPP_DST_PACKET_CNT_INTERLVD_4ELEM | \
+ PPP_DST_PACK_TIGHT | PPP_DST_PACK_ALIGN_LSB | \
+ PPP_DST_OUT_SEL_AXI | PPP_DST_BPP_4BYTES | \
+ PPP_DST_PLANE_INTERLVD)
+
+#define MDP_Y_CBCR_H2V2_DST_REG (PPP_C2R_8BITS | \
+ PPP_C0G_8BITS | PPP_C1B_8BITS | PPP_C3A_8BITS | \
+ PPP_DST_PACKET_CNT_INTERLVD_2ELEM | \
+ PPP_DST_PACK_TIGHT | PPP_DST_PACK_ALIGN_LSB | \
+ PPP_DST_OUT_SEL_AXI | PPP_DST_BPP_2BYTES)
+
+#define MDP_YCRYCB_H2V1_DST_REG (PPP_C2R_8BITS | PPP_C0G_8BITS | \
+ PPP_C1B_8BITS | PPP_C3A_8BITS | PPP_DST_PACKET_CNT_INTERLVD_4ELEM | \
+ PPP_DST_PACK_TIGHT | PPP_DST_PACK_ALIGN_LSB | \
+ PPP_DST_OUT_SEL_AXI | PPP_DST_BPP_2BYTES | \
+ PPP_DST_PLANE_INTERLVD)
+
+#define MDP_Y_CRCB_H2V1_DST_REG (PPP_C2R_8BITS | \
+ PPP_C0G_8BITS | PPP_C1B_8BITS | PPP_C3A_8BITS | \
+ PPP_DST_PACKET_CNT_INTERLVD_2ELEM | PPP_DST_PACK_TIGHT | \
+ PPP_DST_PACK_ALIGN_LSB | PPP_DST_OUT_SEL_AXI | \
+ PPP_DST_BPP_2BYTES)
+
+/* LUT */
+#define MDP_LUT_C0_EN BIT(5)
+#define MDP_LUT_C1_EN BIT(6)
+#define MDP_LUT_C2_EN BIT(7)
+
+/* Dither */
+#define MDP_OP_DITHER_EN BIT(16)
+
+/* Rotator */
+#define MDP_OP_ROT_ON BIT(8)
+#define MDP_OP_ROT_90 BIT(9)
+#define MDP_OP_FLIP_LR BIT(10)
+#define MDP_OP_FLIP_UD BIT(11)
+
+/* Blend */
+#define MDP_OP_BLEND_EN BIT(12)
+#define MDP_OP_BLEND_EQ_SEL BIT(15)
+#define MDP_OP_BLEND_TRANSP_EN BIT(24)
+#define MDP_BLEND_MASK (MDP_OP_BLEND_EN | MDP_OP_BLEND_EQ_SEL | \
+ MDP_OP_BLEND_TRANSP_EN | BIT(14) | BIT(13))
+
+#define MDP_BLEND_ALPHA_SEL 13
+#define MDP_BLEND_ALPHA_MASK 0x3
+#define MDP_BLEND_CONST_ALPHA 24
+#define MDP_BLEND_TRASP_COL_MASK 0xFFFFFF
+
+/* CSC Matrix */
+#define MDP_CSC_RGB2YUV 0
+#define MDP_CSC_YUV2RGB 1
+
+#define MDP_CSC_SIZE 9
+#define MDP_BV_SIZE 3
+#define MDP_LV_SIZE 4
+
+enum ppp_lut_type {
+ LUT_PRE_TABLE = 0,
+ LUT_POST_TABLE,
+};
+
+enum ppp_csc_matrix {
+ CSC_PRIMARY_MATRIX = 0,
+ CSC_SECONDARY_MATRIX,
+};
+
+/* scale tables */
+enum {
+ PPP_DOWNSCALE_PT2TOPT4,
+ PPP_DOWNSCALE_PT4TOPT6,
+ PPP_DOWNSCALE_PT6TOPT8,
+ PPP_DOWNSCALE_PT8TOPT1,
+ PPP_DOWNSCALE_MAX,
+};
+
+struct ppp_table {
+ uint32_t reg;
+ uint32_t val;
+};
+
+struct ppp_csc_table {
+ int direction; /* MDP_CCS_RGB2YUV or YUV2RGB */
+ uint16_t fwd_matrix[MDP_CCS_SIZE]; /* 3x3 color coefficients */
+ uint16_t rev_matrix[MDP_CCS_SIZE]; /* 3x3 color coefficients */
+ uint16_t bv[MDP_BV_SIZE]; /* 1x3 bias vector */
+ uint16_t lv[MDP_LV_SIZE]; /* 1x3 limit vector */
+};
+
+struct ppp_blend {
+ int const_alpha;
+ int trans_color; /*color keying*/
+};
+
+struct ppp_img_prop {
+ int32_t x;
+ int32_t y;
+ uint32_t width;
+ uint32_t height;
+};
+
+struct ppp_img_desc {
+ struct ppp_img_prop prop;
+ struct ppp_img_prop roi;
+ int color_fmt;
+ void *p0; /* plane 0 */
+ void *p1;
+ void *p3;
+ int stride0;
+ int stride1;
+ int stride2;
+};
+
+struct ppp_blit_op {
+ struct ppp_img_desc src;
+ struct ppp_img_desc dst;
+ struct ppp_img_desc bg;
+ struct ppp_blend blend;
+ uint32_t mdp_op; /* Operations */
+};
+
+struct ppp_edge_rep {
+ uint32_t dst_roi_width;
+ uint32_t dst_roi_height;
+ uint32_t is_scale_enabled;
+
+ /*
+ * positions of the luma pixel(relative to the image ) required for
+ * scaling the ROI
+ */
+ int32_t luma_interp_point_left;
+ int32_t luma_interp_point_right;
+ int32_t luma_interp_point_top;
+ int32_t luma_interp_point_bottom;
+
+ /*
+ * positions of the chroma pixel(relative to the image ) required for
+ * interpolating a chroma value at all required luma positions
+ */
+ int32_t chroma_interp_point_left;
+ int32_t chroma_interp_point_right;
+ int32_t chroma_interp_point_top;
+ int32_t chroma_interp_point_bottom;
+
+ /*
+ * a rectangular region within the chroma plane of the "image".
+ * Chroma pixels falling inside of this rectangle belongs to the ROI
+ */
+ int32_t chroma_bound_left;
+ int32_t chroma_bound_right;
+ int32_t chroma_bound_top;
+ int32_t chroma_bound_bottom;
+
+ /*
+ * number of chroma pixels to replicate on the left, right,
+ * top and bottom edge of the ROI.
+ */
+ int32_t chroma_repeat_left;
+ int32_t chroma_repeat_right;
+ int32_t chroma_repeat_top;
+ int32_t chroma_repeat_bottom;
+
+ /*
+ * number of luma pixels to replicate on the left, right,
+ * top and bottom edge of the ROI.
+ */
+ int32_t luma_repeat_left;
+ int32_t luma_repeat_right;
+ int32_t luma_repeat_top;
+ int32_t luma_repeat_bottom;
+};
+
+/* func for ppp register values */
+uint32_t ppp_bpp(uint32_t type);
+uint32_t ppp_src_config(uint32_t type);
+uint32_t ppp_out_config(uint32_t type);
+uint32_t ppp_pack_pattern(uint32_t type);
+uint32_t ppp_dst_op_reg(uint32_t type);
+uint32_t ppp_src_op_reg(uint32_t type);
+bool ppp_per_p_alpha(uint32_t type);
+bool ppp_multi_plane(uint32_t type);
+uint32_t *ppp_default_pre_lut(void);
+uint32_t *ppp_default_post_lut(void);
+struct ppp_csc_table *ppp_csc_rgb2yuv(void);
+struct ppp_csc_table *ppp_csc_table2(void);
+void ppp_load_up_lut(void);
+void ppp_load_gaussian_lut(void);
+void ppp_load_x_scale_table(int idx);
+void ppp_load_y_scale_table(int idx);
+
+int mdp3_ppp_res_init(struct msm_fb_data_type *mfd);
+int mdp3_ppp_init(void);
+int config_ppp_op_mode(struct ppp_blit_op *blit_op);
+void ppp_enable(void);
+int mdp3_ppp_parse_req(void __user *p,
+ struct mdp_async_blit_req_list *req_list_header,
+ int async);
+
+#endif
diff --git a/drivers/video/msm/mdss/mdp3_ppp_data.c b/drivers/video/msm/mdss/mdp3_ppp_data.c
new file mode 100644
index 0000000..d68faad
--- /dev/null
+++ b/drivers/video/msm/mdss/mdp3_ppp_data.c
@@ -0,0 +1,1572 @@
+/* Copyright (c) 2007, 2012-2013 The Linux Foundation. All rights reserved.
+ * Copyright (C) 2007 Google Incorporated
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>
+
+#include "mdss_fb.h"
+#include "mdp3_ppp.h"
+
+/* bg_config_lut not needed since it is same as src */
+const uint32_t src_cfg_lut[MDP_IMGTYPE_LIMIT] = {
+ [MDP_RGB_565] = MDP_RGB_565_SRC_REG,
+ [MDP_BGR_565] = MDP_RGB_565_SRC_REG,
+ [MDP_RGB_888] = MDP_RGB_888_SRC_REG,
+ [MDP_BGR_888] = MDP_RGB_888_SRC_REG,
+ [MDP_BGRA_8888] = MDP_RGBX_8888_SRC_REG,
+ [MDP_RGBA_8888] = MDP_RGBX_8888_SRC_REG,
+ [MDP_ARGB_8888] = MDP_RGBX_8888_SRC_REG,
+ [MDP_XRGB_8888] = MDP_RGBX_8888_SRC_REG,
+ [MDP_RGBX_8888] = MDP_RGBX_8888_SRC_REG,
+ [MDP_Y_CRCB_H2V2] = MDP_Y_CBCR_H2V2_SRC_REG,
+ [MDP_Y_CBCR_H2V2] = MDP_Y_CBCR_H2V2_SRC_REG,
+ [MDP_Y_CBCR_H2V2_ADRENO] = MDP_Y_CBCR_H2V2_SRC_REG,
+ [MDP_YCRYCB_H2V1] = MDP_YCRYCB_H2V1_SRC_REG,
+ [MDP_Y_CBCR_H2V1] = MDP_Y_CRCB_H2V1_SRC_REG,
+ [MDP_Y_CRCB_H2V1] = MDP_Y_CRCB_H2V1_SRC_REG,
+};
+
+const uint32_t out_cfg_lut[MDP_IMGTYPE_LIMIT] = {
+ [MDP_RGB_565] = MDP_RGB_565_DST_REG,
+ [MDP_BGR_565] = MDP_RGB_565_DST_REG,
+ [MDP_RGB_888] = MDP_RGB_888_DST_REG,
+ [MDP_BGR_888] = MDP_RGB_888_DST_REG,
+ [MDP_BGRA_8888] = MDP_RGBX_8888_DST_REG,
+ [MDP_RGBA_8888] = MDP_RGBX_8888_DST_REG,
+ [MDP_ARGB_8888] = MDP_RGBX_8888_DST_REG,
+ [MDP_XRGB_8888] = MDP_RGBX_8888_DST_REG,
+ [MDP_RGBX_8888] = MDP_RGBX_8888_DST_REG,
+ [MDP_Y_CRCB_H2V2] = MDP_Y_CBCR_H2V2_DST_REG,
+ [MDP_Y_CBCR_H2V2] = MDP_Y_CBCR_H2V2_DST_REG,
+ [MDP_Y_CBCR_H2V2_ADRENO] = MDP_Y_CBCR_H2V2_DST_REG,
+ [MDP_YCRYCB_H2V1] = MDP_YCRYCB_H2V1_DST_REG,
+ [MDP_Y_CBCR_H2V1] = MDP_Y_CRCB_H2V1_DST_REG,
+ [MDP_Y_CRCB_H2V1] = MDP_Y_CRCB_H2V1_DST_REG,
+};
+
+const uint32_t pack_patt_lut[MDP_IMGTYPE_LIMIT] = {
+ [MDP_RGB_565] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
+ [MDP_BGR_565] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
+ [MDP_RGB_888] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
+ [MDP_BGR_888] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
+ [MDP_BGRA_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R,
+ CLR_G, CLR_B, 8),
+ [MDP_RGBA_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R,
+ CLR_G, CLR_B, 8),
+ [MDP_ARGB_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R,
+ CLR_G, CLR_B, 8),
+ [MDP_XRGB_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R,
+ CLR_G, CLR_B, 8),
+ [MDP_RGBX_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R,
+ CLR_G, CLR_B, 8),
+ [MDP_Y_CRCB_H2V2] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+ [MDP_Y_CBCR_H2V2] = PPP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+ [MDP_Y_CBCR_H2V2_ADRENO] = PPP_GET_PACK_PATTERN(0, 0, CLR_CB,
+ CLR_CR, 8),
+ [MDP_YCRYCB_H2V1] = PPP_GET_PACK_PATTERN(CLR_Y,
+ CLR_CR, CLR_Y, CLR_CB, 8),
+ [MDP_Y_CBCR_H2V1] = PPP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+ [MDP_Y_CRCB_H2V1] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+};
+
+const uint32_t dst_op_reg[MDP_IMGTYPE_LIMIT] = {
+ [MDP_Y_CRCB_H2V2] = PPP_OP_DST_CHROMA_420,
+ [MDP_Y_CBCR_H2V2] = PPP_OP_DST_CHROMA_420,
+ [MDP_Y_CBCR_H2V1] = PPP_OP_DST_CHROMA_H2V1,
+ [MDP_Y_CRCB_H2V1] = PPP_OP_DST_CHROMA_H2V1,
+ [MDP_YCRYCB_H2V1] = PPP_OP_DST_CHROMA_H2V1,
+};
+
+const uint32_t src_op_reg[MDP_IMGTYPE_LIMIT] = {
+ [MDP_Y_CRCB_H2V2] = PPP_OP_SRC_CHROMA_420 | PPP_OP_COLOR_SPACE_YCBCR,
+ [MDP_Y_CBCR_H2V2] = PPP_OP_SRC_CHROMA_420 | PPP_OP_COLOR_SPACE_YCBCR,
+ [MDP_Y_CBCR_H2V2_ADRENO] = PPP_OP_SRC_CHROMA_420 |
+ PPP_OP_COLOR_SPACE_YCBCR,
+ [MDP_Y_CBCR_H2V1] = PPP_OP_SRC_CHROMA_H2V1,
+ [MDP_Y_CRCB_H2V1] = PPP_OP_SRC_CHROMA_H2V1,
+ [MDP_YCRYCB_H2V1] = PPP_OP_SRC_CHROMA_H2V1,
+};
+
+const uint32_t bytes_per_pixel[MDP_IMGTYPE_LIMIT] = {
+ [MDP_RGB_565] = 2,
+ [MDP_BGR_565] = 2,
+ [MDP_RGB_888] = 3,
+ [MDP_BGR_888] = 3,
+ [MDP_XRGB_8888] = 4,
+ [MDP_ARGB_8888] = 4,
+ [MDP_RGBA_8888] = 4,
+ [MDP_BGRA_8888] = 4,
+ [MDP_RGBX_8888] = 4,
+ [MDP_Y_CBCR_H2V1] = 1,
+ [MDP_Y_CBCR_H2V2] = 1,
+ [MDP_Y_CBCR_H2V2_ADRENO] = 1,
+ [MDP_Y_CRCB_H2V1] = 1,
+ [MDP_Y_CRCB_H2V2] = 1,
+ [MDP_YCRYCB_H2V1] = 2,
+};
+
+const bool per_pixel_alpha[MDP_IMGTYPE_LIMIT] = {
+ [MDP_BGRA_8888] = true,
+ [MDP_RGBA_8888] = true,
+ [MDP_ARGB_8888] = true,
+};
+
+const bool multi_plane[MDP_IMGTYPE_LIMIT] = {
+ [MDP_Y_CRCB_H2V2] = true,
+ [MDP_Y_CBCR_H2V2] = true,
+ [MDP_Y_CBCR_H2V1] = true,
+ [MDP_Y_CRCB_H2V1] = true,
+};
+
+/* lut default */
+uint32_t default_pre_lut_val[PPP_LUT_MAX] = {
+ 0x0,
+ 0x151515,
+ 0x1d1d1d,
+ 0x232323,
+ 0x272727,
+ 0x2b2b2b,
+ 0x2f2f2f,
+ 0x333333,
+ 0x363636,
+ 0x393939,
+ 0x3b3b3b,
+ 0x3e3e3e,
+ 0x404040,
+ 0x434343,
+ 0x454545,
+ 0x474747,
+ 0x494949,
+ 0x4b4b4b,
+ 0x4d4d4d,
+ 0x4f4f4f,
+ 0x515151,
+ 0x535353,
+ 0x555555,
+ 0x565656,
+ 0x585858,
+ 0x5a5a5a,
+ 0x5b5b5b,
+ 0x5d5d5d,
+ 0x5e5e5e,
+ 0x606060,
+ 0x616161,
+ 0x636363,
+ 0x646464,
+ 0x666666,
+ 0x676767,
+ 0x686868,
+ 0x6a6a6a,
+ 0x6b6b6b,
+ 0x6c6c6c,
+ 0x6e6e6e,
+ 0x6f6f6f,
+ 0x707070,
+ 0x717171,
+ 0x727272,
+ 0x747474,
+ 0x757575,
+ 0x767676,
+ 0x777777,
+ 0x787878,
+ 0x797979,
+ 0x7a7a7a,
+ 0x7c7c7c,
+ 0x7d7d7d,
+ 0x7e7e7e,
+ 0x7f7f7f,
+ 0x808080,
+ 0x818181,
+ 0x828282,
+ 0x838383,
+ 0x848484,
+ 0x858585,
+ 0x868686,
+ 0x878787,
+ 0x888888,
+ 0x898989,
+ 0x8a8a8a,
+ 0x8b8b8b,
+ 0x8c8c8c,
+ 0x8d8d8d,
+ 0x8e8e8e,
+ 0x8f8f8f,
+ 0x8f8f8f,
+ 0x909090,
+ 0x919191,
+ 0x929292,
+ 0x939393,
+ 0x949494,
+ 0x959595,
+ 0x969696,
+ 0x969696,
+ 0x979797,
+ 0x989898,
+ 0x999999,
+ 0x9a9a9a,
+ 0x9b9b9b,
+ 0x9c9c9c,
+ 0x9c9c9c,
+ 0x9d9d9d,
+ 0x9e9e9e,
+ 0x9f9f9f,
+ 0xa0a0a0,
+ 0xa0a0a0,
+ 0xa1a1a1,
+ 0xa2a2a2,
+ 0xa3a3a3,
+ 0xa4a4a4,
+ 0xa4a4a4,
+ 0xa5a5a5,
+ 0xa6a6a6,
+ 0xa7a7a7,
+ 0xa7a7a7,
+ 0xa8a8a8,
+ 0xa9a9a9,
+ 0xaaaaaa,
+ 0xaaaaaa,
+ 0xababab,
+ 0xacacac,
+ 0xadadad,
+ 0xadadad,
+ 0xaeaeae,
+ 0xafafaf,
+ 0xafafaf,
+ 0xb0b0b0,
+ 0xb1b1b1,
+ 0xb2b2b2,
+ 0xb2b2b2,
+ 0xb3b3b3,
+ 0xb4b4b4,
+ 0xb4b4b4,
+ 0xb5b5b5,
+ 0xb6b6b6,
+ 0xb6b6b6,
+ 0xb7b7b7,
+ 0xb8b8b8,
+ 0xb8b8b8,
+ 0xb9b9b9,
+ 0xbababa,
+ 0xbababa,
+ 0xbbbbbb,
+ 0xbcbcbc,
+ 0xbcbcbc,
+ 0xbdbdbd,
+ 0xbebebe,
+ 0xbebebe,
+ 0xbfbfbf,
+ 0xc0c0c0,
+ 0xc0c0c0,
+ 0xc1c1c1,
+ 0xc1c1c1,
+ 0xc2c2c2,
+ 0xc3c3c3,
+ 0xc3c3c3,
+ 0xc4c4c4,
+ 0xc5c5c5,
+ 0xc5c5c5,
+ 0xc6c6c6,
+ 0xc6c6c6,
+ 0xc7c7c7,
+ 0xc8c8c8,
+ 0xc8c8c8,
+ 0xc9c9c9,
+ 0xc9c9c9,
+ 0xcacaca,
+ 0xcbcbcb,
+ 0xcbcbcb,
+ 0xcccccc,
+ 0xcccccc,
+ 0xcdcdcd,
+ 0xcecece,
+ 0xcecece,
+ 0xcfcfcf,
+ 0xcfcfcf,
+ 0xd0d0d0,
+ 0xd0d0d0,
+ 0xd1d1d1,
+ 0xd2d2d2,
+ 0xd2d2d2,
+ 0xd3d3d3,
+ 0xd3d3d3,
+ 0xd4d4d4,
+ 0xd4d4d4,
+ 0xd5d5d5,
+ 0xd6d6d6,
+ 0xd6d6d6,
+ 0xd7d7d7,
+ 0xd7d7d7,
+ 0xd8d8d8,
+ 0xd8d8d8,
+ 0xd9d9d9,
+ 0xd9d9d9,
+ 0xdadada,
+ 0xdbdbdb,
+ 0xdbdbdb,
+ 0xdcdcdc,
+ 0xdcdcdc,
+ 0xdddddd,
+ 0xdddddd,
+ 0xdedede,
+ 0xdedede,
+ 0xdfdfdf,
+ 0xdfdfdf,
+ 0xe0e0e0,
+ 0xe0e0e0,
+ 0xe1e1e1,
+ 0xe1e1e1,
+ 0xe2e2e2,
+ 0xe3e3e3,
+ 0xe3e3e3,
+ 0xe4e4e4,
+ 0xe4e4e4,
+ 0xe5e5e5,
+ 0xe5e5e5,
+ 0xe6e6e6,
+ 0xe6e6e6,
+ 0xe7e7e7,
+ 0xe7e7e7,
+ 0xe8e8e8,
+ 0xe8e8e8,
+ 0xe9e9e9,
+ 0xe9e9e9,
+ 0xeaeaea,
+ 0xeaeaea,
+ 0xebebeb,
+ 0xebebeb,
+ 0xececec,
+ 0xececec,
+ 0xededed,
+ 0xededed,
+ 0xeeeeee,
+ 0xeeeeee,
+ 0xefefef,
+ 0xefefef,
+ 0xf0f0f0,
+ 0xf0f0f0,
+ 0xf1f1f1,
+ 0xf1f1f1,
+ 0xf2f2f2,
+ 0xf2f2f2,
+ 0xf2f2f2,
+ 0xf3f3f3,
+ 0xf3f3f3,
+ 0xf4f4f4,
+ 0xf4f4f4,
+ 0xf5f5f5,
+ 0xf5f5f5,
+ 0xf6f6f6,
+ 0xf6f6f6,
+ 0xf7f7f7,
+ 0xf7f7f7,
+ 0xf8f8f8,
+ 0xf8f8f8,
+ 0xf9f9f9,
+ 0xf9f9f9,
+ 0xfafafa,
+ 0xfafafa,
+ 0xfafafa,
+ 0xfbfbfb,
+ 0xfbfbfb,
+ 0xfcfcfc,
+ 0xfcfcfc,
+ 0xfdfdfd,
+ 0xfdfdfd,
+ 0xfefefe,
+ 0xfefefe,
+ 0xffffff,
+ 0xffffff,
+};
+
+uint32_t default_post_lut_val[PPP_LUT_MAX] = {
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x10101,
+ 0x10101,
+ 0x10101,
+ 0x10101,
+ 0x10101,
+ 0x10101,
+ 0x10101,
+ 0x10101,
+ 0x10101,
+ 0x10101,
+ 0x20202,
+ 0x20202,
+ 0x20202,
+ 0x20202,
+ 0x20202,
+ 0x20202,
+ 0x30303,
+ 0x30303,
+ 0x30303,
+ 0x30303,
+ 0x30303,
+ 0x40404,
+ 0x40404,
+ 0x40404,
+ 0x40404,
+ 0x40404,
+ 0x50505,
+ 0x50505,
+ 0x50505,
+ 0x50505,
+ 0x60606,
+ 0x60606,
+ 0x60606,
+ 0x70707,
+ 0x70707,
+ 0x70707,
+ 0x70707,
+ 0x80808,
+ 0x80808,
+ 0x80808,
+ 0x90909,
+ 0x90909,
+ 0xa0a0a,
+ 0xa0a0a,
+ 0xa0a0a,
+ 0xb0b0b,
+ 0xb0b0b,
+ 0xb0b0b,
+ 0xc0c0c,
+ 0xc0c0c,
+ 0xd0d0d,
+ 0xd0d0d,
+ 0xe0e0e,
+ 0xe0e0e,
+ 0xe0e0e,
+ 0xf0f0f,
+ 0xf0f0f,
+ 0x101010,
+ 0x101010,
+ 0x111111,
+ 0x111111,
+ 0x121212,
+ 0x121212,
+ 0x131313,
+ 0x131313,
+ 0x141414,
+ 0x151515,
+ 0x151515,
+ 0x161616,
+ 0x161616,
+ 0x171717,
+ 0x171717,
+ 0x181818,
+ 0x191919,
+ 0x191919,
+ 0x1a1a1a,
+ 0x1b1b1b,
+ 0x1b1b1b,
+ 0x1c1c1c,
+ 0x1c1c1c,
+ 0x1d1d1d,
+ 0x1e1e1e,
+ 0x1f1f1f,
+ 0x1f1f1f,
+ 0x202020,
+ 0x212121,
+ 0x212121,
+ 0x222222,
+ 0x232323,
+ 0x242424,
+ 0x242424,
+ 0x252525,
+ 0x262626,
+ 0x272727,
+ 0x272727,
+ 0x282828,
+ 0x292929,
+ 0x2a2a2a,
+ 0x2b2b2b,
+ 0x2c2c2c,
+ 0x2c2c2c,
+ 0x2d2d2d,
+ 0x2e2e2e,
+ 0x2f2f2f,
+ 0x303030,
+ 0x313131,
+ 0x323232,
+ 0x333333,
+ 0x333333,
+ 0x343434,
+ 0x353535,
+ 0x363636,
+ 0x373737,
+ 0x383838,
+ 0x393939,
+ 0x3a3a3a,
+ 0x3b3b3b,
+ 0x3c3c3c,
+ 0x3d3d3d,
+ 0x3e3e3e,
+ 0x3f3f3f,
+ 0x404040,
+ 0x414141,
+ 0x424242,
+ 0x434343,
+ 0x444444,
+ 0x464646,
+ 0x474747,
+ 0x484848,
+ 0x494949,
+ 0x4a4a4a,
+ 0x4b4b4b,
+ 0x4c4c4c,
+ 0x4d4d4d,
+ 0x4f4f4f,
+ 0x505050,
+ 0x515151,
+ 0x525252,
+ 0x535353,
+ 0x545454,
+ 0x565656,
+ 0x575757,
+ 0x585858,
+ 0x595959,
+ 0x5b5b5b,
+ 0x5c5c5c,
+ 0x5d5d5d,
+ 0x5e5e5e,
+ 0x606060,
+ 0x616161,
+ 0x626262,
+ 0x646464,
+ 0x656565,
+ 0x666666,
+ 0x686868,
+ 0x696969,
+ 0x6a6a6a,
+ 0x6c6c6c,
+ 0x6d6d6d,
+ 0x6f6f6f,
+ 0x707070,
+ 0x717171,
+ 0x737373,
+ 0x747474,
+ 0x767676,
+ 0x777777,
+ 0x797979,
+ 0x7a7a7a,
+ 0x7c7c7c,
+ 0x7d7d7d,
+ 0x7f7f7f,
+ 0x808080,
+ 0x828282,
+ 0x838383,
+ 0x858585,
+ 0x868686,
+ 0x888888,
+ 0x898989,
+ 0x8b8b8b,
+ 0x8d8d8d,
+ 0x8e8e8e,
+ 0x909090,
+ 0x919191,
+ 0x939393,
+ 0x959595,
+ 0x969696,
+ 0x989898,
+ 0x9a9a9a,
+ 0x9b9b9b,
+ 0x9d9d9d,
+ 0x9f9f9f,
+ 0xa1a1a1,
+ 0xa2a2a2,
+ 0xa4a4a4,
+ 0xa6a6a6,
+ 0xa7a7a7,
+ 0xa9a9a9,
+ 0xababab,
+ 0xadadad,
+ 0xafafaf,
+ 0xb0b0b0,
+ 0xb2b2b2,
+ 0xb4b4b4,
+ 0xb6b6b6,
+ 0xb8b8b8,
+ 0xbababa,
+ 0xbbbbbb,
+ 0xbdbdbd,
+ 0xbfbfbf,
+ 0xc1c1c1,
+ 0xc3c3c3,
+ 0xc5c5c5,
+ 0xc7c7c7,
+ 0xc9c9c9,
+ 0xcbcbcb,
+ 0xcdcdcd,
+ 0xcfcfcf,
+ 0xd1d1d1,
+ 0xd3d3d3,
+ 0xd5d5d5,
+ 0xd7d7d7,
+ 0xd9d9d9,
+ 0xdbdbdb,
+ 0xdddddd,
+ 0xdfdfdf,
+ 0xe1e1e1,
+ 0xe3e3e3,
+ 0xe5e5e5,
+ 0xe7e7e7,
+ 0xe9e9e9,
+ 0xebebeb,
+ 0xeeeeee,
+ 0xf0f0f0,
+ 0xf2f2f2,
+ 0xf4f4f4,
+ 0xf6f6f6,
+ 0xf8f8f8,
+ 0xfbfbfb,
+ 0xfdfdfd,
+ 0xffffff,
+};
+
+struct ppp_csc_table rgb2yuv = {
+ .fwd_matrix = {
+ 0x83,
+ 0x102,
+ 0x32,
+ 0xffb5,
+ 0xff6c,
+ 0xe1,
+ 0xe1,
+ 0xff45,
+ 0xffdc,
+ },
+ .rev_matrix = {
+ 0x254,
+ 0x0,
+ 0x331,
+ 0x254,
+ 0xff38,
+ 0xfe61,
+ 0x254,
+ 0x409,
+ 0x0,
+ },
+ .bv = {
+ 0x10,
+ 0x80,
+ 0x80,
+ },
+ .lv = {
+ 0x10,
+ 0xeb,
+ 0x10,
+ 0xf0,
+ },
+};
+
+struct ppp_csc_table default_table2 = {
+ .fwd_matrix = {
+ 0x5d,
+ 0x13a,
+ 0x20,
+ 0xffcd,
+ 0xff54,
+ 0xe1,
+ 0xe1,
+ 0xff35,
+ },
+ .rev_matrix = {
+ 0x254,
+ 0x0,
+ 0x396,
+ 0x254,
+ 0xff94,
+ 0xfef0,
+ 0x254,
+ 0x43a,
+ 0x0,
+ },
+ .bv = {
+ 0x10,
+ 0x80,
+ 0x80,
+ },
+ .lv = {
+ 0x10,
+ 0xeb,
+ 0x10,
+ 0xf0,
+ },
+};
+
+const struct ppp_table upscale_table[PPP_UPSCALE_MAX] = {
+ { 0x5fffc, 0x0 },
+ { 0x50200, 0x7fc00000 },
+ { 0x5fffc, 0xff80000d },
+ { 0x50204, 0x7ec003f9 },
+ { 0x5fffc, 0xfec0001c },
+ { 0x50208, 0x7d4003f3 },
+ { 0x5fffc, 0xfe40002b },
+ { 0x5020c, 0x7b8003ed },
+ { 0x5fffc, 0xfd80003c },
+ { 0x50210, 0x794003e8 },
+ { 0x5fffc, 0xfcc0004d },
+ { 0x50214, 0x76c003e4 },
+ { 0x5fffc, 0xfc40005f },
+ { 0x50218, 0x73c003e0 },
+ { 0x5fffc, 0xfb800071 },
+ { 0x5021c, 0x708003de },
+ { 0x5fffc, 0xfac00085 },
+ { 0x50220, 0x6d0003db },
+ { 0x5fffc, 0xfa000098 },
+ { 0x50224, 0x698003d9 },
+ { 0x5fffc, 0xf98000ac },
+ { 0x50228, 0x654003d8 },
+ { 0x5fffc, 0xf8c000c1 },
+ { 0x5022c, 0x610003d7 },
+ { 0x5fffc, 0xf84000d5 },
+ { 0x50230, 0x5c8003d7 },
+ { 0x5fffc, 0xf7c000e9 },
+ { 0x50234, 0x580003d7 },
+ { 0x5fffc, 0xf74000fd },
+ { 0x50238, 0x534003d8 },
+ { 0x5fffc, 0xf6c00112 },
+ { 0x5023c, 0x4e8003d8 },
+ { 0x5fffc, 0xf6800126 },
+ { 0x50240, 0x494003da },
+ { 0x5fffc, 0xf600013a },
+ { 0x50244, 0x448003db },
+ { 0x5fffc, 0xf600014d },
+ { 0x50248, 0x3f4003dd },
+ { 0x5fffc, 0xf5c00160 },
+ { 0x5024c, 0x3a4003df },
+ { 0x5fffc, 0xf5c00172 },
+ { 0x50250, 0x354003e1 },
+ { 0x5fffc, 0xf5c00184 },
+ { 0x50254, 0x304003e3 },
+ { 0x5fffc, 0xf6000195 },
+ { 0x50258, 0x2b0003e6 },
+ { 0x5fffc, 0xf64001a6 },
+ { 0x5025c, 0x260003e8 },
+ { 0x5fffc, 0xf6c001b4 },
+ { 0x50260, 0x214003eb },
+ { 0x5fffc, 0xf78001c2 },
+ { 0x50264, 0x1c4003ee },
+ { 0x5fffc, 0xf80001cf },
+ { 0x50268, 0x17c003f1 },
+ { 0x5fffc, 0xf90001db },
+ { 0x5026c, 0x134003f3 },
+ { 0x5fffc, 0xfa0001e5 },
+ { 0x50270, 0xf0003f6 },
+ { 0x5fffc, 0xfb4001ee },
+ { 0x50274, 0xac003f9 },
+ { 0x5fffc, 0xfcc001f5 },
+ { 0x50278, 0x70003fb },
+ { 0x5fffc, 0xfe4001fb },
+ { 0x5027c, 0x34003fe },
+};
+
+const struct ppp_table mdp_gaussian_blur_table[PPP_BLUR_SCALE_MAX] = {
+ /* max variance */
+ { 0x5fffc, 0x20000080 },
+ { 0x50280, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50284, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50288, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x5028c, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50290, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50294, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50298, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x5029c, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502a0, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502a4, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502a8, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502ac, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502b0, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502b4, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502b8, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502bc, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502c0, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502c4, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502c8, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502cc, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502d0, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502d4, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502d8, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502dc, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502e0, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502e4, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502e8, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502ec, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502f0, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502f4, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502f8, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x502fc, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50300, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50304, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50308, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x5030c, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50310, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50314, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50318, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x5031c, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50320, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50324, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50328, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x5032c, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50330, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50334, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50338, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x5033c, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50340, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50344, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50348, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x5034c, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50350, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50354, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50358, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x5035c, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50360, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50364, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50368, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x5036c, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50370, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50374, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x50378, 0x20000080 },
+ { 0x5fffc, 0x20000080 },
+ { 0x5037c, 0x20000080 },
+};
+
+const struct ppp_table downscale_x_table_pt2topt4[] = {
+ { 0x5fffc, 0x740008c },
+ { 0x50280, 0x33800088 },
+ { 0x5fffc, 0x800008e },
+ { 0x50284, 0x33400084 },
+ { 0x5fffc, 0x8400092 },
+ { 0x50288, 0x33000080 },
+ { 0x5fffc, 0x9000094 },
+ { 0x5028c, 0x3300007b },
+ { 0x5fffc, 0x9c00098 },
+ { 0x50290, 0x32400077 },
+ { 0x5fffc, 0xa40009b },
+ { 0x50294, 0x32000073 },
+ { 0x5fffc, 0xb00009d },
+ { 0x50298, 0x31c0006f },
+ { 0x5fffc, 0xbc000a0 },
+ { 0x5029c, 0x3140006b },
+ { 0x5fffc, 0xc8000a2 },
+ { 0x502a0, 0x31000067 },
+ { 0x5fffc, 0xd8000a5 },
+ { 0x502a4, 0x30800062 },
+ { 0x5fffc, 0xe4000a8 },
+ { 0x502a8, 0x2fc0005f },
+ { 0x5fffc, 0xec000aa },
+ { 0x502ac, 0x2fc0005b },
+ { 0x5fffc, 0xf8000ad },
+ { 0x502b0, 0x2f400057 },
+ { 0x5fffc, 0x108000b0 },
+ { 0x502b4, 0x2e400054 },
+ { 0x5fffc, 0x114000b2 },
+ { 0x502b8, 0x2e000050 },
+ { 0x5fffc, 0x124000b4 },
+ { 0x502bc, 0x2d80004c },
+ { 0x5fffc, 0x130000b6 },
+ { 0x502c0, 0x2d000049 },
+ { 0x5fffc, 0x140000b8 },
+ { 0x502c4, 0x2c800045 },
+ { 0x5fffc, 0x150000b9 },
+ { 0x502c8, 0x2c000042 },
+ { 0x5fffc, 0x15c000bd },
+ { 0x502cc, 0x2b40003e },
+ { 0x5fffc, 0x16c000bf },
+ { 0x502d0, 0x2a80003b },
+ { 0x5fffc, 0x17c000bf },
+ { 0x502d4, 0x2a000039 },
+ { 0x5fffc, 0x188000c2 },
+ { 0x502d8, 0x29400036 },
+ { 0x5fffc, 0x19c000c4 },
+ { 0x502dc, 0x28800032 },
+ { 0x5fffc, 0x1ac000c5 },
+ { 0x502e0, 0x2800002f },
+ { 0x5fffc, 0x1bc000c7 },
+ { 0x502e4, 0x2740002c },
+ { 0x5fffc, 0x1cc000c8 },
+ { 0x502e8, 0x26c00029 },
+ { 0x5fffc, 0x1dc000c9 },
+ { 0x502ec, 0x26000027 },
+ { 0x5fffc, 0x1ec000cc },
+ { 0x502f0, 0x25000024 },
+ { 0x5fffc, 0x200000cc },
+ { 0x502f4, 0x24800021 },
+ { 0x5fffc, 0x210000cd },
+ { 0x502f8, 0x23800020 },
+ { 0x5fffc, 0x220000ce },
+ { 0x502fc, 0x2300001d },
+};
+
+static const struct ppp_table downscale_x_table_pt4topt6[] = {
+ { 0x5fffc, 0x740008c },
+ { 0x50280, 0x33800088 },
+ { 0x5fffc, 0x800008e },
+ { 0x50284, 0x33400084 },
+ { 0x5fffc, 0x8400092 },
+ { 0x50288, 0x33000080 },
+ { 0x5fffc, 0x9000094 },
+ { 0x5028c, 0x3300007b },
+ { 0x5fffc, 0x9c00098 },
+ { 0x50290, 0x32400077 },
+ { 0x5fffc, 0xa40009b },
+ { 0x50294, 0x32000073 },
+ { 0x5fffc, 0xb00009d },
+ { 0x50298, 0x31c0006f },
+ { 0x5fffc, 0xbc000a0 },
+ { 0x5029c, 0x3140006b },
+ { 0x5fffc, 0xc8000a2 },
+ { 0x502a0, 0x31000067 },
+ { 0x5fffc, 0xd8000a5 },
+ { 0x502a4, 0x30800062 },
+ { 0x5fffc, 0xe4000a8 },
+ { 0x502a8, 0x2fc0005f },
+ { 0x5fffc, 0xec000aa },
+ { 0x502ac, 0x2fc0005b },
+ { 0x5fffc, 0xf8000ad },
+ { 0x502b0, 0x2f400057 },
+ { 0x5fffc, 0x108000b0 },
+ { 0x502b4, 0x2e400054 },
+ { 0x5fffc, 0x114000b2 },
+ { 0x502b8, 0x2e000050 },
+ { 0x5fffc, 0x124000b4 },
+ { 0x502bc, 0x2d80004c },
+ { 0x5fffc, 0x130000b6 },
+ { 0x502c0, 0x2d000049 },
+ { 0x5fffc, 0x140000b8 },
+ { 0x502c4, 0x2c800045 },
+ { 0x5fffc, 0x150000b9 },
+ { 0x502c8, 0x2c000042 },
+ { 0x5fffc, 0x15c000bd },
+ { 0x502cc, 0x2b40003e },
+ { 0x5fffc, 0x16c000bf },
+ { 0x502d0, 0x2a80003b },
+ { 0x5fffc, 0x17c000bf },
+ { 0x502d4, 0x2a000039 },
+ { 0x5fffc, 0x188000c2 },
+ { 0x502d8, 0x29400036 },
+ { 0x5fffc, 0x19c000c4 },
+ { 0x502dc, 0x28800032 },
+ { 0x5fffc, 0x1ac000c5 },
+ { 0x502e0, 0x2800002f },
+ { 0x5fffc, 0x1bc000c7 },
+ { 0x502e4, 0x2740002c },
+ { 0x5fffc, 0x1cc000c8 },
+ { 0x502e8, 0x26c00029 },
+ { 0x5fffc, 0x1dc000c9 },
+ { 0x502ec, 0x26000027 },
+ { 0x5fffc, 0x1ec000cc },
+ { 0x502f0, 0x25000024 },
+ { 0x5fffc, 0x200000cc },
+ { 0x502f4, 0x24800021 },
+ { 0x5fffc, 0x210000cd },
+ { 0x502f8, 0x23800020 },
+ { 0x5fffc, 0x220000ce },
+ { 0x502fc, 0x2300001d },
+};
+
+static const struct ppp_table downscale_x_table_pt6topt8[] = {
+ { 0x5fffc, 0xfe000070 },
+ { 0x50280, 0x4bc00068 },
+ { 0x5fffc, 0xfe000078 },
+ { 0x50284, 0x4bc00060 },
+ { 0x5fffc, 0xfe000080 },
+ { 0x50288, 0x4b800059 },
+ { 0x5fffc, 0xfe000089 },
+ { 0x5028c, 0x4b000052 },
+ { 0x5fffc, 0xfe400091 },
+ { 0x50290, 0x4a80004b },
+ { 0x5fffc, 0xfe40009a },
+ { 0x50294, 0x4a000044 },
+ { 0x5fffc, 0xfe8000a3 },
+ { 0x50298, 0x4940003d },
+ { 0x5fffc, 0xfec000ac },
+ { 0x5029c, 0x48400037 },
+ { 0x5fffc, 0xff0000b4 },
+ { 0x502a0, 0x47800031 },
+ { 0x5fffc, 0xff8000bd },
+ { 0x502a4, 0x4640002b },
+ { 0x5fffc, 0xc5 },
+ { 0x502a8, 0x45000026 },
+ { 0x5fffc, 0x8000ce },
+ { 0x502ac, 0x43800021 },
+ { 0x5fffc, 0x10000d6 },
+ { 0x502b0, 0x4240001c },
+ { 0x5fffc, 0x18000df },
+ { 0x502b4, 0x40800018 },
+ { 0x5fffc, 0x24000e6 },
+ { 0x502b8, 0x3f000014 },
+ { 0x5fffc, 0x30000ee },
+ { 0x502bc, 0x3d400010 },
+ { 0x5fffc, 0x40000f5 },
+ { 0x502c0, 0x3b80000c },
+ { 0x5fffc, 0x50000fc },
+ { 0x502c4, 0x39800009 },
+ { 0x5fffc, 0x6000102 },
+ { 0x502c8, 0x37c00006 },
+ { 0x5fffc, 0x7000109 },
+ { 0x502cc, 0x35800004 },
+ { 0x5fffc, 0x840010e },
+ { 0x502d0, 0x33800002 },
+ { 0x5fffc, 0x9800114 },
+ { 0x502d4, 0x31400000 },
+ { 0x5fffc, 0xac00119 },
+ { 0x502d8, 0x2f4003fe },
+ { 0x5fffc, 0xc40011e },
+ { 0x502dc, 0x2d0003fc },
+ { 0x5fffc, 0xdc00121 },
+ { 0x502e0, 0x2b0003fb },
+ { 0x5fffc, 0xf400125 },
+ { 0x502e4, 0x28c003fa },
+ { 0x5fffc, 0x11000128 },
+ { 0x502e8, 0x268003f9 },
+ { 0x5fffc, 0x12c0012a },
+ { 0x502ec, 0x244003f9 },
+ { 0x5fffc, 0x1480012c },
+ { 0x502f0, 0x224003f8 },
+ { 0x5fffc, 0x1640012e },
+ { 0x502f4, 0x200003f8 },
+ { 0x5fffc, 0x1800012f },
+ { 0x502f8, 0x1e0003f8 },
+ { 0x5fffc, 0x1a00012f },
+ { 0x502fc, 0x1c0003f8 },
+};
+
+static const struct ppp_table downscale_x_table_pt8topt1[] = {
+ { 0x5fffc, 0x0 },
+ { 0x50280, 0x7fc00000 },
+ { 0x5fffc, 0xff80000d },
+ { 0x50284, 0x7ec003f9 },
+ { 0x5fffc, 0xfec0001c },
+ { 0x50288, 0x7d4003f3 },
+ { 0x5fffc, 0xfe40002b },
+ { 0x5028c, 0x7b8003ed },
+ { 0x5fffc, 0xfd80003c },
+ { 0x50290, 0x794003e8 },
+ { 0x5fffc, 0xfcc0004d },
+ { 0x50294, 0x76c003e4 },
+ { 0x5fffc, 0xfc40005f },
+ { 0x50298, 0x73c003e0 },
+ { 0x5fffc, 0xfb800071 },
+ { 0x5029c, 0x708003de },
+ { 0x5fffc, 0xfac00085 },
+ { 0x502a0, 0x6d0003db },
+ { 0x5fffc, 0xfa000098 },
+ { 0x502a4, 0x698003d9 },
+ { 0x5fffc, 0xf98000ac },
+ { 0x502a8, 0x654003d8 },
+ { 0x5fffc, 0xf8c000c1 },
+ { 0x502ac, 0x610003d7 },
+ { 0x5fffc, 0xf84000d5 },
+ { 0x502b0, 0x5c8003d7 },
+ { 0x5fffc, 0xf7c000e9 },
+ { 0x502b4, 0x580003d7 },
+ { 0x5fffc, 0xf74000fd },
+ { 0x502b8, 0x534003d8 },
+ { 0x5fffc, 0xf6c00112 },
+ { 0x502bc, 0x4e8003d8 },
+ { 0x5fffc, 0xf6800126 },
+ { 0x502c0, 0x494003da },
+ { 0x5fffc, 0xf600013a },
+ { 0x502c4, 0x448003db },
+ { 0x5fffc, 0xf600014d },
+ { 0x502c8, 0x3f4003dd },
+ { 0x5fffc, 0xf5c00160 },
+ { 0x502cc, 0x3a4003df },
+ { 0x5fffc, 0xf5c00172 },
+ { 0x502d0, 0x354003e1 },
+ { 0x5fffc, 0xf5c00184 },
+ { 0x502d4, 0x304003e3 },
+ { 0x5fffc, 0xf6000195 },
+ { 0x502d8, 0x2b0003e6 },
+ { 0x5fffc, 0xf64001a6 },
+ { 0x502dc, 0x260003e8 },
+ { 0x5fffc, 0xf6c001b4 },
+ { 0x502e0, 0x214003eb },
+ { 0x5fffc, 0xf78001c2 },
+ { 0x502e4, 0x1c4003ee },
+ { 0x5fffc, 0xf80001cf },
+ { 0x502e8, 0x17c003f1 },
+ { 0x5fffc, 0xf90001db },
+ { 0x502ec, 0x134003f3 },
+ { 0x5fffc, 0xfa0001e5 },
+ { 0x502f0, 0xf0003f6 },
+ { 0x5fffc, 0xfb4001ee },
+ { 0x502f4, 0xac003f9 },
+ { 0x5fffc, 0xfcc001f5 },
+ { 0x502f8, 0x70003fb },
+ { 0x5fffc, 0xfe4001fb },
+ { 0x502fc, 0x34003fe },
+};
+
+static const struct ppp_table *downscale_x_table[PPP_DOWNSCALE_MAX] = {
+ [PPP_DOWNSCALE_PT2TOPT4] = downscale_x_table_pt2topt4,
+ [PPP_DOWNSCALE_PT4TOPT6] = downscale_x_table_pt4topt6,
+ [PPP_DOWNSCALE_PT6TOPT8] = downscale_x_table_pt6topt8,
+ [PPP_DOWNSCALE_PT8TOPT1] = downscale_x_table_pt8topt1,
+};
+
+static const struct ppp_table downscale_y_table_pt2topt4[] = {
+ { 0x5fffc, 0x740008c },
+ { 0x50300, 0x33800088 },
+ { 0x5fffc, 0x800008e },
+ { 0x50304, 0x33400084 },
+ { 0x5fffc, 0x8400092 },
+ { 0x50308, 0x33000080 },
+ { 0x5fffc, 0x9000094 },
+ { 0x5030c, 0x3300007b },
+ { 0x5fffc, 0x9c00098 },
+ { 0x50310, 0x32400077 },
+ { 0x5fffc, 0xa40009b },
+ { 0x50314, 0x32000073 },
+ { 0x5fffc, 0xb00009d },
+ { 0x50318, 0x31c0006f },
+ { 0x5fffc, 0xbc000a0 },
+ { 0x5031c, 0x3140006b },
+ { 0x5fffc, 0xc8000a2 },
+ { 0x50320, 0x31000067 },
+ { 0x5fffc, 0xd8000a5 },
+ { 0x50324, 0x30800062 },
+ { 0x5fffc, 0xe4000a8 },
+ { 0x50328, 0x2fc0005f },
+ { 0x5fffc, 0xec000aa },
+ { 0x5032c, 0x2fc0005b },
+ { 0x5fffc, 0xf8000ad },
+ { 0x50330, 0x2f400057 },
+ { 0x5fffc, 0x108000b0 },
+ { 0x50334, 0x2e400054 },
+ { 0x5fffc, 0x114000b2 },
+ { 0x50338, 0x2e000050 },
+ { 0x5fffc, 0x124000b4 },
+ { 0x5033c, 0x2d80004c },
+ { 0x5fffc, 0x130000b6 },
+ { 0x50340, 0x2d000049 },
+ { 0x5fffc, 0x140000b8 },
+ { 0x50344, 0x2c800045 },
+ { 0x5fffc, 0x150000b9 },
+ { 0x50348, 0x2c000042 },
+ { 0x5fffc, 0x15c000bd },
+ { 0x5034c, 0x2b40003e },
+ { 0x5fffc, 0x16c000bf },
+ { 0x50350, 0x2a80003b },
+ { 0x5fffc, 0x17c000bf },
+ { 0x50354, 0x2a000039 },
+ { 0x5fffc, 0x188000c2 },
+ { 0x50358, 0x29400036 },
+ { 0x5fffc, 0x19c000c4 },
+ { 0x5035c, 0x28800032 },
+ { 0x5fffc, 0x1ac000c5 },
+ { 0x50360, 0x2800002f },
+ { 0x5fffc, 0x1bc000c7 },
+ { 0x50364, 0x2740002c },
+ { 0x5fffc, 0x1cc000c8 },
+ { 0x50368, 0x26c00029 },
+ { 0x5fffc, 0x1dc000c9 },
+ { 0x5036c, 0x26000027 },
+ { 0x5fffc, 0x1ec000cc },
+ { 0x50370, 0x25000024 },
+ { 0x5fffc, 0x200000cc },
+ { 0x50374, 0x24800021 },
+ { 0x5fffc, 0x210000cd },
+ { 0x50378, 0x23800020 },
+ { 0x5fffc, 0x220000ce },
+ { 0x5037c, 0x2300001d },
+};
+
+static const struct ppp_table downscale_y_table_pt4topt6[] = {
+ { 0x5fffc, 0x740008c },
+ { 0x50300, 0x33800088 },
+ { 0x5fffc, 0x800008e },
+ { 0x50304, 0x33400084 },
+ { 0x5fffc, 0x8400092 },
+ { 0x50308, 0x33000080 },
+ { 0x5fffc, 0x9000094 },
+ { 0x5030c, 0x3300007b },
+ { 0x5fffc, 0x9c00098 },
+ { 0x50310, 0x32400077 },
+ { 0x5fffc, 0xa40009b },
+ { 0x50314, 0x32000073 },
+ { 0x5fffc, 0xb00009d },
+ { 0x50318, 0x31c0006f },
+ { 0x5fffc, 0xbc000a0 },
+ { 0x5031c, 0x3140006b },
+ { 0x5fffc, 0xc8000a2 },
+ { 0x50320, 0x31000067 },
+ { 0x5fffc, 0xd8000a5 },
+ { 0x50324, 0x30800062 },
+ { 0x5fffc, 0xe4000a8 },
+ { 0x50328, 0x2fc0005f },
+ { 0x5fffc, 0xec000aa },
+ { 0x5032c, 0x2fc0005b },
+ { 0x5fffc, 0xf8000ad },
+ { 0x50330, 0x2f400057 },
+ { 0x5fffc, 0x108000b0 },
+ { 0x50334, 0x2e400054 },
+ { 0x5fffc, 0x114000b2 },
+ { 0x50338, 0x2e000050 },
+ { 0x5fffc, 0x124000b4 },
+ { 0x5033c, 0x2d80004c },
+ { 0x5fffc, 0x130000b6 },
+ { 0x50340, 0x2d000049 },
+ { 0x5fffc, 0x140000b8 },
+ { 0x50344, 0x2c800045 },
+ { 0x5fffc, 0x150000b9 },
+ { 0x50348, 0x2c000042 },
+ { 0x5fffc, 0x15c000bd },
+ { 0x5034c, 0x2b40003e },
+ { 0x5fffc, 0x16c000bf },
+ { 0x50350, 0x2a80003b },
+ { 0x5fffc, 0x17c000bf },
+ { 0x50354, 0x2a000039 },
+ { 0x5fffc, 0x188000c2 },
+ { 0x50358, 0x29400036 },
+ { 0x5fffc, 0x19c000c4 },
+ { 0x5035c, 0x28800032 },
+ { 0x5fffc, 0x1ac000c5 },
+ { 0x50360, 0x2800002f },
+ { 0x5fffc, 0x1bc000c7 },
+ { 0x50364, 0x2740002c },
+ { 0x5fffc, 0x1cc000c8 },
+ { 0x50368, 0x26c00029 },
+ { 0x5fffc, 0x1dc000c9 },
+ { 0x5036c, 0x26000027 },
+ { 0x5fffc, 0x1ec000cc },
+ { 0x50370, 0x25000024 },
+ { 0x5fffc, 0x200000cc },
+ { 0x50374, 0x24800021 },
+ { 0x5fffc, 0x210000cd },
+ { 0x50378, 0x23800020 },
+ { 0x5fffc, 0x220000ce },
+ { 0x5037c, 0x2300001d },
+};
+
+static const struct ppp_table downscale_y_table_pt6topt8[] = {
+ { 0x5fffc, 0xfe000070 },
+ { 0x50300, 0x4bc00068 },
+ { 0x5fffc, 0xfe000078 },
+ { 0x50304, 0x4bc00060 },
+ { 0x5fffc, 0xfe000080 },
+ { 0x50308, 0x4b800059 },
+ { 0x5fffc, 0xfe000089 },
+ { 0x5030c, 0x4b000052 },
+ { 0x5fffc, 0xfe400091 },
+ { 0x50310, 0x4a80004b },
+ { 0x5fffc, 0xfe40009a },
+ { 0x50314, 0x4a000044 },
+ { 0x5fffc, 0xfe8000a3 },
+ { 0x50318, 0x4940003d },
+ { 0x5fffc, 0xfec000ac },
+ { 0x5031c, 0x48400037 },
+ { 0x5fffc, 0xff0000b4 },
+ { 0x50320, 0x47800031 },
+ { 0x5fffc, 0xff8000bd },
+ { 0x50324, 0x4640002b },
+ { 0x5fffc, 0xc5 },
+ { 0x50328, 0x45000026 },
+ { 0x5fffc, 0x8000ce },
+ { 0x5032c, 0x43800021 },
+ { 0x5fffc, 0x10000d6 },
+ { 0x50330, 0x4240001c },
+ { 0x5fffc, 0x18000df },
+ { 0x50334, 0x40800018 },
+ { 0x5fffc, 0x24000e6 },
+ { 0x50338, 0x3f000014 },
+ { 0x5fffc, 0x30000ee },
+ { 0x5033c, 0x3d400010 },
+ { 0x5fffc, 0x40000f5 },
+ { 0x50340, 0x3b80000c },
+ { 0x5fffc, 0x50000fc },
+ { 0x50344, 0x39800009 },
+ { 0x5fffc, 0x6000102 },
+ { 0x50348, 0x37c00006 },
+ { 0x5fffc, 0x7000109 },
+ { 0x5034c, 0x35800004 },
+ { 0x5fffc, 0x840010e },
+ { 0x50350, 0x33800002 },
+ { 0x5fffc, 0x9800114 },
+ { 0x50354, 0x31400000 },
+ { 0x5fffc, 0xac00119 },
+ { 0x50358, 0x2f4003fe },
+ { 0x5fffc, 0xc40011e },
+ { 0x5035c, 0x2d0003fc },
+ { 0x5fffc, 0xdc00121 },
+ { 0x50360, 0x2b0003fb },
+ { 0x5fffc, 0xf400125 },
+ { 0x50364, 0x28c003fa },
+ { 0x5fffc, 0x11000128 },
+ { 0x50368, 0x268003f9 },
+ { 0x5fffc, 0x12c0012a },
+ { 0x5036c, 0x244003f9 },
+ { 0x5fffc, 0x1480012c },
+ { 0x50370, 0x224003f8 },
+ { 0x5fffc, 0x1640012e },
+ { 0x50374, 0x200003f8 },
+ { 0x5fffc, 0x1800012f },
+ { 0x50378, 0x1e0003f8 },
+ { 0x5fffc, 0x1a00012f },
+ { 0x5037c, 0x1c0003f8 },
+};
+
+static const struct ppp_table downscale_y_table_pt8topt1[] = {
+ { 0x5fffc, 0x0 },
+ { 0x50300, 0x7fc00000 },
+ { 0x5fffc, 0xff80000d },
+ { 0x50304, 0x7ec003f9 },
+ { 0x5fffc, 0xfec0001c },
+ { 0x50308, 0x7d4003f3 },
+ { 0x5fffc, 0xfe40002b },
+ { 0x5030c, 0x7b8003ed },
+ { 0x5fffc, 0xfd80003c },
+ { 0x50310, 0x794003e8 },
+ { 0x5fffc, 0xfcc0004d },
+ { 0x50314, 0x76c003e4 },
+ { 0x5fffc, 0xfc40005f },
+ { 0x50318, 0x73c003e0 },
+ { 0x5fffc, 0xfb800071 },
+ { 0x5031c, 0x708003de },
+ { 0x5fffc, 0xfac00085 },
+ { 0x50320, 0x6d0003db },
+ { 0x5fffc, 0xfa000098 },
+ { 0x50324, 0x698003d9 },
+ { 0x5fffc, 0xf98000ac },
+ { 0x50328, 0x654003d8 },
+ { 0x5fffc, 0xf8c000c1 },
+ { 0x5032c, 0x610003d7 },
+ { 0x5fffc, 0xf84000d5 },
+ { 0x50330, 0x5c8003d7 },
+ { 0x5fffc, 0xf7c000e9 },
+ { 0x50334, 0x580003d7 },
+ { 0x5fffc, 0xf74000fd },
+ { 0x50338, 0x534003d8 },
+ { 0x5fffc, 0xf6c00112 },
+ { 0x5033c, 0x4e8003d8 },
+ { 0x5fffc, 0xf6800126 },
+ { 0x50340, 0x494003da },
+ { 0x5fffc, 0xf600013a },
+ { 0x50344, 0x448003db },
+ { 0x5fffc, 0xf600014d },
+ { 0x50348, 0x3f4003dd },
+ { 0x5fffc, 0xf5c00160 },
+ { 0x5034c, 0x3a4003df },
+ { 0x5fffc, 0xf5c00172 },
+ { 0x50350, 0x354003e1 },
+ { 0x5fffc, 0xf5c00184 },
+ { 0x50354, 0x304003e3 },
+ { 0x5fffc, 0xf6000195 },
+ { 0x50358, 0x2b0003e6 },
+ { 0x5fffc, 0xf64001a6 },
+ { 0x5035c, 0x260003e8 },
+ { 0x5fffc, 0xf6c001b4 },
+ { 0x50360, 0x214003eb },
+ { 0x5fffc, 0xf78001c2 },
+ { 0x50364, 0x1c4003ee },
+ { 0x5fffc, 0xf80001cf },
+ { 0x50368, 0x17c003f1 },
+ { 0x5fffc, 0xf90001db },
+ { 0x5036c, 0x134003f3 },
+ { 0x5fffc, 0xfa0001e5 },
+ { 0x50370, 0xf0003f6 },
+ { 0x5fffc, 0xfb4001ee },
+ { 0x50374, 0xac003f9 },
+ { 0x5fffc, 0xfcc001f5 },
+ { 0x50378, 0x70003fb },
+ { 0x5fffc, 0xfe4001fb },
+ { 0x5037c, 0x34003fe },
+};
+
+static const struct ppp_table *downscale_y_table[PPP_DOWNSCALE_MAX] = {
+ [PPP_DOWNSCALE_PT2TOPT4] = downscale_y_table_pt2topt4,
+ [PPP_DOWNSCALE_PT4TOPT6] = downscale_y_table_pt4topt6,
+ [PPP_DOWNSCALE_PT6TOPT8] = downscale_y_table_pt6topt8,
+ [PPP_DOWNSCALE_PT8TOPT1] = downscale_y_table_pt8topt1,
+};
+
+void ppp_load_table(const struct ppp_table *table, int len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ PPP_WRITEL(table[i].val, table[i].reg);
+}
+
+void ppp_load_up_lut(void)
+{
+ ppp_load_table(upscale_table,
+ PPP_UPSCALE_MAX);
+}
+
+void ppp_load_gaussian_lut(void)
+{
+ ppp_load_table(mdp_gaussian_blur_table,
+ PPP_BLUR_SCALE_MAX);
+}
+
+void ppp_load_x_scale_table(int idx)
+{
+ ppp_load_table(downscale_x_table[idx], 64);
+}
+
+void ppp_load_y_scale_table(int idx)
+{
+ ppp_load_table(downscale_y_table[idx], 64);
+}
+
+uint32_t ppp_bpp(uint32_t type)
+{
+ if (type > MDP_IMGTYPE_LIMIT)
+ return 0;
+ return bytes_per_pixel[type];
+}
+
+uint32_t ppp_src_config(uint32_t type)
+{
+ if (type > MDP_IMGTYPE_LIMIT)
+ return 0;
+ return src_cfg_lut[type];
+}
+
+uint32_t ppp_out_config(uint32_t type)
+{
+ if (type > MDP_IMGTYPE_LIMIT)
+ return 0;
+ return out_cfg_lut[type];
+}
+
+uint32_t ppp_pack_pattern(uint32_t type)
+{
+ if (type > MDP_IMGTYPE_LIMIT)
+ return 0;
+ return pack_patt_lut[type];
+}
+
+uint32_t ppp_dst_op_reg(uint32_t type)
+{
+ if (type > MDP_IMGTYPE_LIMIT)
+ return 0;
+ return dst_op_reg[type];
+}
+
+uint32_t ppp_src_op_reg(uint32_t type)
+{
+ if (type > MDP_IMGTYPE_LIMIT)
+ return 0;
+ return src_op_reg[type];
+}
+
+bool ppp_per_p_alpha(uint32_t type)
+{
+ if (type > MDP_IMGTYPE_LIMIT)
+ return 0;
+ return per_pixel_alpha[type];
+}
+
+bool ppp_multi_plane(uint32_t type)
+{
+ if (type > MDP_IMGTYPE_LIMIT)
+ return 0;
+ return multi_plane[type];
+}
+
+uint32_t *ppp_default_pre_lut(void)
+{
+ return default_pre_lut_val;
+}
+
+uint32_t *ppp_default_post_lut(void)
+{
+ return default_post_lut_val;
+}
+
+struct ppp_csc_table *ppp_csc_rgb2yuv(void)
+{
+ return &rgb2yuv;
+}
+
+struct ppp_csc_table *ppp_csc_table2(void)
+{
+ return &default_table2;
+}
diff --git a/drivers/video/msm/mdss/mdp3_ppp_hwio.c b/drivers/video/msm/mdss/mdp3_ppp_hwio.c
new file mode 100644
index 0000000..309effc
--- /dev/null
+++ b/drivers/video/msm/mdss/mdp3_ppp_hwio.c
@@ -0,0 +1,1184 @@
+/* Copyright (c) 2007, 2012-2013 The Linux Foundation. All rights reserved.
+ * Copyright (C) 2007 Google Incorporated
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/file.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include "linux/proc_fs.h"
+
+#include "mdss_fb.h"
+#include "mdp3_ppp.h"
+#include "mdp3_hwio.h"
+
+/* SHIM Q Factor */
+#define PHI_Q_FACTOR 29
+#define PQF_PLUS_5 (PHI_Q_FACTOR + 5) /* due to 32 phases */
+#define PQF_PLUS_4 (PHI_Q_FACTOR + 4)
+#define PQF_PLUS_2 (PHI_Q_FACTOR + 2) /* to get 4.0 */
+#define PQF_MINUS_2 (PHI_Q_FACTOR - 2) /* to get 0.25 */
+#define PQF_PLUS_5_PLUS_2 (PQF_PLUS_5 + 2)
+#define PQF_PLUS_5_MINUS_2 (PQF_PLUS_5 - 2)
+
+static long long mdp_do_div(long long num, long long den)
+{
+ do_div(num, den);
+ return num;
+}
+
+static int mdp_calc_scale_params(uint32_t org, uint32_t dim_in,
+ uint32_t dim_out, bool is_W, int32_t *phase_init_ptr,
+ uint32_t *phase_step_ptr)
+{
+ bool rpa_on = false;
+ int init_phase = 0;
+ uint64_t numer = 0;
+ uint64_t denom = 0;
+ int64_t point5 = 1;
+ int64_t one = 1;
+ int64_t k1, k2, k3, k4; /* linear equation coefficients */
+ uint64_t int_mask;
+ uint64_t fract_mask;
+ uint64_t Os;
+ int64_t Osprime;
+ int64_t Od;
+ int64_t Odprime;
+ int64_t Oreq;
+ uint32_t mult;
+
+ /*
+ * The phase accumulator should really be rational for all cases in a
+ * general purpose polyphase scaler for a tiled architecture with
+ * non-zero * origin capability because there is no way to represent
+ * certain scale factors in fixed point regardless of precision.
+ * The error incurred in attempting to use fixed point is most
+ * eggregious for SF where 1/SF is an integral multiple of 1/3.
+ *
+ * Set the RPA flag for this dimension.
+ *
+ * In order for 1/SF (dim_in/dim_out) to be an integral multiple of
+ * 1/3, dim_out must be an integral multiple of 3.
+ */
+ if (!(dim_out % 3)) {
+ mult = dim_out / 3;
+ rpa_on = (!(dim_in % mult));
+ }
+
+ numer = dim_out;
+ denom = dim_in;
+
+ /*
+ * convert to U30.34 before division
+ *
+ * The K vectors carry 4 extra bits of precision
+ * and are rounded.
+ *
+ * We initially go 5 bits over then round by adding
+ * 1 and right shifting by 1
+ * so final result is U31.33
+ */
+ numer <<= PQF_PLUS_5;
+
+ /* now calculate the scale factor (aka k3) */
+ k3 = ((mdp_do_div(numer, denom) + 1) >> 1);
+
+ /* check scale factor for legal range [0.25 - 4.0] */
+ if (((k3 >> 4) < (1LL << PQF_MINUS_2)) ||
+ ((k3 >> 4) > (1LL << PQF_PLUS_2))) {
+ return -EINVAL;
+ }
+
+ /* calculate inverse scale factor (aka k1) for phase init */
+ numer = dim_in;
+ denom = dim_out;
+ numer <<= PQF_PLUS_5;
+ k1 = ((mdp_do_div(numer, denom) + 1) >> 1);
+
+ /*
+ * calculate initial phase and ROI overfetch
+ */
+ /* convert point5 & one to S39.24 (will always be positive) */
+ point5 <<= (PQF_PLUS_4 - 1);
+ one <<= PQF_PLUS_4;
+ k2 = ((k1 - one) >> 1);
+ init_phase = (int)(k2 >> 4);
+ k4 = ((k3 - one) >> 1);
+ if (k3 != one) {
+ /* calculate the masks */
+ fract_mask = one - 1;
+ int_mask = ~fract_mask;
+
+ if (!rpa_on) {
+ /*
+ * FIXED POINT IMPLEMENTATION
+ */
+ if (org) {
+ /*
+ * The complicated case; ROI origin != 0
+ * init_phase needs to be adjusted
+ * OF is also position dependent
+ */
+
+ /* map (org - .5) into destination space */
+ Os = ((uint64_t) org << 1) - 1;
+ Od = ((k3 * Os) >> 1) + k4;
+
+ /* take the ceiling */
+ Odprime = (Od & int_mask);
+ if (Odprime != Od)
+ Odprime += one;
+
+ /* now map that back to source space */
+ Osprime = (k1 * (Odprime >> PQF_PLUS_4)) + k2;
+
+ /* then floor & decrement to calc the required
+ starting coordinate */
+ Oreq = (Osprime & int_mask) - one;
+
+ /* calculate initial phase */
+ init_phase = (int)((Osprime - Oreq) >> 4);
+ }
+ } else {
+ /*
+ * RPA IMPLEMENTATION
+ *
+ * init_phase needs to be calculated in all RPA_on cases
+ * because it's a numerator, not a fixed point value.
+ */
+
+ /* map (org - .5) into destination space */
+ Os = ((uint64_t) org << PQF_PLUS_4) - point5;
+ Od = mdp_do_div((dim_out * (Os + point5)),
+ dim_in);
+ Od -= point5;
+
+ /* take the ceiling */
+ Odprime = (Od & int_mask);
+ if (Odprime != Od)
+ Odprime += one;
+
+ /* now map that back to source space */
+ Osprime =
+ mdp_do_div((dim_in * (Odprime + point5)),
+ dim_out);
+ Osprime -= point5;
+
+ /* then floor & decrement to calculate the required
+ starting coordinate */
+ Oreq = (Osprime & int_mask) - one;
+
+ /* calculate initial phase */
+ init_phase = (int)((Osprime - Oreq) >> 4);
+ }
+ }
+
+ /* return the scale parameters */
+ *phase_init_ptr = init_phase;
+ *phase_step_ptr = (uint32_t) (k1 >> 4);
+
+ return 0;
+}
+
+static int scale_idx(int factor)
+{
+ int idx;
+
+ if (factor > 80)
+ idx = PPP_DOWNSCALE_PT8TOPT1;
+ else if (factor > 60)
+ idx = PPP_DOWNSCALE_PT6TOPT8;
+ else if (factor > 40)
+ idx = PPP_DOWNSCALE_PT4TOPT6;
+ else
+ idx = PPP_DOWNSCALE_PT2TOPT4;
+
+ return idx;
+}
+
+inline int32_t comp_conv_rgb2yuv(int32_t comp, int32_t y_high,
+ int32_t y_low, int32_t c_high, int32_t c_low)
+{
+ if (comp < 0)
+ comp = 0;
+ if (comp > 255)
+ comp = 255;
+
+ /* clamp */
+ if (comp < y_low)
+ comp = y_low;
+ if (comp > y_high)
+ comp = y_high;
+ return comp;
+}
+
+static uint32_t conv_rgb2yuv(uint32_t input_pixel,
+ uint16_t *matrix_vector,
+ uint16_t *bv,
+ uint16_t *clamp_vector)
+{
+ uint8_t input_C2, input_C0, input_C1;
+ uint32_t output;
+ int32_t comp_C2, comp_C1, comp_C0, temp;
+ int32_t temp1, temp2, temp3;
+ int32_t matrix[9];
+ int32_t bias_vector[3];
+ int32_t Y_low_limit, Y_high_limit, C_low_limit, C_high_limit;
+ int32_t i;
+
+ input_C2 = (input_pixel >> 16) & 0xFF;
+ input_C1 = (input_pixel >> 8) & 0xFF;
+ input_C0 = (input_pixel >> 0) & 0xFF;
+
+ comp_C0 = input_C0;
+ comp_C1 = input_C1;
+ comp_C2 = input_C2;
+
+ for (i = 0; i < MDP_CSC_SIZE; i++)
+ matrix[i] =
+ ((int32_t) (((int32_t) matrix_vector[i]) << 20)) >> 20;
+
+ bias_vector[0] = (int32_t) (bv[0] & 0xFF);
+ bias_vector[1] = (int32_t) (bv[1] & 0xFF);
+ bias_vector[2] = (int32_t) (bv[2] & 0xFF);
+
+ Y_low_limit = (int32_t) clamp_vector[0];
+ Y_high_limit = (int32_t) clamp_vector[1];
+ C_low_limit = (int32_t) clamp_vector[2];
+ C_high_limit = (int32_t) clamp_vector[3];
+
+ /*
+ * Color Conversion
+ * reorder input colors
+ */
+ temp = comp_C2;
+ comp_C2 = comp_C1;
+ comp_C1 = comp_C0;
+ comp_C0 = temp;
+
+ /* matrix multiplication */
+ temp1 = comp_C0 * matrix[0] + comp_C1 * matrix[1] + comp_C2 * matrix[2];
+ temp2 = comp_C0 * matrix[3] + comp_C1 * matrix[4] + comp_C2 * matrix[5];
+ temp3 = comp_C0 * matrix[6] + comp_C1 * matrix[7] + comp_C2 * matrix[8];
+
+ comp_C0 = temp1 + 0x100;
+ comp_C1 = temp2 + 0x100;
+ comp_C2 = temp3 + 0x100;
+
+ /* take interger part */
+ comp_C0 >>= 9;
+ comp_C1 >>= 9;
+ comp_C2 >>= 9;
+
+ /* post bias (+) */
+ comp_C0 += bias_vector[0];
+ comp_C1 += bias_vector[1];
+ comp_C2 += bias_vector[2];
+
+ /* limit pixel to 8-bit */
+ comp_C0 = comp_conv_rgb2yuv(comp_C0, Y_high_limit,
+ Y_low_limit, C_high_limit, C_low_limit);
+ comp_C1 = comp_conv_rgb2yuv(comp_C1, Y_high_limit,
+ Y_low_limit, C_high_limit, C_low_limit);
+ comp_C2 = comp_conv_rgb2yuv(comp_C2, Y_high_limit,
+ Y_low_limit, C_high_limit, C_low_limit);
+
+ output = (comp_C2 << 16) | (comp_C1 << 8) | comp_C0;
+ return output;
+}
+
+inline void y_h_even_num(struct ppp_img_desc *img)
+{
+ img->roi.y = (img->roi.y / 2) * 2;
+ img->roi.height = (img->roi.height / 2) * 2;
+}
+
+inline void x_w_even_num(struct ppp_img_desc *img)
+{
+ img->roi.x = (img->roi.x / 2) * 2;
+ img->roi.width = (img->roi.width / 2) * 2;
+}
+
+bool check_if_rgb(int color)
+{
+ bool rgb = false;
+ switch (color) {
+ case MDP_RGB_565:
+ case MDP_BGR_565:
+ case MDP_RGB_888:
+ case MDP_BGR_888:
+ case MDP_BGRA_8888:
+ case MDP_RGBA_8888:
+ case MDP_ARGB_8888:
+ case MDP_XRGB_8888:
+ case MDP_RGBX_8888:
+ rgb = true;
+ default:
+ break;
+ }
+ return rgb;
+}
+
+static uint8_t *mdp_adjust_rot_addr(struct ppp_blit_op *iBuf,
+ uint8_t *addr, uint32_t bpp, uint32_t uv)
+{
+ uint32_t dest_ystride = iBuf->dst.prop.width * bpp;
+ uint32_t h_slice = 1;
+ if (0)
+ return 0;
+
+ if (uv && ((iBuf->dst.color_fmt == MDP_Y_CBCR_H2V2) ||
+ (iBuf->dst.color_fmt == MDP_Y_CRCB_H2V2)))
+ h_slice = 2;
+
+ if (((iBuf->mdp_op & MDPOP_ROT90) == MDPOP_ROT90) ^
+ ((iBuf->mdp_op & MDPOP_LR) == MDPOP_LR)) {
+ addr +=
+ (iBuf->dst.roi.width -
+ MIN(16, iBuf->dst.roi.width)) * bpp;
+ }
+ if ((iBuf->mdp_op & MDPOP_UD) == MDPOP_UD) {
+ if (1) {
+ addr +=
+ ((iBuf->dst.roi.height -
+ MIN(16, iBuf->dst.roi.height))/h_slice) *
+ dest_ystride;
+ } else {
+ addr +=
+ (iBuf->dst.roi.width -
+ MIN(16, iBuf->dst.roi.width)) * bpp;
+ }
+ }
+
+ return addr;
+}
+
+void mdp_adjust_start_addr(struct ppp_blit_op *blit_op,
+ struct ppp_img_desc *img, int v_slice,
+ int h_slice, int layer)
+{
+ uint32_t bpp = ppp_bpp(img->color_fmt);
+ int x = img->roi.x;
+ int y = img->roi.y;
+ uint32_t width = img->prop.width;
+
+ if (img->color_fmt == MDP_Y_CBCR_H2V2_ADRENO && layer == 0)
+ img->p0 += (x + y * ALIGN(width, 32)) * bpp;
+ else
+ img->p0 += (x + y * width) * bpp;
+ if (layer != 0)
+ img->p0 = mdp_adjust_rot_addr(blit_op, img->p0, bpp, 0);
+
+ if (img->p1) {
+ /*
+ * MDP_Y_CBCR_H2V2/MDP_Y_CRCB_H2V2 cosite for now
+ * we need to shift x direction same as y dir for offsite
+ */
+ if (img->color_fmt == MDP_Y_CBCR_H2V2_ADRENO
+ && layer == 0)
+ img->p1 += ((x / h_slice) * h_slice + ((y == 0) ? 0 :
+ (((y + 1) / v_slice - 1) * (ALIGN(width/2, 32) * 2))))
+ * bpp;
+ else
+ img->p1 += ((x / h_slice) * h_slice +
+ ((y == 0) ? 0 : ((y + 1) / v_slice - 1) * width)) * bpp;
+ if (layer != 0)
+ img->p1 = mdp_adjust_rot_addr(blit_op, img->p1, bpp, 1);
+ }
+}
+
+int load_ppp_lut(int tableType, uint32_t *lut)
+{
+ int i;
+ uint32_t base_addr;
+
+ base_addr = tableType ? MDP3_PPP_POST_LUT : MDP3_PPP_PRE_LUT;
+ for (i = 0; i < PPP_LUT_MAX; i++)
+ PPP_WRITEL(lut[i], base_addr + MDP3_PPP_LUTn(i));
+
+ return 0;
+}
+
+/* Configure Primary CSC Matrix */
+int load_primary_matrix(struct ppp_csc_table *csc)
+{
+ int i;
+
+ for (i = 0; i < MDP_CSC_SIZE; i++)
+ PPP_WRITEL(csc->fwd_matrix[i], MDP3_PPP_CSC_PFMVn(i));
+
+ for (i = 0; i < MDP_CSC_SIZE; i++)
+ PPP_WRITEL(csc->rev_matrix[i], MDP3_PPP_CSC_PRMVn(i));
+
+ for (i = 0; i < MDP_BV_SIZE; i++)
+ PPP_WRITEL(csc->bv[i], MDP3_PPP_CSC_PBVn(i));
+
+ for (i = 0; i < MDP_LV_SIZE; i++)
+ PPP_WRITEL(csc->lv[i], MDP3_PPP_CSC_PLVn(i));
+
+ return 0;
+}
+
+/* Load Secondary CSC Matrix */
+int load_secondary_matrix(struct ppp_csc_table *csc)
+{
+ int i;
+
+ for (i = 0; i < MDP_CSC_SIZE; i++)
+ PPP_WRITEL(csc->fwd_matrix[i], MDP3_PPP_CSC_SFMVn(i));
+
+ for (i = 0; i < MDP_CSC_SIZE; i++)
+ PPP_WRITEL(csc->rev_matrix[i], MDP3_PPP_CSC_SRMVn(i));
+
+ for (i = 0; i < MDP_BV_SIZE; i++)
+ PPP_WRITEL(csc->bv[i], MDP3_PPP_CSC_SBVn(i));
+
+ for (i = 0; i < MDP_LV_SIZE; i++)
+ PPP_WRITEL(csc->lv[i], MDP3_PPP_CSC_SLVn(i));
+ return 0;
+}
+
+int load_csc_matrix(int matrix_type, struct ppp_csc_table *csc)
+{
+ if (matrix_type == CSC_PRIMARY_MATRIX)
+ return load_primary_matrix(csc);
+
+ return load_secondary_matrix(csc);
+}
+
+int config_ppp_src(struct ppp_img_desc *src)
+{
+ uint32_t val;
+
+ val = ((src->roi.height & MDP3_PPP_XY_MASK) << MDP3_PPP_XY_OFFSET) |
+ (src->roi.width & MDP3_PPP_XY_MASK);
+ PPP_WRITEL(val, MDP3_PPP_SRC_SIZE);
+
+ PPP_WRITEL(src->p0, MDP3_PPP_SRCP0_ADDR);
+ PPP_WRITEL(src->p1, MDP3_PPP_SRCP1_ADDR);
+ PPP_WRITEL(src->p3, MDP3_PPP_SRCP3_ADDR);
+
+ val = (src->stride0 & MDP3_PPP_STRIDE_MASK) |
+ ((src->stride1 & MDP3_PPP_STRIDE_MASK) <<
+ MDP3_PPP_STRIDE1_OFFSET);
+ PPP_WRITEL(val, MDP3_PPP_SRC_YSTRIDE1_ADDR);
+ val = ((src->stride2 & MDP3_PPP_STRIDE_MASK) <<
+ MDP3_PPP_STRIDE1_OFFSET);
+ PPP_WRITEL(val, MDP3_PPP_SRC_YSTRIDE2_ADDR);
+
+ val = ppp_src_config(src->color_fmt);
+ val |= (src->roi.x % 2) ? PPP_SRC_BPP_ROI_ODD_X : 0;
+ val |= (src->roi.y % 2) ? PPP_SRC_BPP_ROI_ODD_Y : 0;
+ PPP_WRITEL(val, MDP3_PPP_SRC_FORMAT);
+ PPP_WRITEL(ppp_pack_pattern(src->color_fmt),
+ MDP3_PPP_SRC_UNPACK_PATTERN1);
+ return 0;
+}
+
+int config_ppp_out(struct ppp_img_desc *dst)
+{
+ uint32_t val;
+ bool pseudoplanr_output = false;
+
+ switch (dst->color_fmt) {
+ case MDP_Y_CBCR_H2V2:
+ case MDP_Y_CRCB_H2V2:
+ case MDP_Y_CBCR_H2V1:
+ case MDP_Y_CRCB_H2V1:
+ pseudoplanr_output = true;
+ break;
+ default:
+ break;
+ }
+ val = ppp_out_config(dst->color_fmt);
+ if (pseudoplanr_output)
+ val |= PPP_DST_PLANE_PSEUDOPLN;
+ PPP_WRITEL(val, MDP3_PPP_OUT_FORMAT);
+ PPP_WRITEL(ppp_pack_pattern(dst->color_fmt),
+ MDP3_PPP_OUT_PACK_PATTERN1);
+
+ val = ((dst->roi.height & MDP3_PPP_XY_MASK) << MDP3_PPP_XY_OFFSET) |
+ (dst->roi.width & MDP3_PPP_XY_MASK);
+ PPP_WRITEL(val, MDP3_PPP_OUT_SIZE);
+
+ PPP_WRITEL(dst->p0, MDP3_PPP_OUTP0_ADDR);
+ PPP_WRITEL(dst->p1, MDP3_PPP_OUTP1_ADDR);
+ PPP_WRITEL(dst->p3, MDP3_PPP_OUTP3_ADDR);
+
+ val = (dst->stride0 & MDP3_PPP_STRIDE_MASK) |
+ ((dst->stride1 & MDP3_PPP_STRIDE_MASK) <<
+ MDP3_PPP_STRIDE1_OFFSET);
+ PPP_WRITEL(val, MDP3_PPP_OUT_YSTRIDE1_ADDR);
+ val = ((dst->stride2 & MDP3_PPP_STRIDE_MASK) <<
+ MDP3_PPP_STRIDE1_OFFSET);
+ PPP_WRITEL(val, MDP3_PPP_OUT_YSTRIDE2_ADDR);
+ return 0;
+}
+
+int config_ppp_background(struct ppp_img_desc *bg)
+{
+ uint32_t val;
+
+ PPP_WRITEL(bg->p0, MDP3_PPP_BGP0_ADDR);
+ PPP_WRITEL(bg->p1, MDP3_PPP_BGP1_ADDR);
+ PPP_WRITEL(bg->p3, MDP3_PPP_BGP3_ADDR);
+
+ val = (bg->stride0 & MDP3_PPP_STRIDE_MASK) |
+ ((bg->stride1 & MDP3_PPP_STRIDE_MASK) <<
+ MDP3_PPP_STRIDE1_OFFSET);
+ PPP_WRITEL(val, MDP3_PPP_BG_YSTRIDE1_ADDR);
+ val = ((bg->stride2 & MDP3_PPP_STRIDE_MASK) <<
+ MDP3_PPP_STRIDE1_OFFSET);
+ PPP_WRITEL(val, MDP3_PPP_BG_YSTRIDE2_ADDR);
+
+ PPP_WRITEL(ppp_src_config(bg->color_fmt),
+ MDP3_PPP_BG_FORMAT);
+ PPP_WRITEL(ppp_pack_pattern(bg->color_fmt),
+ MDP3_PPP_BG_UNPACK_PATTERN1);
+ return 0;
+}
+
+void ppp_edge_rep_luma_pixel(struct ppp_blit_op *blit_op,
+ struct ppp_edge_rep *er)
+{
+ if (blit_op->mdp_op & MDPOP_ASCALE) {
+
+ er->is_scale_enabled = 1;
+
+ if (blit_op->mdp_op & MDPOP_ROT90) {
+ er->dst_roi_width = blit_op->dst.roi.height;
+ er->dst_roi_height = blit_op->dst.roi.width;
+ } else {
+ er->dst_roi_width = blit_op->dst.roi.width;
+ er->dst_roi_height = blit_op->dst.roi.height;
+ }
+
+ /*
+ * Find out the luma pixels needed for scaling in the
+ * x direction (LEFT and RIGHT). Locations of pixels are
+ * relative to the ROI. Upper-left corner of ROI corresponds
+ * to coordinates (0,0). Also set the number of luma pixel
+ * to repeat.
+ */
+ if (blit_op->src.roi.width > 3 * er->dst_roi_width) {
+ /* scale factor < 1/3 */
+ er->luma_interp_point_right =
+ (blit_op->src.roi.width - 1);
+ } else if (blit_op->src.roi.width == 3 * er->dst_roi_width) {
+ /* scale factor == 1/3 */
+ er->luma_interp_point_right =
+ (blit_op->src.roi.width - 1) + 1;
+ er->luma_repeat_right = 1;
+ } else if ((blit_op->src.roi.width > er->dst_roi_width) &&
+ (blit_op->src.roi.width < 3 * er->dst_roi_width)) {
+ /* 1/3 < scale factor < 1 */
+ er->luma_interp_point_left = -1;
+ er->luma_interp_point_right =
+ (blit_op->src.roi.width - 1) + 1;
+ er->luma_repeat_left = 1;
+ er->luma_repeat_right = 1;
+ } else if (blit_op->src.roi.width == er->dst_roi_width) {
+ /* scale factor == 1 */
+ er->luma_interp_point_left = -1;
+ er->luma_interp_point_right =
+ (blit_op->src.roi.width - 1) + 2;
+ er->luma_repeat_left = 1;
+ er->luma_repeat_right = 2;
+ } else {
+ /* scale factor > 1 */
+ er->luma_interp_point_left = -2;
+ er->luma_interp_point_right =
+ (blit_op->src.roi.width - 1) + 2;
+ er->luma_repeat_left = 2;
+ er->luma_repeat_right = 2;
+ }
+
+ /*
+ * Find out the number of pixels needed for scaling in the
+ * y direction (TOP and BOTTOM). Locations of pixels are
+ * relative to the ROI. Upper-left corner of ROI corresponds
+ * to coordinates (0,0). Also set the number of luma pixel
+ * to repeat.
+ */
+ if (blit_op->src.roi.height > 3 * er->dst_roi_height) {
+ er->luma_interp_point_bottom =
+ (blit_op->src.roi.height - 1);
+ } else if (blit_op->src.roi.height == 3 * er->dst_roi_height) {
+ er->luma_interp_point_bottom =
+ (blit_op->src.roi.height - 1) + 1;
+ er->luma_repeat_bottom = 1;
+ } else if ((blit_op->src.roi.height > er->dst_roi_height) &&
+ (blit_op->src.roi.height < 3 * er->dst_roi_height)) {
+ er->luma_interp_point_top = -1;
+ er->luma_interp_point_bottom =
+ (blit_op->src.roi.height - 1) + 1;
+ er->luma_repeat_top = 1;
+ er->luma_repeat_bottom = 1;
+ } else if (blit_op->src.roi.height == er->dst_roi_height) {
+ er->luma_interp_point_top = -1;
+ er->luma_interp_point_bottom =
+ (blit_op->src.roi.height - 1) + 2;
+ er->luma_repeat_top = 1;
+ er->luma_repeat_bottom = 2;
+ } else {
+ er->luma_interp_point_top = -2;
+ er->luma_interp_point_bottom =
+ (blit_op->src.roi.height - 1) + 2;
+ er->luma_repeat_top = 2;
+ er->luma_repeat_bottom = 2;
+ }
+ } else {
+ /*
+ * Since no scaling needed, Tile Fetch does not require any
+ * more luma pixel than what the ROI contains.
+ */
+ er->luma_interp_point_right =
+ (int32_t) (blit_op->src.roi.width - 1);
+ er->luma_interp_point_bottom =
+ (int32_t) (blit_op->src.roi.height - 1);
+ }
+ /* After adding the ROI offsets, we have locations of
+ * luma_interp_points relative to the image.
+ */
+ er->luma_interp_point_left += (int32_t) (blit_op->src.roi.x);
+ er->luma_interp_point_right += (int32_t) (blit_op->src.roi.x);
+ er->luma_interp_point_top += (int32_t) (blit_op->src.roi.y);
+ er->luma_interp_point_bottom += (int32_t) (blit_op->src.roi.y);
+}
+
+void ppp_edge_rep_chroma_pixel(struct ppp_blit_op *blit_op,
+ struct ppp_edge_rep *er)
+{
+ bool chroma_edge_enable = true;
+ uint32_t is_yuv_offsite_vertical = 0;
+
+ /* find out which chroma pixels are needed for chroma upsampling. */
+ switch (blit_op->src.color_fmt) {
+ case MDP_Y_CBCR_H2V1:
+ case MDP_Y_CRCB_H2V1:
+ case MDP_YCRYCB_H2V1:
+ er->chroma_interp_point_left = er->luma_interp_point_left >> 1;
+ er->chroma_interp_point_right =
+ (er->luma_interp_point_right + 1) >> 1;
+ er->chroma_interp_point_top = er->luma_interp_point_top;
+ er->chroma_interp_point_bottom = er->luma_interp_point_bottom;
+ break;
+
+ case MDP_Y_CBCR_H2V2:
+ case MDP_Y_CBCR_H2V2_ADRENO:
+ case MDP_Y_CRCB_H2V2:
+ er->chroma_interp_point_left = er->luma_interp_point_left >> 1;
+ er->chroma_interp_point_right =
+ (er->luma_interp_point_right + 1) >> 1;
+ er->chroma_interp_point_top =
+ (er->luma_interp_point_top - 1) >> 1;
+ er->chroma_interp_point_bottom =
+ (er->luma_interp_point_bottom + 1) >> 1;
+ is_yuv_offsite_vertical = 1;
+ break;
+
+ default:
+ chroma_edge_enable = false;
+ er->chroma_interp_point_left = er->luma_interp_point_left;
+ er->chroma_interp_point_right = er->luma_interp_point_right;
+ er->chroma_interp_point_top = er->luma_interp_point_top;
+ er->chroma_interp_point_bottom = er->luma_interp_point_bottom;
+
+ break;
+ }
+
+ if (chroma_edge_enable) {
+ /* Defines which chroma pixels belongs to the roi */
+ switch (blit_op->src.color_fmt) {
+ case MDP_Y_CBCR_H2V1:
+ case MDP_Y_CRCB_H2V1:
+ case MDP_YCRYCB_H2V1:
+ er->chroma_bound_left = blit_op->src.roi.x / 2;
+ /* there are half as many chroma pixel as luma pixels */
+ er->chroma_bound_right =
+ (blit_op->src.roi.width +
+ blit_op->src.roi.x - 1) / 2;
+ er->chroma_bound_top = blit_op->src.roi.y;
+ er->chroma_bound_bottom =
+ (blit_op->src.roi.height + blit_op->src.roi.y - 1);
+ break;
+ case MDP_Y_CBCR_H2V2:
+ case MDP_Y_CBCR_H2V2_ADRENO:
+ case MDP_Y_CRCB_H2V2:
+ /*
+ * cosite in horizontal dir, and offsite in vertical dir
+ * width of chroma ROI is 1/2 of size of luma ROI
+ * height of chroma ROI is 1/2 of size of luma ROI
+ */
+ er->chroma_bound_left = blit_op->src.roi.x / 2;
+ er->chroma_bound_right =
+ (blit_op->src.roi.width +
+ blit_op->src.roi.x - 1) / 2;
+ er->chroma_bound_top = blit_op->src.roi.y / 2;
+ er->chroma_bound_bottom =
+ (blit_op->src.roi.height +
+ blit_op->src.roi.y - 1) / 2;
+ break;
+
+ default:
+ /*
+ * If no valid chroma sub-sampling format specified,
+ * assume 4:4:4 ( i.e. fully sampled).
+ */
+ er->chroma_bound_left = blit_op->src.roi.x;
+ er->chroma_bound_right = blit_op->src.roi.width +
+ blit_op->src.roi.x - 1;
+ er->chroma_bound_top = blit_op->src.roi.y;
+ er->chroma_bound_bottom =
+ (blit_op->src.roi.height + blit_op->src.roi.y - 1);
+ break;
+ }
+
+ /*
+ * Knowing which chroma pixels are needed, and which chroma
+ * pixels belong to the ROI (i.e. available for fetching ),
+ * calculate how many chroma pixels Tile Fetch needs to
+ * duplicate. If any required chroma pixels falls outside
+ * of the ROI, Tile Fetch must obtain them by replicating
+ * pixels.
+ */
+ if (er->chroma_bound_left > er->chroma_interp_point_left)
+ er->chroma_repeat_left =
+ er->chroma_bound_left -
+ er->chroma_interp_point_left;
+ else
+ er->chroma_repeat_left = 0;
+
+ if (er->chroma_interp_point_right > er->chroma_bound_right)
+ er->chroma_repeat_right =
+ er->chroma_interp_point_right -
+ er->chroma_bound_right;
+ else
+ er->chroma_repeat_right = 0;
+
+ if (er->chroma_bound_top > er->chroma_interp_point_top)
+ er->chroma_repeat_top =
+ er->chroma_bound_top -
+ er->chroma_interp_point_top;
+ else
+ er->chroma_repeat_top = 0;
+
+ if (er->chroma_interp_point_bottom > er->chroma_bound_bottom)
+ er->chroma_repeat_bottom =
+ er->chroma_interp_point_bottom -
+ er->chroma_bound_bottom;
+ else
+ er->chroma_repeat_bottom = 0;
+
+ if (er->is_scale_enabled && (blit_op->src.roi.height == 1)
+ && is_yuv_offsite_vertical) {
+ er->chroma_repeat_bottom = 3;
+ er->chroma_repeat_top = 0;
+ }
+ }
+}
+
+int config_ppp_edge_rep(struct ppp_blit_op *blit_op)
+{
+ uint32_t reg = 0;
+ struct ppp_edge_rep er;
+
+ memset(&er, 0, sizeof(er));
+
+ ppp_edge_rep_luma_pixel(blit_op, &er);
+
+ /*
+ * After adding the ROI offsets, we have locations of
+ * chroma_interp_points relative to the image.
+ */
+ er.chroma_interp_point_left = er.luma_interp_point_left;
+ er.chroma_interp_point_right = er.luma_interp_point_right;
+ er.chroma_interp_point_top = er.luma_interp_point_top;
+ er.chroma_interp_point_bottom = er.luma_interp_point_bottom;
+
+ ppp_edge_rep_chroma_pixel(blit_op, &er);
+ /* ensure repeats are >=0 and no larger than 3 pixels */
+ if ((er.chroma_repeat_left < 0) || (er.chroma_repeat_right < 0) ||
+ (er.chroma_repeat_top < 0) || (er.chroma_repeat_bottom < 0))
+ return -EINVAL;
+ if ((er.chroma_repeat_left > 3) || (er.chroma_repeat_right > 3) ||
+ (er.chroma_repeat_top > 3) || (er.chroma_repeat_bottom > 3))
+ return -EINVAL;
+ if ((er.luma_repeat_left < 0) || (er.luma_repeat_right < 0) ||
+ (er.luma_repeat_top < 0) || (er.luma_repeat_bottom < 0))
+ return -EINVAL;
+ if ((er.luma_repeat_left > 3) || (er.luma_repeat_right > 3) ||
+ (er.luma_repeat_top > 3) || (er.luma_repeat_bottom > 3))
+ return -EINVAL;
+
+ reg |= (er.chroma_repeat_left & 3) << MDP_LEFT_CHROMA;
+ reg |= (er.chroma_repeat_right & 3) << MDP_RIGHT_CHROMA;
+ reg |= (er.chroma_repeat_top & 3) << MDP_TOP_CHROMA;
+ reg |= (er.chroma_repeat_bottom & 3) << MDP_BOTTOM_CHROMA;
+ reg |= (er.luma_repeat_left & 3) << MDP_LEFT_LUMA;
+ reg |= (er.luma_repeat_right & 3) << MDP_RIGHT_LUMA;
+ reg |= (er.luma_repeat_top & 3) << MDP_TOP_LUMA;
+ reg |= (er.luma_repeat_bottom & 3) << MDP_BOTTOM_LUMA;
+ PPP_WRITEL(reg, MDP3_PPP_SRC_EDGE_REP);
+ return 0;
+}
+
+int config_ppp_bg_edge_rep(struct ppp_blit_op *blit_op)
+{
+ uint32_t reg = 0;
+
+ switch (blit_op->dst.color_fmt) {
+ case MDP_Y_CBCR_H2V2:
+ case MDP_Y_CRCB_H2V2:
+ if (blit_op->dst.roi.y == 0)
+ reg |= BIT(MDP_TOP_CHROMA);
+
+ if ((blit_op->dst.roi.y + blit_op->dst.roi.height) ==
+ blit_op->dst.prop.height) {
+ reg |= BIT(MDP_BOTTOM_CHROMA);
+ }
+
+ if (((blit_op->dst.roi.x + blit_op->dst.roi.width) ==
+ blit_op->dst.prop.width) &&
+ ((blit_op->dst.roi.width % 2) == 0))
+ reg |= BIT(MDP_RIGHT_CHROMA);
+ break;
+ case MDP_Y_CBCR_H2V1:
+ case MDP_Y_CRCB_H2V1:
+ case MDP_YCRYCB_H2V1:
+ if (((blit_op->dst.roi.x + blit_op->dst.roi.width) ==
+ blit_op->dst.prop.width) &&
+ ((blit_op->dst.roi.width % 2) == 0))
+ reg |= BIT(MDP_RIGHT_CHROMA);
+ break;
+ default:
+ break;
+ }
+ PPP_WRITEL(reg, MDP3_PPP_BG_EDGE_REP);
+ return 0;
+}
+
+int config_ppp_lut(uint32_t *pppop_reg_ptr, int lut_c0_en,
+ int lut_c1_en, int lut_c2_en)
+{
+ if (lut_c0_en)
+ *pppop_reg_ptr |= MDP_LUT_C0_EN;
+ if (lut_c1_en)
+ *pppop_reg_ptr |= MDP_LUT_C1_EN;
+ if (lut_c2_en)
+ *pppop_reg_ptr |= MDP_LUT_C2_EN;
+ return 0;
+}
+
+int config_ppp_scale(struct ppp_blit_op *blit_op, uint32_t *pppop_reg_ptr)
+{
+ struct ppp_img_desc *src = &blit_op->src;
+ struct ppp_img_desc *dst = &blit_op->dst;
+ uint32_t dstW, dstH;
+ uint32_t x_fac, y_fac;
+ uint32_t mdp_blur = 0;
+ uint32_t phase_init_x, phase_init_y, phase_step_x, phase_step_y;
+ int x_idx, y_idx;
+
+ if (blit_op->mdp_op & MDPOP_ASCALE) {
+ if (blit_op->mdp_op & MDPOP_ROT90) {
+ dstW = dst->roi.height;
+ dstH = dst->roi.width;
+ } else {
+ dstW = dst->roi.width;
+ dstH = dst->roi.height;
+ }
+ *pppop_reg_ptr |=
+ (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON);
+
+ mdp_blur = blit_op->mdp_op & MDPOP_BLUR;
+
+ if ((dstW != src->roi.width) ||
+ (dstH != src->roi.height) || mdp_blur) {
+
+ mdp_calc_scale_params(blit_op->src.roi.x,
+ blit_op->src.roi.width,
+ dstW, 1, &phase_init_x,
+ &phase_step_x);
+ mdp_calc_scale_params(blit_op->src.roi.y,
+ blit_op->src.roi.height,
+ dstH, 0, &phase_init_y,
+ &phase_step_y);
+
+ PPP_WRITEL(phase_init_x, MDP3_PPP_SCALE_PHASEX_INIT);
+ PPP_WRITEL(phase_init_y, MDP3_PPP_SCALE_PHASEY_INIT);
+ PPP_WRITEL(phase_step_x, MDP3_PPP_SCALE_PHASEX_STEP);
+ PPP_WRITEL(phase_step_y, MDP3_PPP_SCALE_PHASEY_STEP);
+
+
+ if (dstW > src->roi.width || dstW > src->roi.height)
+ ppp_load_up_lut();
+
+ if (mdp_blur)
+ ppp_load_gaussian_lut();
+
+ if (dstW <= src->roi.width) {
+ x_fac = (dstW * 100) / src->roi.width;
+ x_idx = scale_idx(x_fac);
+ ppp_load_x_scale_table(x_idx);
+ }
+ if (dstH <= src->roi.height) {
+ y_fac = (dstH * 100) / src->roi.height;
+ y_idx = scale_idx(y_fac);
+ ppp_load_y_scale_table(y_idx);
+ }
+
+ } else {
+ blit_op->mdp_op &= ~(MDPOP_ASCALE);
+ }
+ }
+ config_ppp_edge_rep(blit_op);
+ config_ppp_bg_edge_rep(blit_op);
+ return 0;
+}
+
+int config_ppp_csc(int src_color, int dst_color, uint32_t *pppop_reg_ptr)
+{
+ bool inputRGB, outputRGB;
+
+ inputRGB = check_if_rgb(src_color);
+ outputRGB = check_if_rgb(dst_color);
+
+ if ((!inputRGB) && (outputRGB))
+ *pppop_reg_ptr |= PPP_OP_CONVERT_YCBCR2RGB |
+ PPP_OP_CONVERT_ON;
+ if ((inputRGB) && (!outputRGB))
+ *pppop_reg_ptr |= PPP_OP_CONVERT_ON;
+
+ return 0;
+}
+
+int config_ppp_blend(struct ppp_blit_op *blit_op,
+ uint32_t *pppop_reg_ptr)
+{
+ struct ppp_csc_table *csc;
+ uint32_t alpha, trans_color;
+ uint32_t val = 0;
+ int c_fmt = blit_op->src.color_fmt;
+ int bg_alpha;
+
+ csc = ppp_csc_rgb2yuv();
+ alpha = blit_op->blend.const_alpha;
+ trans_color = blit_op->blend.trans_color;
+ if (blit_op->mdp_op & MDPOP_FG_PM_ALPHA) {
+ if (ppp_per_p_alpha(c_fmt)) {
+ *pppop_reg_ptr |= PPP_OP_ROT_ON |
+ PPP_OP_BLEND_ON |
+ PPP_OP_BLEND_CONSTANT_ALPHA;
+ } else {
+ if ((blit_op->mdp_op & MDPOP_ALPHAB)
+ && (blit_op->blend.const_alpha == 0xff)) {
+ blit_op->mdp_op &= ~(MDPOP_ALPHAB);
+ }
+
+ if ((blit_op->mdp_op & MDPOP_ALPHAB)
+ || (blit_op->mdp_op & MDPOP_TRANSP)) {
+
+ *pppop_reg_ptr |= PPP_OP_ROT_ON |
+ PPP_OP_BLEND_ON |
+ PPP_OP_BLEND_CONSTANT_ALPHA |
+ PPP_OP_BLEND_ALPHA_BLEND_NORMAL;
+ }
+ }
+
+ bg_alpha = PPP_BLEND_BG_USE_ALPHA_SEL |
+ PPP_BLEND_BG_ALPHA_REVERSE;
+
+ if ((ppp_per_p_alpha(c_fmt)) && !(blit_op->mdp_op &
+ MDPOP_LAYER_IS_FG)) {
+ bg_alpha |= PPP_BLEND_BG_SRCPIXEL_ALPHA;
+ } else {
+ bg_alpha |= PPP_BLEND_BG_CONSTANT_ALPHA;
+ bg_alpha |= blit_op->blend.const_alpha << 24;
+ }
+ PPP_WRITEL(bg_alpha, MDP3_PPP_BLEND_BG_ALPHA_SEL);
+
+ if (blit_op->mdp_op & MDPOP_TRANSP)
+ *pppop_reg_ptr |= PPP_BLEND_CALPHA_TRNASP;
+ } else if (ppp_per_p_alpha(c_fmt)) {
+ if (blit_op->mdp_op & MDPOP_LAYER_IS_FG)
+ *pppop_reg_ptr |= PPP_OP_ROT_ON |
+ PPP_OP_BLEND_ON |
+ PPP_OP_BLEND_CONSTANT_ALPHA;
+ else
+ *pppop_reg_ptr |= PPP_OP_ROT_ON |
+ PPP_OP_BLEND_ON |
+ PPP_OP_BLEND_SRCPIXEL_ALPHA;
+ PPP_WRITEL(0, MDP3_PPP_BLEND_BG_ALPHA_SEL);
+ } else {
+ if ((blit_op->mdp_op & MDPOP_ALPHAB)
+ && (blit_op->blend.const_alpha == 0xff)) {
+ blit_op->mdp_op &=
+ ~(MDPOP_ALPHAB);
+ }
+
+ if ((blit_op->mdp_op & MDPOP_ALPHAB)
+ || (blit_op->mdp_op & MDPOP_TRANSP)) {
+ *pppop_reg_ptr |= PPP_OP_ROT_ON |
+ PPP_OP_BLEND_ON |
+ PPP_OP_BLEND_CONSTANT_ALPHA |
+ PPP_OP_BLEND_ALPHA_BLEND_NORMAL;
+ }
+
+ if (blit_op->mdp_op & MDPOP_TRANSP)
+ *pppop_reg_ptr |=
+ PPP_BLEND_CALPHA_TRNASP;
+ PPP_WRITEL(0, MDP3_PPP_BLEND_BG_ALPHA_SEL);
+ }
+
+ if (*pppop_reg_ptr & PPP_OP_BLEND_ON) {
+ blit_op->bg = blit_op->dst;
+ config_ppp_background(&blit_op->bg);
+
+ if (blit_op->dst.color_fmt == MDP_YCRYCB_H2V1) {
+ *pppop_reg_ptr |= PPP_OP_BG_CHROMA_H2V1;
+ if (blit_op->mdp_op & MDPOP_TRANSP) {
+ trans_color = conv_rgb2yuv(trans_color,
+ &csc->fwd_matrix[0],
+ &csc->bv[0],
+ &csc->lv[0]);
+ }
+ }
+ }
+ val = (alpha << MDP_BLEND_CONST_ALPHA);
+ val |= (trans_color & MDP_BLEND_TRASP_COL_MASK);
+ PPP_WRITEL(val, MDP3_PPP_BLEND_PARAM);
+ return 0;
+}
+
+int config_ppp_rotation(uint32_t mdp_op, uint32_t *pppop_reg_ptr)
+{
+ *pppop_reg_ptr |= PPP_OP_ROT_ON;
+
+ if (mdp_op & MDPOP_ROT90)
+ *pppop_reg_ptr |= PPP_OP_ROT_90;
+ if (mdp_op & MDPOP_LR)
+ *pppop_reg_ptr |= PPP_OP_FLIP_LR;
+ if (mdp_op & MDPOP_UD)
+ *pppop_reg_ptr |= PPP_OP_FLIP_UD;
+
+ return 0;
+}
+
+int config_ppp_op_mode(struct ppp_blit_op *blit_op)
+{
+ uint32_t ppp_operation_reg = 0;
+ int sv_slice, sh_slice;
+ int dv_slice, dh_slice;
+
+ sv_slice = sh_slice = dv_slice = dh_slice = 1;
+
+ ppp_operation_reg |= ppp_dst_op_reg(blit_op->dst.color_fmt);
+ switch (blit_op->dst.color_fmt) {
+ case MDP_Y_CBCR_H2V2:
+ case MDP_Y_CRCB_H2V2:
+ y_h_even_num(&blit_op->dst);
+ y_h_even_num(&blit_op->src);
+ dv_slice = 2;
+ case MDP_Y_CBCR_H2V1:
+ case MDP_Y_CRCB_H2V1:
+ case MDP_YCRYCB_H2V1:
+ x_w_even_num(&blit_op->dst);
+ x_w_even_num(&blit_op->src);
+ dh_slice = 2;
+ break;
+ default:
+ break;
+ }
+
+ ppp_operation_reg |= ppp_src_op_reg(blit_op->src.color_fmt);
+ switch (blit_op->src.color_fmt) {
+ case MDP_Y_CBCR_H2V2:
+ case MDP_Y_CBCR_H2V2_ADRENO:
+ case MDP_Y_CRCB_H2V2:
+ sh_slice = sv_slice = 2;
+ break;
+ case MDP_YCRYCB_H2V1:
+ x_w_even_num(&blit_op->dst);
+ x_w_even_num(&blit_op->src);
+ case MDP_Y_CBCR_H2V1:
+ case MDP_Y_CRCB_H2V1:
+ sh_slice = 2;
+ break;
+ default:
+ break;
+ }
+
+ config_ppp_csc(blit_op->src.color_fmt,
+ blit_op->dst.color_fmt, &ppp_operation_reg);
+
+ if (blit_op->mdp_op & MDPOP_DITHER)
+ ppp_operation_reg |= PPP_OP_DITHER_EN;
+
+ if (blit_op->mdp_op & MDPOP_ROTATION)
+ config_ppp_rotation(blit_op->mdp_op, &ppp_operation_reg);
+
+ if (blit_op->src.color_fmt == MDP_Y_CBCR_H2V2_ADRENO) {
+ blit_op->src.stride0 = ALIGN(blit_op->src.prop.width, 32) *
+ ppp_bpp(blit_op->src.color_fmt);
+ blit_op->src.stride1 = 2 * ALIGN(blit_op->src.prop.width/2, 32);
+ } else {
+ blit_op->src.stride0 = blit_op->src.prop.width *
+ ppp_bpp(blit_op->src.color_fmt);
+ blit_op->src.stride1 = blit_op->src.stride0;
+ }
+
+ blit_op->dst.stride0 = blit_op->dst.prop.width *
+ ppp_bpp(blit_op->dst.color_fmt);
+
+ if (ppp_multi_plane(blit_op->dst.color_fmt)) {
+ blit_op->dst.p1 = blit_op->dst.p0;
+ blit_op->dst.p1 += blit_op->dst.prop.width *
+ blit_op->dst.prop.height *
+ ppp_bpp(blit_op->dst.color_fmt);
+ } else {
+ blit_op->dst.p1 = NULL;
+ }
+
+ /* Jumping from Y-Plane to Chroma Plane */
+ /* first pixel addr calculation */
+ mdp_adjust_start_addr(blit_op, &blit_op->src, sv_slice, sh_slice, 0);
+ mdp_adjust_start_addr(blit_op, &blit_op->dst, dv_slice, dh_slice, 2);
+
+ config_ppp_scale(blit_op, &ppp_operation_reg);
+
+ config_ppp_blend(blit_op, &ppp_operation_reg);
+
+ config_ppp_src(&blit_op->src);
+ config_ppp_out(&blit_op->dst);
+ PPP_WRITEL(ppp_operation_reg, MDP3_PPP_OP_MODE);
+ mb();
+ return 0;
+}
+
+void ppp_enable(void)
+{
+ PPP_WRITEL(0x1000, 0x30);
+ mb();
+}
+
+int mdp3_ppp_init(void)
+{
+ load_ppp_lut(LUT_PRE_TABLE, ppp_default_pre_lut());
+ load_ppp_lut(LUT_POST_TABLE, ppp_default_post_lut());
+ load_csc_matrix(CSC_PRIMARY_MATRIX, ppp_csc_rgb2yuv());
+ load_csc_matrix(CSC_SECONDARY_MATRIX, ppp_csc_table2());
+ return 0;
+}
diff --git a/drivers/video/msm/mdss/mdss.h b/drivers/video/msm/mdss/mdss.h
index 5be0173..1311dab 100644
--- a/drivers/video/msm/mdss/mdss.h
+++ b/drivers/video/msm/mdss/mdss.h
@@ -110,12 +110,17 @@
void *video_intf;
u32 nintf;
+ struct mdss_ad_info *ad_cfgs;
+ u32 nad_cfgs;
+ struct workqueue_struct *ad_calc_wq;
+
struct ion_client *iclient;
int iommu_attached;
struct mdss_iommu_map_type *iommu_map;
struct early_suspend early_suspend;
void *debug_data;
+ int current_bus_idx;
};
extern struct mdss_data_type *mdss_res;
diff --git a/drivers/video/msm/mdss/mdss_debug.c b/drivers/video/msm/mdss/mdss_debug.c
index 0918db1..13fba26 100644
--- a/drivers/video/msm/mdss/mdss_debug.c
+++ b/drivers/video/msm/mdss/mdss_debug.c
@@ -14,6 +14,7 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/debugfs.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/printk.h>
@@ -438,3 +439,151 @@
return 0;
}
+
+static struct mdss_mdp_misr_map {
+ u32 ctrl_reg;
+ u32 value_reg;
+ u32 crc_op_mode;
+ u32 crc_index;
+ u32 crc_value[MISR_CRC_BATCH_SIZE];
+} mdss_mdp_misr_table[DISPLAY_MISR_MAX] = {
+ [DISPLAY_MISR_DSI0] = {
+ .ctrl_reg = MDSS_MDP_LP_MISR_CTRL_DSI0,
+ .value_reg = MDSS_MDP_LP_MISR_SIGN_DSI0,
+ },
+ [DISPLAY_MISR_DSI1] = {
+ .ctrl_reg = MDSS_MDP_LP_MISR_CTRL_DSI1,
+ .value_reg = MDSS_MDP_LP_MISR_SIGN_DSI1,
+ },
+ [DISPLAY_MISR_EDP] = {
+ .ctrl_reg = MDSS_MDP_LP_MISR_CTRL_EDP,
+ .value_reg = MDSS_MDP_LP_MISR_SIGN_EDP,
+ },
+ [DISPLAY_MISR_HDMI] = {
+ .ctrl_reg = MDSS_MDP_LP_MISR_CTRL_HDMI,
+ .value_reg = MDSS_MDP_LP_MISR_SIGN_HDMI,
+ },
+};
+
+static inline struct mdss_mdp_misr_map *mdss_misr_get_map(u32 block_id)
+{
+ struct mdss_mdp_misr_map *map;
+
+ if (block_id > DISPLAY_MISR_LCDC) {
+ pr_err("MISR Block id (%d) out of range\n", block_id);
+ return NULL;
+ }
+
+ map = mdss_mdp_misr_table + block_id;
+ if ((map->ctrl_reg == 0) || (map->value_reg == 0)) {
+ pr_err("MISR Block id (%d) config not found\n", block_id);
+ return NULL;
+ }
+
+ return map;
+}
+
+int mdss_misr_crc_set(struct mdss_data_type *mdata, struct mdp_misr *req)
+{
+ struct mdss_mdp_misr_map *map;
+ u32 config = 0;
+
+ map = mdss_misr_get_map(req->block_id);
+ if (!map) {
+ pr_err("Invalid MISR Block=%d\n", req->block_id);
+ return -EINVAL;
+ }
+
+ map->crc_op_mode = req->crc_op_mode;
+ memset(map->crc_value, 0, sizeof(map->crc_value));
+
+ pr_debug("MISR Config (BlockId %d) (Frame Count = %d)\n",
+ req->block_id, req->frame_count);
+
+ config = (MDSS_MDP_LP_MISR_CTRL_FRAME_COUNT_MASK & req->frame_count) |
+ (MDSS_MDP_LP_MISR_CTRL_ENABLE);
+
+ writel_relaxed(MDSS_MDP_LP_MISR_CTRL_STATUS_CLEAR,
+ mdata->mdp_base + map->ctrl_reg);
+ /* ensure clear is done */
+ wmb();
+ if (MISR_OP_BM == map->crc_op_mode) {
+ writel_relaxed(MISR_CRC_BATCH_CFG,
+ mdata->mdp_base + map->ctrl_reg);
+ } else {
+ writel_relaxed(config,
+ mdata->mdp_base + map->ctrl_reg);
+
+ config = readl_relaxed(mdata->mdp_base + map->ctrl_reg);
+ pr_debug("MISR_CTRL = 0x%x", config);
+ }
+ return 0;
+}
+
+int mdss_misr_crc_get(struct mdss_data_type *mdata, struct mdp_misr *resp)
+{
+ struct mdss_mdp_misr_map *map;
+ u32 status;
+ int ret = 0;
+ int i;
+
+ map = mdss_misr_get_map(resp->block_id);
+ if (!map) {
+ pr_err("Invalid MISR Block=%d\n", resp->block_id);
+ return -EINVAL;
+ }
+
+ switch (map->crc_op_mode) {
+ case MISR_OP_SFM:
+ case MISR_OP_MFM:
+ ret = readl_poll_timeout(mdata->mdp_base + map->ctrl_reg,
+ status, status & MDSS_MDP_LP_MISR_CTRL_STATUS,
+ MISR_POLL_SLEEP, MISR_POLL_TIMEOUT);
+
+ pr_debug("Status of Get MISR_CTRL = 0x%x", status);
+ if (ret == 0) {
+ resp->crc_value[0] =
+ readl_relaxed(mdata->mdp_base + map->value_reg);
+ pr_debug("CRC %d=0x%x\n", resp->block_id,
+ resp->crc_value[0]);
+ } else {
+ pr_warn("MISR %d busy with status 0x%x\n",
+ resp->block_id, status);
+ }
+ break;
+ case MISR_OP_BM:
+ for (i = 0; i < MISR_CRC_BATCH_SIZE; i++)
+ resp->crc_value[i] = map->crc_value[i];
+ map->crc_index = 0;
+ break;
+ default:
+ ret = -ENOSYS;
+ break;
+ }
+
+ return ret;
+}
+
+/* This function is expected to be called from interrupt context */
+void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id)
+{
+ struct mdss_mdp_misr_map *map;
+ u32 status, config;
+
+ map = mdss_misr_get_map(block_id);
+ if (!map || (map->crc_op_mode != MISR_OP_BM))
+ return;
+
+ config = MISR_CRC_BATCH_CFG;
+
+ status = readl_relaxed(mdata->mdp_base + map->ctrl_reg);
+ if (status & MDSS_MDP_LP_MISR_CTRL_STATUS) {
+ map->crc_value[map->crc_index] =
+ readl_relaxed(mdata->mdp_base + map->value_reg);
+ map->crc_index++;
+ if (map->crc_index == MISR_CRC_BATCH_SIZE)
+ map->crc_index = 0;
+ config |= MDSS_MDP_LP_MISR_CTRL_STATUS_CLEAR;
+ }
+ writel_relaxed(config, mdata->mdp_base + map->ctrl_reg);
+}
diff --git a/drivers/video/msm/mdss/mdss_debug.h b/drivers/video/msm/mdss/mdss_debug.h
index 167fa8a..29eb16c 100644
--- a/drivers/video/msm/mdss/mdss_debug.h
+++ b/drivers/video/msm/mdss/mdss_debug.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -16,24 +16,30 @@
#include "mdss.h"
+#define MISR_POLL_SLEEP 2000
+#define MISR_POLL_TIMEOUT 32000
+#define MISR_CRC_BATCH_SIZE 32
+#define MISR_CRC_BATCH_CFG 0x101
+
#ifdef CONFIG_DEBUG_FS
int mdss_debugfs_init(struct mdss_data_type *mdata);
int mdss_debugfs_remove(struct mdss_data_type *mdata);
int mdss_debug_register_base(const char *name, void __iomem *base,
size_t max_offset);
+int mdss_misr_crc_set(struct mdss_data_type *mdata, struct mdp_misr *req);
+int mdss_misr_crc_get(struct mdss_data_type *mdata, struct mdp_misr *resp);
+void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id);
#else
-static inline int mdss_debugfs_init(struct mdss_data_type *mdata)
-{
- return 0;
-}
+static inline int mdss_debugfs_init(struct mdss_data_type *mdata) { return 0; }
static inline int mdss_debugfs_remove(struct mdss_data_type *mdata)
-{
- return 0;
-}
+{ return 0; }
static inline int mdss_debug_register_base(const char *name, void __iomem *base,
- size_t max_offset)
-{
- return 0;
-}
+ size_t max_offset) { return 0; }
+static inline int mdss_misr_crc_set(struct mdss_data_type *mdata,
+ struct mdp_misr *reg) { return 0; }
+static inline int mdss_misr_crc_get(struct mdss_data_type *mdata,
+ struct mdp_misr *resp) { return 0; }
+static inline void mdss_misr_crc_collect(struct mdss_data_type *mdata,
+ int block_id) { }
#endif
#endif /* MDSS_DEBUG_H */
diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c
index acac6b9..366209b 100644
--- a/drivers/video/msm/mdss/mdss_dsi.c
+++ b/drivers/video/msm/mdss/mdss_dsi.c
@@ -408,12 +408,19 @@
pr_debug("%s+: ctrl=%p ndx=%d\n", __func__,
ctrl_pdata, ctrl_pdata->ndx);
- mdss_dsi_clk_disable(pdata);
- mdss_dsi_unprepare_clocks(ctrl_pdata);
+ if (pdata->panel_info.type == MIPI_CMD_PANEL)
+ mdss_dsi_clk_ctrl(ctrl_pdata, 1);
/* disable DSI controller */
mdss_dsi_controller_cfg(0, pdata);
+ mdss_dsi_clk_ctrl(ctrl_pdata, 0);
+
+ /* disable DSI phy */
+ mdss_dsi_phy_enable(ctrl_pdata, 0);
+
+ mdss_dsi_disable_bus_clocks(ctrl_pdata);
+
ret = mdss_dsi_panel_power_on(pdata, 0);
if (ret) {
pr_err("%s: Panel power off failed\n", __func__);
@@ -425,37 +432,6 @@
return ret;
}
-int mdss_dsi_cont_splash_on(struct mdss_panel_data *pdata)
-{
- int ret = 0;
- struct mipi_panel_info *mipi;
-
- pr_info("%s:%d DSI on for continuous splash.\n", __func__, __LINE__);
-
- if (pdata == NULL) {
- pr_err("%s: Invalid input data\n", __func__);
- return -EINVAL;
- }
-
- mipi = &pdata->panel_info.mipi;
-
- ret = mdss_dsi_panel_power_on(pdata, 1);
- if (ret) {
- pr_err("%s: Panel power on failed\n", __func__);
- return ret;
- }
- mdss_dsi_sw_reset(pdata);
- mdss_dsi_host_init(mipi, pdata);
-
- pdata->panel_info.panel_power_on = 1;
-
- mdss_dsi_op_mode_config(mipi->mode, pdata);
-
- pr_debug("%s-:End\n", __func__);
- return ret;
-}
-
-
int mdss_dsi_on(struct mdss_panel_data *pdata)
{
int ret = 0;
@@ -493,11 +469,18 @@
pdata->panel_info.panel_power_on = 1;
+ ret = mdss_dsi_enable_bus_clocks(ctrl_pdata);
+ if (ret) {
+ pr_err("%s: failed to enable bus clocks. rc=%d\n", __func__,
+ ret);
+ mdss_dsi_panel_power_on(pdata, 0);
+ return ret;
+ }
+
mdss_dsi_phy_sw_reset((ctrl_pdata->ctrl_base));
mdss_dsi_phy_init(pdata);
- mdss_dsi_prepare_clocks(ctrl_pdata);
- mdss_dsi_clk_enable(pdata);
+ mdss_dsi_clk_ctrl(ctrl_pdata, 1);
clk_rate = pdata->panel_info.clk_rate;
clk_rate = min(clk_rate, pdata->panel_info.clk_max);
@@ -575,6 +558,9 @@
wmb();
}
+ if (pdata->panel_info.type == MIPI_CMD_PANEL)
+ mdss_dsi_clk_ctrl(ctrl_pdata, 0);
+
pr_debug("%s-:\n", __func__);
return 0;
}
@@ -596,12 +582,15 @@
panel_data);
mipi = &pdata->panel_info.mipi;
- ret = ctrl_pdata->on(pdata);
- if (ret) {
- pr_err("%s: unable to initialize the panel\n", __func__);
- return ret;
+ if (!(ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT)) {
+ ret = ctrl_pdata->on(pdata);
+ if (ret) {
+ pr_err("%s: unable to initialize the panel\n",
+ __func__);
+ return ret;
+ }
+ ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_INIT;
}
-
mdss_dsi_op_mode_config(mipi->mode, pdata);
pr_debug("%s-:\n", __func__);
@@ -626,10 +615,52 @@
mdss_dsi_op_mode_config(DSI_CMD_MODE, pdata);
- ret = ctrl_pdata->off(pdata);
- if (ret) {
- pr_err("%s: Panel OFF failed\n", __func__);
- return ret;
+ if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) {
+ ret = ctrl_pdata->off(pdata);
+ if (ret) {
+ pr_err("%s: Panel OFF failed\n", __func__);
+ return ret;
+ }
+ ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT;
+ }
+ pr_debug("%s-:End\n", __func__);
+ return ret;
+}
+
+int mdss_dsi_cont_splash_on(struct mdss_panel_data *pdata)
+{
+ int ret = 0;
+ struct mipi_panel_info *mipi;
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
+
+ pr_info("%s:%d DSI on for continuous splash.\n", __func__, __LINE__);
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ mipi = &pdata->panel_info.mipi;
+
+ ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
+ panel_data);
+
+ pr_debug("%s+: ctrl=%p ndx=%d\n", __func__,
+ ctrl_pdata, ctrl_pdata->ndx);
+
+ WARN((ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT),
+ "Incorrect Ctrl state=0x%x\n", ctrl_pdata->ctrl_state);
+
+ mdss_dsi_sw_reset(pdata);
+ mdss_dsi_host_init(mipi, pdata);
+
+ if (ctrl_pdata->on_cmds.link_state == DSI_LP_MODE) {
+ mdss_dsi_op_mode_config(DSI_CMD_MODE, pdata);
+ ret = mdss_dsi_unblank(pdata);
+ if (ret) {
+ pr_err("%s: unblank failed\n", __func__);
+ return ret;
+ }
}
pr_debug("%s-:End\n", __func__);
@@ -653,35 +684,47 @@
switch (event) {
case MDSS_EVENT_UNBLANK:
rc = mdss_dsi_on(pdata);
- if (ctrl_pdata->on_cmds->ctrl_state == DSI_LP_MODE) {
+ if (ctrl_pdata->on_cmds.link_state == DSI_LP_MODE)
rc = mdss_dsi_unblank(pdata);
- }
break;
case MDSS_EVENT_PANEL_ON:
- if (ctrl_pdata->on_cmds->ctrl_state == DSI_HS_MODE)
+ ctrl_pdata->ctrl_state |= CTRL_STATE_MDP_ACTIVE;
+ if (ctrl_pdata->on_cmds.link_state == DSI_HS_MODE)
rc = mdss_dsi_unblank(pdata);
break;
case MDSS_EVENT_BLANK:
- if (ctrl_pdata->off_cmds->ctrl_state == DSI_HS_MODE) {
+ if (ctrl_pdata->off_cmds.link_state == DSI_HS_MODE)
rc = mdss_dsi_blank(pdata);
- }
break;
case MDSS_EVENT_PANEL_OFF:
- if (ctrl_pdata->off_cmds->ctrl_state == DSI_LP_MODE) {
+ ctrl_pdata->ctrl_state &= ~CTRL_STATE_MDP_ACTIVE;
+ if (ctrl_pdata->off_cmds.link_state == DSI_LP_MODE)
rc = mdss_dsi_blank(pdata);
- }
rc = mdss_dsi_off(pdata);
break;
case MDSS_EVENT_CONT_SPLASH_FINISH:
- if (ctrl_pdata->on_cmds->ctrl_state == DSI_LP_MODE) {
+ ctrl_pdata->ctrl_state &= ~CTRL_STATE_MDP_ACTIVE;
+ if (ctrl_pdata->on_cmds.link_state == DSI_LP_MODE) {
rc = mdss_dsi_cont_splash_on(pdata);
} else {
pr_debug("%s:event=%d, Dsi On not called: ctrl_state: %d\n",
__func__, event,
- ctrl_pdata->on_cmds->ctrl_state);
+ ctrl_pdata->on_cmds.link_state);
rc = -EINVAL;
}
break;
+ case MDSS_EVENT_PANEL_CLK_CTRL:
+ mdss_dsi_clk_req(ctrl_pdata, (int)arg);
+ break;
+ case MDSS_EVENT_DSI_CMDLIST_KOFF:
+ mdss_dsi_cmdlist_commit(ctrl_pdata, 1);
+ break;
+ case MDSS_EVENT_CONT_SPLASH_BEGIN:
+ if (ctrl_pdata->off_cmds.link_state == DSI_HS_MODE) {
+ /* Panel is Enabled in Bootloader */
+ rc = mdss_dsi_blank(pdata);
+ }
+ break;
default:
pr_debug("%s: unhandled event=%d\n", __func__, event);
break;
@@ -1053,17 +1096,16 @@
ctrl_pdata->panel_data.event_handler = mdss_dsi_event_handler;
- ctrl_pdata->on_cmds = panel_data->dsi_panel_on_cmds;
- ctrl_pdata->off_cmds = panel_data->dsi_panel_off_cmds;
+ ctrl_pdata->on_cmds = panel_data->on_cmds;
+ ctrl_pdata->off_cmds = panel_data->off_cmds;
memcpy(&((ctrl_pdata->panel_data).panel_info),
&(panel_data->panel_info),
sizeof(struct mdss_panel_info));
- mdss_dsi_irq_handler_config(ctrl_pdata);
ctrl_pdata->panel_data.set_backlight = panel_data->bl_fnc;
ctrl_pdata->bklt_ctrl = panel_data->panel_info.bklt_ctrl;
- ctrl_pdata->pwm_gpio = panel_data->panel_info.pwm_gpio;
+ ctrl_pdata->pwm_pmic_gpio = panel_data->panel_info.pwm_pmic_gpio;
ctrl_pdata->pwm_period = panel_data->panel_info.pwm_period;
ctrl_pdata->pwm_lpg_chan = panel_data->panel_info.pwm_lpg_chan;
ctrl_pdata->bklt_max = panel_data->panel_info.bl_max;
@@ -1071,29 +1113,48 @@
if (ctrl_pdata->bklt_ctrl == BL_PWM)
mdss_dsi_panel_pwm_cfg(ctrl_pdata);
+ mdss_dsi_ctrl_init(ctrl_pdata);
/*
* register in mdp driver
*/
+ ctrl_pdata->pclk_rate = dsi_pclk_rate;
+ ctrl_pdata->byte_clk_rate = panel_data->panel_info.clk_rate / 8;
+ pr_debug("%s: pclk=%d, bclk=%d\n", __func__,
+ ctrl_pdata->pclk_rate, ctrl_pdata->byte_clk_rate);
+
+ ctrl_pdata->ctrl_state = CTRL_STATE_UNKNOWN;
cont_splash_enabled = of_property_read_bool(pdev->dev.of_node,
"qcom,cont-splash-enabled");
if (!cont_splash_enabled) {
- pr_info("%s:%d Continous splash flag not found.\n",
+ pr_info("%s:%d Continuous splash flag not found.\n",
__func__, __LINE__);
ctrl_pdata->panel_data.panel_info.cont_splash_enabled = 0;
ctrl_pdata->panel_data.panel_info.panel_power_on = 0;
} else {
- pr_info("%s:%d Continous splash flag enabled.\n",
+ pr_info("%s:%d Continuous splash flag enabled.\n",
__func__, __LINE__);
ctrl_pdata->panel_data.panel_info.cont_splash_enabled = 1;
ctrl_pdata->panel_data.panel_info.panel_power_on = 1;
- }
+ rc = mdss_dsi_panel_power_on(&(ctrl_pdata->panel_data), 1);
+ if (rc) {
+ pr_err("%s: Panel power on failed\n", __func__);
+ return rc;
+ }
+ rc = mdss_dsi_enable_bus_clocks(ctrl_pdata);
+ if (rc) {
+ pr_err("%s: failed to enable bus clocks. rc=%d\n",
+ __func__, rc);
+ rc = mdss_dsi_panel_power_on(
+ &(ctrl_pdata->panel_data), 0);
+ return rc;
+ }
- if (ctrl_pdata->panel_data.panel_info.cont_splash_enabled) {
- mdss_dsi_prepare_clocks(ctrl_pdata);
- mdss_dsi_clk_enable(&(ctrl_pdata->panel_data));
+ mdss_dsi_clk_ctrl(ctrl_pdata, 1);
+ ctrl_pdata->ctrl_state |=
+ (CTRL_STATE_PANEL_INIT | CTRL_STATE_MDP_ACTIVE);
}
rc = mdss_register_panel(ctrl_pdev, &(ctrl_pdata->panel_data));
@@ -1109,11 +1170,6 @@
ctrl_pdata->on = panel_data->on;
ctrl_pdata->off = panel_data->off;
- ctrl_pdata->pclk_rate = dsi_pclk_rate;
- ctrl_pdata->byte_clk_rate = panel_data->panel_info.clk_rate / 8;
- pr_debug("%s: pclk=%d, bclk=%d\n", __func__,
- ctrl_pdata->pclk_rate, ctrl_pdata->byte_clk_rate);
-
if (panel_data->panel_info.pdest == DISPLAY_1) {
mdss_debug_register_base("dsi0",
ctrl_pdata->ctrl_base, ctrl_pdata->reg_size);
@@ -1153,8 +1209,6 @@
{
int ret;
- mdss_dsi_init();
-
ret = mdss_dsi_register_driver();
if (ret) {
pr_err("mdss_dsi_register_driver() failed!\n");
diff --git a/drivers/video/msm/mdss/mdss_dsi.h b/drivers/video/msm/mdss/mdss_dsi.h
index 197ff7a..965a23f 100644
--- a/drivers/video/msm/mdss/mdss_dsi.h
+++ b/drivers/video/msm/mdss/mdss_dsi.h
@@ -84,11 +84,15 @@
UNKNOWN_CTRL,
};
-enum dsi_ctrl_state {
+enum dsi_ctrl_op_mode {
DSI_LP_MODE,
DSI_HS_MODE,
};
+#define CTRL_STATE_UNKNOWN 0x00
+#define CTRL_STATE_PANEL_INIT BIT(0)
+#define CTRL_STATE_MDP_ACTIVE BIT(1)
+
#define DSI_NON_BURST_SYNCH_PULSE 0
#define DSI_NON_BURST_SYNCH_EVENT 1
#define DSI_BURST_MODE 2
@@ -127,6 +131,10 @@
#define DSI_CMD_TRIGGER_SW_SEOF 0x05 /* cmd dma only */
#define DSI_CMD_TRIGGER_SW_TE 0x06
+#define DSI_VIDEO_TERM BIT(16)
+#define DSI_MDP_TERM BIT(8)
+#define DSI_CMD_TERM BIT(0)
+
extern struct device dsi_dev;
extern int mdss_dsi_clk_on;
extern u32 dsi_irq;
@@ -182,7 +190,6 @@
#define DSI_HDR_DATA1(data) ((data) & 0x0ff)
#define DSI_HDR_WC(wc) ((wc) & 0x0ffff)
-#define DSI_BUF_SIZE 1024
#define MDSS_DSI_MRPS 0x04 /* Maximum Return Packet Size */
#define MDSS_DSI_LEN 8 /* 4 x 4 - 6 - 2, bytes dcs header+crc-align */
@@ -234,35 +241,67 @@
#define DTYPE_DCS_READ1_RESP 0x21 /* 1 parameter, short */
#define DTYPE_DCS_READ2_RESP 0x22 /* 2 parameter, short */
+
+struct dsi_ctrl_hdr {
+ char dtype; /* data type */
+ char last; /* last in chain */
+ char vc; /* virtual chan */
+ char ack; /* ask ACK from peripheral */
+ char wait; /* ms */
+ short dlen; /* 16 bits */
+} __packed;
+
struct dsi_cmd_desc {
- int dtype;
- int last;
- int vc;
- int ack; /* ask ACK from peripheral */
- int wait;
- int dlen;
+ struct dsi_ctrl_hdr dchdr;
char *payload;
};
+struct dsi_panel_cmds {
+ char *buf;
+ int blen;
+ struct dsi_cmd_desc *cmds;
+ int cmd_cnt;
+ int link_state;
+};
+
+#define CMD_REQ_MAX 4
+
+typedef void (*fxn)(u32 data);
+
+#define CMD_REQ_RX 0x0001
+#define CMD_REQ_COMMIT 0x0002
+#define CMD_CLK_CTRL 0x0004
+#define CMD_REQ_NO_MAX_PKT_SIZE 0x0008
+
+struct dcs_cmd_req {
+ struct dsi_cmd_desc *cmds;
+ int cmds_cnt;
+ u32 flags;
+ int rlen; /* rx length */
+ fxn cb;
+};
+
+struct dcs_cmd_list {
+ int put;
+ int get;
+ int tot;
+ struct dcs_cmd_req list[CMD_REQ_MAX];
+};
+
struct dsi_kickoff_action {
struct list_head act_entry;
void (*action) (void *);
void *data;
};
-struct dsi_panel_cmds_list {
- struct dsi_cmd_desc *buf;
- u32 size;
- char ctrl_state;
-};
-
struct mdss_panel_common_pdata {
struct mdss_panel_info panel_info;
int (*on) (struct mdss_panel_data *pdata);
int (*off) (struct mdss_panel_data *pdata);
void (*bl_fnc) (struct mdss_panel_data *pdata, u32 bl_level);
- struct dsi_panel_cmds_list *dsi_panel_on_cmds;
- struct dsi_panel_cmds_list *dsi_panel_off_cmds;
+
+ struct dsi_panel_cmds on_cmds;
+ struct dsi_panel_cmds off_cmds;
};
struct dsi_drv_cm_data {
@@ -272,17 +311,26 @@
int broadcast_enable;
};
+enum {
+ DSI_CTRL_0,
+ DSI_CTRL_1,
+ DSI_CTRL_MAX,
+};
+
struct mdss_dsi_ctrl_pdata {
- int ndx;
+ int ndx; /* panel_num */
int (*on) (struct mdss_panel_data *pdata);
int (*off) (struct mdss_panel_data *pdata);
struct mdss_panel_data panel_data;
- struct mdss_hw *mdss_hw;
unsigned char *ctrl_base;
int reg_size;
+ u32 clk_cnt;
+ struct clk *ahb_clk;
+ struct clk *axi_clk;
struct clk *byte_clk;
struct clk *esc_clk;
struct clk *pixel_clk;
+ u8 ctrl_state;
int irq_cnt;
int mdss_dsi_clk_on;
int rst_gpio;
@@ -290,16 +338,32 @@
int disp_te_gpio;
int bklt_ctrl; /* backlight ctrl */
int pwm_period;
- int pwm_gpio;
+ int pwm_pmic_gpio;
int pwm_lpg_chan;
int bklt_max;
struct pwm_device *pwm_bl;
- struct dsi_panel_cmds_list *on_cmds;
- struct dsi_panel_cmds_list *off_cmds;
struct dsi_drv_cm_data shared_pdata;
u32 pclk_rate;
u32 byte_clk_rate;
struct dss_module_power power_data;
+ u32 dsi_irq_mask;
+ struct mdss_hw *dsi_hw;
+
+ struct dsi_panel_cmds on_cmds;
+ struct dsi_panel_cmds off_cmds;
+
+ struct dcs_cmd_list cmdlist;
+ struct completion dma_comp;
+ struct completion mdp_comp;
+ struct completion video_comp;
+ spinlock_t irq_lock;
+ spinlock_t mdp_lock;
+ int mdp_busy;
+ struct mutex mutex;
+ struct mutex cmd_mutex;
+
+ struct dsi_buf tx_buf;
+ struct dsi_buf rx_buf;
};
int dsi_panel_device_register(struct platform_device *pdev,
@@ -310,29 +374,26 @@
void mdss_dsi_init(void);
int mdss_dsi_buf_alloc(struct dsi_buf *, int size);
int mdss_dsi_cmd_dma_add(struct dsi_buf *dp, struct dsi_cmd_desc *cm);
-int mdss_dsi_cmds_tx(struct mdss_panel_data *pdata,
- struct dsi_buf *dp, struct dsi_cmd_desc *cmds, int cnt);
+int mdss_dsi_cmds_tx(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct dsi_cmd_desc *cmds, int cnt);
-int mdss_dsi_cmd_dma_tx(struct dsi_buf *dp,
- struct mdss_panel_data *pdata);
-int mdss_dsi_cmd_reg_tx(u32 data,
- unsigned char *ctrl_base);
-int mdss_dsi_cmds_rx(struct mdss_panel_data *pdata,
- struct dsi_buf *tp, struct dsi_buf *rp,
- struct dsi_cmd_desc *cmds, int len);
-int mdss_dsi_cmd_dma_rx(struct dsi_buf *tp, int rlen,
- struct mdss_panel_data *pdata);
+int mdss_dsi_cmds_rx(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct dsi_cmd_desc *cmds, int rlen, u32 rx_flags);
+
void mdss_dsi_host_init(struct mipi_panel_info *pinfo,
struct mdss_panel_data *pdata);
void mdss_dsi_op_mode_config(int mode,
struct mdss_panel_data *pdata);
void mdss_dsi_cmd_mode_ctrl(int enable);
void mdp4_dsi_cmd_trigger(void);
-void mdss_dsi_cmd_mdp_start(struct mdss_panel_data *pdata);
+void mdss_dsi_cmd_mdp_start(struct mdss_dsi_ctrl_pdata *ctrl);
void mdss_dsi_cmd_bta_sw_trigger(struct mdss_panel_data *pdata);
void mdss_dsi_ack_err_status(unsigned char *dsi_base);
-void mdss_dsi_clk_enable(struct mdss_panel_data *pdata);
-void mdss_dsi_clk_disable(struct mdss_panel_data *pdata);
+void mdss_dsi_clk_enable(struct mdss_dsi_ctrl_pdata *ctrl);
+void mdss_dsi_clk_disable(struct mdss_dsi_ctrl_pdata *ctrl);
+void mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, int enable);
+void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl,
+ int enable);
void mdss_dsi_controller_cfg(int enable,
struct mdss_panel_data *pdata);
void mdss_dsi_sw_reset(struct mdss_panel_data *pdata);
@@ -348,11 +409,22 @@
void mdss_dsi_clk_deinit(struct mdss_dsi_ctrl_pdata *ctrl_pdata);
void mdss_dsi_prepare_clocks(struct mdss_dsi_ctrl_pdata *ctrl_pdata);
void mdss_dsi_unprepare_clocks(struct mdss_dsi_ctrl_pdata *ctrl_pdata);
+int mdss_dsi_enable_bus_clocks(struct mdss_dsi_ctrl_pdata *ctrl_pdata);
+void mdss_dsi_disable_bus_clocks(struct mdss_dsi_ctrl_pdata *ctrl_pdata);
void mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable);
-void mdss_dsi_phy_enable(unsigned char *ctrl_base, int on);
+void mdss_dsi_phy_enable(struct mdss_dsi_ctrl_pdata *ctrl, int on);
void mdss_dsi_phy_init(struct mdss_panel_data *pdata);
void mdss_dsi_phy_sw_reset(unsigned char *ctrl_base);
void mdss_dsi_cmd_test_pattern(struct mdss_panel_data *pdata);
void mdss_dsi_panel_pwm_cfg(struct mdss_dsi_ctrl_pdata *ctrl);
+void mdss_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl);
+void mdss_dsi_cmd_mdp_busy(struct mdss_dsi_ctrl_pdata *ctrl);
+void mdss_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl);
+void mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp);
+int mdss_dsi_cmdlist_put(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct dcs_cmd_req *cmdreq);
+struct dcs_cmd_req *mdss_dsi_cmdlist_get(struct mdss_dsi_ctrl_pdata *ctrl);
+void mdss_dsi_cmdlist_kickoff(int intf);
+
#endif /* MDSS_DSI_H */
diff --git a/drivers/video/msm/mdss/mdss_dsi_host.c b/drivers/video/msm/mdss/mdss_dsi_host.c
index 22ff08c..72e3c64 100644
--- a/drivers/video/msm/mdss/mdss_dsi_host.c
+++ b/drivers/video/msm/mdss/mdss_dsi_host.c
@@ -25,12 +25,11 @@
#include "mdss.h"
#include "mdss_dsi.h"
-static struct completion dsi_dma_comp;
-static spinlock_t dsi_irq_lock;
-static spinlock_t dsi_mdp_lock;
-static int dsi_mdp_busy;
static struct mdss_dsi_ctrl_pdata *left_ctrl_pdata;
+static struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX];
+
+
struct mdss_hw mdss_dsi0_hw = {
.hw_ndx = MDSS_HW_DSI0,
.ptr = NULL,
@@ -43,55 +42,128 @@
.irq_handler = mdss_dsi_isr,
};
-void mdss_dsi_init(void)
-{
- init_completion(&dsi_dma_comp);
- spin_lock_init(&dsi_irq_lock);
- spin_lock_init(&dsi_mdp_lock);
-}
-
-void mdss_dsi_irq_handler_config(struct mdss_dsi_ctrl_pdata *ctrl)
+void mdss_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl)
{
if (ctrl->panel_data.panel_info.pdest == DISPLAY_1) {
mdss_dsi0_hw.ptr = (void *)(ctrl);
- ctrl->mdss_hw = &mdss_dsi0_hw;
+ ctrl->dsi_hw = &mdss_dsi0_hw;
+ ctrl->ndx = DSI_CTRL_0;
} else {
mdss_dsi1_hw.ptr = (void *)(ctrl);
- ctrl->mdss_hw = &mdss_dsi1_hw;
+ ctrl->dsi_hw = &mdss_dsi1_hw;
+ ctrl->ndx = DSI_CTRL_1;
}
- if (!mdss_register_irq(ctrl->mdss_hw))
+ ctrl_list[ctrl->ndx] = ctrl; /* keep it */
+
+ if (mdss_register_irq(ctrl->dsi_hw))
pr_err("%s: mdss_register_irq failed.\n", __func__);
+
+ pr_debug("%s: ndx=%d base=%p\n", __func__, ctrl->ndx, ctrl->ctrl_base);
+
+ init_completion(&ctrl->dma_comp);
+ init_completion(&ctrl->mdp_comp);
+ init_completion(&ctrl->video_comp);
+ spin_lock_init(&ctrl->irq_lock);
+ spin_lock_init(&ctrl->mdp_lock);
+ mutex_init(&ctrl->mutex);
+ mutex_init(&ctrl->cmd_mutex);
+ mdss_dsi_buf_alloc(&ctrl->tx_buf, SZ_4K);
+ mdss_dsi_buf_alloc(&ctrl->rx_buf, SZ_4K);
}
-void mdss_dsi_irq_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, int enable, int isr)
+/*
+ * acquire ctrl->mutex first
+ */
+void mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, int enable)
{
- unsigned long flags;
-
- if (ctrl == NULL) {
- pr_err("%s: Invalid ctrl\n", __func__);
- return;
- }
-
- spin_lock_irqsave(&dsi_irq_lock, flags);
+ mutex_lock(&ctrl->mutex);
if (enable) {
- if (ctrl->irq_cnt == 0)
- mdss_enable_irq(ctrl->mdss_hw);
- ctrl->irq_cnt++;
+ if (ctrl->clk_cnt == 0) {
+ mdss_dsi_prepare_clocks(ctrl);
+ mdss_dsi_clk_enable(ctrl);
+ }
+ ctrl->clk_cnt++;
} else {
- if (ctrl->irq_cnt) {
- ctrl->irq_cnt--;
- if (ctrl->irq_cnt == 0) {
- if (isr)
- mdss_disable_irq_nosync(ctrl->mdss_hw);
- else
- mdss_disable_irq(ctrl->mdss_hw);
+ if (ctrl->clk_cnt) {
+ ctrl->clk_cnt--;
+ if (ctrl->clk_cnt == 0) {
+ mdss_dsi_clk_disable(ctrl);
+ mdss_dsi_unprepare_clocks(ctrl);
}
}
}
- pr_debug("%s: ctrl=%d enable=%d cnt=%d\n", __func__,
- ctrl->ndx, enable, ctrl->irq_cnt);
- spin_unlock_irqrestore(&dsi_irq_lock, flags);
+ pr_debug("%s: ctrl ndx=%d enabled=%d clk_cnt=%d\n",
+ __func__, ctrl->ndx, enable, ctrl->clk_cnt);
+ mutex_unlock(&ctrl->mutex);
+}
+
+void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl, int enable)
+{
+ if (enable == 0) {
+ /* need wait before disable */
+ mutex_lock(&ctrl->cmd_mutex);
+ mdss_dsi_cmd_mdp_busy(ctrl);
+ mutex_unlock(&ctrl->cmd_mutex);
+ }
+
+ mdss_dsi_clk_ctrl(ctrl, enable);
+}
+
+void mdss_dsi_enable_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 term)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctrl->irq_lock, flags);
+ if (ctrl->dsi_irq_mask & term) {
+ spin_unlock_irqrestore(&ctrl->irq_lock, flags);
+ return;
+ }
+ if (ctrl->dsi_irq_mask == 0) {
+ mdss_enable_irq(ctrl->dsi_hw);
+ pr_debug("%s: IRQ Enable, ndx=%d mask=%x term=%x\n", __func__,
+ ctrl->ndx, (int)ctrl->dsi_irq_mask, (int)term);
+ }
+ ctrl->dsi_irq_mask |= term;
+ spin_unlock_irqrestore(&ctrl->irq_lock, flags);
+}
+
+void mdss_dsi_disable_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 term)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctrl->irq_lock, flags);
+ if (!(ctrl->dsi_irq_mask & term)) {
+ spin_unlock_irqrestore(&ctrl->irq_lock, flags);
+ return;
+ }
+ ctrl->dsi_irq_mask &= ~term;
+ if (ctrl->dsi_irq_mask == 0) {
+ mdss_disable_irq(ctrl->dsi_hw);
+ pr_debug("%s: IRQ Disable, ndx=%d mask=%x term=%x\n", __func__,
+ ctrl->ndx, (int)ctrl->dsi_irq_mask, (int)term);
+ }
+ spin_unlock_irqrestore(&ctrl->irq_lock, flags);
+}
+
+/*
+ * mdss_dsi_disale_irq_nosync() should be called
+ * from interrupt context
+ */
+void mdss_dsi_disable_irq_nosync(struct mdss_dsi_ctrl_pdata *ctrl, u32 term)
+{
+ spin_lock(&ctrl->irq_lock);
+ if (!(ctrl->dsi_irq_mask & term)) {
+ spin_unlock(&ctrl->irq_lock);
+ return;
+ }
+ ctrl->dsi_irq_mask &= ~term;
+ if (ctrl->dsi_irq_mask == 0) {
+ mdss_disable_irq_nosync(ctrl->dsi_hw);
+ pr_debug("%s: IRQ Disable, ndx=%d mask=%x term=%x\n", __func__,
+ ctrl->ndx, (int)ctrl->dsi_irq_mask, (int)term);
+ }
+ spin_unlock(&ctrl->irq_lock);
}
/*
@@ -140,7 +212,7 @@
int mdss_dsi_buf_alloc(struct dsi_buf *dp, int size)
{
- dp->start = kmalloc(size, GFP_KERNEL);
+ dp->start = dma_alloc_writecombine(NULL, size, &dp->dmap, GFP_KERNEL);
if (dp->start == NULL) {
pr_err("%s:%u\n", __func__, __LINE__);
return -ENOMEM;
@@ -162,18 +234,20 @@
*/
static int mdss_dsi_generic_lwrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
{
+ struct dsi_ctrl_hdr *dchdr;
char *bp;
u32 *hp;
- int i, len;
+ int i, len = 0;
+ dchdr = &cm->dchdr;
bp = mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
/* fill up payload */
if (cm->payload) {
- len = cm->dlen;
+ len = dchdr->dlen;
len += 3;
len &= ~0x03; /* multipled by 4 */
- for (i = 0; i < cm->dlen; i++)
+ for (i = 0; i < dchdr->dlen; i++)
*bp++ = cm->payload[i];
/* append 0xff to the end */
@@ -186,16 +260,17 @@
/* fill up header */
hp = dp->hdr;
*hp = 0;
- *hp = DSI_HDR_WC(cm->dlen);
- *hp |= DSI_HDR_VC(cm->vc);
+ *hp = DSI_HDR_WC(dchdr->dlen);
+ *hp |= DSI_HDR_VC(dchdr->vc);
*hp |= DSI_HDR_LONG_PKT;
*hp |= DSI_HDR_DTYPE(DTYPE_GEN_LWRITE);
- if (cm->last)
+ if (dchdr->last)
*hp |= DSI_HDR_LAST;
mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
+ len += DSI_HOST_HDR_SIZE;
- return dp->len;
+ return len;
}
/*
@@ -203,10 +278,12 @@
*/
static int mdss_dsi_generic_swrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
{
+ struct dsi_ctrl_hdr *dchdr;
u32 *hp;
int len;
- if (cm->dlen && cm->payload == 0) {
+ dchdr = &cm->dchdr;
+ if (dchdr->dlen && cm->payload == 0) {
pr_err("%s: NO payload error\n", __func__);
return 0;
}
@@ -214,12 +291,12 @@
mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
hp = dp->hdr;
*hp = 0;
- *hp |= DSI_HDR_VC(cm->vc);
- if (cm->last)
+ *hp |= DSI_HDR_VC(dchdr->vc);
+ if (dchdr->last)
*hp |= DSI_HDR_LAST;
- len = (cm->dlen > 2) ? 2 : cm->dlen;
+ len = (dchdr->dlen > 2) ? 2 : dchdr->dlen;
if (len == 1) {
*hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE1);
@@ -236,8 +313,7 @@
}
mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
-
- return dp->len; /* 4 bytes */
+ return DSI_HOST_HDR_SIZE; /* 4 bytes */
}
/*
@@ -245,10 +321,12 @@
*/
static int mdss_dsi_generic_read(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
{
+ struct dsi_ctrl_hdr *dchdr;
u32 *hp;
int len;
- if (cm->dlen && cm->payload == 0) {
+ dchdr = &cm->dchdr;
+ if (dchdr->dlen && cm->payload == 0) {
pr_err("%s: NO payload error\n", __func__);
return 0;
}
@@ -256,12 +334,12 @@
mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
hp = dp->hdr;
*hp = 0;
- *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_VC(dchdr->vc);
*hp |= DSI_HDR_BTA;
- if (cm->last)
+ if (dchdr->last)
*hp |= DSI_HDR_LAST;
- len = (cm->dlen > 2) ? 2 : cm->dlen;
+ len = (dchdr->dlen > 2) ? 2 : dchdr->dlen;
if (len == 1) {
*hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ1);
@@ -278,7 +356,7 @@
}
mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
- return dp->len; /* 4 bytes */
+ return DSI_HOST_HDR_SIZE; /* 4 bytes */
}
/*
@@ -286,10 +364,12 @@
*/
static int mdss_dsi_dcs_lwrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
{
+ struct dsi_ctrl_hdr *dchdr;
char *bp;
u32 *hp;
- int i, len;
+ int i, len = 0;
+ dchdr = &cm->dchdr;
bp = mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
/*
@@ -297,10 +377,10 @@
* dcs command byte (first byte) followed by payload
*/
if (cm->payload) {
- len = cm->dlen;
+ len = dchdr->dlen;
len += 3;
len &= ~0x03; /* multipled by 4 */
- for (i = 0; i < cm->dlen; i++)
+ for (i = 0; i < dchdr->dlen; i++)
*bp++ = cm->payload[i];
/* append 0xff to the end */
@@ -313,16 +393,17 @@
/* fill up header */
hp = dp->hdr;
*hp = 0;
- *hp = DSI_HDR_WC(cm->dlen);
- *hp |= DSI_HDR_VC(cm->vc);
+ *hp = DSI_HDR_WC(dchdr->dlen);
+ *hp |= DSI_HDR_VC(dchdr->vc);
*hp |= DSI_HDR_LONG_PKT;
*hp |= DSI_HDR_DTYPE(DTYPE_DCS_LWRITE);
- if (cm->last)
+ if (dchdr->last)
*hp |= DSI_HDR_LAST;
mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
- return dp->len;
+ len += DSI_HOST_HDR_SIZE;
+ return len;
}
/*
@@ -330,9 +411,11 @@
*/
static int mdss_dsi_dcs_swrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
{
+ struct dsi_ctrl_hdr *dchdr;
u32 *hp;
int len;
+ dchdr = &cm->dchdr;
if (cm->payload == 0) {
pr_err("%s: NO payload error\n", __func__);
return -EINVAL;
@@ -341,20 +424,20 @@
mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
hp = dp->hdr;
*hp = 0;
- *hp |= DSI_HDR_VC(cm->vc);
- if (cm->ack) /* ask ACK trigger msg from peripeheral */
+ *hp |= DSI_HDR_VC(dchdr->vc);
+ if (dchdr->ack) /* ask ACK trigger msg from peripeheral */
*hp |= DSI_HDR_BTA;
- if (cm->last)
+ if (dchdr->last)
*hp |= DSI_HDR_LAST;
- len = (cm->dlen > 1) ? 1 : cm->dlen;
+ len = (dchdr->dlen > 1) ? 1 : dchdr->dlen;
*hp |= DSI_HDR_DTYPE(DTYPE_DCS_WRITE);
*hp |= DSI_HDR_DATA1(cm->payload[0]); /* dcs command byte */
*hp |= DSI_HDR_DATA2(0);
mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
- return dp->len;
+ return DSI_HOST_HDR_SIZE; /* 4 bytes */
}
/*
@@ -362,9 +445,11 @@
*/
static int mdss_dsi_dcs_swrite1(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
{
+ struct dsi_ctrl_hdr *dchdr;
u32 *hp;
- if (cm->dlen < 2 || cm->payload == 0) {
+ dchdr = &cm->dchdr;
+ if (dchdr->dlen < 2 || cm->payload == 0) {
pr_err("%s: NO payload error\n", __func__);
return -EINVAL;
}
@@ -372,10 +457,10 @@
mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
hp = dp->hdr;
*hp = 0;
- *hp |= DSI_HDR_VC(cm->vc);
- if (cm->ack) /* ask ACK trigger msg from peripeheral */
+ *hp |= DSI_HDR_VC(dchdr->vc);
+ if (dchdr->ack) /* ask ACK trigger msg from peripeheral */
*hp |= DSI_HDR_BTA;
- if (cm->last)
+ if (dchdr->last)
*hp |= DSI_HDR_LAST;
*hp |= DSI_HDR_DTYPE(DTYPE_DCS_WRITE1);
@@ -383,8 +468,7 @@
*hp |= DSI_HDR_DATA2(cm->payload[1]); /* parameter */
mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
-
- return dp->len;
+ return DSI_HOST_HDR_SIZE; /* 4 bytes */
}
/*
* mipi dsi dcs read with 0 parameters
@@ -392,8 +476,10 @@
static int mdss_dsi_dcs_read(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
{
+ struct dsi_ctrl_hdr *dchdr;
u32 *hp;
+ dchdr = &cm->dchdr;
if (cm->payload == 0) {
pr_err("%s: NO payload error\n", __func__);
return -EINVAL;
@@ -402,92 +488,97 @@
mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
hp = dp->hdr;
*hp = 0;
- *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_VC(dchdr->vc);
*hp |= DSI_HDR_BTA;
*hp |= DSI_HDR_DTYPE(DTYPE_DCS_READ);
- if (cm->last)
+ if (dchdr->last)
*hp |= DSI_HDR_LAST;
*hp |= DSI_HDR_DATA1(cm->payload[0]); /* dcs command byte */
*hp |= DSI_HDR_DATA2(0);
mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
-
- return dp->len; /* 4 bytes */
+ return DSI_HOST_HDR_SIZE; /* 4 bytes */
}
static int mdss_dsi_cm_on(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
{
+ struct dsi_ctrl_hdr *dchdr;
u32 *hp;
+ dchdr = &cm->dchdr;
mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
hp = dp->hdr;
*hp = 0;
- *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_VC(dchdr->vc);
*hp |= DSI_HDR_DTYPE(DTYPE_CM_ON);
- if (cm->last)
+ if (dchdr->last)
*hp |= DSI_HDR_LAST;
mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
-
- return dp->len; /* 4 bytes */
+ return DSI_HOST_HDR_SIZE; /* 4 bytes */
}
static int mdss_dsi_cm_off(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
{
+ struct dsi_ctrl_hdr *dchdr;
u32 *hp;
+ dchdr = &cm->dchdr;
mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
hp = dp->hdr;
*hp = 0;
- *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_VC(dchdr->vc);
*hp |= DSI_HDR_DTYPE(DTYPE_CM_OFF);
- if (cm->last)
+ if (dchdr->last)
*hp |= DSI_HDR_LAST;
mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
-
- return dp->len; /* 4 bytes */
+ return DSI_HOST_HDR_SIZE; /* 4 bytes */
}
static int mdss_dsi_peripheral_on(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
{
+ struct dsi_ctrl_hdr *dchdr;
u32 *hp;
+ dchdr = &cm->dchdr;
mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
hp = dp->hdr;
*hp = 0;
- *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_VC(dchdr->vc);
*hp |= DSI_HDR_DTYPE(DTYPE_PERIPHERAL_ON);
- if (cm->last)
+ if (dchdr->last)
*hp |= DSI_HDR_LAST;
mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
-
- return dp->len; /* 4 bytes */
+ return DSI_HOST_HDR_SIZE; /* 4 bytes */
}
static int mdss_dsi_peripheral_off(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
{
+ struct dsi_ctrl_hdr *dchdr;
u32 *hp;
+ dchdr = &cm->dchdr;
mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
hp = dp->hdr;
*hp = 0;
- *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_VC(dchdr->vc);
*hp |= DSI_HDR_DTYPE(DTYPE_PERIPHERAL_OFF);
- if (cm->last)
+ if (dchdr->last)
*hp |= DSI_HDR_LAST;
mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
-
- return dp->len; /* 4 bytes */
+ return DSI_HOST_HDR_SIZE; /* 4 bytes */
}
static int mdss_dsi_set_max_pktsize(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
{
+ struct dsi_ctrl_hdr *dchdr;
u32 *hp;
+ dchdr = &cm->dchdr;
if (cm->payload == 0) {
pr_err("%s: NO payload error\n", __func__);
return 0;
@@ -496,55 +587,56 @@
mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
hp = dp->hdr;
*hp = 0;
- *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_VC(dchdr->vc);
*hp |= DSI_HDR_DTYPE(DTYPE_MAX_PKTSIZE);
- if (cm->last)
+ if (dchdr->last)
*hp |= DSI_HDR_LAST;
*hp |= DSI_HDR_DATA1(cm->payload[0]);
*hp |= DSI_HDR_DATA2(cm->payload[1]);
mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
-
- return dp->len; /* 4 bytes */
+ return DSI_HOST_HDR_SIZE; /* 4 bytes */
}
static int mdss_dsi_null_pkt(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
{
+ struct dsi_ctrl_hdr *dchdr;
u32 *hp;
+ dchdr = &cm->dchdr;
mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
hp = dp->hdr;
*hp = 0;
- *hp = DSI_HDR_WC(cm->dlen);
+ *hp = DSI_HDR_WC(dchdr->dlen);
*hp |= DSI_HDR_LONG_PKT;
- *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_VC(dchdr->vc);
*hp |= DSI_HDR_DTYPE(DTYPE_NULL_PKT);
- if (cm->last)
+ if (dchdr->last)
*hp |= DSI_HDR_LAST;
mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
-
- return dp->len; /* 4 bytes */
+ return DSI_HOST_HDR_SIZE; /* 4 bytes */
}
static int mdss_dsi_blank_pkt(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
{
+ struct dsi_ctrl_hdr *dchdr;
u32 *hp;
+ dchdr = &cm->dchdr;
mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE);
hp = dp->hdr;
*hp = 0;
- *hp = DSI_HDR_WC(cm->dlen);
+ *hp = DSI_HDR_WC(dchdr->dlen);
*hp |= DSI_HDR_LONG_PKT;
- *hp |= DSI_HDR_VC(cm->vc);
+ *hp |= DSI_HDR_VC(dchdr->vc);
*hp |= DSI_HDR_DTYPE(DTYPE_BLANK_PKT);
- if (cm->last)
+ if (dchdr->last)
*hp |= DSI_HDR_LAST;
mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE);
-
- return dp->len; /* 4 bytes */
+ return DSI_HOST_HDR_SIZE; /* 4 bytes */
}
/*
@@ -552,9 +644,12 @@
*/
int mdss_dsi_cmd_dma_add(struct dsi_buf *dp, struct dsi_cmd_desc *cm)
{
+ struct dsi_ctrl_hdr *dchdr;
int len = 0;
- switch (cm->dtype) {
+ dchdr = &cm->dchdr;
+
+ switch (dchdr->dtype) {
case DTYPE_GEN_WRITE:
case DTYPE_GEN_WRITE1:
case DTYPE_GEN_WRITE2:
@@ -603,7 +698,7 @@
break;
default:
pr_debug("%s: dtype=%x NOT supported\n",
- __func__, cm->dtype);
+ __func__, dchdr->dtype);
break;
}
@@ -813,12 +908,11 @@
dsi_ctrl |= BIT(0); /* enable dsi */
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, dsi_ctrl);
- mdss_dsi_irq_ctrl(ctrl_pdata, 1, 0); /* enable dsi irq */
wmb();
}
-void mipi_set_tx_power_mode(int mode, struct mdss_panel_data *pdata)
+void mdss_set_tx_power_mode(int mode, struct mdss_panel_data *pdata)
{
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
u32 data;
@@ -1032,94 +1126,102 @@
return 4;
}
+static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct dsi_buf *tp);
+
+static int mdss_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct dsi_buf *rp, int rlen);
+
+static int mdss_dsi_cmds2buf_tx(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct dsi_cmd_desc *cmds, int cnt)
+{
+ struct dsi_buf *tp;
+ struct dsi_cmd_desc *cm;
+ struct dsi_ctrl_hdr *dchdr;
+ int len, tot = 0;
+
+ tp = &ctrl->tx_buf;
+ mdss_dsi_buf_init(tp);
+ cm = cmds;
+ len = 0;
+ while (cnt--) {
+ dchdr = &cm->dchdr;
+ mdss_dsi_buf_reserve(tp, len);
+ len = mdss_dsi_cmd_dma_add(tp, cm);
+ tot += len;
+ if (dchdr->last) {
+ tp->data = tp->start; /* begin of buf */
+ mdss_dsi_enable_irq(ctrl, DSI_CMD_TERM);
+ mdss_dsi_cmd_dma_tx(ctrl, tp);
+ if (dchdr->wait)
+ usleep(dchdr->wait * 1000);
+
+ mdss_dsi_buf_init(tp);
+ len = 0;
+ }
+ cm++;
+ }
+ return tot;
+}
+
/*
* mdss_dsi_cmds_tx:
- * ov_mutex need to be acquired before call this function.
+ * thread context only
*/
-int mdss_dsi_cmds_tx(struct mdss_panel_data *pdata,
- struct dsi_buf *tp, struct dsi_cmd_desc *cmds, int cnt)
+int mdss_dsi_cmds_tx(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct dsi_cmd_desc *cmds, int cnt)
{
- struct dsi_cmd_desc *cm;
- u32 dsi_ctrl, ctrl;
- int i, video_mode;
- unsigned long flag;
- struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
+ u32 dsi_ctrl, data;
+ int video_mode;
- if (pdata == NULL) {
- pr_err("%s: Invalid input data\n", __func__);
- return -EINVAL;
- }
-
- ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
- panel_data);
-
- if (ctrl_pdata->shared_pdata.broadcast_enable)
- if (pdata->panel_info.pdest == DISPLAY_1) {
+ if (ctrl->shared_pdata.broadcast_enable) {
+ if (ctrl->ndx == DSI_CTRL_0) {
pr_debug("%s: Broadcast mode. 1st ctrl\n",
__func__);
return 0;
}
+ }
+
+ if (ctrl->shared_pdata.broadcast_enable) {
+ if ((ctrl->ndx == DSI_CTRL_1)
+ && (left_ctrl_pdata != NULL)) {
+ dsi_ctrl = MIPI_INP(left_ctrl_pdata->ctrl_base
+ + 0x0004);
+ video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
+ if (video_mode) {
+ data = dsi_ctrl | 0x04; /* CMD_MODE_EN */
+ MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
+ data);
+ }
+ }
+ }
/* turn on cmd mode
* for video mode, do not send cmds more than
* one pixel line, since it only transmit it
* during BLLP.
*/
-
- if (ctrl_pdata->shared_pdata.broadcast_enable)
- if ((pdata->panel_info.pdest == DISPLAY_2)
- && (left_ctrl_pdata != NULL)) {
- dsi_ctrl = MIPI_INP(left_ctrl_pdata->ctrl_base
- + 0x0004);
- video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
- if (video_mode) {
- ctrl = dsi_ctrl | 0x04; /* CMD_MODE_EN */
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
- ctrl);
- }
- }
-
- dsi_ctrl = MIPI_INP((ctrl_pdata->ctrl_base) + 0x0004);
+ dsi_ctrl = MIPI_INP((ctrl->ctrl_base) + 0x0004);
video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
if (video_mode) {
- ctrl = dsi_ctrl | 0x04; /* CMD_MODE_EN */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, ctrl);
+ data = dsi_ctrl | 0x04; /* CMD_MODE_EN */
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0004, data);
}
- spin_lock_irqsave(&dsi_mdp_lock, flag);
- mdss_dsi_irq_ctrl(ctrl_pdata, 1, 0);
-
- dsi_mdp_busy = true;
- spin_unlock_irqrestore(&dsi_mdp_lock, flag);
-
- cm = cmds;
- mdss_dsi_buf_init(tp);
- for (i = 0; i < cnt; i++) {
- mdss_dsi_buf_init(tp);
- mdss_dsi_cmd_dma_add(tp, cm);
- mdss_dsi_cmd_dma_tx(tp, pdata);
- if (cm->wait)
- msleep(cm->wait);
- cm++;
- }
-
- spin_lock_irqsave(&dsi_mdp_lock, flag);
- dsi_mdp_busy = false;
- mdss_dsi_irq_ctrl(ctrl_pdata, 0, 0);
- spin_unlock_irqrestore(&dsi_mdp_lock, flag);
+ mdss_dsi_cmds2buf_tx(ctrl, cmds, cnt);
if (video_mode)
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004,
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0004,
dsi_ctrl); /* restore */
return cnt;
}
-/* MDSS_DSI_MRPS, Maximum Return Packet Size */
+/* MIPI_DSI_MRPS, Maximum Return Packet Size */
static char max_pktsize[2] = {0x00, 0x00}; /* LSB tx first, 10 bytes */
-static struct dsi_cmd_desc pkt_size_cmd[] = {
- {DTYPE_MAX_PKTSIZE, 1, 0, 0, 0,
- sizeof(max_pktsize), max_pktsize}
+static struct dsi_cmd_desc pkt_size_cmd = {
+ {DTYPE_MAX_PKTSIZE, 1, 0, 0, 0, sizeof(max_pktsize)},
+ max_pktsize,
};
/*
@@ -1127,31 +1229,24 @@
* plus DCS header, ECC and CRC for DCS long read response
* mdss_dsi_controller only have 4x32 bits register ( 16 bytes) to
* hold data per transaction.
- * MDSS_DSI_LEN equal to 8
+ * MIPI_DSI_LEN equal to 8
* len should be either 4 or 8
- * any return data more than MDSS_DSI_LEN need to be break down
+ * any return data more than MIPI_DSI_LEN need to be break down
* to multiple transactions.
*
* ov_mutex need to be acquired before call this function.
*/
-int mdss_dsi_cmds_rx(struct mdss_panel_data *pdata,
- struct dsi_buf *tp, struct dsi_buf *rp,
- struct dsi_cmd_desc *cmds, int rlen)
+
+int mdss_dsi_cmds_rx(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct dsi_cmd_desc *cmds, int rlen, u32 rx_flags)
{
int cnt, len, diff, pkt_size;
- unsigned long flag;
+ struct dsi_buf *tp, *rp;
+ int no_max_pkt_size;
char cmd;
- struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
- if (pdata == NULL) {
- pr_err("%s: Invalid input data\n", __func__);
- return -EINVAL;
- }
-
- ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
- panel_data);
-
- if (pdata->panel_info.mipi.no_max_pkt_size)
+ no_max_pkt_size = rx_flags & CMD_REQ_NO_MAX_PKT_SIZE;
+ if (no_max_pkt_size)
rlen = ALIGN(rlen, 4); /* Only support rlen = 4*n */
len = rlen;
@@ -1176,33 +1271,33 @@
cnt = len + 6; /* 4 bytes header + 2 bytes crc */
}
- spin_lock_irqsave(&dsi_mdp_lock, flag);
- mdss_dsi_irq_ctrl(ctrl_pdata, 1, 0);
- dsi_mdp_busy = true;
- spin_unlock_irqrestore(&dsi_mdp_lock, flag);
+ tp = &ctrl->tx_buf;
+ rp = &ctrl->rx_buf;
- if (!pdata->panel_info.mipi.no_max_pkt_size) {
+ if (!no_max_pkt_size) {
/* packet size need to be set at every read */
pkt_size = len;
max_pktsize[0] = pkt_size;
+ mdss_dsi_enable_irq(ctrl, DSI_CMD_TERM);
mdss_dsi_buf_init(tp);
- mdss_dsi_cmd_dma_add(tp, pkt_size_cmd);
- mdss_dsi_cmd_dma_tx(tp, pdata);
+ mdss_dsi_cmd_dma_add(tp, &pkt_size_cmd);
+ mdss_dsi_cmd_dma_tx(ctrl, tp);
pr_debug("%s: Max packet size sent\n", __func__);
}
+ mdss_dsi_enable_irq(ctrl, DSI_CMD_TERM);
mdss_dsi_buf_init(tp);
mdss_dsi_cmd_dma_add(tp, cmds);
/* transmit read comamnd to client */
- mdss_dsi_cmd_dma_tx(tp, pdata);
+ mdss_dsi_cmd_dma_tx(ctrl, tp);
/*
* once cmd_dma_done interrupt received,
* return data from client is ready and stored
* at RDBK_DATA register already
*/
mdss_dsi_buf_init(rp);
- if (pdata->panel_info.mipi.no_max_pkt_size) {
+ if (no_max_pkt_size) {
/*
* expect rlen = n * 4
* short alignement for start addr
@@ -1210,14 +1305,9 @@
rp->data += 2;
}
- mdss_dsi_cmd_dma_rx(rp, cnt, pdata);
+ mdss_dsi_cmd_dma_rx(ctrl, rp, cnt);
- spin_lock_irqsave(&dsi_mdp_lock, flag);
- dsi_mdp_busy = false;
- mdss_dsi_irq_ctrl(ctrl_pdata, 0, 0);
- spin_unlock_irqrestore(&dsi_mdp_lock, flag);
-
- if (pdata->panel_info.mipi.no_max_pkt_size) {
+ if (no_max_pkt_size) {
/*
* remove extra 2 bytes from previous
* rx transaction at shift register
@@ -1255,32 +1345,21 @@
return rp->len;
}
-int mdss_dsi_cmd_dma_tx(struct dsi_buf *tp,
- struct mdss_panel_data *pdata)
+#define DMA_TX_TIMEOUT 200
+
+static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct dsi_buf *tp)
{
int len;
int domain = MDSS_IOMMU_DOMAIN_UNSECURE;
char *bp;
unsigned long size, addr;
- struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
- if (pdata == NULL) {
- pr_err("%s: Invalid input data\n", __func__);
- return -EINVAL;
- }
-
- ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
- panel_data);
bp = tp->data;
len = ALIGN(tp->len, 4);
size = ALIGN(tp->len, SZ_4K);
- tp->dmap = dma_map_single(&dsi_dev, tp->data, size, DMA_TO_DEVICE);
- if (dma_mapping_error(&dsi_dev, tp->dmap)) {
- pr_err("%s: dmap mapp failed\n", __func__);
- return -ENOMEM;
- }
if (is_mdss_iommu_attached()) {
int ret = msm_iommu_map_contig_buffer(tp->dmap,
@@ -1294,53 +1373,47 @@
addr = tp->dmap;
}
- INIT_COMPLETION(dsi_dma_comp);
+ INIT_COMPLETION(ctrl->dma_comp);
- if (ctrl_pdata->shared_pdata.broadcast_enable)
- if ((pdata->panel_info.pdest == DISPLAY_2)
+ if (ctrl->shared_pdata.broadcast_enable)
+ if ((ctrl->ndx == DSI_CTRL_1)
&& (left_ctrl_pdata != NULL)) {
MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x048, addr);
MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x04c, len);
}
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x048, addr);
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x04c, len);
+ MIPI_OUTP((ctrl->ctrl_base) + 0x048, addr);
+ MIPI_OUTP((ctrl->ctrl_base) + 0x04c, len);
wmb();
- if (ctrl_pdata->shared_pdata.broadcast_enable)
- if ((pdata->panel_info.pdest == DISPLAY_2)
+ if (ctrl->shared_pdata.broadcast_enable)
+ if ((ctrl->ndx == DSI_CTRL_1)
&& (left_ctrl_pdata != NULL)) {
MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x090, 0x01);
}
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x090, 0x01); /* trigger */
+ MIPI_OUTP((ctrl->ctrl_base) + 0x090, 0x01); /* trigger */
wmb();
- wait_for_completion(&dsi_dma_comp);
+ if (!wait_for_completion_timeout(&ctrl->dma_comp,
+ msecs_to_jiffies(DMA_TX_TIMEOUT))) {
+ pr_err("%s: dma timeout error\n", __func__);
+ }
if (is_mdss_iommu_attached())
msm_iommu_unmap_contig_buffer(addr,
mdss_get_iommu_domain(domain), 0, size);
- dma_unmap_single(&dsi_dev, tp->dmap, size, DMA_TO_DEVICE);
- tp->dmap = 0;
return tp->len;
}
-int mdss_dsi_cmd_dma_rx(struct dsi_buf *rp, int rlen,
- struct mdss_panel_data *pdata)
+static int mdss_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct dsi_buf *rp, int rlen)
+
{
u32 *lp, data;
int i, off, cnt;
- struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
- if (pdata == NULL) {
- pr_err("%s: Invalid input data\n", __func__);
- return -EINVAL;
- }
-
- ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
- panel_data);
lp = (u32 *)rp->data;
cnt = rlen;
cnt += 3;
@@ -1352,106 +1425,300 @@
off = 0x06c; /* DSI_RDBK_DATA0 */
off += ((cnt - 1) * 4);
-
for (i = 0; i < cnt; i++) {
- data = (u32)MIPI_INP((ctrl_pdata->ctrl_base) + off);
+ data = (u32)MIPI_INP((ctrl->ctrl_base) + off);
*lp++ = ntohl(data); /* to network byte order */
pr_debug("%s: data = 0x%x and ntohl(data) = 0x%x\n",
__func__, data, ntohl(data));
off -= 4;
rp->len += sizeof(*lp);
}
-
return rlen;
}
-void mdss_dsi_ack_err_status(unsigned char *dsi_base)
+#define VSYNC_PERIOD 17
+
+void mdss_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ unsigned long flag;
+ u32 data;
+
+ /* DSI_INTL_CTRL */
+ data = MIPI_INP((ctrl->ctrl_base) + 0x0110);
+ data |= DSI_INTR_VIDEO_DONE_MASK;
+
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data);
+
+ spin_lock_irqsave(&ctrl->mdp_lock, flag);
+ INIT_COMPLETION(ctrl->video_comp);
+ mdss_dsi_enable_irq(ctrl, DSI_VIDEO_TERM);
+ spin_unlock_irqrestore(&ctrl->mdp_lock, flag);
+
+ wait_for_completion_timeout(&ctrl->video_comp,
+ msecs_to_jiffies(VSYNC_PERIOD * 4));
+
+ data = MIPI_INP((ctrl->ctrl_base) + 0x0110);
+ data &= ~DSI_INTR_VIDEO_DONE_MASK;
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data);
+}
+
+static void mdss_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ mdss_dsi_wait4video_done(ctrl);
+ /* delay 4 ms to skip BLLP */
+ usleep(4000);
+}
+
+void mdss_dsi_cmd_mdp_start(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ unsigned long flag;
+
+ spin_lock_irqsave(&ctrl->mdp_lock, flag);
+ mdss_dsi_enable_irq(ctrl, DSI_MDP_TERM);
+ ctrl->mdp_busy = true;
+ INIT_COMPLETION(ctrl->mdp_comp);
+ spin_unlock_irqrestore(&ctrl->mdp_lock, flag);
+}
+
+void mdss_dsi_cmd_mdp_busy(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ unsigned long flags;
+ int need_wait = 0;
+
+ pr_debug("%s: start pid=%d\n",
+ __func__, current->pid);
+ spin_lock_irqsave(&ctrl->mdp_lock, flags);
+ if (ctrl->mdp_busy == true)
+ need_wait++;
+ spin_unlock_irqrestore(&ctrl->mdp_lock, flags);
+
+ if (need_wait) {
+ /* wait until DMA finishes the current job */
+ pr_debug("%s: pending pid=%d\n",
+ __func__, current->pid);
+ wait_for_completion(&ctrl->mdp_comp);
+ }
+ pr_debug("%s: done pid=%d\n",
+ __func__, current->pid);
+}
+
+void mdss_dsi_cmdlist_tx(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct dcs_cmd_req *req)
+{
+ int ret;
+
+ ret = mdss_dsi_cmds_tx(ctrl, req->cmds, req->cmds_cnt);
+
+ if (req->cb)
+ req->cb(ret);
+
+}
+
+void mdss_dsi_cmdlist_rx(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct dcs_cmd_req *req)
+{
+ int len;
+ u32 data, *dp;
+ struct dsi_buf *rp;
+
+ len = mdss_dsi_cmds_rx(ctrl, req->cmds, req->rlen, req->flags);
+ rp = &ctrl->rx_buf;
+ dp = (u32 *)rp->data;
+ data = *dp;
+
+ if (req->cb)
+ req->cb(data);
+}
+
+void mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp)
+{
+ struct dcs_cmd_req *req;
+ u32 data;
+
+ mutex_lock(&ctrl->cmd_mutex);
+ req = mdss_dsi_cmdlist_get(ctrl);
+
+ /* make sure dsi_cmd_mdp is idle */
+ mdss_dsi_cmd_mdp_busy(ctrl);
+
+ if (req == NULL)
+ goto need_lock;
+
+ pr_debug("%s: from_mdp=%d pid=%d\n", __func__, from_mdp, current->pid);
+ mdss_dsi_clk_ctrl(ctrl, 1);
+
+ data = MIPI_INP((ctrl->ctrl_base) + 0x0004);
+ if (data & 0x02) {
+ /* video mode, make sure video engine is busy
+ * so dcs command will be sent at start of BLLP
+ */
+ mdss_dsi_wait4video_eng_busy(ctrl);
+ } else {
+ /* command mode */
+ if (!from_mdp) { /* cmdlist_put */
+ /* make sure dsi_cmd_mdp is idle */
+ mdss_dsi_cmd_mdp_busy(ctrl);
+ }
+ }
+
+ if (req->flags & CMD_REQ_RX)
+ mdss_dsi_cmdlist_rx(ctrl, req);
+ else
+ mdss_dsi_cmdlist_tx(ctrl, req);
+
+ mdss_dsi_clk_ctrl(ctrl, 0);
+
+need_lock:
+
+ if (from_mdp) /* from pipe_commit */
+ mdss_dsi_cmd_mdp_start(ctrl);
+
+ mutex_unlock(&ctrl->cmd_mutex);
+}
+
+/*
+ * mdss_dsi_cmd_get: ctrl->cmd_mutex acquired by caller
+ */
+struct dcs_cmd_req *mdss_dsi_cmdlist_get(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ struct dcs_cmd_list *clist;
+ struct dcs_cmd_req *req = NULL;
+
+ clist = &ctrl->cmdlist;
+ if (clist->get != clist->put) {
+ req = &clist->list[clist->get];
+ clist->get++;
+ clist->get %= CMD_REQ_MAX;
+ clist->tot--;
+ pr_debug("%s: tot=%d put=%d get=%d\n", __func__,
+ clist->tot, clist->put, clist->get);
+ }
+ return req;
+}
+
+int mdss_dsi_cmdlist_put(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct dcs_cmd_req *cmdreq)
+{
+ struct dcs_cmd_req *req;
+ struct dcs_cmd_list *clist;
+ int ret = 0;
+
+ mutex_lock(&ctrl->cmd_mutex);
+ clist = &ctrl->cmdlist;
+ req = &clist->list[clist->put];
+ *req = *cmdreq;
+ clist->put++;
+ clist->put %= CMD_REQ_MAX;
+ clist->tot++;
+ if (clist->put == clist->get) {
+ /* drop the oldest one */
+ pr_debug("%s: DROP, tot=%d put=%d get=%d\n", __func__,
+ clist->tot, clist->put, clist->get);
+ clist->get++;
+ clist->get %= CMD_REQ_MAX;
+ clist->tot--;
+ }
+ mutex_unlock(&ctrl->cmd_mutex);
+
+ ret++;
+ pr_debug("%s: tot=%d put=%d get=%d\n", __func__,
+ clist->tot, clist->put, clist->get);
+
+ if (req->flags & CMD_REQ_COMMIT)
+ mdss_dsi_cmdlist_commit(ctrl, 0);
+
+ return ret;
+}
+
+void mdss_dsi_ack_err_status(unsigned char *base)
{
u32 status;
- status = MIPI_INP(dsi_base + 0x0068);/* DSI_ACK_ERR_STATUS */
+ status = MIPI_INP(base + 0x0068);/* DSI_ACK_ERR_STATUS */
if (status) {
- MIPI_OUTP(dsi_base + 0x0068, status);
+ MIPI_OUTP(base + 0x0068, status);
pr_debug("%s: status=%x\n", __func__, status);
}
}
-void mdss_dsi_timeout_status(unsigned char *dsi_base)
+void mdss_dsi_timeout_status(unsigned char *base)
{
u32 status;
- status = MIPI_INP(dsi_base + 0x00c0);/* DSI_TIMEOUT_STATUS */
+ status = MIPI_INP(base + 0x00c0);/* DSI_TIMEOUT_STATUS */
if (status & 0x0111) {
- MIPI_OUTP(dsi_base + 0x00c0, status);
+ MIPI_OUTP(base + 0x00c0, status);
pr_debug("%s: status=%x\n", __func__, status);
}
}
-void mdss_dsi_dln0_phy_err(unsigned char *dsi_base)
+void mdss_dsi_dln0_phy_err(unsigned char *base)
{
u32 status;
- status = MIPI_INP(dsi_base + 0x00b4);/* DSI_DLN0_PHY_ERR */
+ status = MIPI_INP(base + 0x00b4);/* DSI_DLN0_PHY_ERR */
if (status & 0x011111) {
- MIPI_OUTP(dsi_base + 0x00b4, status);
+ MIPI_OUTP(base + 0x00b4, status);
pr_debug("%s: status=%x\n", __func__, status);
}
}
-void mdss_dsi_fifo_status(unsigned char *dsi_base)
+void mdss_dsi_fifo_status(unsigned char *base)
{
u32 status;
- status = MIPI_INP(dsi_base + 0x000c);/* DSI_FIFO_STATUS */
+ status = MIPI_INP(base + 0x000c);/* DSI_FIFO_STATUS */
if (status & 0x44444489) {
- MIPI_OUTP(dsi_base + 0x000c, status);
+ MIPI_OUTP(base + 0x000c, status);
pr_debug("%s: status=%x\n", __func__, status);
}
}
-void mdss_dsi_status(unsigned char *dsi_base)
+void mdss_dsi_status(unsigned char *base)
{
u32 status;
- status = MIPI_INP(dsi_base + 0x0008);/* DSI_STATUS */
+ status = MIPI_INP(base + 0x0008);/* DSI_STATUS */
if (status & 0x80000000) {
- MIPI_OUTP(dsi_base + 0x0008, status);
+ MIPI_OUTP(base + 0x0008, status);
pr_debug("%s: status=%x\n", __func__, status);
}
}
-void mdss_dsi_error(unsigned char *dsi_base)
+void mdss_dsi_error(struct mdss_dsi_ctrl_pdata *ctrl)
{
+ unsigned char *base;
+
+ base = ctrl->ctrl_base;
+
/* DSI_ERR_INT_MASK0 */
- mdss_dsi_ack_err_status(dsi_base); /* mask0, 0x01f */
- mdss_dsi_timeout_status(dsi_base); /* mask0, 0x0e0 */
- mdss_dsi_fifo_status(dsi_base); /* mask0, 0x133d00 */
- mdss_dsi_status(dsi_base); /* mask0, 0xc0100 */
- mdss_dsi_dln0_phy_err(dsi_base); /* mask0, 0x3e00000 */
+ mdss_dsi_ack_err_status(base); /* mask0, 0x01f */
+ mdss_dsi_timeout_status(base); /* mask0, 0x0e0 */
+ mdss_dsi_fifo_status(base); /* mask0, 0x133d00 */
+ mdss_dsi_status(base); /* mask0, 0xc0100 */
+ mdss_dsi_dln0_phy_err(base); /* mask0, 0x3e00000 */
}
irqreturn_t mdss_dsi_isr(int irq, void *ptr)
{
u32 isr;
- unsigned char *dsi_base;
- struct mdss_dsi_ctrl_pdata *ctrl_pdata =
+ struct mdss_dsi_ctrl_pdata *ctrl =
(struct mdss_dsi_ctrl_pdata *)ptr;
- dsi_base = ctrl_pdata->ctrl_base;
- if (!dsi_base)
+ if (!ctrl->ctrl_base)
pr_err("%s:%d DSI base adr no Initialized",
__func__, __LINE__);
- isr = MIPI_INP(dsi_base + 0x0110);/* DSI_INTR_CTRL */
- MIPI_OUTP(dsi_base + 0x0110, isr);
+ isr = MIPI_INP(ctrl->ctrl_base + 0x0110);/* DSI_INTR_CTRL */
+ MIPI_OUTP(ctrl->ctrl_base + 0x0110, isr);
- if (ctrl_pdata->shared_pdata.broadcast_enable)
- if ((ctrl_pdata->panel_data.panel_info.pdest == DISPLAY_2)
+ if (ctrl->shared_pdata.broadcast_enable)
+ if ((ctrl->panel_data.panel_info.pdest == DISPLAY_2)
&& (left_ctrl_pdata != NULL)) {
u32 isr0;
isr0 = MIPI_INP(left_ctrl_pdata->ctrl_base
@@ -1459,24 +1726,38 @@
MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0110, isr0);
}
- pr_debug("%s: isr=%x %x", __func__, isr, (int)DSI_INTR_ERROR);
+ pr_debug("%s: isr=%x", __func__, isr);
- if (isr & DSI_INTR_ERROR)
- mdss_dsi_error(dsi_base);
-
- if (isr & DSI_INTR_VIDEO_DONE) {
- /*
- * do something here
- */
+ if (isr & DSI_INTR_ERROR) {
+ pr_err("%s: isr=%x %x", __func__, isr, (int)DSI_INTR_ERROR);
+ spin_lock(&ctrl->mdp_lock);
+ ctrl->mdp_busy = false;
+ mdss_dsi_disable_irq_nosync(ctrl, DSI_MDP_TERM);
+ complete(&ctrl->mdp_comp);
+ mdss_dsi_error(ctrl);
+ spin_unlock(&ctrl->mdp_lock);
}
- if (isr & DSI_INTR_CMD_DMA_DONE)
- complete(&dsi_dma_comp);
+ if (isr & DSI_INTR_VIDEO_DONE) {
+ spin_lock(&ctrl->mdp_lock);
+ mdss_dsi_disable_irq_nosync(ctrl, DSI_VIDEO_TERM);
+ complete(&ctrl->video_comp);
+ spin_unlock(&ctrl->mdp_lock);
+ }
+
+ if (isr & DSI_INTR_CMD_DMA_DONE) {
+ spin_lock(&ctrl->mdp_lock);
+ mdss_dsi_disable_irq_nosync(ctrl, DSI_CMD_TERM);
+ complete(&ctrl->dma_comp);
+ spin_unlock(&ctrl->mdp_lock);
+ }
if (isr & DSI_INTR_CMD_MDP_DONE) {
- spin_lock(&dsi_mdp_lock);
- dsi_mdp_busy = false;
- spin_unlock(&dsi_mdp_lock);
+ spin_lock(&ctrl->mdp_lock);
+ ctrl->mdp_busy = false;
+ mdss_dsi_disable_irq_nosync(ctrl, DSI_MDP_TERM);
+ complete(&ctrl->mdp_comp);
+ spin_unlock(&ctrl->mdp_lock);
}
return IRQ_HANDLED;
diff --git a/drivers/video/msm/mdss/mdss_dsi_panel.c b/drivers/video/msm/mdss/mdss_dsi_panel.c
index c56cd41..05a84e3 100644
--- a/drivers/video/msm/mdss/mdss_dsi_panel.c
+++ b/drivers/video/msm/mdss/mdss_dsi_panel.c
@@ -26,9 +26,6 @@
#define DT_CMD_HDR 6
-static struct dsi_buf dsi_panel_tx_buf;
-static struct dsi_buf dsi_panel_rx_buf;
-
DEFINE_LED_TRIGGER(bl_led_trigger);
static struct mdss_dsi_phy_ctrl phy_params;
@@ -37,16 +34,18 @@
{
int ret;
- if (!gpio_is_valid(ctrl->pwm_gpio)) {
- pr_err("%s: pwm_gpio=%d Invalid\n", __func__,
- ctrl->pwm_gpio);
+ if (!gpio_is_valid(ctrl->pwm_pmic_gpio)) {
+ pr_err("%s: pwm_pmic_gpio=%d Invalid\n", __func__,
+ ctrl->pwm_pmic_gpio);
+ ctrl->pwm_pmic_gpio = -1;
return;
}
- ret = gpio_request(ctrl->pwm_gpio, "disp_pwm");
+ ret = gpio_request(ctrl->pwm_pmic_gpio, "disp_pwm");
if (ret) {
- pr_err("%s: pwm_gpio=%d request failed\n", __func__,
- ctrl->pwm_gpio);
+ pr_err("%s: pwm_pmic_gpio=%d request failed\n", __func__,
+ ctrl->pwm_pmic_gpio);
+ ctrl->pwm_pmic_gpio = -1;
return;
}
@@ -54,8 +53,8 @@
if (ctrl->pwm_bl == NULL || IS_ERR(ctrl->pwm_bl)) {
pr_err("%s: lpg_chan=%d pwm request failed", __func__,
ctrl->pwm_lpg_chan);
- gpio_free(ctrl->pwm_gpio);
- ctrl->pwm_gpio = -1;
+ gpio_free(ctrl->pwm_pmic_gpio);
+ ctrl->pwm_pmic_gpio = -1;
}
}
@@ -74,7 +73,7 @@
pr_debug("%s: bklt_ctrl=%d pwm_period=%d pwm_gpio=%d pwm_lpg_chan=%d\n",
__func__, ctrl->bklt_ctrl, ctrl->pwm_period,
- ctrl->pwm_gpio, ctrl->pwm_lpg_chan);
+ ctrl->pwm_pmic_gpio, ctrl->pwm_lpg_chan);
pr_debug("%s: ndx=%d level=%d duty=%d\n", __func__,
ctrl->ndx, level, duty);
@@ -90,6 +89,71 @@
pr_err("%s: pwm_enable() failed err=%d\n", __func__, ret);
}
+static char dcs_cmd[2] = {0x54, 0x00}; /* DTYPE_DCS_READ */
+static struct dsi_cmd_desc dcs_read_cmd = {
+ {DTYPE_DCS_READ, 1, 0, 1, 5, sizeof(dcs_cmd)},
+ dcs_cmd
+};
+
+u32 mdss_dsi_dcs_read(struct mdss_dsi_ctrl_pdata *ctrl,
+ char cmd0, char cmd1)
+{
+ struct dcs_cmd_req cmdreq;
+
+ dcs_cmd[0] = cmd0;
+ dcs_cmd[1] = cmd1;
+ memset(&cmdreq, 0, sizeof(cmdreq));
+ cmdreq.cmds = &dcs_read_cmd;
+ cmdreq.cmds_cnt = 1;
+ cmdreq.flags = CMD_REQ_RX | CMD_REQ_COMMIT;
+ cmdreq.rlen = 1;
+ cmdreq.cb = NULL; /* call back */
+ mdss_dsi_cmdlist_put(ctrl, &cmdreq);
+ /*
+ * blocked here, until call back called
+ */
+
+ return 0;
+}
+
+static void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct dsi_panel_cmds *pcmds)
+{
+ struct dcs_cmd_req cmdreq;
+
+ memset(&cmdreq, 0, sizeof(cmdreq));
+ cmdreq.cmds = pcmds->cmds;
+ cmdreq.cmds_cnt = pcmds->cmd_cnt;
+ cmdreq.flags = CMD_REQ_COMMIT;
+ cmdreq.rlen = 0;
+ cmdreq.cb = NULL;
+
+ mdss_dsi_cmdlist_put(ctrl, &cmdreq);
+}
+
+static char led_pwm1[2] = {0x51, 0x0}; /* DTYPE_DCS_WRITE1 */
+static struct dsi_cmd_desc backlight_cmd = {
+ {DTYPE_DCS_WRITE1, 1, 0, 0, 1, sizeof(led_pwm1)},
+ led_pwm1
+};
+
+static void mdss_dsi_panel_bklt_dcs(struct mdss_dsi_ctrl_pdata *ctrl, int level)
+{
+ struct dcs_cmd_req cmdreq;
+
+ pr_debug("%s: level=%d\n", __func__, level);
+
+ led_pwm1[1] = (unsigned char)level;
+
+ memset(&cmdreq, 0, sizeof(cmdreq));
+ cmdreq.cmds = &backlight_cmd;
+ cmdreq.cmds_cnt = 1;
+ cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL;
+ cmdreq.rlen = 0;
+ cmdreq.cb = NULL;
+
+ mdss_dsi_cmdlist_put(ctrl, &cmdreq);
+}
void mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable)
{
@@ -125,6 +189,12 @@
msleep(20);
if (gpio_is_valid(ctrl_pdata->disp_en_gpio))
gpio_set_value((ctrl_pdata->disp_en_gpio), 1);
+ if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) {
+ pr_debug("%s: Panel Not properly turned OFF\n",
+ __func__);
+ ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT;
+ pr_debug("%s: Reset panel done\n", __func__);
+ }
} else {
gpio_set_value((ctrl_pdata->rst_gpio), 0);
if (gpio_is_valid(ctrl_pdata->disp_en_gpio))
@@ -152,6 +222,9 @@
case BL_PWM:
mdss_dsi_panel_bklt_pwm(ctrl_pdata, bl_level);
break;
+ case BL_DCS_CMD:
+ mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level);
+ break;
default:
pr_err("%s: Unknown bl_ctrl configuration\n",
__func__);
@@ -159,14 +232,6 @@
}
}
-static char set_tear_on[2] = {0x35, 0x00};
-static struct dsi_cmd_desc dsi_tear_on_cmd = {
- DTYPE_DCS_WRITE1, 1, 0, 0, 0, sizeof(set_tear_on), set_tear_on};
-
-static char set_tear_off[2] = {0x34, 0x00};
-static struct dsi_cmd_desc dsi_tear_off_cmd = {
- DTYPE_DCS_WRITE, 1, 0, 0, 0, sizeof(set_tear_off), set_tear_off};
-
static int mdss_dsi_panel_on(struct mdss_panel_data *pdata)
{
struct mipi_panel_info *mipi;
@@ -183,14 +248,10 @@
pr_debug("%s: ctrl=%p ndx=%d\n", __func__, ctrl, ctrl->ndx);
- if (ctrl->on_cmds->size)
- mdss_dsi_cmds_tx(pdata, &dsi_panel_tx_buf,
- ctrl->on_cmds->buf,
- ctrl->on_cmds->size);
+ if (ctrl->on_cmds.cmd_cnt)
+ mdss_dsi_panel_cmds_send(ctrl, &ctrl->on_cmds);
- mdss_dsi_cmds_tx(pdata, &dsi_panel_tx_buf,
- &dsi_tear_on_cmd, 1);
-
+ pr_debug("%s:-\n", __func__);
return 0;
}
@@ -211,17 +272,95 @@
mipi = &pdata->panel_info.mipi;
- mdss_dsi_cmds_tx(pdata, &dsi_panel_tx_buf,
- &dsi_tear_off_cmd, 1);
+ if (ctrl->off_cmds.cmd_cnt)
+ mdss_dsi_panel_cmds_send(ctrl, &ctrl->off_cmds);
- if (ctrl->off_cmds->size)
- mdss_dsi_cmds_tx(pdata, &dsi_panel_tx_buf,
- ctrl->off_cmds->buf,
- ctrl->off_cmds->size);
+ pr_debug("%s:-\n", __func__);
+ return 0;
+}
+
+
+static int mdss_dsi_parse_dcs_cmds(struct device_node *np,
+ struct dsi_panel_cmds *pcmds, char *cmd_key, char *link_key)
+{
+ const char *data;
+ int blen = 0, len;
+ char *buf, *bp;
+ struct dsi_ctrl_hdr *dchdr;
+ int i, cnt;
+
+ data = of_get_property(np, cmd_key, &blen);
+ if (!data) {
+ pr_err("%s: failed, key=%s\n", __func__, cmd_key);
+ return -ENOMEM;
+ }
+
+ buf = kzalloc(sizeof(char) * blen, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy(buf, data, blen);
+
+ /* scan dcs commands */
+ bp = buf;
+ len = blen;
+ cnt = 0;
+ while (len > sizeof(*dchdr)) {
+ dchdr = (struct dsi_ctrl_hdr *)bp;
+ dchdr->dlen = ntohs(dchdr->dlen);
+ if (dchdr->dlen > len) {
+ pr_err("%s: dtsi cmd=%x error, len=%d",
+ __func__, dchdr->dtype, dchdr->dlen);
+ return -ENOMEM;
+ }
+ bp += sizeof(*dchdr);
+ len -= sizeof(*dchdr);
+ bp += dchdr->dlen;
+ len -= dchdr->dlen;
+ cnt++;
+ }
+
+ if (len != 0) {
+ pr_err("%s: dcs_cmd=%x len=%d error!",
+ __func__, buf[0], blen);
+ kfree(buf);
+ return -ENOMEM;
+ }
+
+ pcmds->cmds = kzalloc(cnt * sizeof(struct dsi_cmd_desc),
+ GFP_KERNEL);
+ if (!pcmds->cmds)
+ return -ENOMEM;
+
+ pcmds->cmd_cnt = cnt;
+ pcmds->buf = buf;
+ pcmds->blen = blen;
+
+ bp = buf;
+ len = blen;
+ for (i = 0; i < cnt; i++) {
+ dchdr = (struct dsi_ctrl_hdr *)bp;
+ len -= sizeof(*dchdr);
+ bp += sizeof(*dchdr);
+ pcmds->cmds[i].dchdr = *dchdr;
+ pcmds->cmds[i].payload = bp;
+ bp += dchdr->dlen;
+ len -= dchdr->dlen;
+ }
+
+ pcmds->link_state = DSI_LP_MODE; /* default */
+
+ data = of_get_property(np, link_key, NULL);
+ if (!strncmp(data, "DSI_HS_MODE", 11))
+ pcmds->link_state = DSI_HS_MODE;
+
+ pr_debug("%s: dcs_cmd=%x len=%d, cmd_cnt=%d link_state=%d\n", __func__,
+ pcmds->buf[0], pcmds->blen, pcmds->cmd_cnt, pcmds->link_state);
return 0;
}
+
static int mdss_panel_parse_dt(struct platform_device *pdev,
struct mdss_panel_common_pdata *panel_data)
{
@@ -229,12 +368,8 @@
u32 res[6], tmp;
u32 fbc_res[7];
int rc, i, len;
- int cmd_plen, data_offset;
const char *data;
static const char *bl_ctrl_type, *pdest;
- static const char *on_cmds_state, *off_cmds_state;
- char *on_cmds = NULL, *off_cmds = NULL;
- int num_of_on_cmds = 0, num_of_off_cmds = 0;
bool fbc_enabled = false;
rc = of_property_read_u32_array(np, "qcom,mdss-pan-res", res, 2);
@@ -301,15 +436,15 @@
} else if (!strncmp(bl_ctrl_type, "bl_ctrl_pwm", 11)) {
panel_data->panel_info.bklt_ctrl = BL_PWM;
- rc = of_property_read_u32(np, "qcom,dsi-pwm-period", &tmp);
+ rc = of_property_read_u32(np, "qcom,pwm-period", &tmp);
if (rc) {
- pr_err("%s:%d, Error, dsi pwm_period\n",
+ pr_err("%s:%d, Error, panel pwm_period\n",
__func__, __LINE__);
return -EINVAL;
}
panel_data->panel_info.pwm_period = tmp;
- rc = of_property_read_u32(np, "qcom,dsi-lpg-channel", &tmp);
+ rc = of_property_read_u32(np, "qcom,pwm-lpg-channel", &tmp);
if (rc) {
pr_err("%s:%d, Error, dsi lpg channel\n",
__func__, __LINE__);
@@ -317,8 +452,10 @@
}
panel_data->panel_info.pwm_lpg_chan = tmp;
- tmp = of_get_named_gpio(np, "qcom,dsi-pwm-gpio", 0);
- panel_data->panel_info.pwm_gpio = tmp;
+ tmp = of_get_named_gpio(np, "qcom,pwm-pmic-gpio", 0);
+ panel_data->panel_info.pwm_pmic_gpio = tmp;
+ } else if (!strncmp(bl_ctrl_type, "bl_ctrl_dcs", 11)) {
+ panel_data->panel_info.bklt_ctrl = BL_DCS_CMD;
} else {
pr_debug("%s: Unknown backlight control\n", __func__);
panel_data->panel_info.bklt_ctrl = UNKNOWN_CTRL;
@@ -529,171 +666,15 @@
panel_data->panel_info.bpp;
}
- data = of_get_property(np, "qcom,panel-on-cmds", &len);
- if (!data) {
- pr_err("%s:%d, Unable to read ON cmds", __func__, __LINE__);
- goto error;
- }
+ mdss_dsi_parse_dcs_cmds(np, &panel_data->on_cmds,
+ "qcom,panel-on-cmds", "qcom,on-cmds-dsi-state");
- on_cmds = kzalloc(sizeof(char) * len, GFP_KERNEL);
- if (!on_cmds)
- return -ENOMEM;
-
- memcpy(on_cmds, data, len);
-
- data_offset = 0;
- cmd_plen = 0;
- while ((len - data_offset) >= DT_CMD_HDR) {
- data_offset += (DT_CMD_HDR - 1);
- cmd_plen = on_cmds[data_offset++];
- data_offset += cmd_plen;
- num_of_on_cmds++;
- }
- if (!num_of_on_cmds) {
- pr_err("%s:%d, No ON cmds specified", __func__, __LINE__);
- goto error;
- }
-
- panel_data->dsi_panel_on_cmds =
- kzalloc(sizeof(struct dsi_panel_cmds_list), GFP_KERNEL);
- if (!panel_data->dsi_panel_on_cmds)
- return -ENOMEM;
-
- (panel_data->dsi_panel_on_cmds)->buf =
- kzalloc((num_of_on_cmds * sizeof(struct dsi_cmd_desc)),
- GFP_KERNEL);
- if (!(panel_data->dsi_panel_on_cmds)->buf)
- return -ENOMEM;
-
- data_offset = 0;
- for (i = 0; i < num_of_on_cmds; i++) {
- panel_data->dsi_panel_on_cmds->buf[i].dtype =
- on_cmds[data_offset++];
- panel_data->dsi_panel_on_cmds->buf[i].last =
- on_cmds[data_offset++];
- panel_data->dsi_panel_on_cmds->buf[i].vc =
- on_cmds[data_offset++];
- panel_data->dsi_panel_on_cmds->buf[i].ack =
- on_cmds[data_offset++];
- panel_data->dsi_panel_on_cmds->buf[i].wait =
- on_cmds[data_offset++];
- panel_data->dsi_panel_on_cmds->buf[i].dlen =
- on_cmds[data_offset++];
- panel_data->dsi_panel_on_cmds->buf[i].payload =
- &on_cmds[data_offset];
- data_offset += (panel_data->dsi_panel_on_cmds->buf[i].dlen);
- }
-
- if (data_offset != len) {
- pr_err("%s:%d, Incorrect ON command entries",
- __func__, __LINE__);
- goto error;
- }
-
- (panel_data->dsi_panel_on_cmds)->size = num_of_on_cmds;
-
- on_cmds_state = of_get_property(pdev->dev.of_node,
- "qcom,on-cmds-dsi-state", NULL);
- if (!strncmp(on_cmds_state, "DSI_LP_MODE", 11)) {
- (panel_data->dsi_panel_on_cmds)->ctrl_state =
- DSI_LP_MODE;
- } else if (!strncmp(on_cmds_state, "DSI_HS_MODE", 11)) {
- (panel_data->dsi_panel_on_cmds)->ctrl_state =
- DSI_HS_MODE;
- } else {
- pr_debug("%s: ON cmds state not specified. Set Default\n",
- __func__);
- (panel_data->dsi_panel_on_cmds)->ctrl_state =
- DSI_LP_MODE;
- }
-
- data = of_get_property(np, "qcom,panel-off-cmds", &len);
- if (!data) {
- pr_err("%s:%d, Unable to read OFF cmds", __func__, __LINE__);
- goto error;
- }
-
- off_cmds = kzalloc(sizeof(char) * len, GFP_KERNEL);
- if (!off_cmds)
- return -ENOMEM;
-
- memcpy(off_cmds, data, len);
-
- data_offset = 0;
- cmd_plen = 0;
- while ((len - data_offset) >= DT_CMD_HDR) {
- data_offset += (DT_CMD_HDR - 1);
- cmd_plen = off_cmds[data_offset++];
- data_offset += cmd_plen;
- num_of_off_cmds++;
- }
- if (!num_of_off_cmds) {
- pr_err("%s:%d, No OFF cmds specified", __func__, __LINE__);
- goto error;
- }
-
- panel_data->dsi_panel_off_cmds =
- kzalloc(sizeof(struct dsi_panel_cmds_list), GFP_KERNEL);
- if (!panel_data->dsi_panel_off_cmds)
- return -ENOMEM;
-
- (panel_data->dsi_panel_off_cmds)->buf = kzalloc(num_of_off_cmds
- * sizeof(struct dsi_cmd_desc),
- GFP_KERNEL);
- if (!(panel_data->dsi_panel_off_cmds)->buf)
- return -ENOMEM;
-
- data_offset = 0;
- for (i = 0; i < num_of_off_cmds; i++) {
- panel_data->dsi_panel_off_cmds->buf[i].dtype =
- off_cmds[data_offset++];
- panel_data->dsi_panel_off_cmds->buf[i].last =
- off_cmds[data_offset++];
- panel_data->dsi_panel_off_cmds->buf[i].vc =
- off_cmds[data_offset++];
- panel_data->dsi_panel_off_cmds->buf[i].ack =
- off_cmds[data_offset++];
- panel_data->dsi_panel_off_cmds->buf[i].wait =
- off_cmds[data_offset++];
- panel_data->dsi_panel_off_cmds->buf[i].dlen =
- off_cmds[data_offset++];
- panel_data->dsi_panel_off_cmds->buf[i].payload =
- &off_cmds[data_offset];
- data_offset += (panel_data->dsi_panel_off_cmds->buf[i].dlen);
- }
-
- if (data_offset != len) {
- pr_err("%s:%d, Incorrect OFF command entries",
- __func__, __LINE__);
- goto error;
- }
-
- (panel_data->dsi_panel_off_cmds)->size = num_of_off_cmds;
-
- off_cmds_state = of_get_property(pdev->dev.of_node,
- "qcom,off-cmds-dsi-state", NULL);
- if (!strncmp(off_cmds_state, "DSI_LP_MODE", 11)) {
- (panel_data->dsi_panel_off_cmds)->ctrl_state =
- DSI_LP_MODE;
- } else if (!strncmp(off_cmds_state, "DSI_HS_MODE", 11)) {
- (panel_data->dsi_panel_off_cmds)->ctrl_state =
- DSI_HS_MODE;
- } else {
- pr_debug("%s: ON cmds state not specified. Set Default\n",
- __func__);
- (panel_data->dsi_panel_off_cmds)->ctrl_state =
- DSI_LP_MODE;
- }
+ mdss_dsi_parse_dcs_cmds(np, &panel_data->off_cmds,
+ "qcom,panel-off-cmds", "qcom,off-cmds-dsi-state");
return 0;
-error:
- kfree((panel_data->dsi_panel_on_cmds)->buf);
- kfree((panel_data->dsi_panel_off_cmds)->buf);
- kfree(panel_data->dsi_panel_on_cmds);
- kfree(panel_data->dsi_panel_off_cmds);
- kfree(on_cmds);
- kfree(off_cmds);
+error:
return -EINVAL;
}
@@ -744,9 +725,6 @@
static int __init mdss_dsi_panel_init(void)
{
- mdss_dsi_buf_alloc(&dsi_panel_tx_buf, ALIGN(DSI_BUF_SIZE, SZ_4K));
- mdss_dsi_buf_alloc(&dsi_panel_rx_buf, ALIGN(DSI_BUF_SIZE, SZ_4K));
-
return platform_driver_register(&this_driver);
}
module_init(mdss_dsi_panel_init);
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index c3957c5..d9fffa8 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -42,9 +42,13 @@
#include <linux/sync.h>
#include <linux/sw_sync.h>
#include <linux/file.h>
+#include <linux/memory_alloc.h>
#include <mach/board.h>
#include <mach/memory.h>
+#include <mach/iommu.h>
+#include <mach/iommu_domains.h>
+#include <mach/msm_memtypes.h>
#include "mdss_fb.h"
@@ -92,13 +96,14 @@
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data;
if (!mfd)
pr_err("%s mfd NULL\n", __func__);
+ mfd->no_update.value = NOTIFY_TYPE_NO_UPDATE;
complete(&mfd->no_update.comp);
}
static int mdss_fb_notify_update(struct msm_fb_data_type *mfd,
unsigned long *argp)
{
- int ret, notify;
+ int ret, notify, to_user;
ret = copy_from_user(¬ify, argp, sizeof(int));
if (ret) {
@@ -113,17 +118,20 @@
INIT_COMPLETION(mfd->update.comp);
ret = wait_for_completion_interruptible_timeout(
&mfd->update.comp, 4 * HZ);
+ to_user = mfd->update.value;
} else {
INIT_COMPLETION(mfd->no_update.comp);
ret = wait_for_completion_interruptible_timeout(
&mfd->no_update.comp, 4 * HZ);
+ to_user = mfd->no_update.value;
}
if (ret == 0)
ret = -ETIMEDOUT;
- return (ret > 0) ? 0 : ret;
+ else if (ret > 0)
+ ret = copy_to_user(argp, &to_user, sizeof(int));
+ return ret;
}
-#define MAX_BACKLIGHT_BRIGHTNESS 255
static int lcd_backlight_registered;
static void mdss_fb_set_bl_brightness(struct led_classdev *led_cdev,
@@ -132,25 +140,28 @@
struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent);
int bl_lvl;
- if (value > MAX_BACKLIGHT_BRIGHTNESS)
- value = MAX_BACKLIGHT_BRIGHTNESS;
+ if (value > MDSS_MAX_BL_BRIGHTNESS)
+ value = MDSS_MAX_BL_BRIGHTNESS;
/* This maps android backlight level 0 to 255 into
driver backlight level 0 to bl_max with rounding */
- bl_lvl = (2 * value * mfd->panel_info->bl_max +
- MAX_BACKLIGHT_BRIGHTNESS) / (2 * MAX_BACKLIGHT_BRIGHTNESS);
+ MDSS_BRIGHT_TO_BL(bl_lvl, value, mfd->panel_info->bl_max,
+ MDSS_MAX_BL_BRIGHTNESS);
if (!bl_lvl && value)
bl_lvl = 1;
- mutex_lock(&mfd->lock);
- mdss_fb_set_backlight(mfd, bl_lvl);
- mutex_unlock(&mfd->lock);
+ if (!IS_CALIB_MODE_BL(mfd) && (!mfd->ext_bl_ctrl || !value ||
+ !mfd->bl_level)) {
+ mutex_lock(&mfd->bl_lock);
+ mdss_fb_set_backlight(mfd, bl_lvl);
+ mutex_unlock(&mfd->bl_lock);
+ }
}
static struct led_classdev backlight_led = {
.name = "lcd-backlight",
- .brightness = MAX_BACKLIGHT_BRIGHTNESS,
+ .brightness = MDSS_MAX_BL_BRIGHTNESS,
.brightness_set = mdss_fb_set_bl_brightness,
};
@@ -263,6 +274,7 @@
mfd->mdp = *mdp_instance;
mutex_init(&mfd->lock);
+ mutex_init(&mfd->bl_lock);
fbi_list[fbi_list_index++] = fbi;
@@ -524,13 +536,13 @@
(*bl_lvl) = temp;
}
-/* must call this function from within mfd->lock */
+/* must call this function from within mfd->bl_lock */
void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl)
{
struct mdss_panel_data *pdata;
u32 temp = bkl_lvl;
- if (!mfd->panel_power_on || !bl_updated) {
+ if ((!mfd->panel_power_on || !bl_updated) && !IS_CALIB_MODE_BL(mfd)) {
unset_bl_level = bkl_lvl;
return;
} else {
@@ -540,7 +552,8 @@
pdata = dev_get_platdata(&mfd->pdev->dev);
if ((pdata) && (pdata->set_backlight)) {
- mdss_fb_scale_bl(mfd, &temp);
+ if (!IS_CALIB_MODE_BL(mfd))
+ mdss_fb_scale_bl(mfd, &temp);
/*
* Even though backlight has been scaled, want to show that
* backlight has been set to bkl_lvl to those that read from
@@ -556,6 +569,13 @@
pdata->set_backlight(pdata, temp);
mfd->bl_level = bkl_lvl;
bl_level_old = temp;
+
+ if (mfd->mdp.update_ad_input) {
+ mutex_unlock(&mfd->bl_lock);
+ /* Will trigger ad_setup which will grab bl_lock */
+ mfd->mdp.update_ad_input(mfd);
+ mutex_lock(&mfd->bl_lock);
+ }
}
}
@@ -566,11 +586,11 @@
if (unset_bl_level && !bl_updated) {
pdata = dev_get_platdata(&mfd->pdev->dev);
if ((pdata) && (pdata->set_backlight)) {
- mutex_lock(&mfd->lock);
+ mutex_lock(&mfd->bl_lock);
mfd->bl_level = unset_bl_level;
pdata->set_backlight(pdata, mfd->bl_level);
bl_level_old = unset_bl_level;
- mutex_unlock(&mfd->lock);
+ mutex_unlock(&mfd->bl_lock);
bl_updated = 1;
}
}
@@ -591,6 +611,9 @@
ret = mfd->mdp.on_fnc(mfd);
if (ret == 0)
mfd->panel_power_on = true;
+ mutex_lock(&mfd->update.lock);
+ mfd->update.type = NOTIFY_TYPE_UPDATE;
+ mutex_unlock(&mfd->update.lock);
}
break;
@@ -602,13 +625,20 @@
if (mfd->panel_power_on && mfd->mdp.off_fnc) {
int curr_pwr_state;
+ mutex_lock(&mfd->update.lock);
+ mfd->update.type = NOTIFY_TYPE_SUSPEND;
+ mutex_unlock(&mfd->update.lock);
del_timer(&mfd->no_update.timer);
+ mfd->no_update.value = NOTIFY_TYPE_SUSPEND;
complete(&mfd->no_update.comp);
mfd->op_enable = false;
curr_pwr_state = mfd->panel_power_on;
+ mutex_lock(&mfd->bl_lock);
+ mdss_fb_set_backlight(mfd, 0);
mfd->panel_power_on = false;
bl_updated = 0;
+ mutex_unlock(&mfd->bl_lock);
ret = mfd->mdp.off_fnc(mfd);
if (ret)
@@ -626,12 +656,7 @@
static int mdss_fb_blank(int blank_mode, struct fb_info *info)
{
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
- if (blank_mode == FB_BLANK_POWERDOWN) {
- struct fb_event event;
- event.info = info;
- event.data = &blank_mode;
- fb_notifier_call_chain(FB_EVENT_BLANK, &event);
- }
+
mdss_fb_pan_idle(mfd);
if (mfd->op_enable == 0) {
if (blank_mode == FB_BLANK_UNBLANK)
@@ -656,23 +681,22 @@
unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
- mdss_fb_pan_idle(mfd);
- if (off >= len) {
- /* memory mapped io */
- off -= len;
- if (info->var.accel_flags) {
- mutex_unlock(&info->lock);
- return -EINVAL;
- }
- start = info->fix.mmio_start;
- len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
+ if (!start) {
+ pr_warn("No framebuffer memory is allocated.\n");
+ return -ENOMEM;
}
+ mdss_fb_pan_idle(mfd);
+
/* Set VM flags. */
start &= PAGE_MASK;
- if ((vma->vm_end - vma->vm_start + off) > len)
+ if ((vma->vm_end <= vma->vm_start) ||
+ (off >= len) ||
+ ((vma->vm_end - vma->vm_start) > (len - off)))
return -EINVAL;
off += start;
+ if (off < start)
+ return -EINVAL;
vma->vm_pgoff = off >> PAGE_SHIFT;
/* This is an IO map - tell maydump to skip this VMA */
vma->vm_flags |= VM_IO | VM_RESERVED;
@@ -713,13 +737,68 @@
.fb_mmap = mdss_fb_mmap,
};
+static int mdss_fb_alloc_fbmem_iommu(struct msm_fb_data_type *mfd, int dom)
+{
+ void *virt = NULL;
+ unsigned long phys = 0;
+ size_t size = 0;
+ struct platform_device *pdev = mfd->pdev;
+
+ if (!pdev || !pdev->dev.of_node) {
+ pr_err("Invalid device node\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(pdev->dev.of_node,
+ "qcom,memory-reservation-size",
+ &size) || !size) {
+ mfd->fbi->screen_base = NULL;
+ mfd->fbi->fix.smem_start = 0;
+ mfd->fbi->fix.smem_len = 0;
+ return 0;
+ }
+
+ pr_info("%s frame buffer reserve_size=0x%x\n", __func__, size);
+
+ if (size < PAGE_ALIGN(mfd->fbi->fix.line_length *
+ mfd->fbi->var.yres_virtual))
+ pr_warn("reserve size is smaller than framebuffer size\n");
+
+ virt = allocate_contiguous_memory(size, MEMTYPE_EBI1, SZ_1M, 0);
+ if (!virt) {
+ pr_err("unable to alloc fbmem size=%u\n", size);
+ return -ENOMEM;
+ }
+
+ phys = memory_pool_node_paddr(virt);
+
+ msm_iommu_map_contig_buffer(phys, dom, 0, size, SZ_4K, 0,
+ &mfd->iova);
+ pr_info("allocating %u bytes at %p (%lx phys) for fb %d\n",
+ size, virt, phys, mfd->index);
+
+ mfd->fbi->screen_base = virt;
+ mfd->fbi->fix.smem_start = phys;
+ mfd->fbi->fix.smem_len = size;
+
+ return 0;
+}
+
static int mdss_fb_alloc_fbmem(struct msm_fb_data_type *mfd)
{
- if (!mfd->mdp.fb_mem_alloc_fnc) {
+
+ if (mfd->mdp.fb_mem_alloc_fnc)
+ return mfd->mdp.fb_mem_alloc_fnc(mfd);
+ else if (mfd->mdp.fb_mem_get_iommu_domain) {
+ int dom = mfd->mdp.fb_mem_get_iommu_domain();
+ if (dom >= 0)
+ return mdss_fb_alloc_fbmem_iommu(mfd, dom);
+ else
+ return -ENOMEM;
+ } else {
pr_err("no fb memory allocator function defined\n");
return -ENOMEM;
}
- return mfd->mdp.fb_mem_alloc_fnc(mfd);
}
static int mdss_fb_register(struct msm_fb_data_type *mfd)
@@ -904,6 +983,7 @@
mfd->op_enable = true;
+ mutex_init(&mfd->update.lock);
mutex_init(&mfd->no_update.lock);
mutex_init(&mfd->sync_mutex);
init_timer(&mfd->no_update.timer);
@@ -956,6 +1036,7 @@
result = mdss_fb_blank_sub(FB_BLANK_UNBLANK, info,
mfd->op_enable);
if (result) {
+ pm_runtime_put(info->dev);
pr_err("mdss_fb_open: can't turn on display!\n");
return result;
}
@@ -1143,6 +1224,7 @@
struct mdp_display_commit disp_commit;
memset(&disp_commit, 0, sizeof(disp_commit));
disp_commit.wait_for_finish = true;
+ memcpy(&disp_commit.var, var, sizeof(struct fb_var_screeninfo));
return mdss_fb_pan_display_ex(info, &disp_commit);
}
@@ -1209,6 +1291,7 @@
mdss_fb_wait_for_fence(mfd);
if (mfd->mdp.kickoff_fnc)
mfd->mdp.kickoff_fnc(mfd);
+ mdss_fb_update_backlight(mfd);
mdss_fb_signal_timeline(mfd);
} else {
var = &fb_backup->disp_commit.var;
@@ -1535,12 +1618,15 @@
static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
- struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ struct msm_fb_data_type *mfd;
void __user *argp = (void __user *)arg;
struct mdp_page_protection fb_page_protection;
int ret = -ENOSYS;
struct mdp_buf_sync buf_sync;
+ if (!info || !info->par)
+ return -EINVAL;
+ mfd = (struct msm_fb_data_type *)info->par;
mdss_fb_power_setting_idle(mfd);
mdss_fb_pan_idle(mfd);
@@ -1652,7 +1738,7 @@
if (!mdp_instance) {
pr_err("mdss mdp resource not initialized yet\n");
- return -ENODEV;
+ return -EPROBE_DEFER;
}
node = of_parse_phandle(pdev->dev.of_node, "qcom,mdss-fb-map", 0);
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h
index fdbbea9..98bca03 100644
--- a/drivers/video/msm/mdss/mdss_fb.h
+++ b/drivers/video/msm/mdss/mdss_fb.h
@@ -26,7 +26,7 @@
#define MSM_FB_MAX_DEV_LIST 32
#define MSM_FB_ENABLE_DBGFS
-#define WAIT_FENCE_FIRST_TIMEOUT MSEC_PER_SEC
+#define WAIT_FENCE_FIRST_TIMEOUT (3 * MSEC_PER_SEC)
#define WAIT_FENCE_FINAL_TIMEOUT (10 * MSEC_PER_SEC)
/* Display op timeout should be greater than total timeout */
#define WAIT_DISP_OP_TIMEOUT ((WAIT_FENCE_FIRST_TIMEOUT + \
@@ -50,12 +50,14 @@
struct timer_list timer;
struct completion comp;
struct mutex lock;
+ int value;
};
struct msm_fb_data_type;
struct msm_mdp_interface {
int (*fb_mem_alloc_fnc)(struct msm_fb_data_type *mfd);
+ int (*fb_mem_get_iommu_domain)(void);
int (*init_fnc)(struct msm_fb_data_type *mfd);
int (*on_fnc)(struct msm_fb_data_type *mfd);
int (*off_fnc)(struct msm_fb_data_type *mfd);
@@ -67,11 +69,18 @@
int (*lut_update)(struct msm_fb_data_type *mfd, struct fb_cmap *cmap);
int (*do_histogram)(struct msm_fb_data_type *mfd,
struct mdp_histogram *hist);
+ int (*update_ad_input)(struct msm_fb_data_type *mfd);
int (*panel_register_done)(struct mdss_panel_data *pdata);
u32 (*fb_stride)(u32 fb_index, u32 xres, int bpp);
void *private1;
};
+#define IS_CALIB_MODE_BL(mfd) (((mfd)->calib_mode) & MDSS_CALIB_MODE_BL)
+#define MDSS_BRIGHT_TO_BL(out, v, bl_max, max_bright) do {\
+ out = (2 * (v) * (bl_max) + max_bright)\
+ / (2 * max_bright);\
+ } while (0)
+
struct msm_fb_data_type {
u32 key;
u32 index;
@@ -99,9 +108,12 @@
unsigned long cursor_buf_phys;
unsigned long cursor_buf_iova;
+ u32 ext_bl_ctrl;
+ u32 calib_mode;
u32 bl_level;
u32 bl_scale;
u32 bl_min_lvl;
+ struct mutex bl_lock;
struct mutex lock;
struct platform_device *pdev;
@@ -138,6 +150,25 @@
struct mdp_display_commit disp_commit;
};
+static inline void mdss_fb_update_notify_update(struct msm_fb_data_type *mfd)
+{
+ int needs_complete = 0;
+ mutex_lock(&mfd->update.lock);
+ mfd->update.value = mfd->update.type;
+ needs_complete = mfd->update.value == NOTIFY_TYPE_UPDATE;
+ mutex_unlock(&mfd->update.lock);
+ if (needs_complete) {
+ complete(&mfd->update.comp);
+ mutex_lock(&mfd->no_update.lock);
+ if (mfd->no_update.timer.function)
+ del_timer(&(mfd->no_update.timer));
+
+ mfd->no_update.timer.expires = jiffies + (2 * HZ);
+ add_timer(&mfd->no_update.timer);
+ mutex_unlock(&mfd->no_update.lock);
+ }
+}
+
int mdss_fb_get_phys_info(unsigned long *start, unsigned long *len, int fb_num);
void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl);
void mdss_fb_update_backlight(struct msm_fb_data_type *mfd);
diff --git a/drivers/video/msm/mdss/mdss_hdmi_cec.c b/drivers/video/msm/mdss/mdss_hdmi_cec.c
index 694fcde..2cf47fc 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_cec.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_cec.c
@@ -785,12 +785,16 @@
io = cec_ctrl->init_data.io;
- if (!cec_ctrl->cec_enabled)
- return 0;
-
cec_intr = DSS_REG_R_ND(io, HDMI_CEC_INT);
DEV_DBG("%s: cec interrupt status is [0x%x]\n", __func__, cec_intr);
+ if (!cec_ctrl->cec_enabled) {
+ DEV_ERR("%s: cec is not enabled. Just clear int and return.\n",
+ __func__);
+ DSS_REG_W(io, HDMI_CEC_INT, cec_intr);
+ return 0;
+ }
+
cec_status = DSS_REG_R_ND(io, HDMI_CEC_STATUS);
DEV_DBG("%s: cec status is [0x%x]\n", __func__, cec_status);
diff --git a/drivers/video/msm/mdss/mdss_hdmi_edid.c b/drivers/video/msm/mdss/mdss_hdmi_edid.c
index e87f028..1876057 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_edid.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_edid.c
@@ -119,6 +119,12 @@
89909, 119880, 148352, 119880, false},
{HDMI_VFRMT_1280x720p120_16_9, 1280, 720, false, 1650, 370, 750, 30,
90000, 120000, 148500, 120000, false},
+ {HDMI_VFRMT_1280x1024p60_5_4, 1280, 1024, false, 1688, 408, 1066, 42,
+ 63981, 60020, 108000, 60000, false},
+
+ /* All 1024 H Active */
+ {HDMI_VFRMT_1024x768p60_4_3, 1024, 768, false, 1344, 320, 806, 38,
+ 48363, 60004, 65000, 60000, false},
/* All 1440 H Active */
{HDMI_VFRMT_1440x576i50_4_3, 1440, 576, true, 1728, 288, 625, 24,
@@ -1016,7 +1022,7 @@
static void hdmi_edid_get_display_mode(struct hdmi_edid_ctrl *edid_ctrl,
const u8 *data_buf, u32 num_of_cea_blocks)
{
- u8 i = 0;
+ u8 i = 0, offset = 0, std_blk = 0;
u32 video_format = HDMI_VFRMT_640x480p60_4_3;
u32 has480p = false;
u8 len;
@@ -1175,6 +1181,72 @@
}
}
+ std_blk = 0;
+ offset = 0;
+ while (std_blk < 8) {
+ if ((edid_blk0[0x26 + offset] == 0x81) &&
+ (edid_blk0[0x26 + offset + 1] == 0x80)) {
+ pr_debug("%s: 108MHz: off=[%x] stdblk=[%x]\n",
+ __func__, offset, std_blk);
+ hdmi_edid_add_sink_video_format(sink_data,
+ HDMI_VFRMT_1280x1024p60_5_4);
+ }
+ if ((edid_blk0[0x26 + offset] == 0x61) &&
+ (edid_blk0[0x26 + offset + 1] == 0x40)) {
+ pr_debug("%s: 65MHz: off=[%x] stdblk=[%x]\n",
+ __func__, offset, std_blk);
+ hdmi_edid_add_sink_video_format(sink_data,
+ HDMI_VFRMT_1024x768p60_4_3);
+ break;
+ } else {
+ offset += 2;
+ }
+ std_blk++;
+ }
+ /* check if the EDID revision is 4 (version 1.4) */
+ if (edid_blk0[0x13] == 4) {
+ u8 start = 0x36;
+ i = 0;
+ /* Check each of 4 - 18 bytes descriptors */
+ while (i < 4) {
+ u8 iter = start;
+ u32 header_1 = 0;
+ u8 header_2 = 0;
+ header_1 = edid_blk0[iter++];
+ header_1 = header_1 << 8 | edid_blk0[iter++];
+ header_1 = header_1 << 8 | edid_blk0[iter++];
+ header_1 = header_1 << 8 | edid_blk0[iter++];
+ header_2 = edid_blk0[iter];
+ if (header_1 == 0x000000F7 &&
+ header_2 == 0x00) {
+ iter++;
+ /* VESA DMT Standard Version (0x0A)*/
+ iter++;
+ /* First set of supported formats */
+ iter++;
+ /* Second set of supported formats */
+ if (edid_blk0[iter] & 0x02) {
+ pr_debug("%s: DMT 1280x1024@60\n",
+ __func__);
+ hdmi_edid_add_sink_video_format(
+ sink_data,
+ HDMI_VFRMT_1280x1024p60_5_4);
+ break;
+ }
+ }
+ i++;
+ start += 0x12;
+ }
+ }
+
+ /* Established Timing I and II */
+ if (edid_blk0[0x24] & BIT(3)) {
+ pr_debug("%s: 65MHz: off=[%x] stdblk=[%x]\n",
+ __func__, offset, std_blk);
+ hdmi_edid_add_sink_video_format(sink_data,
+ HDMI_VFRMT_1024x768p60_4_3);
+ }
+
hdmi_edid_get_extended_video_formats(edid_ctrl, data_buf+0x80);
/* mandaroty 3d format */
diff --git a/drivers/video/msm/mdss/mdss_hdmi_hdcp.c b/drivers/video/msm/mdss/mdss_hdmi_hdcp.c
index ef17229..1f0efd3 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_hdcp.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_hdcp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013 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
@@ -287,7 +287,7 @@
timeout_count = 100;
keys_state = (link0_status >> 28) & 0x7;
while ((keys_state != HDCP_KEYS_STATE_VALID) &&
- timeout_count--) {
+ --timeout_count) {
link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS);
keys_state = (link0_status >> 28) & 0x7;
DEV_DBG("%s: %s: Keys not ready(%d). s=%d\n, l0=%0x08x",
@@ -320,7 +320,7 @@
link0_status);
msleep(20);
}
- } while (!an_ready && timeout_count--);
+ } while (!an_ready && --timeout_count);
if (!timeout_count) {
rc = -ETIMEDOUT;
@@ -1057,11 +1057,16 @@
io = hdcp_ctrl->init_data.core_io;
- /* Ignore HDCP interrupts if HDCP is disabled */
- if (HDCP_STATE_INACTIVE == hdcp_ctrl->hdcp_state)
- return 0;
-
hdcp_int_val = DSS_REG_R(io, HDMI_HDCP_INT_CTRL);
+
+ /* Ignore HDCP interrupts if HDCP is disabled */
+ if (HDCP_STATE_INACTIVE == hdcp_ctrl->hdcp_state) {
+ DEV_ERR("%s: HDCP inactive. Just clear int and return.\n",
+ __func__);
+ DSS_REG_W(io, HDMI_HDCP_INT_CTRL, hdcp_int_val);
+ return 0;
+ }
+
if (hdcp_int_val & BIT(0)) {
/* AUTH_SUCCESS_INT */
DSS_REG_W(io, HDMI_HDCP_INT_CTRL, (hdcp_int_val | BIT(1)));
diff --git a/drivers/video/msm/mdss/mdss_hdmi_mhl.h b/drivers/video/msm/mdss/mdss_hdmi_mhl.h
index 8fef63e..8a9d4fc 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_mhl.h
+++ b/drivers/video/msm/mdss/mdss_hdmi_mhl.h
@@ -19,9 +19,9 @@
struct msm_hdmi_mhl_ops {
u8 (*tmds_enabled)(struct platform_device *pdev);
int (*set_mhl_max_pclk)(struct platform_device *pdev, u32 max_val);
+ int (*set_upstream_hpd)(struct platform_device *pdev, uint8_t on);
};
int msm_hdmi_register_mhl(struct platform_device *pdev,
- struct msm_hdmi_mhl_ops *ops);
-
+ struct msm_hdmi_mhl_ops *ops, void *data);
#endif /* __MDSS_HDMI_MHL_H__ */
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c
index 9a90b88..287f2cd 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -81,9 +81,12 @@
struct hdmi_tx_audio_acr lut[AUDIO_SAMPLE_RATE_MAX];
};
+static int hdmi_tx_set_mhl_hpd(struct platform_device *pdev, uint8_t on);
static int hdmi_tx_sysfs_enable_hpd(struct hdmi_tx_ctrl *hdmi_ctrl, int on);
static irqreturn_t hdmi_tx_isr(int irq, void *data);
static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl);
+static int hdmi_tx_enable_power(struct hdmi_tx_ctrl *hdmi_ctrl,
+ enum hdmi_tx_power_module_type module, int enable);
struct mdss_hw hdmi_tx_hw = {
.hw_ndx = MDSS_HW_HDMI,
@@ -93,12 +96,15 @@
struct dss_gpio hpd_gpio_config[] = {
{0, 1, COMPATIBLE_NAME "-hpd"},
- {0, 1, COMPATIBLE_NAME "-ddc-clk"},
- {0, 1, COMPATIBLE_NAME "-ddc-data"},
{0, 1, COMPATIBLE_NAME "-mux-en"},
{0, 0, COMPATIBLE_NAME "-mux-sel"}
};
+struct dss_gpio ddc_gpio_config[] = {
+ {0, 1, COMPATIBLE_NAME "-ddc-clk"},
+ {0, 1, COMPATIBLE_NAME "-ddc-data"}
+};
+
struct dss_gpio core_gpio_config[] = {
};
@@ -110,6 +116,7 @@
{
switch (module) {
case HDMI_TX_HPD_PM: return "HDMI_TX_HPD_PM";
+ case HDMI_TX_DDC_PM: return "HDMI_TX_DDC_PM";
case HDMI_TX_CORE_PM: return "HDMI_TX_CORE_PM";
case HDMI_TX_CEC_PM: return "HDMI_TX_CEC_PM";
default: return "???";
@@ -123,9 +130,9 @@
{0x18, 0x18, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
0x28, 0x28, 0x28, 0x28, 0x18, 0x28, 0x18, 0x28, 0x28,
0x28, 0x28}, /*01*/
- {0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x88, 0x00, 0x04, 0x04, 0x04,
- 0x04, 0x04}, /*02*/
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00}, /*02*/
{0x02, 0x06, 0x11, 0x15, 0x04, 0x13, 0x10, 0x05, 0x1F,
0x14, 0x20, 0x22, 0x21, 0x01, 0x03, 0x11, 0x00, 0x00,
0x00, 0x00}, /*03*/
@@ -183,10 +190,19 @@
{20480, 247500} } },
};
+static bool is_cea_format(int mode)
+{
+ if ((mode > 0) && (mode < HDMI_EVFRMT_END))
+ return true;
+ else
+ return false;
+}
+
const char *hdmi_tx_pm_name(enum hdmi_tx_power_module_type module)
{
switch (module) {
case HDMI_TX_HPD_PM: return "HDMI_TX_HPD_PM";
+ case HDMI_TX_DDC_PM: return "HDMI_TX_DDC_PM";
case HDMI_TX_CORE_PM: return "HDMI_TX_CORE_PM";
case HDMI_TX_CEC_PM: return "HDMI_TX_CEC_PM";
default: return "???";
@@ -375,6 +391,10 @@
return rc;
}
+ if (hdmi_ctrl->mhl_max_pclk && hpd &&
+ (!hdmi_ctrl->mhl_hpd_on || hdmi_ctrl->hpd_feature_on))
+ return 0;
+
if (0 == hpd && hdmi_ctrl->hpd_feature_on) {
rc = hdmi_tx_sysfs_enable_hpd(hdmi_ctrl, false);
} else if (1 == hpd && !hdmi_ctrl->hpd_feature_on) {
@@ -598,6 +618,37 @@
}
} /* hdmi_tx_set_audio_switch_node */
+static int hdmi_tx_config_avmute(struct hdmi_tx_ctrl *hdmi_ctrl, int set)
+{
+ struct dss_io_data *io;
+
+ if (!hdmi_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
+ if (!io->base) {
+ DEV_ERR("%s: Core io is not initialized\n", __func__);
+ return -EINVAL;
+ }
+
+ if (set)
+ DSS_REG_W(io, HDMI_GC,
+ DSS_REG_R(io, HDMI_GC) | BIT(0));
+ else
+ DSS_REG_W(io, HDMI_GC,
+ DSS_REG_R(io, HDMI_GC) & ~BIT(0));
+
+ /* Enable AV Mute tranmission here */
+ DSS_REG_W(io, HDMI_VBI_PKT_CTRL,
+ DSS_REG_R(io, HDMI_VBI_PKT_CTRL) | (BIT(4) & BIT(5)));
+
+ DEV_DBG("%s: AVMUTE %s\n", __func__, set ? "set" : "cleared");
+
+ return 0;
+} /* hdmi_tx_config_avmute */
+
void hdmi_tx_hdcp_cb(void *ptr, enum hdmi_hdcp_state status)
{
int rc = 0;
@@ -613,13 +664,25 @@
switch (status) {
case HDCP_STATE_AUTHENTICATED:
- if (hdmi_ctrl->hpd_state)
+ if (hdmi_ctrl->hpd_state) {
+ /* Clear AV Mute */
+ rc = hdmi_tx_config_avmute(hdmi_ctrl, 0);
+ if (rc)
+ DEV_ERR("%s: Failed to clear av mute. rc=%d\n",
+ __func__, rc);
hdmi_tx_set_audio_switch_node(hdmi_ctrl, 1, false);
+ }
break;
case HDCP_STATE_AUTH_FAIL:
hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0, false);
if (hdmi_ctrl->hpd_state) {
+ /* Set AV Mute */
+ rc = hdmi_tx_config_avmute(hdmi_ctrl, 1);
+ if (rc)
+ DEV_ERR("%s: Failed to set av mute. rc=%d\n",
+ __func__, rc);
+
DEV_DBG("%s: Reauthenticating\n", __func__);
rc = hdmi_hdcp_reauthenticate(
hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP]);
@@ -810,11 +873,19 @@
DEV_DBG("%s: Got HPD interrupt\n", __func__);
if (hdmi_ctrl->hpd_state) {
+ if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, true)) {
+ DEV_ERR("%s: Failed to enable ddc power\n", __func__);
+ return;
+ }
+
hdmi_tx_read_sink_info(hdmi_ctrl);
hdmi_tx_send_cable_notification(hdmi_ctrl, 1);
DEV_INFO("%s: sense cable CONNECTED: state switch to %d\n",
__func__, hdmi_ctrl->sdev.state);
} else {
+ if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, false))
+ DEV_WARN("%s: Failed to disable ddc power\n", __func__);
+
hdmi_tx_send_cable_notification(hdmi_ctrl, 0);
DEV_INFO("%s: sense cable DISCONNECTED: state switch to %d\n",
__func__, hdmi_ctrl->sdev.state);
@@ -1935,7 +2006,7 @@
}
int msm_hdmi_register_mhl(struct platform_device *pdev,
- struct msm_hdmi_mhl_ops *ops)
+ struct msm_hdmi_mhl_ops *ops, void *data)
{
struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev);
@@ -1951,6 +2022,8 @@
ops->tmds_enabled = hdmi_tx_tmds_enabled;
ops->set_mhl_max_pclk = hdmi_tx_set_mhl_max_pclk;
+ ops->set_upstream_hpd = hdmi_tx_set_mhl_hpd;
+
return 0;
}
@@ -2082,7 +2155,8 @@
return rc;
}
- if (!hdmi_tx_is_dvi_mode(hdmi_ctrl)) {
+ if (!hdmi_tx_is_dvi_mode(hdmi_ctrl) &&
+ is_cea_format(hdmi_ctrl->video_resolution)) {
rc = hdmi_tx_audio_setup(hdmi_ctrl);
if (rc) {
DEV_ERR("%s: hdmi_msm_audio_setup failed. rc=%d\n",
@@ -2298,6 +2372,7 @@
static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl)
{
int rc = 0;
+ struct dss_io_data *io = NULL;
if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -2309,10 +2384,29 @@
return;
}
+ io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
+ if (!io->base) {
+ DEV_ERR("%s: core io not inititalized\n", __func__);
+ return;
+ }
+
+ /* finish the ongoing hpd work if any */
+ flush_work_sync(&hdmi_ctrl->hpd_int_work);
+
+ /* Turn off HPD interrupts */
+ DSS_REG_W(io, HDMI_HPD_INT_CTRL, 0);
+
mdss_disable_irq(&hdmi_tx_hw);
hdmi_tx_set_mode(hdmi_ctrl, false);
+ if (hdmi_ctrl->hpd_state) {
+ rc = hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, 0);
+ if (rc)
+ DEV_INFO("%s: Failed to disable ddc power. Error=%d\n",
+ __func__, rc);
+ }
+
rc = hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_HPD_PM, 0);
if (rc)
DEV_INFO("%s: Failed to disable hpd power. Error=%d\n",
@@ -2406,12 +2500,50 @@
return rc;
} /* hdmi_tx_sysfs_enable_hpd */
+static int hdmi_tx_set_mhl_hpd(struct platform_device *pdev, uint8_t on)
+{
+ int rc = 0;
+ struct hdmi_tx_ctrl *hdmi_ctrl = NULL;
+
+ hdmi_ctrl = platform_get_drvdata(pdev);
+
+ if (!hdmi_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ /* mhl status should override */
+ hdmi_ctrl->mhl_hpd_on = on;
+
+ if (!on && hdmi_ctrl->hpd_feature_on) {
+ rc = hdmi_tx_sysfs_enable_hpd(hdmi_ctrl, false);
+ } else if (on && !hdmi_ctrl->hpd_feature_on) {
+ rc = hdmi_tx_sysfs_enable_hpd(hdmi_ctrl, true);
+ } else {
+ DEV_DBG("%s: hpd is already '%s'. return\n", __func__,
+ hdmi_ctrl->hpd_feature_on ? "enabled" : "disabled");
+ return rc;
+ }
+
+ if (!rc) {
+ hdmi_ctrl->hpd_feature_on =
+ (~hdmi_ctrl->hpd_feature_on) & BIT(0);
+ DEV_DBG("%s: '%d'\n", __func__, hdmi_ctrl->hpd_feature_on);
+ } else {
+ DEV_ERR("%s: failed to '%s' hpd. rc = %d\n", __func__,
+ on ? "enable" : "disable", rc);
+ }
+
+ return rc;
+
+}
+
static irqreturn_t hdmi_tx_isr(int irq, void *data)
{
struct dss_io_data *io = NULL;
struct hdmi_tx_ctrl *hdmi_ctrl = (struct hdmi_tx_ctrl *)data;
- if (!hdmi_ctrl || !hdmi_ctrl->hpd_initialized) {
+ if (!hdmi_ctrl) {
DEV_WARN("%s: invalid input data, ISR ignored\n", __func__);
return IRQ_HANDLED;
}
@@ -2661,6 +2793,12 @@
case MDSS_EVENT_PANEL_ON:
if (hdmi_ctrl->hdcp_feature_on && hdmi_ctrl->present_hdcp) {
+ /* Set AV Mute before starting authentication */
+ rc = hdmi_tx_config_avmute(hdmi_ctrl, 1);
+ if (rc)
+ DEV_ERR("%s: Failed to set av mute. rc=%d\n",
+ __func__, rc);
+
DEV_DBG("%s: Starting HDCP authentication\n", __func__);
rc = hdmi_hdcp_authenticate(
hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP]);
@@ -2836,7 +2974,7 @@
switch (module_type) {
case HDMI_TX_HPD_PM:
- mp->num_clk = 2;
+ mp->num_clk = 3;
mp->clk_config = devm_kzalloc(dev, sizeof(struct dss_clk) *
mp->num_clk, GFP_KERNEL);
if (!mp->clk_config) {
@@ -2852,6 +2990,16 @@
snprintf(mp->clk_config[1].clk_name, 32, "%s", "core_clk");
mp->clk_config[1].type = DSS_CLK_OTHER;
mp->clk_config[1].rate = 19200000;
+
+ /*
+ * This clock is required to clock MDSS interrupt registers
+ * when HDMI is the only block turned on within MDSS. Since
+ * rate for this clock is controlled by MDP driver, treat this
+ * similar to AHB clock and do not set rate for it.
+ */
+ snprintf(mp->clk_config[2].clk_name, 32, "%s", "mdp_core_clk");
+ mp->clk_config[2].type = DSS_CLK_AHB;
+ mp->clk_config[2].rate = 0;
break;
case HDMI_TX_CORE_PM:
@@ -2874,6 +3022,7 @@
mp->clk_config[1].rate = 0;
break;
+ case HDMI_TX_DDC_PM:
case HDMI_TX_CEC_PM:
mp->num_clk = 0;
DEV_DBG("%s: no clk\n", __func__);
@@ -2933,6 +3082,9 @@
case HDMI_TX_HPD_PM:
mod_name = "hpd";
break;
+ case HDMI_TX_DDC_PM:
+ mod_name = "ddc";
+ break;
case HDMI_TX_CORE_PM:
mod_name = "core";
break;
@@ -3129,6 +3281,10 @@
gpio_list_size = ARRAY_SIZE(hpd_gpio_config);
gpio_list = hpd_gpio_config;
break;
+ case HDMI_TX_DDC_PM:
+ gpio_list_size = ARRAY_SIZE(ddc_gpio_config);
+ gpio_list = ddc_gpio_config;
+ break;
case HDMI_TX_CORE_PM:
gpio_list_size = ARRAY_SIZE(core_gpio_config);
gpio_list = core_gpio_config;
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.h b/drivers/video/msm/mdss/mdss_hdmi_tx.h
index 8d9a477..d4f8e67 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.h
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013, 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
@@ -25,6 +25,7 @@
enum hdmi_tx_power_module_type {
HDMI_TX_HPD_PM,
+ HDMI_TX_DDC_PM,
HDMI_TX_CORE_PM,
HDMI_TX_CEC_PM,
HDMI_TX_MAX_PM
@@ -61,6 +62,7 @@
u32 hpd_initialized;
u8 timing_gen_on;
u32 mhl_max_pclk;
+ u8 mhl_hpd_on;
struct completion hpd_done;
struct work_struct hpd_int_work;
diff --git a/drivers/video/msm/mdss/mdss_hdmi_util.c b/drivers/video/msm/mdss/mdss_hdmi_util.c
index 0c8b0f8..53dfc71 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_util.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_util.c
@@ -109,8 +109,8 @@
hdmi_supported_video_mode_lut, MSM_HDMI_MODES_XTND);
/* Add any other specific DVI timings (DVI modes, etc.) */
- MSM_HDMI_MODES_SET_TIMING(hdmi_supported_video_mode_lut,
- HDMI_VFRMT_2560x1600p60_16_9);
+ MSM_HDMI_MODES_SET_SUPP_TIMINGS(
+ hdmi_supported_video_mode_lut, MSM_HDMI_MODES_DVI);
} /* hdmi_setup_video_mode_lut */
const char *hdmi_get_single_video_3d_fmt_2string(u32 format)
diff --git a/drivers/video/msm/mdss/mdss_io_util.c b/drivers/video/msm/mdss/mdss_io_util.c
index ff52e4c..809db43 100644
--- a/drivers/video/msm/mdss/mdss_io_util.c
+++ b/drivers/video/msm/mdss/mdss_io_util.c
@@ -276,15 +276,17 @@
DEV_DBG("%pS->%s: %s disable\n",
__builtin_return_address(0), __func__,
in_gpio[i].gpio_name);
-
- gpio_free(in_gpio[i].gpio);
+ if (in_gpio[i].gpio)
+ gpio_free(in_gpio[i].gpio);
}
}
return rc;
disable_gpio:
for (i--; i >= 0; i--)
- gpio_free(in_gpio[i].gpio);
+ if (in_gpio[i].gpio)
+ gpio_free(in_gpio[i].gpio);
+
return rc;
} /* msm_dss_enable_gpio */
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index 2f09fee..c4bf67e 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -55,9 +55,15 @@
#include "mdss_debug.h"
struct mdss_data_type *mdss_res;
+
+static int mdss_fb_mem_get_iommu_domain(void)
+{
+ return mdss_get_iommu_domain(MDSS_IOMMU_DOMAIN_UNSECURE);
+}
+
struct msm_mdp_interface mdp5 = {
.init_fnc = mdss_mdp_overlay_init,
- .fb_mem_alloc_fnc = mdss_mdp_alloc_fb_mem,
+ .fb_mem_get_iommu_domain = mdss_fb_mem_get_iommu_domain,
.panel_register_done = mdss_panel_register_done,
.fb_stride = mdss_mdp_fb_stride,
};
@@ -134,39 +140,7 @@
char *prop_name);
static int mdss_mdp_parse_dt_smp(struct platform_device *pdev);
static int mdss_mdp_parse_dt_misc(struct platform_device *pdev);
-
-int mdss_mdp_alloc_fb_mem(struct msm_fb_data_type *mfd)
-{
- int dom;
- void *virt = NULL;
- unsigned long phys = 0;
- size_t size;
- u32 yres = mfd->fbi->var.yres_virtual;
-
- size = PAGE_ALIGN(mfd->fbi->fix.line_length * yres);
-
- if (mfd->index == 0) {
- virt = allocate_contiguous_memory(size, MEMTYPE_EBI1, SZ_1M, 0);
- if (!virt) {
- pr_err("unable to alloc fbmem size=%u\n", size);
- return -ENOMEM;
- }
- phys = memory_pool_node_paddr(virt);
- dom = mdss_get_iommu_domain(MDSS_IOMMU_DOMAIN_UNSECURE);
- msm_iommu_map_contig_buffer(phys, dom, 0, size, SZ_4K, 0,
- &mfd->iova);
-
- pr_debug("allocating %u bytes at %p (%lx phys) for fb %d\n",
- size, virt, phys, mfd->index);
- } else
- size = 0;
-
- mfd->fbi->screen_base = virt;
- mfd->fbi->fix.smem_start = phys;
- mfd->fbi->fix.smem_len = size;
-
- return 0;
-}
+static int mdss_mdp_parse_dt_ad_cfg(struct platform_device *pdev);
u32 mdss_mdp_fb_stride(u32 fb_index, u32 xres, int bpp)
{
@@ -371,7 +345,6 @@
int mdss_mdp_bus_scale_set_quota(u64 ab_quota, u64 ib_quota)
{
- static int current_bus_idx;
int bus_idx;
if (mdss_res->bus_hdl < 1) {
@@ -385,14 +358,14 @@
int num_cases = mdp_bus_scale_table.num_usecases;
struct msm_bus_vectors *vect = NULL;
- bus_idx = (current_bus_idx % (num_cases - 1)) + 1;
+ bus_idx = (mdss_res->current_bus_idx % (num_cases - 1)) + 1;
- /* aligning to avoid performing updates for small changes */
- ab_quota = ALIGN(ab_quota, SZ_64M);
- ib_quota = ALIGN(ib_quota, SZ_64M);
+ vect = mdp_bus_scale_table.usecase[mdss_res->current_bus_idx].
+ vectors;
- vect = mdp_bus_scale_table.usecase[current_bus_idx].vectors;
- if ((ab_quota == vect->ab) && (ib_quota == vect->ib)) {
+ /* avoid performing updates for small changes */
+ if ((ALIGN(ab_quota, SZ_64M) == ALIGN(vect->ab, SZ_64M)) &&
+ (ALIGN(ib_quota, SZ_64M) == ALIGN(vect->ib, SZ_64M))) {
pr_debug("skip bus scaling, no change in vectors\n");
return 0;
}
@@ -404,7 +377,7 @@
pr_debug("bus scale idx=%d ab=%llu ib=%llu\n", bus_idx,
vect->ab, vect->ib);
}
- current_bus_idx = bus_idx;
+ mdss_res->current_bus_idx = bus_idx;
return msm_bus_scale_client_update_request(mdss_res->bus_hdl, bus_idx);
}
@@ -653,8 +626,14 @@
if (mdata->vsync_ena)
mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, enable);
- if (!enable)
+ if (!enable) {
+ msm_bus_scale_client_update_request(
+ mdss_res->bus_hdl, 0);
pm_runtime_put(&mdata->pdev->dev);
+ } else {
+ msm_bus_scale_client_update_request(
+ mdss_res->bus_hdl, mdss_res->current_bus_idx);
+ }
}
mutex_unlock(&mdp_clk_lock);
@@ -1004,6 +983,7 @@
goto probe_done;
}
mdata->irq = res->start;
+ mdss_mdp_hw.ptr = mdata;
/*populate hw iomem base info from device tree*/
rc = mdss_mdp_parse_dt(pdev);
@@ -1050,6 +1030,7 @@
probe_done:
if (IS_ERR_VALUE(rc)) {
+ mdss_mdp_hw.ptr = NULL;
mdss_res = NULL;
mdss_mdp_pp_term(&pdev->dev);
}
@@ -1161,6 +1142,12 @@
return rc;
}
+ rc = mdss_mdp_parse_dt_ad_cfg(pdev);
+ if (rc) {
+ pr_err("Error in device tree : ad\n");
+ return rc;
+ }
+
return 0;
}
@@ -1483,6 +1470,41 @@
return 0;
}
+static int mdss_mdp_parse_dt_ad_cfg(struct platform_device *pdev)
+{
+ struct mdss_data_type *mdata = platform_get_drvdata(pdev);
+ u32 *ad_offsets = NULL;
+ int rc;
+
+ mdata->nad_cfgs = mdss_mdp_parse_dt_prop_len(pdev, "qcom,mdss-ad-off");
+
+ if (mdata->nad_cfgs == 0) {
+ mdata->ad_cfgs = NULL;
+ return 0;
+ }
+ if (mdata->nad_cfgs > mdata->nmixers_intf)
+ return -EINVAL;
+
+ ad_offsets = kzalloc(sizeof(u32) * mdata->nad_cfgs, GFP_KERNEL);
+ if (!ad_offsets) {
+ pr_err("no mem assigned: kzalloc fail\n");
+ return -ENOMEM;
+ }
+
+ rc = mdss_mdp_parse_dt_handler(pdev, "qcom,mdss-ad-off", ad_offsets,
+ mdata->nad_cfgs);
+ if (rc)
+ goto parse_done;
+
+ rc = mdss_mdp_ad_addr_setup(mdata, ad_offsets);
+ if (rc)
+ pr_err("unable to setup assertive display\n");
+
+parse_done:
+ kfree(ad_offsets);
+ return rc;
+}
+
static int mdss_mdp_parse_dt_handler(struct platform_device *pdev,
char *prop_name, u32 *offsets, int len)
{
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index d1fdbab..7c9b5a9 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -109,6 +109,12 @@
struct mdss_mdp_ctl;
typedef void (*mdp_vsync_handler_t)(struct mdss_mdp_ctl *, ktime_t);
+struct mdss_mdp_vsync_handler {
+ bool enabled;
+ mdp_vsync_handler_t vsync_handler;
+ struct list_head list;
+};
+
struct mdss_mdp_ctl {
u32 num;
char __iomem *base;
@@ -122,6 +128,7 @@
u32 opmode;
u32 flush_bits;
+ bool is_video_mode;
u32 play_cnt;
u32 vsync_cnt;
u32 underrun_cnt;
@@ -143,13 +150,19 @@
struct mutex lock;
struct mdss_panel_data *panel_data;
+ struct mdss_mdp_vsync_handler vsync_handler;
int (*start_fnc) (struct mdss_mdp_ctl *ctl);
int (*stop_fnc) (struct mdss_mdp_ctl *ctl);
int (*prepare_fnc) (struct mdss_mdp_ctl *ctl, void *arg);
int (*display_fnc) (struct mdss_mdp_ctl *ctl, void *arg);
int (*wait_fnc) (struct mdss_mdp_ctl *ctl, void *arg);
- int (*set_vsync_handler) (struct mdss_mdp_ctl *, mdp_vsync_handler_t);
+ int (*wait_pingpong) (struct mdss_mdp_ctl *ctl, void *arg);
+ u32 (*read_line_cnt_fnc) (struct mdss_mdp_ctl *);
+ int (*add_vsync_handler) (struct mdss_mdp_ctl *,
+ struct mdss_mdp_vsync_handler *);
+ int (*remove_vsync_handler) (struct mdss_mdp_ctl *,
+ struct mdss_mdp_vsync_handler *);
void *priv_data;
};
@@ -235,6 +248,28 @@
spinlock_t hist_lock;
};
+struct mdss_ad_info {
+ char __iomem *base;
+ u8 num;
+ u32 sts;
+ u32 state;
+ u32 ad_data;
+ u32 ad_data_mode;
+ struct mdss_ad_init init;
+ struct mdss_ad_cfg cfg;
+ struct mutex lock;
+ struct work_struct calc_work;
+ struct msm_fb_data_type *mfd;
+ struct mdss_mdp_vsync_handler handle;
+ struct completion comp;
+ u32 last_str;
+ u32 last_bl;
+ u32 calc_itr;
+ uint32_t bl_bright_shift;
+ uint32_t bl_lin[AD_BL_LIN_LEN];
+ uint32_t bl_lin_inv[AD_BL_LIN_LEN];
+};
+
struct pp_sts_type {
u32 pa_sts;
u32 pcc_sts;
@@ -317,6 +352,7 @@
int borderfill_enable;
int overlay_play_enable;
int hw_refresh;
+ void *cpu_pm_hdl;
struct mdss_data_type *mdata;
struct mutex ov_lock;
@@ -325,6 +361,13 @@
struct list_head overlay_list;
struct list_head pipes_used;
struct list_head pipes_cleanup;
+ bool mixer_swap;
+};
+
+struct mdss_mdp_perf_params {
+ u32 ib_quota;
+ u32 ab_quota;
+ u32 mdp_clk_rate;
};
#define is_vig_pipe(_pipe_id_) ((_pipe_id_) <= MDSS_MDP_SSPP_VIG2)
@@ -352,7 +395,7 @@
irqreturn_t mdss_mdp_isr(int irq, void *ptr);
int mdss_iommu_attach(struct mdss_data_type *mdata);
-int mdss_mdp_copy_splash_screen(struct mdss_panel_data *pdata);
+int mdss_mdp_video_copy_splash_screen(struct mdss_panel_data *pdata);
void mdss_mdp_irq_clear(struct mdss_data_type *mdata,
u32 intr_type, u32 intf_num);
int mdss_mdp_irq_enable(u32 intr_type, u32 intf_num);
@@ -382,6 +425,10 @@
struct mdss_mdp_ctl *mdss_mdp_ctl_init(struct mdss_panel_data *pdata,
struct msm_fb_data_type *mfd);
+int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl);
+int mdss_mdp_video_copy_splash_screen(struct mdss_panel_data *pdata);
+void mdss_mdp_ctl_splash_start(struct mdss_panel_data *pdata);
+int mdss_mdp_ctl_splash_finish(struct mdss_mdp_ctl *ctl);
int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl);
int mdss_mdp_ctl_split_display_setup(struct mdss_mdp_ctl *ctl,
struct mdss_panel_data *pdata);
@@ -389,6 +436,8 @@
int mdss_mdp_ctl_start(struct mdss_mdp_ctl *ctl);
int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl);
int mdss_mdp_ctl_intf_event(struct mdss_mdp_ctl *ctl, int event, void *arg);
+int mdss_mdp_perf_calc_pipe(struct mdss_mdp_pipe *pipe,
+ struct mdss_mdp_perf_params *perf);
struct mdss_mdp_mixer *mdss_mdp_wb_mixer_alloc(int rotator);
int mdss_mdp_wb_mixer_destroy(struct mdss_mdp_mixer *mixer);
@@ -399,6 +448,9 @@
int mdss_mdp_mixer_pipe_unstage(struct mdss_mdp_pipe *pipe);
int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg);
int mdss_mdp_display_wait4comp(struct mdss_mdp_ctl *ctl);
+int mdss_mdp_display_wait4pingpong(struct mdss_mdp_ctl *ctl);
+int mdss_mdp_display_wakeup_time(struct mdss_mdp_ctl *ctl,
+ ktime_t *wakeup_time);
int mdss_mdp_csc_setup(u32 block, u32 blk_idx, u32 tbl_idx, u32 csc_type);
int mdss_mdp_csc_setup_data(u32 block, u32 blk_idx, u32 tbl_idx,
@@ -407,7 +459,7 @@
int mdss_mdp_pp_init(struct device *dev);
void mdss_mdp_pp_term(struct device *dev);
-int mdss_mdp_pp_resume(u32 mixer_num);
+int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 mixer_num);
int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl);
int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl);
@@ -426,7 +478,7 @@
u32 *copyback);
int mdss_mdp_igc_lut_config(struct mdss_mdp_ctl *ctl,
struct mdp_igc_lut_data *config,
- u32 *copyback);
+ u32 *copyback, u32 copy_from_kernel);
int mdss_mdp_argc_config(struct mdss_mdp_ctl *ctl,
struct mdp_pgc_lut_data *config,
u32 *copyback);
@@ -447,6 +499,14 @@
struct mdp_histogram_data *hist);
void mdss_mdp_hist_intr_done(u32 isr);
+int mdss_mdp_ad_config(struct msm_fb_data_type *mfd,
+ struct mdss_ad_init_cfg *init_cfg);
+int mdss_mdp_ad_input(struct msm_fb_data_type *mfd,
+ struct mdss_ad_input *input, int wait);
+int mdss_mdp_ad_addr_setup(struct mdss_data_type *mdata, u32 *ad_off);
+int mdss_mdp_calib_mode(struct msm_fb_data_type *mfd,
+ struct mdss_calib_cfg *cfg);
+
struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(struct mdss_mdp_mixer *mixer,
u32 type);
struct mdss_mdp_pipe *mdss_mdp_pipe_get(struct mdss_data_type *mdata, u32 ndx);
@@ -458,6 +518,7 @@
int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe);
void mdss_mdp_smp_unreserve(struct mdss_mdp_pipe *pipe);
+void mdss_mdp_smp_release(struct mdss_mdp_pipe *pipe);
int mdss_mdp_pipe_addr_setup(struct mdss_data_type *mdata, u32 *offsets,
u32 *ftch_y_id, u32 type, u32 num_base, u32 len);
@@ -487,10 +548,13 @@
int mdss_mdp_wb_ioctl_handler(struct msm_fb_data_type *mfd, u32 cmd, void *arg);
int mdss_mdp_get_ctl_mixers(u32 fb_num, u32 *mixer_id);
-int mdss_mdp_alloc_fb_mem(struct msm_fb_data_type *mfd);
u32 mdss_mdp_fb_stride(u32 fb_index, u32 xres, int bpp);
int mdss_panel_register_done(struct mdss_panel_data *pdata);
+int mdss_mdp_limited_lut_igc_config(struct mdss_mdp_ctl *ctl);
+int mdss_mdp_calib_config(struct mdp_calib_config_data *cfg, u32 *copyback);
+
+int mdss_mdp_pipe_is_staged(struct mdss_mdp_pipe *pipe);
#define mfd_to_mdp5_data(mfd) (mfd->mdp.private1)
#define mfd_to_mdata(mfd) (((struct mdss_overlay_private *)\
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index abec9b9..e81e3f5 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -23,7 +23,8 @@
/* truncate at 1k */
#define MDSS_MDP_BUS_FACTOR_SHIFT 10
/* 1.5 bus fudge factor */
-#define MDSS_MDP_BUS_FUDGE_FACTOR(val) (((val) / 2) * 3)
+#define MDSS_MDP_BUS_FUDGE_FACTOR_IB(val) (((val) / 2) * 3)
+#define MDSS_MDP_BUS_FUDGE_FACTOR_AB(val) (val << 1)
/* 1.25 clock fudge factor */
#define MDSS_MDP_CLK_FUDGE_FACTOR(val) (((val) * 5) / 4)
@@ -47,6 +48,15 @@
writel_relaxed(val, mixer->base + reg);
}
+static inline u32 mdss_mdp_get_pclk_rate(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info;
+
+ return (ctl->intf_type == MDSS_INTF_DSI) ?
+ pinfo->mipi.dsi_pclk_rate :
+ pinfo->clk_rate;
+}
+
static int mdss_mdp_ctl_perf_commit(struct mdss_data_type *mdata, u32 flags)
{
struct mdss_mdp_ctl *ctl;
@@ -72,7 +82,8 @@
}
if (flags & MDSS_MDP_PERF_UPDATE_BUS) {
bus_ab_quota = bus_ib_quota << MDSS_MDP_BUS_FACTOR_SHIFT;
- bus_ib_quota = MDSS_MDP_BUS_FUDGE_FACTOR(bus_ib_quota);
+ bus_ab_quota = MDSS_MDP_BUS_FUDGE_FACTOR_AB(bus_ab_quota);
+ bus_ib_quota = MDSS_MDP_BUS_FUDGE_FACTOR_IB(bus_ib_quota);
bus_ib_quota <<= MDSS_MDP_BUS_FACTOR_SHIFT;
mdss_mdp_bus_scale_set_quota(bus_ab_quota, bus_ib_quota);
@@ -87,13 +98,80 @@
return 0;
}
+/**
+ * mdss_mdp_perf_calc_pipe() - calculate performance numbers required by pipe
+ * @pipe: Source pipe struct containing updated pipe params
+ * @perf: Structure containing values that should be updated for
+ * performance tuning
+ *
+ * Function calculates the minimum required performance calculations in order
+ * to avoid MDP underflow. The calculations are based on the way MDP
+ * fetches (bandwidth requirement) and processes data through MDP pipeline
+ * (MDP clock requirement) based on frame size and scaling requirements.
+ */
+int mdss_mdp_perf_calc_pipe(struct mdss_mdp_pipe *pipe,
+ struct mdss_mdp_perf_params *perf)
+{
+ struct mdss_mdp_mixer *mixer;
+ int fps = DEFAULT_FRAME_RATE;
+ u32 quota, rate, v_total, src_h;
+
+ if (!pipe || !perf || !pipe->mixer)
+ return -EINVAL;
+
+ mixer = pipe->mixer;
+ if (mixer->rotator_mode) {
+ v_total = pipe->flags & MDP_ROT_90 ? pipe->dst.w : pipe->dst.h;
+ } else if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
+ struct mdss_panel_info *pinfo;
+
+ pinfo = &mixer->ctl->panel_data->panel_info;
+ fps = mdss_panel_get_framerate(pinfo);
+ v_total = mdss_panel_get_vtotal(pinfo);
+ } else {
+ v_total = mixer->height;
+ }
+
+ /*
+ * when doing vertical decimation lines will be skipped, hence there is
+ * no need to account for these lines in MDP clock or request bus
+ * bandwidth to fetch them.
+ */
+ src_h = pipe->src.h >> pipe->vert_deci;
+
+ quota = fps * pipe->src.w * src_h;
+ if (pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_420)
+ quota = (quota * 3) / 2;
+ else
+ quota *= pipe->src_fmt->bpp;
+
+ rate = pipe->dst.w;
+ if (src_h > pipe->dst.h)
+ rate = (rate * src_h) / pipe->dst.h;
+
+ rate *= v_total * fps;
+ if (mixer->rotator_mode) {
+ rate /= 4; /* block mode fetch at 4 pix/clk */
+ quota *= 2; /* bus read + write */
+ perf->ib_quota = quota;
+ } else {
+ perf->ib_quota = (quota / pipe->dst.h) * v_total;
+ }
+ perf->ab_quota = quota;
+ perf->mdp_clk_rate = rate;
+
+ pr_debug("mixer=%d pnum=%d clk_rate=%u bus ab=%u ib=%u\n",
+ mixer->num, pipe->num, rate, perf->ab_quota, perf->ib_quota);
+
+ return 0;
+}
+
static void mdss_mdp_perf_mixer_update(struct mdss_mdp_mixer *mixer,
u32 *bus_ab_quota, u32 *bus_ib_quota,
u32 *clk_rate)
{
struct mdss_mdp_pipe *pipe;
- const int fps = 60;
- u32 quota, rate;
+ int fps = DEFAULT_FRAME_RATE;
u32 v_total;
int i;
u32 max_clk_rate = 0, ab_total = 0, ib_total = 0;
@@ -102,17 +180,13 @@
*bus_ib_quota = 0;
*clk_rate = 0;
- if (mixer->rotator_mode) {
- pipe = mixer->stage_pipe[0]; /* rotator pipe */
- v_total = pipe->flags & MDP_ROT_90 ? pipe->dst.w : pipe->dst.h;
- } else {
+ if (!mixer->rotator_mode) {
int is_writeback = false;
if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
struct mdss_panel_info *pinfo;
pinfo = &mixer->ctl->panel_data->panel_info;
- v_total = (pinfo->yres + pinfo->lcdc.v_back_porch +
- pinfo->lcdc.v_front_porch +
- pinfo->lcdc.v_pulse_width);
+ fps = mdss_panel_get_framerate(pinfo);
+ v_total = mdss_panel_get_vtotal(pinfo);
if (pinfo->type == WRITEBACK_PANEL)
is_writeback = true;
@@ -131,7 +205,7 @@
}
for (i = 0; i < MDSS_MDP_MAX_STAGE; i++) {
- u32 ib_quota;
+ struct mdss_mdp_perf_params perf;
pipe = mixer->stage_pipe[i];
if (pipe == NULL)
continue;
@@ -141,33 +215,13 @@
max_clk_rate = 0;
}
- quota = fps * pipe->src.w * pipe->src.h;
- if (pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_420)
- quota = (quota * 3) / 2;
- else
- quota *= pipe->src_fmt->bpp;
+ if (mdss_mdp_perf_calc_pipe(pipe, &perf))
+ continue;
- rate = pipe->dst.w;
- if (pipe->src.h > pipe->dst.h)
- rate = (rate * pipe->src.h) / pipe->dst.h;
-
- rate *= v_total * fps;
- if (mixer->rotator_mode) {
- rate /= 4; /* block mode fetch at 4 pix/clk */
- quota *= 2; /* bus read + write */
- ib_quota = quota;
- } else {
- ib_quota = (quota / pipe->dst.h) * v_total;
- }
-
-
- pr_debug("mixer=%d pnum=%d clk_rate=%u bus ab=%u ib=%u\n",
- mixer->num, pipe->num, rate, quota, ib_quota);
-
- ab_total += quota >> MDSS_MDP_BUS_FACTOR_SHIFT;
- ib_total += ib_quota >> MDSS_MDP_BUS_FACTOR_SHIFT;
- if (rate > max_clk_rate)
- max_clk_rate = rate;
+ ab_total += perf.ab_quota >> MDSS_MDP_BUS_FACTOR_SHIFT;
+ ib_total += perf.ib_quota >> MDSS_MDP_BUS_FACTOR_SHIFT;
+ if (perf.mdp_clk_rate > max_clk_rate)
+ max_clk_rate = perf.mdp_clk_rate;
}
*bus_ab_quota += ab_total;
@@ -202,13 +256,7 @@
max_clk_rate = clk_rate;
if (ctl->intf_type) {
- struct mdss_panel_info *pinfo;
-
- pinfo = &ctl->panel_data->panel_info;
- clk_rate = (ctl->intf_type == MDSS_INTF_DSI) ?
- pinfo->mipi.dsi_pclk_rate :
- pinfo->clk_rate;
-
+ clk_rate = mdss_mdp_get_pclk_rate(ctl);
/* minimum clock rate due to inefficiency in 3dmux */
clk_rate = mult_frac(clk_rate >> 1, 9, 8);
if (clk_rate > max_clk_rate)
@@ -245,13 +293,14 @@
return ret;
}
-static struct mdss_mdp_ctl *mdss_mdp_ctl_alloc(struct mdss_data_type *mdata)
+static struct mdss_mdp_ctl *mdss_mdp_ctl_alloc(struct mdss_data_type *mdata,
+ u32 off)
{
struct mdss_mdp_ctl *ctl = NULL;
- int cnum;
+ u32 cnum;
mutex_lock(&mdss_mdp_ctl_lock);
- for (cnum = 0; cnum < mdata->nctl; cnum++) {
+ for (cnum = off; cnum < mdata->nctl; cnum++) {
ctl = mdata->ctl_off + cnum;
if (ctl->ref_cnt == 0) {
ctl->ref_cnt++;
@@ -289,6 +338,7 @@
}
mutex_lock(&mdss_mdp_ctl_lock);
ctl->ref_cnt--;
+ ctl->intf_num = MDSS_MDP_NO_INTF;
ctl->is_secure = false;
ctl->power_on = false;
ctl->start_fnc = NULL;
@@ -296,7 +346,9 @@
ctl->prepare_fnc = NULL;
ctl->display_fnc = NULL;
ctl->wait_fnc = NULL;
- ctl->set_vsync_handler = NULL;
+ ctl->read_line_cnt_fnc = NULL;
+ ctl->add_vsync_handler = NULL;
+ ctl->remove_vsync_handler = NULL;
mutex_unlock(&mdss_mdp_ctl_lock);
return 0;
@@ -385,7 +437,7 @@
struct mdss_mdp_ctl *ctl = NULL;
struct mdss_mdp_mixer *mixer = NULL;
- ctl = mdss_mdp_ctl_alloc(mdss_res);
+ ctl = mdss_mdp_ctl_alloc(mdss_res, mdss_res->nmixers_intf);
if (!ctl)
return NULL;
@@ -445,6 +497,29 @@
return 0;
}
+void mdss_mdp_ctl_splash_start(struct mdss_panel_data *pdata)
+{
+ switch (pdata->panel_info.type) {
+ case MIPI_VIDEO_PANEL:
+ mdss_mdp_video_copy_splash_screen(pdata);
+ break;
+ case MIPI_CMD_PANEL:
+ default:
+ break;
+ }
+}
+
+int mdss_mdp_ctl_splash_finish(struct mdss_mdp_ctl *ctl)
+{
+ switch (ctl->panel_data->panel_info.type) {
+ case MIPI_VIDEO_PANEL:
+ return mdss_mdp_video_reconfigure_splash_done(ctl);
+ case MIPI_CMD_PANEL:
+ default:
+ return 0;
+ }
+}
+
static inline int mdss_mdp_set_split_ctl(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_ctl *split_ctl)
{
@@ -640,22 +715,25 @@
int ret = 0;
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
- ctl = mdss_mdp_ctl_alloc(mdata);
+ ctl = mdss_mdp_ctl_alloc(mdata, MDSS_MDP_CTL0);
if (!ctl) {
pr_err("unable to allocate ctl\n");
return ERR_PTR(-ENOMEM);
}
ctl->mfd = mfd;
ctl->panel_data = pdata;
+ ctl->is_video_mode = false;
switch (pdata->panel_info.type) {
case EDP_PANEL:
+ ctl->is_video_mode = true;
ctl->intf_num = MDSS_MDP_INTF0;
ctl->intf_type = MDSS_INTF_EDP;
ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE;
ctl->start_fnc = mdss_mdp_video_start;
break;
case MIPI_VIDEO_PANEL:
+ ctl->is_video_mode = true;
if (pdata->panel_info.pdest == DISPLAY_1)
ctl->intf_num = MDSS_MDP_INTF1;
else
@@ -674,10 +752,14 @@
ctl->start_fnc = mdss_mdp_cmd_start;
break;
case DTV_PANEL:
+ ctl->is_video_mode = true;
ctl->intf_num = MDSS_MDP_INTF3;
ctl->intf_type = MDSS_INTF_HDMI;
ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE;
ctl->start_fnc = mdss_mdp_video_start;
+ ret = mdss_mdp_limited_lut_igc_config(ctl);
+ if (ret)
+ pr_err("Unable to config IGC LUT data");
break;
case WRITEBACK_PANEL:
ctl->intf_num = MDSS_MDP_NO_INTF;
@@ -881,7 +963,7 @@
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_LAYER(i), 0);
mixer = ctl->mixer_left;
- mdss_mdp_pp_resume(mixer->num);
+ mdss_mdp_pp_resume(ctl, mixer->num);
mixer->params_changed++;
temp = MDSS_MDP_REG_READ(MDSS_MDP_REG_DISP_INTF_SEL);
@@ -928,7 +1010,7 @@
ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_RESET, NULL);
if (ret) {
pr_err("panel power on failed ctl=%d\n", ctl->num);
- return ret;
+ goto error;
}
ret = mdss_mdp_ctl_start_sub(ctl);
@@ -941,7 +1023,7 @@
struct mdss_mdp_mixer *mixer = ctl->mixer_right;
u32 out, off;
- mdss_mdp_pp_resume(mixer->num);
+ mdss_mdp_pp_resume(ctl, mixer->num);
mixer->params_changed++;
out = (mixer->height << 16) | mixer->width;
off = MDSS_MDP_REG_LM_OFFSET(mixer->num);
@@ -951,6 +1033,7 @@
}
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+error:
mutex_unlock(&ctl->lock);
return ret;
@@ -1189,16 +1272,19 @@
struct mdss_mdp_mixer *mdss_mdp_mixer_get(struct mdss_mdp_ctl *ctl, int mux)
{
struct mdss_mdp_mixer *mixer = NULL;
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(ctl->mfd);
if (!ctl)
return NULL;
switch (mux) {
case MDSS_MDP_MIXER_MUX_DEFAULT:
case MDSS_MDP_MIXER_MUX_LEFT:
- mixer = ctl->mixer_left;
+ mixer = mdp5_data->mixer_swap ?
+ ctl->mixer_right : ctl->mixer_left;
break;
case MDSS_MDP_MIXER_MUX_RIGHT:
- mixer = ctl->mixer_right;
+ mixer = mdp5_data->mixer_swap ?
+ ctl->mixer_left : ctl->mixer_right;
break;
}
@@ -1290,9 +1376,10 @@
if (mutex_lock_interruptible(&ctl->lock))
return -EINTR;
- mixer->params_changed++;
- mixer->stage_pipe[pipe->mixer_stage] = NULL;
-
+ if (pipe == mixer->stage_pipe[pipe->mixer_stage]) {
+ mixer->params_changed++;
+ mixer->stage_pipe[pipe->mixer_stage] = NULL;
+ }
mutex_unlock(&ctl->lock);
return 0;
@@ -1309,6 +1396,71 @@
return 0;
}
+int mdss_mdp_display_wakeup_time(struct mdss_mdp_ctl *ctl,
+ ktime_t *wakeup_time)
+{
+ struct mdss_panel_info *pinfo;
+ u32 clk_rate, clk_period;
+ u32 current_line, total_line;
+ u32 time_of_line, time_to_vsync;
+ ktime_t current_time = ktime_get();
+
+ if (!ctl->read_line_cnt_fnc)
+ return -ENOSYS;
+
+ pinfo = &ctl->panel_data->panel_info;
+ if (!pinfo)
+ return -ENODEV;
+
+ clk_rate = mdss_mdp_get_pclk_rate(ctl);
+
+ clk_rate /= 1000; /* in kHz */
+ if (!clk_rate)
+ return -EINVAL;
+
+ /*
+ * calculate clk_period as pico second to maintain good
+ * accuracy with high pclk rate and this number is in 17 bit
+ * range.
+ */
+ clk_period = 1000000000 / clk_rate;
+ if (!clk_period)
+ return -EINVAL;
+
+ time_of_line = (pinfo->lcdc.h_back_porch +
+ pinfo->lcdc.h_front_porch +
+ pinfo->lcdc.h_pulse_width +
+ pinfo->xres) * clk_period;
+
+ time_of_line /= 1000; /* in nano second */
+ if (!time_of_line)
+ return -EINVAL;
+
+ current_line = ctl->read_line_cnt_fnc(ctl);
+
+ total_line = pinfo->lcdc.v_back_porch +
+ pinfo->lcdc.v_front_porch +
+ pinfo->lcdc.v_pulse_width +
+ pinfo->yres;
+
+ if (current_line > total_line)
+ return -EINVAL;
+
+ time_to_vsync = time_of_line * (total_line - current_line);
+ if (!time_to_vsync)
+ return -EINVAL;
+
+ *wakeup_time = ktime_add_ns(current_time, time_to_vsync);
+
+ pr_debug("clk_rate=%dkHz clk_period=%d cur_line=%d tot_line=%d\n",
+ clk_rate, clk_period, current_line, total_line);
+ pr_debug("time_to_vsync=%d current_time=%d wakeup_time=%d\n",
+ time_to_vsync, (int)ktime_to_ms(current_time),
+ (int)ktime_to_ms(*wakeup_time));
+
+ return 0;
+}
+
int mdss_mdp_display_wait4comp(struct mdss_mdp_ctl *ctl)
{
int ret;
@@ -1335,6 +1487,27 @@
return ret;
}
+int mdss_mdp_display_wait4pingpong(struct mdss_mdp_ctl *ctl)
+{
+ int ret;
+
+ ret = mutex_lock_interruptible(&ctl->lock);
+ if (ret)
+ return ret;
+
+ if (!ctl->power_on) {
+ mutex_unlock(&ctl->lock);
+ return 0;
+ }
+
+ if (ctl->wait_pingpong)
+ ret = ctl->wait_pingpong(ctl, NULL);
+
+ mutex_unlock(&ctl->lock);
+
+ return ret;
+}
+
int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg)
{
struct mdss_mdp_ctl *sctl = NULL;
@@ -1398,7 +1571,6 @@
mdss_mdp_pp_setup_locked(ctl);
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, ctl->flush_bits);
if (sctl) {
- mdss_mdp_pp_setup_locked(sctl);
mdss_mdp_ctl_write(sctl, MDSS_MDP_REG_CTL_FLUSH,
sctl->flush_bits);
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index 5221106..741c7a7 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -351,6 +351,41 @@
#define MDSS_MDP_REG_WB_DST_ADDR_SW_STATUS 0x2B0
+#define MDSS_MDP_REG_AD_BYPASS 0x000
+#define MDSS_MDP_REG_AD_CTRL_0 0x004
+#define MDSS_MDP_REG_AD_CTRL_1 0x008
+#define MDSS_MDP_REG_AD_FRAME_SIZE 0x00C
+#define MDSS_MDP_REG_AD_CON_CTRL_0 0x010
+#define MDSS_MDP_REG_AD_CON_CTRL_1 0x014
+#define MDSS_MDP_REG_AD_STR_MAN 0x018
+#define MDSS_MDP_REG_AD_VAR 0x01C
+#define MDSS_MDP_REG_AD_DITH 0x020
+#define MDSS_MDP_REG_AD_DITH_CTRL 0x024
+#define MDSS_MDP_REG_AD_AMP_LIM 0x028
+#define MDSS_MDP_REG_AD_SLOPE 0x02C
+#define MDSS_MDP_REG_AD_BW_LVL 0x030
+#define MDSS_MDP_REG_AD_LOGO_POS 0x034
+#define MDSS_MDP_REG_AD_LUT_FI 0x038
+#define MDSS_MDP_REG_AD_LUT_CC 0x07C
+#define MDSS_MDP_REG_AD_STR_LIM 0x0C8
+#define MDSS_MDP_REG_AD_CALIB_AB 0x0CC
+#define MDSS_MDP_REG_AD_CALIB_CD 0x0D0
+#define MDSS_MDP_REG_AD_MODE_SEL 0x0D4
+#define MDSS_MDP_REG_AD_TFILT_CTRL 0x0D8
+#define MDSS_MDP_REG_AD_BL_MINMAX 0x0DC
+#define MDSS_MDP_REG_AD_BL 0x0E0
+#define MDSS_MDP_REG_AD_BL_MAX 0x0E8
+#define MDSS_MDP_REG_AD_AL 0x0EC
+#define MDSS_MDP_REG_AD_AL_MIN 0x0F0
+#define MDSS_MDP_REG_AD_AL_FILT 0x0F4
+#define MDSS_MDP_REG_AD_CFG_BUF 0x0F8
+#define MDSS_MDP_REG_AD_LUT_AL 0x100
+#define MDSS_MDP_REG_AD_TARG_STR 0x144
+#define MDSS_MDP_REG_AD_START_CALC 0x148
+#define MDSS_MDP_REG_AD_STR_OUT 0x14C
+#define MDSS_MDP_REG_AD_BL_OUT 0x154
+#define MDSS_MDP_REG_AD_CALC_DONE 0x158
+
enum mdss_mdp_dspp_index {
MDSS_MDP_DSPP0,
MDSS_MDP_DSPP1,
@@ -403,6 +438,9 @@
#define MDSS_MDP_REG_INTF_TEST_CTL 0x054
#define MDSS_MDP_REG_INTF_TP_COLOR0 0x058
#define MDSS_MDP_REG_INTF_TP_COLOR1 0x05C
+#define MDSS_MDP_REG_INTF_FRAME_LINE_COUNT_EN 0x0A8
+#define MDSS_MDP_REG_INTF_FRAME_COUNT 0x0AC
+#define MDSS_MDP_REG_INTF_LINE_COUNT 0x0B0
#define MDSS_MDP_REG_INTF_DEFLICKER_CONFIG 0x0F0
#define MDSS_MDP_REG_INTF_DEFLICKER_STRNG_COEFF 0x0F4
@@ -477,4 +515,33 @@
MDSS_MDP_SMP_CLIENT_RGB2_FETCH,
};
-#endif /* MDSS_MDP_HWIO_H */
+#define MDSS_MDP_LP_MISR_SEL 0x450
+#define MDSS_MDP_LP_MISR_CTRL_MDP 0x454
+#define MDSS_MDP_LP_MISR_CTRL_HDMI 0x458
+#define MDSS_MDP_LP_MISR_CTRL_EDP 0x45C
+#define MDSS_MDP_LP_MISR_CTRL_DSI0 0x460
+#define MDSS_MDP_LP_MISR_CTRL_DSI1 0x464
+
+#define MDSS_MDP_LP_MISR_SIGN_MDP 0x468
+#define MDSS_MDP_LP_MISR_SIGN_EDP 0x46C
+#define MDSS_MDP_LP_MISR_SIGN_HDMI 0x470
+#define MDSS_MDP_LP_MISR_SIGN_DSI0 0x474
+#define MDSS_MDP_LP_MISR_SIGN_DSI1 0x478
+
+#define MDSS_MDP_LP_MISR_CTRL_FRAME_COUNT_MASK 0xFF
+#define MDSS_MDP_LP_MISR_CTRL_ENABLE BIT(8)
+#define MDSS_MDP_LP_MISR_CTRL_STATUS BIT(9)
+#define MDSS_MDP_LP_MISR_CTRL_STATUS_CLEAR BIT(10)
+
+#define MDSS_MDP_LP_MISR_SEL_LMIX0_BLEND 0x08
+#define MDSS_MDP_LP_MISR_SEL_LMIX0_GC 0x09
+#define MDSS_MDP_LP_MISR_SEL_LMIX1_BLEND 0x0A
+#define MDSS_MDP_LP_MISR_SEL_LMIX1_GC 0x0B
+#define MDSS_MDP_LP_MISR_SEL_LMIX2_BLEND 0x0C
+#define MDSS_MDP_LP_MISR_SEL_LMIX2_GC 0x0D
+#define MDSS_MDP_LP_MISR_SEL_LMIX3_BLEND 0x0E
+#define MDSS_MDP_LP_MISR_SEL_LMIX3_GC 0x0F
+#define MDSS_MDP_LP_MISR_SEL_LMIX4_BLEND 0x10
+#define MDSS_MDP_LP_MISR_SEL_LMIX4_GC 0x11
+
+#endif
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
index fff8a6e..bd4f3ea 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
@@ -14,11 +14,12 @@
#include <linux/kernel.h>
#include "mdss_panel.h"
#include "mdss_mdp.h"
+#include "mdss_dsi.h"
#define VSYNC_EXPIRE_TICK 4
#define START_THRESHOLD 4
-#define CONTINUE_TRESHOLD 4
+#define CONTINUE_THRESHOLD 4
#define MAX_SESSIONS 2
@@ -26,11 +27,12 @@
#define KOFF_TIMEOUT msecs_to_jiffies(84)
struct mdss_mdp_cmd_ctx {
+ struct mdss_mdp_ctl *ctl;
u32 pp_num;
u8 ref_cnt;
+ struct completion vsync_comp;
struct completion pp_comp;
struct completion stop_comp;
- atomic_t vsync_ref;
mdp_vsync_handler_t send_vsync;
int panel_on;
int koff_cnt;
@@ -44,13 +46,22 @@
/* te config */
u8 tear_check;
- u16 total_lcd_lines;
- u16 v_porch; /* vertical porches */
- u32 vsync_cnt;
+ u16 height; /* panel height */
+ u16 vporch; /* vertical porches */
+ u16 start_threshold;
+ u32 vclk_line; /* vsync clock per line */
};
struct mdss_mdp_cmd_ctx mdss_mdp_cmd_ctx_list[MAX_SESSIONS];
+/*
+ * TE configuration:
+ * dsi byte clock calculated base on 70 fps
+ * around 14 ms to complete a kickoff cycle if te disabled
+ * vclk_line base on 60 fps
+ * write is faster than read
+ * init == start == rdptr
+ */
static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_mixer *mixer,
struct mdss_mdp_cmd_ctx *ctx, int enable)
{
@@ -59,17 +70,23 @@
cfg = BIT(19); /* VSYNC_COUNTER_EN */
if (ctx->tear_check)
cfg |= BIT(20); /* VSYNC_IN_EN */
- cfg |= ctx->vsync_cnt;
+ cfg |= ctx->vclk_line;
mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_SYNC_CONFIG_VSYNC, cfg);
mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_SYNC_CONFIG_HEIGHT,
0xfff0); /* set to verh height */
- mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_VSYNC_INIT_VAL, 0);
- mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_RD_PTR_IRQ, 0);
- mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_START_POS, ctx->v_porch);
+ mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_VSYNC_INIT_VAL,
+ ctx->height);
+
+ mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_RD_PTR_IRQ,
+ ctx->height + 1);
+
+ mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_START_POS,
+ ctx->height);
+
mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_SYNC_THRESH,
- (CONTINUE_TRESHOLD << 16) | (START_THRESHOLD));
+ (CONTINUE_THRESHOLD << 16) | (ctx->start_threshold));
mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_TEAR_CHECK_EN, enable);
return 0;
@@ -85,7 +102,6 @@
if (pinfo->mipi.vsync_enable && enable) {
u32 mdp_vsync_clk_speed_hz, total_lines;
- u32 vsync_cnt_cfg_dem;
mdss_mdp_vsync_clk_enable(1);
@@ -100,21 +116,21 @@
}
ctx->tear_check = pinfo->mipi.hw_vsync_mode;
-
- total_lines = pinfo->lcdc.v_back_porch +
- pinfo->lcdc.v_front_porch +
- pinfo->lcdc.v_pulse_width + pinfo->yres;
-
- vsync_cnt_cfg_dem =
- mult_frac(pinfo->mipi.frame_rate * total_lines,
- 1, 100);
-
- ctx->vsync_cnt = mdp_vsync_clk_speed_hz / vsync_cnt_cfg_dem;
-
- ctx->v_porch = pinfo->lcdc.v_back_porch +
+ ctx->height = pinfo->yres;
+ ctx->vporch = pinfo->lcdc.v_back_porch +
pinfo->lcdc.v_front_porch +
pinfo->lcdc.v_pulse_width;
- ctx->total_lcd_lines = total_lines;
+
+ ctx->start_threshold = START_THRESHOLD;
+
+ total_lines = ctx->height + ctx->vporch;
+ total_lines *= pinfo->mipi.frame_rate;
+ ctx->vclk_line = mdp_vsync_clk_speed_hz / total_lines;
+
+ pr_debug("%s: fr=%d tline=%d vcnt=%d thold=%d vrate=%d\n",
+ __func__, pinfo->mipi.frame_rate, total_lines,
+ ctx->vclk_line, ctx->start_threshold,
+ mdp_vsync_clk_speed_hz);
} else {
enable = 0;
}
@@ -141,6 +157,8 @@
return;
}
+ complete_all(&ctx->vsync_comp);
+
pr_debug("%s: num=%d ctx=%d expire=%d koff=%d\n", __func__, ctl->num,
ctx->pp_num, ctx->expire, ctx->koff_cnt);
@@ -216,6 +234,9 @@
mdss_mdp_irq_disable(MDSS_MDP_IRQ_PING_PONG_RD_PTR,
ctx->pp_num);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ /* disable dsi clock */
+ mdss_mdp_ctl_intf_event(ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL,
+ (void *)0);
complete(&ctx->stop_comp);
pr_debug("%s: SET_CLK_OFF, pid=%d\n", __func__, current->pid);
} else {
@@ -225,7 +246,7 @@
}
static int mdss_mdp_cmd_vsync_ctrl(struct mdss_mdp_ctl *ctl,
- mdp_vsync_handler_t send_vsync)
+ struct mdss_mdp_vsync_handler *handler)
{
struct mdss_mdp_cmd_ctx *ctx;
unsigned long flags;
@@ -237,13 +258,14 @@
return -ENODEV;
}
- enable = (send_vsync != NULL);
+ enable = (handler->vsync_handler != NULL);
+
+ mutex_lock(&ctx->clk_mtx);
pr_debug("%s: ctx=%p ctx=%d enabled=%d %d clk_enabled=%d clk_ctrl=%d\n",
__func__, ctx, ctx->pp_num, ctx->vsync_enabled, enable,
ctx->clk_enabled, ctx->clk_control);
- mutex_lock(&ctx->clk_mtx);
if (ctx->vsync_enabled == enable) {
mutex_unlock(&ctx->clk_mtx);
return 0;
@@ -253,9 +275,11 @@
spin_lock_irqsave(&ctx->clk_lock, flags);
ctx->clk_control = 0;
ctx->expire = 0;
- ctx->send_vsync = send_vsync;
+ ctx->send_vsync = handler->vsync_handler;
spin_unlock_irqrestore(&ctx->clk_lock, flags);
if (ctx->clk_enabled == 0) {
+ mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_PANEL_CLK_CTRL,
+ (void *)1);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_RD_PTR,
ctx->pp_num);
@@ -284,10 +308,11 @@
return;
}
+ mutex_lock(&ctx->clk_mtx);
+
pr_debug("%s: ctx=%p num=%d clk_enabled=%d\n", __func__,
ctx, ctx->pp_num, ctx->clk_enabled);
- mutex_lock(&ctx->clk_mtx);
spin_lock_irqsave(&ctx->clk_lock, flags);
ctx->koff_cnt++;
ctx->clk_control = 0;
@@ -299,6 +324,8 @@
spin_unlock_irqrestore(&ctx->clk_lock, flags);
if (set_clk_on) {
+ mdss_mdp_ctl_intf_event(ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL,
+ (void *)1);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
ctx->vsync_enabled = 1;
mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_RD_PTR, ctx->pp_num);
@@ -308,10 +335,12 @@
mutex_unlock(&ctx->clk_mtx);
}
-static int mdss_mdp_cmd_wait4comp(struct mdss_mdp_ctl *ctl, void *arg)
+static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg)
{
struct mdss_mdp_cmd_ctx *ctx;
- int rc;
+ unsigned long flags;
+ int need_wait = 0;
+ int rc = 0;
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data;
if (!ctx) {
@@ -319,11 +348,25 @@
return -ENODEV;
}
- pr_debug("%s: intf_num=%d ctx=%p\n", __func__, ctl->intf_num, ctx);
+ spin_lock_irqsave(&ctx->clk_lock, flags);
+ if (ctx->koff_cnt > 0)
+ need_wait = 1;
+ spin_unlock_irqrestore(&ctx->clk_lock, flags);
- rc = wait_for_completion_interruptible_timeout(&ctx->pp_comp,
- KOFF_TIMEOUT);
- WARN(rc <= 0, "cmd kickoff timed out (%d) ctl=%d\n", rc, ctl->num);
+ pr_debug("%s: need_wait=%d intf_num=%d ctx=%p\n",
+ __func__, need_wait, ctl->intf_num, ctx);
+
+ if (need_wait) {
+ rc = wait_for_completion_timeout(
+ &ctx->pp_comp, KOFF_TIMEOUT);
+
+ if (rc <= 0) {
+ WARN(1, "cmd kickoff timed out (%d) ctl=%d\n",
+ rc, ctl->num);
+ rc = -EPERM;
+ } else
+ rc = 0;
+ }
return rc;
}
@@ -339,11 +382,6 @@
return -ENODEV;
}
- pr_debug("%s: kickoff intf_num=%d ctx=%p\n", __func__,
- ctl->intf_num, ctx);
-
- mdss_mdp_cmd_chk_clock(ctx);
-
if (ctx->panel_on == 0) {
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_UNBLANK, NULL);
WARN(rc, "intf %d unblank error (%d)\n", ctl->intf_num, rc);
@@ -354,10 +392,19 @@
WARN(rc, "intf %d panel on error (%d)\n", ctl->intf_num, rc);
}
+ mdss_mdp_cmd_chk_clock(ctx);
+
+ /*
+ * tx dcs command if had any
+ */
+ mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_DSI_CMDLIST_KOFF, NULL);
+
+ INIT_COMPLETION(ctx->vsync_comp);
INIT_COMPLETION(ctx->pp_comp);
mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num);
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_START, 1);
+ mb();
return 0;
}
@@ -366,6 +413,7 @@
{
struct mdss_mdp_cmd_ctx *ctx;
int need_wait = 0;
+ struct mdss_mdp_vsync_handler null_handle;
int ret;
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data;
@@ -389,9 +437,10 @@
ctx->panel_on = 0;
- mdss_mdp_cmd_vsync_ctrl(ctl, NULL);
+ null_handle.vsync_handler = NULL;
+ mdss_mdp_cmd_vsync_ctrl(ctl, &null_handle);
- mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_RD_PTR, ctl->intf_num,
+ mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_RD_PTR, ctx->pp_num,
NULL, NULL);
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num,
NULL, NULL);
@@ -405,6 +454,12 @@
ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_PANEL_OFF, NULL);
WARN(ret, "intf %d unblank error (%d)\n", ctl->intf_num, ret);
+ ctl->stop_fnc = NULL;
+ ctl->display_fnc = NULL;
+ ctl->wait_pingpong = NULL;
+ ctl->add_vsync_handler = NULL;
+ ctl->remove_vsync_handler = NULL;
+
pr_debug("%s:-\n", __func__);
return 0;
@@ -442,10 +497,11 @@
return -ENODEV;
}
+ ctx->ctl = ctl;
ctx->pp_num = mixer->num;
+ init_completion(&ctx->vsync_comp);
init_completion(&ctx->pp_comp);
init_completion(&ctx->stop_comp);
- atomic_set(&ctx->vsync_ref, 0);
spin_lock_init(&ctx->clk_lock);
mutex_init(&ctx->clk_mtx);
INIT_WORK(&ctx->clk_work, clk_ctrl_work);
@@ -467,8 +523,9 @@
ctl->stop_fnc = mdss_mdp_cmd_stop;
ctl->display_fnc = mdss_mdp_cmd_kickoff;
- ctl->wait_fnc = mdss_mdp_cmd_wait4comp;
- ctl->set_vsync_handler = mdss_mdp_cmd_vsync_ctrl;
+ ctl->wait_pingpong = mdss_mdp_cmd_wait4pingpong;
+ ctl->add_vsync_handler = mdss_mdp_cmd_vsync_ctrl;
+ ctl->remove_vsync_handler = mdss_mdp_cmd_vsync_ctrl;
pr_debug("%s:-\n", __func__);
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
index 6e631e9..ab028e4 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
@@ -13,11 +13,18 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
+#include <linux/iopoll.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+
#include "mdss_fb.h"
#include "mdss_mdp.h"
-/* wait for at most 2 vsync for lowest refresh rate (24hz) */
-#define VSYNC_TIMEOUT msecs_to_jiffies(84)
+/* wait for at least 2 vsyncs for lowest refresh rate (24hz) */
+#define VSYNC_TIMEOUT_US 100000
+
+#define MDP_INTR_MASK_INTF_VSYNC(intf_num) \
+ (1 << (2 * (intf_num - MDSS_MDP_INTF0) + MDSS_MDP_IRQ_INTF_VSYNC))
/* intf timing settings */
struct intf_timing_params {
@@ -45,12 +52,14 @@
u8 ref_cnt;
u8 timegen_en;
+ bool polling_en;
+ u32 poll_cnt;
struct completion vsync_comp;
int wait_pending;
atomic_t vsync_ref;
spinlock_t vsync_lock;
- mdp_vsync_handler_t vsync_handler;
+ struct list_head vsync_handlers;
};
static inline void mdp_video_write(struct mdss_mdp_video_ctx *ctx,
@@ -59,6 +68,22 @@
writel_relaxed(val, ctx->base + reg);
}
+static inline u32 mdp_video_read(struct mdss_mdp_video_ctx *ctx,
+ u32 reg)
+{
+ return readl_relaxed(ctx->base + reg);
+}
+
+static inline u32 mdss_mdp_video_line_count(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
+ u32 line_cnt = 0;
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ line_cnt = mdp_video_read(ctx, MDSS_MDP_REG_INTF_LINE_COUNT);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ return line_cnt;
+}
+
int mdss_mdp_video_addr_setup(struct mdss_data_type *mdata,
u32 *offsets, u32 count)
{
@@ -76,6 +101,7 @@
offsets[i], head[i].base);
head[i].ref_cnt = 0;
head[i].intf_num = i + MDSS_MDP_INTF0;
+ INIT_LIST_HEAD(&head[i].vsync_handlers);
}
mdata->video_intf = head;
@@ -171,18 +197,19 @@
p->underflow_clr);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_HSYNC_SKEW, p->hsync_skew);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_POLARITY_CTL, polarity_ctl);
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_FRAME_LINE_COUNT_EN, 0x3);
return 0;
}
-static inline void video_vsync_irq_enable(struct mdss_mdp_ctl *ctl)
+static inline void video_vsync_irq_enable(struct mdss_mdp_ctl *ctl, bool clear)
{
struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
if (atomic_inc_return(&ctx->vsync_ref) == 1)
mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
- else
+ else if (clear)
mdss_mdp_irq_clear(ctl->mdata, MDSS_MDP_IRQ_INTF_VSYNC,
ctl->intf_num);
}
@@ -195,12 +222,45 @@
mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
}
-static int mdss_mdp_video_set_vsync_handler(struct mdss_mdp_ctl *ctl,
- mdp_vsync_handler_t vsync_handler)
+static int mdss_mdp_video_add_vsync_handler(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_vsync_handler *handle)
{
struct mdss_mdp_video_ctx *ctx;
unsigned long flags;
- int need_update;
+ int ret = 0;
+ bool irq_en = false;
+
+ if (!handle || !(handle->vsync_handler)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data;
+ if (!ctx) {
+ pr_err("invalid ctx for ctl=%d\n", ctl->num);
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ spin_lock_irqsave(&ctx->vsync_lock, flags);
+ if (!handle->enabled) {
+ handle->enabled = true;
+ list_add(&handle->list, &ctx->vsync_handlers);
+ irq_en = true;
+ }
+ spin_unlock_irqrestore(&ctx->vsync_lock, flags);
+ if (irq_en)
+ video_vsync_irq_enable(ctl, false);
+exit:
+ return ret;
+}
+
+static int mdss_mdp_video_remove_vsync_handler(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_vsync_handler *handle)
+{
+ struct mdss_mdp_video_ctx *ctx;
+ unsigned long flags;
+ bool irq_dis = false;
ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data;
if (!ctx) {
@@ -209,24 +269,21 @@
}
spin_lock_irqsave(&ctx->vsync_lock, flags);
- need_update = (!ctx->vsync_handler && vsync_handler) ||
- (ctx->vsync_handler && !vsync_handler);
- ctx->vsync_handler = vsync_handler;
- spin_unlock_irqrestore(&ctx->vsync_lock, flags);
-
- if (need_update) {
- if (vsync_handler)
- video_vsync_irq_enable(ctl);
- else
- video_vsync_irq_disable(ctl);
+ if (handle->enabled) {
+ handle->enabled = false;
+ list_del_init(&handle->list);
+ irq_dis = true;
}
-
+ spin_unlock_irqrestore(&ctx->vsync_lock, flags);
+ if (irq_dis)
+ video_vsync_irq_disable(ctl);
return 0;
}
static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl)
{
struct mdss_mdp_video_ctx *ctx;
+ struct mdss_mdp_vsync_handler *tmp, *handle;
int rc;
pr_debug("stop ctl=%d\n", ctl->num);
@@ -257,7 +314,8 @@
ctl->intf_num);
}
- mdss_mdp_video_set_vsync_handler(ctl, NULL);
+ list_for_each_entry_safe(handle, tmp, &ctx->vsync_handlers, list)
+ mdss_mdp_video_remove_vsync_handler(ctl, handle);
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num,
NULL, NULL);
@@ -274,6 +332,7 @@
{
struct mdss_mdp_ctl *ctl = arg;
struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
+ struct mdss_mdp_vsync_handler *tmp;
ktime_t vsync_time;
if (!ctx) {
@@ -284,15 +343,56 @@
vsync_time = ktime_get();
ctl->vsync_cnt++;
- pr_debug("intr ctl=%d vsync cnt=%u\n", ctl->num, ctl->vsync_cnt);
+ pr_debug("intr ctl=%d vsync cnt=%u vsync_time=%d\n",
+ ctl->num, ctl->vsync_cnt, (int)ktime_to_ms(vsync_time));
+ ctx->polling_en = false;
complete_all(&ctx->vsync_comp);
spin_lock(&ctx->vsync_lock);
- if (ctx->vsync_handler)
- ctx->vsync_handler(ctl, vsync_time);
+ list_for_each_entry(tmp, &ctx->vsync_handlers, list) {
+ tmp->vsync_handler(ctl, vsync_time);
+ }
spin_unlock(&ctx->vsync_lock);
}
+static int mdss_mdp_video_pollwait(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
+ u32 mask, status;
+ int rc;
+
+ mask = MDP_INTR_MASK_INTF_VSYNC(ctl->intf_num);
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ rc = readl_poll_timeout(ctl->mdata->mdp_base + MDSS_MDP_REG_INTR_STATUS,
+ status,
+ (status & mask) || try_wait_for_completion(&ctx->vsync_comp),
+ 1000,
+ VSYNC_TIMEOUT_US);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ if (rc == 0) {
+ pr_debug("vsync poll successful! rc=%d status=0x%x\n",
+ rc, status);
+ ctx->poll_cnt++;
+ if (status) {
+ struct mdss_mdp_vsync_handler *tmp;
+ unsigned long flags;
+ ktime_t vsync_time = ktime_get();
+
+ spin_lock_irqsave(&ctx->vsync_lock, flags);
+ list_for_each_entry(tmp, &ctx->vsync_handlers, list)
+ tmp->vsync_handler(ctl, vsync_time);
+ spin_unlock_irqrestore(&ctx->vsync_lock, flags);
+ }
+ } else {
+ pr_warn("vsync poll timed out! rc=%d status=0x%x mask=0x%x\n",
+ rc, status, mask);
+ }
+
+ return rc;
+}
+
static int mdss_mdp_video_wait4comp(struct mdss_mdp_ctl *ctl, void *arg)
{
struct mdss_mdp_video_ctx *ctx;
@@ -306,9 +406,22 @@
WARN(!ctx->wait_pending, "waiting without commit! ctl=%d", ctl->num);
- rc = wait_for_completion_interruptible_timeout(&ctx->vsync_comp,
- VSYNC_TIMEOUT);
- WARN(rc <= 0, "vsync timed out (%d) ctl=%d\n", rc, ctl->num);
+ if (ctx->polling_en) {
+ rc = mdss_mdp_video_pollwait(ctl);
+ } else {
+ rc = wait_for_completion_interruptible_timeout(&ctx->vsync_comp,
+ usecs_to_jiffies(VSYNC_TIMEOUT_US));
+ if (rc < 0) {
+ pr_warn("vsync wait interrupted ctl=%d\n", ctl->num);
+ } else if (rc == 0) {
+ pr_warn("vsync wait timeout %d, fallback to poll mode\n",
+ ctl->num);
+ ctx->polling_en++;
+ rc = mdss_mdp_video_pollwait(ctl);
+ } else {
+ rc = 0;
+ }
+ }
if (ctx->wait_pending) {
ctx->wait_pending = 0;
@@ -325,7 +438,7 @@
return;
ctl->underrun_cnt++;
- pr_warn("display underrun detected for ctl=%d count=%d\n", ctl->num,
+ pr_debug("display underrun detected for ctl=%d count=%d\n", ctl->num,
ctl->underrun_cnt);
}
@@ -344,7 +457,7 @@
if (!ctx->wait_pending) {
ctx->wait_pending++;
- video_vsync_irq_enable(ctl);
+ video_vsync_irq_enable(ctl, true);
INIT_COMPLETION(ctx->vsync_comp);
} else {
WARN(1, "commit without wait! ctl=%d", ctl->num);
@@ -352,7 +465,13 @@
if (!ctx->timegen_en) {
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_UNBLANK, NULL);
- WARN(rc, "intf %d unblank error (%d)\n", ctl->intf_num, rc);
+ if (rc) {
+ pr_warn("intf #%d unblank error (%d)\n",
+ ctl->intf_num, rc);
+ video_vsync_irq_disable(ctl);
+ ctx->wait_pending = 0;
+ return rc;
+ }
pr_debug("enabling timing gen for intf=%d\n", ctl->intf_num);
@@ -363,7 +482,7 @@
wmb();
rc = wait_for_completion_interruptible_timeout(&ctx->vsync_comp,
- VSYNC_TIMEOUT);
+ usecs_to_jiffies(VSYNC_TIMEOUT_US));
WARN(rc <= 0, "timeout (%d) enabling timegen on ctl=%d\n",
rc, ctl->num);
@@ -375,6 +494,99 @@
return 0;
}
+int mdss_mdp_video_copy_splash_screen(struct mdss_panel_data *pdata)
+{
+ void *virt = NULL;
+ unsigned long bl_fb_addr = 0;
+ unsigned long *bl_fb_addr_va;
+ unsigned long pipe_addr, pipe_src_size;
+ u32 height, width, rgb_size, bpp;
+ size_t size;
+ static struct ion_handle *ihdl;
+ struct ion_client *iclient = mdss_get_ionclient();
+ static ion_phys_addr_t phys;
+
+ pipe_addr = MDSS_MDP_REG_SSPP_OFFSET(3) +
+ MDSS_MDP_REG_SSPP_SRC0_ADDR;
+ pipe_src_size =
+ MDSS_MDP_REG_SSPP_OFFSET(3) + MDSS_MDP_REG_SSPP_SRC_SIZE;
+
+ bpp = 3;
+ rgb_size = MDSS_MDP_REG_READ(pipe_src_size);
+ bl_fb_addr = MDSS_MDP_REG_READ(pipe_addr);
+
+ height = (rgb_size >> 16) & 0xffff;
+ width = rgb_size & 0xffff;
+ size = PAGE_ALIGN(height * width * bpp);
+ pr_debug("%s:%d splash_height=%d splash_width=%d Buffer size=%d\n",
+ __func__, __LINE__, height, width, size);
+
+ ihdl = ion_alloc(iclient, size, SZ_1M,
+ ION_HEAP(ION_QSECOM_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL(ihdl)) {
+ pr_err("unable to alloc fbmem from ion (%p)\n", ihdl);
+ return -ENOMEM;
+ }
+
+ pdata->panel_info.splash_ihdl = ihdl;
+
+ virt = ion_map_kernel(iclient, ihdl);
+ ion_phys(iclient, ihdl, &phys, &size);
+
+ pr_debug("%s %d Allocating %u bytes at 0x%lx (%pa phys)\n",
+ __func__, __LINE__, size,
+ (unsigned long int)virt, &phys);
+
+ bl_fb_addr_va = (unsigned long *)ioremap(bl_fb_addr, size);
+
+ memcpy(virt, bl_fb_addr_va, size);
+
+ MDSS_MDP_REG_WRITE(pipe_addr, phys);
+ MDSS_MDP_REG_WRITE(MDSS_MDP_REG_CTL_FLUSH + MDSS_MDP_REG_CTL_OFFSET(0),
+ 0x48);
+
+ return 0;
+}
+
+int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl)
+{
+ struct ion_client *iclient = mdss_get_ionclient();
+ struct mdss_panel_data *pdata;
+ int ret = 0, off;
+ int mdss_mdp_rev = MDSS_MDP_REG_READ(MDSS_MDP_REG_HW_VERSION);
+ int mdss_v2_intf_off = 0;
+
+ off = 0;
+
+ pdata = ctl->panel_data;
+
+ pdata->panel_info.cont_splash_enabled = 0;
+
+ ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_BEGIN,
+ NULL);
+ if (ret) {
+ pr_err("%s: Failed to handle 'CONT_SPLASH_BEGIN' event\n",
+ __func__);
+ return ret;
+ }
+
+ mdss_mdp_ctl_write(ctl, 0, MDSS_MDP_LM_BORDER_COLOR);
+ off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
+
+ if (mdss_mdp_rev == MDSS_MDP_HW_REV_102)
+ mdss_v2_intf_off = 0xEC00;
+
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN -
+ mdss_v2_intf_off, 0);
+ /* wait for 1 VSYNC for the pipe to be unstaged */
+ msleep(20);
+ ion_free(iclient, pdata->panel_info.splash_ihdl);
+ ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_FINISH,
+ NULL);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ return ret;
+}
+
int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl)
{
struct mdss_data_type *mdata;
@@ -453,7 +665,9 @@
ctl->stop_fnc = mdss_mdp_video_stop;
ctl->display_fnc = mdss_mdp_video_display;
ctl->wait_fnc = mdss_mdp_video_wait4comp;
- ctl->set_vsync_handler = mdss_mdp_video_set_vsync_handler;
+ ctl->read_line_cnt_fnc = mdss_mdp_video_line_count;
+ ctl->add_vsync_handler = mdss_mdp_video_add_vsync_handler;
+ ctl->remove_vsync_handler = mdss_mdp_video_remove_vsync_handler;
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
index 0c08eda..e06695f 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
@@ -349,8 +349,13 @@
rc = wait_for_completion_interruptible_timeout(&ctx->wb_comp,
KOFF_TIMEOUT);
- WARN(rc <= 0, "writeback kickoff timed out (%d) ctl=%d\n",
- rc, ctl->num);
+ if (rc <= 0) {
+ rc = -ENODEV;
+ WARN(1, "writeback kickoff timed out (%d) ctl=%d\n",
+ rc, ctl->num);
+ } else {
+ rc = 0;
+ }
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); /* clock off */
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 48bab7f..808800a 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -24,8 +24,10 @@
#include <linux/msm_mdp.h>
#include <mach/iommu_domains.h>
+#include <mach/event_timer.h>
#include "mdss.h"
+#include "mdss_debug.h"
#include "mdss_fb.h"
#include "mdss_mdp.h"
#include "mdss_mdp_rotator.h"
@@ -37,6 +39,8 @@
static atomic_t ov_active_panels = ATOMIC_INIT(0);
static int mdss_mdp_overlay_free_fb_pipe(struct msm_fb_data_type *mfd);
+static int mdss_mdp_overlay_fb_parse_dt(struct msm_fb_data_type *mfd);
+static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd);
static int mdss_mdp_overlay_get(struct msm_fb_data_type *mfd,
struct mdp_overlay *req)
@@ -62,11 +66,19 @@
{
u32 xres, yres;
u32 min_src_size, min_dst_size;
+ int content_secure;
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
xres = mfd->fbi->var.xres;
yres = mfd->fbi->var.yres;
+ content_secure = (req->flags & MDP_SECURE_OVERLAY_SESSION);
+ if (!ctl->is_secure && content_secure &&
+ (mfd->panel.type == WRITEBACK_PANEL)) {
+ pr_debug("return due to security concerns\n");
+ return -EPERM;
+ }
if (mdata->mdp_rev >= MDSS_MDP_HW_REV_102) {
min_src_size = fmt->is_yuv ? 2 : 1;
min_dst_size = 1;
@@ -162,20 +174,31 @@
return -EINVAL;
}
- if ((fmt->chroma_sample == MDSS_MDP_CHROMA_420 ||
- fmt->chroma_sample == MDSS_MDP_CHROMA_H2V1) &&
- ((req->src_rect.w * (MAX_UPSCALE_RATIO / 2)) < dst_w)) {
- pr_err("too much YUV upscaling Width %d->%d\n",
- req->src_rect.w, req->dst_rect.w);
- return -EINVAL;
+ if (req->flags & MDP_BWC_EN) {
+ if ((req->src.width != req->src_rect.w) ||
+ (req->src.height != req->src_rect.h)) {
+ pr_err("BWC: unequal src img and rect w,h\n");
+ return -EINVAL;
+ }
}
- if ((fmt->chroma_sample == MDSS_MDP_CHROMA_420 ||
- fmt->chroma_sample == MDSS_MDP_CHROMA_H1V2) &&
- (req->src_rect.h * (MAX_UPSCALE_RATIO / 2)) < dst_h) {
- pr_err("too much YUV upscaling Height %d->%d\n",
- req->src_rect.h, req->dst_rect.h);
- return -EINVAL;
+ if (req->flags & MDP_DEINTERLACE) {
+ if (req->flags & MDP_SOURCE_ROTATED_90) {
+ if ((req->src_rect.w % 4) != 0) {
+ pr_err("interlaced rect not h/4\n");
+ return -EINVAL;
+ }
+ } else if ((req->src_rect.h % 4) != 0) {
+ pr_err("interlaced rect not h/4\n");
+ return -EINVAL;
+ }
+ }
+ } else {
+ if (req->flags & MDP_DEINTERLACE) {
+ if ((req->src_rect.h % 4) != 0) {
+ pr_err("interlaced rect h not multiple of 4\n");
+ return -EINVAL;
+ }
}
}
@@ -266,6 +289,32 @@
return ret;
}
+static int __mdp_pipe_tune_perf(struct mdss_mdp_pipe *pipe)
+{
+ struct mdss_data_type *mdata = pipe->mixer->ctl->mdata;
+ struct mdss_mdp_perf_params perf;
+ int rc;
+
+ for (;;) {
+ rc = mdss_mdp_perf_calc_pipe(pipe, &perf);
+
+ if (!rc && (perf.mdp_clk_rate <= mdata->max_mdp_clk_rate))
+ break;
+
+ /*
+ * if decimation is available try to reduce minimum clock rate
+ * requirement by applying vertical decimation and reduce
+ * mdp clock requirement
+ */
+ if (mdata->has_decimation && (pipe->vert_deci < MAX_DECIMATION))
+ pipe->vert_deci++;
+ else
+ return -EPERM;
+ }
+
+ return 0;
+}
+
static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
struct mdp_overlay *req,
struct mdss_mdp_pipe **ppipe)
@@ -273,7 +322,7 @@
struct mdss_mdp_format_params *fmt;
struct mdss_mdp_pipe *pipe;
struct mdss_mdp_mixer *mixer = NULL;
- u32 pipe_type, mixer_mux, len, src_format;
+ u32 pipe_type, mixer_mux, len;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdp_histogram_start_req hist;
int ret;
@@ -301,13 +350,13 @@
pr_debug("pipe ctl=%u req id=%x mux=%d\n", mdp5_data->ctl->num, req->id,
mixer_mux);
- src_format = req->src.format;
if (req->flags & (MDP_SOURCE_ROTATED_90 | MDP_BWC_EN))
- src_format = mdss_mdp_get_rotator_dst_format(src_format);
+ req->src.format =
+ mdss_mdp_get_rotator_dst_format(req->src.format);
- fmt = mdss_mdp_get_format_params(src_format);
+ fmt = mdss_mdp_get_format_params(req->src.format);
if (!fmt) {
- pr_err("invalid pipe format %d\n", src_format);
+ pr_err("invalid pipe format %d\n", req->src.format);
return -EINVAL;
}
@@ -320,7 +369,7 @@
if (pipe && pipe->ndx != req->id) {
pr_debug("replacing pnum=%d at stage=%d mux=%d\n",
pipe->num, req->z_order, mixer_mux);
- pipe->params_changed = true;
+ mdss_mdp_mixer_pipe_unstage(pipe);
}
mixer = mdss_mdp_mixer_get(mdp5_data->ctl, mixer_mux);
@@ -474,6 +523,12 @@
}
}
+ ret = __mdp_pipe_tune_perf(pipe);
+ if (ret) {
+ pr_debug("unable to satisfy performance. ret=%d\n", ret);
+ goto exit_fail;
+ }
+
ret = mdss_mdp_smp_reserve(pipe);
if (ret) {
pr_debug("mdss_mdp_smp_reserve failed. ret=%d\n", ret);
@@ -483,6 +538,7 @@
pipe->params_changed++;
req->id = pipe->ndx;
+ req->vert_deci = pipe->vert_deci;
*ppipe = pipe;
@@ -586,7 +642,7 @@
return 0;
}
-static int mdss_mdp_overlay_cleanup(struct msm_fb_data_type *mfd)
+static void mdss_mdp_overlay_cleanup(struct msm_fb_data_type *mfd)
{
struct mdss_mdp_pipe *pipe, *tmp;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
@@ -598,6 +654,7 @@
list_move(&pipe->cleanup_list, &destroy_pipes);
mdss_mdp_overlay_free_buf(&pipe->back_buf);
mdss_mdp_overlay_free_buf(&pipe->front_buf);
+ pipe->mfd = NULL;
}
list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) {
@@ -610,96 +667,6 @@
mutex_unlock(&mfd->lock);
list_for_each_entry_safe(pipe, tmp, &destroy_pipes, cleanup_list)
mdss_mdp_pipe_destroy(pipe);
-
- return 0;
-}
-
-int mdss_mdp_copy_splash_screen(struct mdss_panel_data *pdata)
-{
- void *virt = NULL;
- unsigned long bl_fb_addr = 0;
- unsigned long *bl_fb_addr_va;
- unsigned long pipe_addr, pipe_src_size;
- u32 height, width, rgb_size, bpp;
- size_t size;
- static struct ion_handle *ihdl;
- struct ion_client *iclient = mdss_get_ionclient();
- static ion_phys_addr_t phys;
-
- pipe_addr = MDSS_MDP_REG_SSPP_OFFSET(3) +
- MDSS_MDP_REG_SSPP_SRC0_ADDR;
- pipe_src_size =
- MDSS_MDP_REG_SSPP_OFFSET(3) + MDSS_MDP_REG_SSPP_SRC_SIZE;
-
- bpp = 3;
- rgb_size = MDSS_MDP_REG_READ(pipe_src_size);
- bl_fb_addr = MDSS_MDP_REG_READ(pipe_addr);
-
- height = (rgb_size >> 16) & 0xffff;
- width = rgb_size & 0xffff;
- size = PAGE_ALIGN(height * width * bpp);
- pr_debug("%s:%d splash_height=%d splash_width=%d Buffer size=%d\n",
- __func__, __LINE__, height, width, size);
-
- ihdl = ion_alloc(iclient, size, SZ_1M,
- ION_HEAP(ION_QSECOM_HEAP_ID), 0);
- if (IS_ERR_OR_NULL(ihdl)) {
- pr_err("unable to alloc fbmem from ion (%p)\n", ihdl);
- return -ENOMEM;
- }
-
- pdata->panel_info.splash_ihdl = ihdl;
-
- virt = ion_map_kernel(iclient, ihdl);
- ion_phys(iclient, ihdl, &phys, &size);
-
- pr_debug("%s %d Allocating %u bytes at 0x%lx (%pa phys)\n",
- __func__, __LINE__, size,
- (unsigned long int)virt, &phys);
-
- bl_fb_addr_va = (unsigned long *)ioremap(bl_fb_addr, size);
-
- memcpy(virt, bl_fb_addr_va, size);
-
- MDSS_MDP_REG_WRITE(pipe_addr, phys);
- MDSS_MDP_REG_WRITE(MDSS_MDP_REG_CTL_FLUSH + MDSS_MDP_REG_CTL_OFFSET(0),
- 0x48);
-
- return 0;
-
-}
-
-int mdss_mdp_reconfigure_splash_done(struct mdss_mdp_ctl *ctl)
-{
- struct ion_client *iclient = mdss_get_ionclient();
- struct mdss_panel_data *pdata;
- int ret = 0, off;
- int mdss_mdp_rev = MDSS_MDP_REG_READ(MDSS_MDP_REG_HW_VERSION);
- int mdss_v2_intf_off = 0;
-
- off = 0;
-
- pdata = ctl->panel_data;
-
- pdata->panel_info.cont_splash_enabled = 0;
-
- ion_free(iclient, pdata->panel_info.splash_ihdl);
-
- mdss_mdp_ctl_write(ctl, 0, MDSS_MDP_LM_BORDER_COLOR);
- off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
-
- if (mdss_mdp_rev == MDSS_MDP_HW_REV_102)
- mdss_v2_intf_off = 0xEC00;
-
- /* wait for 1 VSYNC for the pipe to be unstaged */
- msleep(20);
- MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN -
- mdss_v2_intf_off, 0);
- ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_FINISH,
- NULL);
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
- mdss_mdp_footswitch_ctrl_splash(0);
- return ret;
}
static int mdss_mdp_overlay_start(struct msm_fb_data_type *mfd)
@@ -718,8 +685,10 @@
return rc;
}
- if (mfd->panel_info->cont_splash_enabled)
- mdss_mdp_reconfigure_splash_done(mdp5_data->ctl);
+ if (mfd->panel_info->cont_splash_enabled) {
+ mdss_mdp_ctl_splash_finish(mdp5_data->ctl);
+ mdss_mdp_footswitch_ctrl_splash(0);
+ }
if (!is_mdss_iommu_attached()) {
mdss_iommu_attach(mdss_res);
@@ -740,24 +709,58 @@
return rc;
}
+static void mdss_mdp_overlay_update_pm(struct mdss_overlay_private *mdp5_data)
+{
+ ktime_t wakeup_time;
+
+ if (!mdp5_data->cpu_pm_hdl)
+ return;
+
+ if (mdss_mdp_display_wakeup_time(mdp5_data->ctl, &wakeup_time))
+ return;
+
+ activate_event_timer(mdp5_data->cpu_pm_hdl, wakeup_time);
+}
+
int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd)
{
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
- struct mdss_mdp_pipe *pipe;
+ struct mdss_mdp_pipe *pipe, *next;
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
int ret;
mutex_lock(&mdp5_data->ov_lock);
mutex_lock(&mfd->lock);
- list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) {
+
+ ret = mdss_mdp_display_wait4pingpong(mdp5_data->ctl);
+ if (ret) {
+ mutex_unlock(&mfd->lock);
+ mutex_unlock(&mdp5_data->ov_lock);
+ return ret;
+ }
+
+ list_for_each_entry_safe(pipe, next, &mdp5_data->pipes_used,
+ used_list) {
struct mdss_mdp_data *buf;
if (pipe->back_buf.num_planes) {
buf = &pipe->back_buf;
+ } else if (ctl->play_cnt == 0) {
+ pipe->params_changed++;
+ buf = &pipe->front_buf;
} else if (!pipe->params_changed) {
+ if (pipe->mixer) {
+ if (!mdss_mdp_pipe_is_staged(pipe)) {
+ list_del(&pipe->used_list);
+ list_add(&pipe->cleanup_list,
+ &mdp5_data->pipes_cleanup);
+ }
+ }
continue;
} else if (pipe->front_buf.num_planes) {
buf = &pipe->front_buf;
} else {
pr_warn("pipe queue w/o buffer. unstaging layer\n");
+ pipe->params_changed = 0;
mdss_mdp_mixer_pipe_unstage(pipe);
continue;
}
@@ -780,19 +783,13 @@
if (IS_ERR_VALUE(ret))
goto commit_fail;
+ mdss_mdp_overlay_update_pm(mdp5_data);
+
ret = mdss_mdp_display_wait4comp(mdp5_data->ctl);
- complete(&mfd->update.comp);
- mutex_lock(&mfd->no_update.lock);
- if (mfd->no_update.timer.function)
- del_timer(&(mfd->no_update.timer));
-
- mfd->no_update.timer.expires = jiffies + (2 * HZ);
- add_timer(&mfd->no_update.timer);
- mutex_unlock(&mfd->no_update.lock);
-
+ mdss_fb_update_notify_update(mfd);
commit_fail:
- ret = mdss_mdp_overlay_cleanup(mfd);
+ mdss_mdp_overlay_cleanup(mfd);
mutex_unlock(&mdp5_data->ov_lock);
@@ -987,6 +984,39 @@
return ret;
}
+static void mdss_mdp_overlay_force_cleanup(struct msm_fb_data_type *mfd)
+{
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ struct mdss_mdp_ctl *ctl = mdp5_data->ctl;
+ int ret;
+
+ pr_debug("forcing cleanup to unset dma pipes on fb%d\n", mfd->index);
+
+ /*
+ * video mode panels require the layer to be unstaged and wait for
+ * vsync to be able to release buffer.
+ */
+ if (ctl && ctl->is_video_mode) {
+ ret = mdss_mdp_display_commit(ctl, NULL);
+ if (!IS_ERR_VALUE(ret))
+ mdss_mdp_display_wait4comp(ctl);
+ }
+
+ mdss_mdp_overlay_cleanup(mfd);
+}
+
+static void mdss_mdp_overlay_force_dma_cleanup(struct mdss_data_type *mdata)
+{
+ struct mdss_mdp_pipe *pipe;
+ int i;
+
+ for (i = 0; i < mdata->ndma_pipes; i++) {
+ pipe = mdata->dma_pipes + i;
+ if (atomic_read(&pipe->ref_cnt) && pipe->mfd)
+ mdss_mdp_overlay_force_cleanup(pipe->mfd);
+ }
+}
+
static int mdss_mdp_overlay_play(struct msm_fb_data_type *mfd,
struct msmfb_overlay_data *req)
{
@@ -1011,6 +1041,8 @@
}
if (req->id & MDSS_MDP_ROT_SESSION_MASK) {
+ mdss_mdp_overlay_force_dma_cleanup(mfd_to_mdata(mfd));
+
ret = mdss_mdp_overlay_rotate(mfd, req);
} else if (req->id == BORDERFILL_NDX) {
pr_debug("borderfill enable\n");
@@ -1129,7 +1161,8 @@
fbi = mfd->fbi;
- if (fbi->fix.smem_len == 0 || mdp5_data->borderfill_enable) {
+ if (!fbi->fix.smem_start || fbi->fix.smem_len == 0 ||
+ mdp5_data->borderfill_enable) {
mfd->mdp.kickoff_fnc(mfd);
return;
}
@@ -1160,9 +1193,13 @@
goto pan_display_error;
}
- if (is_mdss_iommu_attached())
+ if (is_mdss_iommu_attached()) {
+ if (!mfd->iova) {
+ pr_err("mfd iova is zero\n");
+ goto pan_display_error;
+ }
data.p[0].addr = mfd->iova;
- else
+ } else
data.p[0].addr = fbi->fix.smem_start;
data.p[0].addr += offset;
@@ -1246,7 +1283,7 @@
if (!ctl)
return -ENODEV;
- if (!ctl->set_vsync_handler)
+ if (!ctl->add_vsync_handler || !ctl->remove_vsync_handler)
return -ENOTSUPP;
rc = mutex_lock_interruptible(&ctl->lock);
@@ -1269,9 +1306,9 @@
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
if (en)
- rc = ctl->set_vsync_handler(ctl, mdss_mdp_overlay_handle_vsync);
+ rc = ctl->add_vsync_handler(ctl, &ctl->vsync_handler);
else
- rc = ctl->set_vsync_handler(ctl, NULL);
+ rc = ctl->remove_vsync_handler(ctl, &ctl->vsync_handler);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
mutex_unlock(&ctl->lock);
@@ -1287,20 +1324,14 @@
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
unsigned long flags;
u64 vsync_ticks;
- unsigned long timeout;
int ret;
if (!mdp5_data->ctl || !mdp5_data->ctl->power_on)
return 0;
- timeout = msecs_to_jiffies(VSYNC_PERIOD * 5);
- ret = wait_for_completion_interruptible_timeout(&mdp5_data->vsync_comp,
- timeout);
- if (ret <= 0) {
- pr_debug("Sending current time as vsync timestamp for fb%d\n",
- mfd->index);
- mdp5_data->vsync_time = ktime_get();
- }
+ ret = wait_for_completion_interruptible(&mdp5_data->vsync_comp);
+ if (ret < 0)
+ return ret;
spin_lock_irqsave(&mdp5_data->vsync_lock, flags);
vsync_ticks = ktime_to_ns(mdp5_data->vsync_time);
@@ -1461,7 +1492,7 @@
{
int ret = 0;
int curr_bl;
- mutex_lock(&mfd->lock);
+ mutex_lock(&mfd->bl_lock);
curr_bl = mfd->bl_level;
mfd->bl_scale = data->scale;
mfd->bl_min_lvl = data->min_lvl;
@@ -1470,7 +1501,7 @@
/* update current backlight to use new scaling*/
mdss_fb_set_backlight(mfd, curr_bl);
- mutex_unlock(&mfd->lock);
+ mutex_unlock(&mfd->bl_lock);
return ret;
}
@@ -1480,6 +1511,7 @@
int ret;
struct msmfb_mdp_pp mdp_pp;
u32 copyback = 0;
+ u32 copy_from_kernel = 0;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
ret = copy_from_user(&mdp_pp, argp, sizeof(mdp_pp));
@@ -1506,7 +1538,7 @@
mdp5_data->ctl,
(struct mdp_igc_lut_data *)
&mdp_pp.data.lut_cfg_data.data,
- ©back);
+ ©back, copy_from_kernel);
break;
case mdp_lut_pgc:
@@ -1544,8 +1576,26 @@
ret = mdss_bl_scale_config(mfd, (struct mdp_bl_scale_data *)
&mdp_pp.data.bl_scale_data);
break;
+ case mdp_op_ad_cfg:
+ ret = mdss_mdp_ad_config(mfd, &mdp_pp.data.ad_init_cfg);
+ break;
+ case mdp_op_ad_input:
+ ret = mdss_mdp_ad_input(mfd, &mdp_pp.data.ad_input, 1);
+ if (ret > 0) {
+ ret = 0;
+ copyback = 1;
+ }
+ break;
+ case mdp_op_calib_cfg:
+ ret = mdss_mdp_calib_config((struct mdp_calib_config_data *)
+ &mdp_pp.data.calib_cfg, ©back);
+ break;
+ case mdp_op_calib_mode:
+ ret = mdss_mdp_calib_mode(mfd, &mdp_pp.data.mdss_calib_cfg);
+ break;
default:
- pr_err("Unsupported request to MDP_PP IOCTL.\n");
+ pr_err("Unsupported request to MDP_PP IOCTL. %d = op\n",
+ mdp_pp.op);
ret = -EINVAL;
break;
}
@@ -1604,6 +1654,7 @@
static int mdss_fb_set_metadata(struct msm_fb_data_type *mfd,
struct msmfb_metadata *metadata)
{
+ struct mdss_data_type *mdata = mfd_to_mdata(mfd);
int ret = 0;
switch (metadata->op) {
case metadata_op_vic:
@@ -1613,6 +1664,11 @@
else
ret = -EINVAL;
break;
+ case metadata_op_crc:
+ if (!mfd->panel_power_on)
+ return -EPERM;
+ ret = mdss_misr_crc_set(mdata, &metadata->data.misr_request);
+ break;
default:
pr_warn("unsupported request to MDP META IOCTL\n");
ret = -EINVAL;
@@ -1639,15 +1695,21 @@
static int mdss_fb_get_metadata(struct msm_fb_data_type *mfd,
struct msmfb_metadata *metadata)
{
+ struct mdss_data_type *mdata = mfd_to_mdata(mfd);
int ret = 0;
switch (metadata->op) {
case metadata_op_frame_rate:
metadata->data.panel_frame_rate =
- mdss_get_panel_framerate(mfd);
+ mdss_panel_get_framerate(mfd->panel_info);
break;
case metadata_op_get_caps:
ret = mdss_fb_get_hw_caps(mfd, &metadata->data.caps);
break;
+ case metadata_op_crc:
+ if (!mfd->panel_power_on)
+ return -EPERM;
+ ret = mdss_misr_crc_get(mdata, &metadata->data.misr_request);
+ break;
default:
pr_warn("Unsupported request to MDP META IOCTL.\n");
ret = -EINVAL;
@@ -1720,11 +1782,8 @@
struct msmfb_overlay_data data;
ret = copy_from_user(&data, argp, sizeof(data));
- if (!ret) {
+ if (!ret)
ret = mdss_mdp_overlay_play(mfd, &data);
- if (!IS_ERR_VALUE(ret))
- mdss_fb_update_backlight(mfd);
- }
if (ret)
pr_debug("OVERLAY_PLAY failed (%d)\n", ret);
@@ -1812,6 +1871,8 @@
mfd->index);
return PTR_ERR(ctl);
}
+ ctl->vsync_handler.vsync_handler =
+ mdss_mdp_overlay_handle_vsync;
if (mfd->split_display && pdata->next) {
/* enable split display */
@@ -1834,7 +1895,10 @@
return rc;
}
- if (!IS_ERR_VALUE(rc) && mdp5_data->vsync_pending) {
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("Failed to turn on fb%d\n", mfd->index);
+ mdss_mdp_overlay_off(mfd);
+ } else if (mdp5_data->vsync_pending) {
mdp5_data->vsync_pending = 0;
mdss_mdp_overlay_vsync_ctrl(mfd, mdp5_data->vsync_pending);
}
@@ -1861,7 +1925,19 @@
if (!mdp5_data->ctl->power_on)
return 0;
- mdss_mdp_overlay_release_all(mfd);
+ if (!mfd->ref_cnt) {
+ mdss_mdp_overlay_release_all(mfd);
+ } else {
+ int need_cleanup;
+ mutex_lock(&mfd->lock);
+ need_cleanup = !list_empty(&mdp5_data->pipes_cleanup);
+ mutex_unlock(&mfd->lock);
+
+ if (need_cleanup) {
+ pr_debug("cleaning up some pipes\n");
+ mdss_mdp_overlay_kickoff(mfd);
+ }
+ }
rc = mdss_mdp_ctl_stop(mdp5_data->ctl);
if (rc == 0) {
@@ -1891,7 +1967,7 @@
if (pdata->panel_info.cont_splash_enabled) {
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
mdss_mdp_footswitch_ctrl_splash(1);
- mdss_mdp_copy_splash_screen(pdata);
+ mdss_mdp_ctl_splash_start(pdata);
}
return 0;
}
@@ -1935,6 +2011,10 @@
}
mfd->mdp.private1 = mdp5_data;
+ rc = mdss_mdp_overlay_fb_parse_dt(mfd);
+ if (rc)
+ return rc;
+
rc = sysfs_create_group(&dev->kobj, &vsync_fs_attr_group);
if (rc) {
pr_err("vsync sysfs group creation failed, ret=%d\n", rc);
@@ -1947,8 +2027,27 @@
kobject_uevent(&dev->kobj, KOBJ_ADD);
pr_debug("vsync kobject_uevent(KOBJ_ADD)\n");
+ mdp5_data->cpu_pm_hdl = add_event_timer(NULL, (void *)mdp5_data);
+ if (!mdp5_data->cpu_pm_hdl)
+ pr_warn("%s: unable to add event timer\n", __func__);
+
return rc;
init_fail:
kfree(mdp5_data);
return rc;
}
+
+static int mdss_mdp_overlay_fb_parse_dt(struct msm_fb_data_type *mfd)
+{
+ struct platform_device *pdev = mfd->pdev;
+ struct mdss_overlay_private *mdp5_mdata = mfd_to_mdp5_data(mfd);
+
+ mdp5_mdata->mixer_swap = of_property_read_bool(pdev->dev.of_node,
+ "qcom,mdss-mixer-swap");
+ if (mdp5_mdata->mixer_swap) {
+ pr_info("mixer swap is enabled for fb device=%s\n",
+ pdev->name);
+ }
+
+ return 0;
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
index 0f65530..3b91ced 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pipe.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c
@@ -22,6 +22,8 @@
#define SMP_MB_SIZE (mdss_res->smp_mb_size)
#define SMP_MB_CNT (mdss_res->smp_mb_cnt)
#define SMP_ENTRIES_PER_MB (SMP_MB_SIZE / 16)
+#define SMP_MB_ENTRY_SIZE 16
+#define MAX_BPP 4
static DEFINE_MUTEX(mdss_mdp_sspp_lock);
static DEFINE_MUTEX(mdss_mdp_smp_lock);
@@ -94,17 +96,31 @@
static void mdss_mdp_smp_set_wm_levels(struct mdss_mdp_pipe *pipe, int mb_cnt)
{
- u32 entries, val, wm[3];
+ u32 fetch_size, val, wm[3];
- entries = mb_cnt * SMP_ENTRIES_PER_MB;
- val = entries >> 2;
+ fetch_size = mb_cnt * SMP_MB_SIZE;
+
+ /*
+ * when doing hflip, one line is reserved to be consumed down the
+ * pipeline. This line will always be marked as full even if it doesn't
+ * have any data. In order to generate proper priority levels ignore
+ * this region while setting up watermark levels
+ */
+ if (pipe->flags & MDP_FLIP_LR) {
+ u8 bpp = pipe->src_fmt->is_yuv ? 1 :
+ pipe->src_fmt->bpp;
+ fetch_size -= (pipe->src.w * bpp);
+ }
+
+ /* 1/4 of SMP pool that is being fetched */
+ val = (fetch_size / SMP_MB_ENTRY_SIZE) >> 2;
wm[0] = val;
wm[1] = wm[0] + val;
wm[2] = wm[1] + val;
- pr_debug("pnum=%d watermarks %u,%u,%u\n", pipe->num,
- wm[0], wm[1], wm[2]);
+ pr_debug("pnum=%d fetch_size=%u watermarks %u,%u,%u\n", pipe->num,
+ fetch_size, wm[0], wm[1], wm[2]);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_REQPRIO_FIFO_WM_0, wm[0]);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_REQPRIO_FIFO_WM_1, wm[1]);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_REQPRIO_FIFO_WM_2, wm[2]);
@@ -138,7 +154,7 @@
u32 num_blks = 0, reserved = 0;
struct mdss_mdp_plane_sizes ps;
int i;
- int rc = 0;
+ int rc = 0, rot_mode = 0;
u32 nlines;
if (pipe->bwc_mode) {
@@ -157,15 +173,27 @@
pipe->src.w, pipe->src.h, &ps, 0);
if (rc)
return rc;
+
+ if (pipe->mixer && pipe->mixer->rotator_mode)
+ rot_mode = 1;
+ else if (ps.num_planes == 1)
+ ps.ystride[0] = MAX_BPP *
+ max(pipe->mixer->width, pipe->src.w);
}
+ nlines = pipe->bwc_mode ? 1 : 2;
+
mutex_lock(&mdss_mdp_smp_lock);
for (i = 0; i < ps.num_planes; i++) {
- nlines = pipe->bwc_mode ? ps.rau_h[i] : 2;
- num_blks = DIV_ROUND_UP(nlines * ps.ystride[i], SMP_MB_SIZE);
+ if (rot_mode) {
+ num_blks = 1;
+ } else {
+ num_blks = DIV_ROUND_UP(ps.ystride[i] * nlines,
+ SMP_MB_SIZE);
- if (mdata->mdp_rev == MDSS_MDP_HW_REV_100)
- num_blks = roundup_pow_of_two(num_blks);
+ if (mdata->mdp_rev == MDSS_MDP_HW_REV_100)
+ num_blks = roundup_pow_of_two(num_blks);
+ }
pr_debug("reserving %d mmb for pnum=%d plane=%d\n",
num_blks, pipe->num, i);
@@ -202,6 +230,13 @@
return 0;
}
+void mdss_mdp_smp_release(struct mdss_mdp_pipe *pipe)
+{
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ mdss_mdp_smp_free(pipe);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+}
+
int mdss_mdp_smp_setup(struct mdss_data_type *mdata, u32 cnt, u32 size)
{
if (!mdata)
@@ -690,3 +725,8 @@
return ret;
}
+
+int mdss_mdp_pipe_is_staged(struct mdss_mdp_pipe *pipe)
+{
+ return (pipe == pipe->mixer->stage_pipe[pipe->mixer_stage]);
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index 8bd5674..963d3fb 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -93,6 +93,72 @@
static u32 dither_depth_map[9] = {
0, 0, 0, 0, 0, 1, 2, 3, 3};
+static u32 igc_limited[IGC_LUT_ENTRIES] = {
+ 16777472, 17826064, 18874656, 19923248,
+ 19923248, 20971840, 22020432, 23069024,
+ 24117616, 25166208, 26214800, 26214800,
+ 27263392, 28311984, 29360576, 30409168,
+ 31457760, 32506352, 32506352, 33554944,
+ 34603536, 35652128, 36700720, 37749312,
+ 38797904, 38797904, 39846496, 40895088,
+ 41943680, 42992272, 44040864, 45089456,
+ 45089456, 46138048, 47186640, 48235232,
+ 49283824, 50332416, 51381008, 51381008,
+ 52429600, 53478192, 54526784, 55575376,
+ 56623968, 57672560, 58721152, 58721152,
+ 59769744, 60818336, 61866928, 62915520,
+ 63964112, 65012704, 65012704, 66061296,
+ 67109888, 68158480, 69207072, 70255664,
+ 71304256, 71304256, 72352848, 73401440,
+ 74450032, 75498624, 76547216, 77595808,
+ 77595808, 78644400, 79692992, 80741584,
+ 81790176, 82838768, 83887360, 83887360,
+ 84935952, 85984544, 87033136, 88081728,
+ 89130320, 90178912, 90178912, 91227504,
+ 92276096, 93324688, 94373280, 95421872,
+ 96470464, 96470464, 97519056, 98567648,
+ 99616240, 100664832, 101713424, 102762016,
+ 102762016, 103810608, 104859200, 105907792,
+ 106956384, 108004976, 109053568, 109053568,
+ 110102160, 111150752, 112199344, 113247936,
+ 114296528, 115345120, 115345120, 116393712,
+ 117442304, 118490896, 119539488, 120588080,
+ 121636672, 121636672, 122685264, 123733856,
+ 124782448, 125831040, 126879632, 127928224,
+ 127928224, 128976816, 130025408, 131074000,
+ 132122592, 133171184, 134219776, 135268368,
+ 135268368, 136316960, 137365552, 138414144,
+ 139462736, 140511328, 141559920, 141559920,
+ 142608512, 143657104, 144705696, 145754288,
+ 146802880, 147851472, 147851472, 148900064,
+ 149948656, 150997248, 152045840, 153094432,
+ 154143024, 154143024, 155191616, 156240208,
+ 157288800, 158337392, 159385984, 160434576,
+ 160434576, 161483168, 162531760, 163580352,
+ 164628944, 165677536, 166726128, 166726128,
+ 167774720, 168823312, 169871904, 170920496,
+ 171969088, 173017680, 173017680, 174066272,
+ 175114864, 176163456, 177212048, 178260640,
+ 179309232, 179309232, 180357824, 181406416,
+ 182455008, 183503600, 184552192, 185600784,
+ 185600784, 186649376, 187697968, 188746560,
+ 189795152, 190843744, 191892336, 191892336,
+ 192940928, 193989520, 195038112, 196086704,
+ 197135296, 198183888, 198183888, 199232480,
+ 200281072, 201329664, 202378256, 203426848,
+ 204475440, 204475440, 205524032, 206572624,
+ 207621216, 208669808, 209718400, 210766992,
+ 211815584, 211815584, 212864176, 213912768,
+ 214961360, 216009952, 217058544, 218107136,
+ 218107136, 219155728, 220204320, 221252912,
+ 222301504, 223350096, 224398688, 224398688,
+ 225447280, 226495872, 227544464, 228593056,
+ 229641648, 230690240, 230690240, 231738832,
+ 232787424, 233836016, 234884608, 235933200,
+ 236981792, 236981792, 238030384, 239078976,
+ 240127568, 241176160, 242224752, 243273344,
+ 243273344, 244321936, 245370528, 246419120};
+
#define GAMUT_T0_SIZE 125
#define GAMUT_T1_SIZE 100
#define GAMUT_T2_SIZE 80
@@ -119,6 +185,42 @@
#define PP_STS_ENABLE 0x1
#define PP_STS_GAMUT_FIRST 0x2
+#define PP_AD_STATE_INIT 0x2
+#define PP_AD_STATE_CFG 0x4
+#define PP_AD_STATE_DATA 0x8
+#define PP_AD_STATE_RUN 0x10
+#define PP_AD_STATE_VSYNC 0x20
+#define PP_AD_STATE_BL_LIN 0x40
+
+#define PP_AD_STATE_IS_INITCFG(st) (((st) & PP_AD_STATE_INIT) &&\
+ ((st) & PP_AD_STATE_CFG))
+
+#define PP_AD_STATE_IS_READY(st) (((st) & PP_AD_STATE_INIT) &&\
+ ((st) & PP_AD_STATE_CFG) &&\
+ ((st) & PP_AD_STATE_DATA))
+
+#define PP_AD_STS_DIRTY_INIT 0x2
+#define PP_AD_STS_DIRTY_CFG 0x4
+#define PP_AD_STS_DIRTY_DATA 0x8
+#define PP_AD_STS_DIRTY_VSYNC 0x10
+
+#define PP_AD_STS_IS_DIRTY(sts) (((sts) & PP_AD_STS_DIRTY_INIT) ||\
+ ((sts) & PP_AD_STS_DIRTY_CFG))
+
+/* Bits 0 and 1 */
+#define MDSS_AD_INPUT_AMBIENT (0x03)
+/* Bits 3 and 7 */
+#define MDSS_AD_INPUT_STRENGTH (0x88)
+/*
+ * Check data by shifting by mode to see if it matches to the
+ * MDSS_AD_INPUT_* bitfields
+ */
+#define MDSS_AD_MODE_DATA_MATCH(mode, data) ((1 << (mode)) & (data))
+#define MDSS_AD_RUNNING_AUTO_BL(ad) (((ad)->state & PP_AD_STATE_RUN) &&\
+ ((ad)->cfg.mode == MDSS_AD_MODE_AUTO_BL))
+#define MDSS_AD_RUNNING_AUTO_STR(ad) (((ad)->state & PP_AD_STATE_RUN) &&\
+ ((ad)->cfg.mode == MDSS_AD_MODE_AUTO_STR))
+
#define SHARP_STRENGTH_DEFAULT 32
#define SHARP_EDGE_THR_DEFAULT 112
#define SHARP_SMOOTH_THR_DEFAULT 8
@@ -145,11 +247,9 @@
struct mdp_dither_cfg_data dither_disp_cfg[MDSS_BLOCK_DISP_NUM];
struct mdp_gamut_cfg_data gamut_disp_cfg[MDSS_BLOCK_DISP_NUM];
uint16_t gamut_tbl[MDSS_BLOCK_DISP_NUM][GAMUT_TOTAL_TABLE_SIZE];
- struct pp_hist_col_info
- *hist_col[MDSS_BLOCK_DISP_NUM][MDSS_MDP_MAX_DSPP];
u32 hist_data[MDSS_BLOCK_DISP_NUM][HIST_V_SIZE];
/* physical info */
- struct pp_sts_type pp_dspp_sts[MDSS_MDP_MAX_DSPP];
+ struct pp_sts_type pp_disp_sts[MDSS_BLOCK_DISP_NUM];
struct pp_hist_col_info dspp_hist[MDSS_MDP_MAX_DSPP];
};
@@ -187,7 +287,18 @@
static void pp_sharp_config(char __iomem *offset,
struct pp_sts_type *pp_sts,
struct mdp_sharp_cfg *sharp_config);
+static int mdss_ad_init_checks(struct msm_fb_data_type *mfd);
+static int mdss_mdp_get_ad(struct msm_fb_data_type *mfd,
+ struct mdss_ad_info **ad);
+static int pp_update_ad_input(struct msm_fb_data_type *mfd);
+static void pp_ad_vsync_handler(struct mdss_mdp_ctl *ctl, ktime_t t);
+static void pp_ad_cfg_write(struct mdss_ad_info *ad);
+static void pp_ad_init_write(struct mdss_ad_info *ad);
+static void pp_ad_input_write(struct mdss_ad_info *ad, u32 bl_lvl);
+static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd);
+static void pp_ad_cfg_lut(char __iomem *offset, u32 *data);
+static u32 last_sts, last_state;
int mdss_mdp_csc_setup_data(u32 block, u32 blk_idx, u32 tbl_idx,
struct mdp_csc_cfg *data)
@@ -631,7 +742,7 @@
else
scale_config |= /* RGB, A */
(MDSS_MDP_SCALE_FILTER_PCMN << 10) |
- (MDSS_MDP_SCALE_FILTER_NEAREST << 18);
+ (MDSS_MDP_SCALE_FILTER_PCMN << 18);
}
phasey_step = mdss_mdp_scale_phase_step(
@@ -688,7 +799,7 @@
else
scale_config |= /* RGB, A */
(MDSS_MDP_SCALE_FILTER_PCMN << 8) |
- (MDSS_MDP_SCALE_FILTER_NEAREST << 16);
+ (MDSS_MDP_SCALE_FILTER_PCMN << 16);
}
phasex_step = mdss_mdp_scale_phase_step(
@@ -781,14 +892,19 @@
return ret;
}
-static int pp_mixer_setup(u32 disp_num, struct mdss_mdp_ctl *ctl,
+static int pp_mixer_setup(u32 disp_num,
struct mdss_mdp_mixer *mixer)
{
u32 flags, offset, dspp_num, opmode = 0;
struct mdp_pgc_lut_data *pgc_config;
struct pp_sts_type *pp_sts;
+ struct mdss_mdp_ctl *ctl;
dspp_num = mixer->num;
+ if (!mixer || !mixer->ctl)
+ return -EINVAL;
+ ctl = mixer->ctl;
+
/* no corresponding dspp */
if ((mixer->type != MDSS_MDP_MIXER_TYPE_INTF) ||
(dspp_num >= MDSS_MDP_MAX_DSPP))
@@ -798,7 +914,7 @@
else
flags = 0;
- pp_sts = &mdss_pp_res->pp_dspp_sts[dspp_num];
+ pp_sts = &mdss_pp_res->pp_disp_sts[disp_num];
/* GC_LUT is in layer mixer */
if (flags & PP_FLAGS_DIRTY_ARGC) {
pgc_config = &mdss_pp_res->argc_disp_cfg[disp_num];
@@ -896,8 +1012,7 @@
return ret;
}
-static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_ctl *ctl,
- struct mdss_mdp_mixer *mixer)
+static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
{
u32 flags, base, offset, dspp_num, opmode = 0;
struct mdp_dither_cfg_data *dither_cfg;
@@ -906,10 +1021,15 @@
u32 data;
char __iomem *basel;
int i, ret = 0;
+ struct mdss_data_type *mdata;
+ struct mdss_mdp_ctl *ctl;
+ u32 mixer_cnt;
+ u32 mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER];
- if (!mixer || !ctl)
+ if (!mixer || !mixer->ctl || !mixer->ctl->mdata)
return -EINVAL;
-
+ ctl = mixer->ctl;
+ mdata = ctl->mdata;
dspp_num = mixer->num;
/* no corresponding dspp */
if ((mixer->type != MDSS_MDP_MIXER_TYPE_INTF) ||
@@ -929,11 +1049,23 @@
else
flags = 0;
- /* nothing to update */
- if ((!flags) && (!(opmode)))
- goto dspp_exit;
+ mixer_cnt = mdss_mdp_get_ctl_mixers(disp_num, mixer_id);
+ if (dspp_num < mdata->nad_cfgs && (mixer_cnt != 2) &&
+ ctl->mfd->panel_info->type != MIPI_CMD_PANEL) {
+ ret = mdss_mdp_ad_setup(ctl->mfd);
+ if (ret < 0)
+ pr_warn("ad_setup(dspp%d) returns %d", dspp_num, ret);
+ }
+ /* call calibration specific processing here */
+ if (ctl->mfd->calib_mode)
+ goto flush_exit;
- pp_sts = &mdss_pp_res->pp_dspp_sts[dspp_num];
+ /* nothing to update */
+ if ((!flags) && (!(opmode)) && (ret <= 0))
+ goto dspp_exit;
+ ret = 0;
+
+ pp_sts = &mdss_pp_res->pp_disp_sts[disp_num];
pp_pa_config(flags, base + MDSS_MDP_REG_DSPP_PA_BASE, pp_sts,
&mdss_pp_res->pa_disp_cfg[disp_num]);
@@ -1018,6 +1150,7 @@
if (pp_sts->pgc_sts & PP_STS_ENABLE)
opmode |= (1 << 22);
+flush_exit:
writel_relaxed(opmode, basel + MDSS_MDP_REG_DSPP_OP_MODE);
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, BIT(13 + dspp_num));
wmb();
@@ -1058,12 +1191,12 @@
mutex_lock(&mdss_pp_mutex);
if (ctl->mixer_left) {
- pp_mixer_setup(disp_num, ctl, ctl->mixer_left);
- pp_dspp_setup(disp_num, ctl, ctl->mixer_left);
+ pp_mixer_setup(disp_num, ctl->mixer_left);
+ pp_dspp_setup(disp_num, ctl->mixer_left);
}
if (ctl->mixer_right) {
- pp_mixer_setup(disp_num, ctl, ctl->mixer_right);
- pp_dspp_setup(disp_num, ctl, ctl->mixer_right);
+ pp_mixer_setup(disp_num, ctl->mixer_right);
+ pp_dspp_setup(disp_num, ctl->mixer_right);
}
/* clear dirty flag */
if (disp_num < MDSS_BLOCK_DISP_NUM)
@@ -1077,76 +1210,98 @@
* Set dirty and write bits on features that were enabled so they will be
* reconfigured
*/
-int mdss_mdp_pp_resume(u32 mixer_num)
+int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num)
{
- u32 flags = 0;
+ u32 flags = 0, disp_num, bl;
struct pp_sts_type pp_sts;
-
- if (mixer_num >= MDSS_MDP_MAX_DSPP) {
- pr_warn("invalid mixer_num");
+ struct mdss_ad_info *ad;
+ struct mdss_data_type *mdata = ctl->mdata;
+ if (dspp_num >= MDSS_MDP_MAX_DSPP) {
+ pr_warn("invalid dspp_num");
return -EINVAL;
}
+ disp_num = ctl->mfd->index;
- pp_sts = mdss_pp_res->pp_dspp_sts[mixer_num];
+ if (dspp_num < mdata->nad_cfgs) {
+ ad = &mdata->ad_cfgs[dspp_num];
+
+ if (PP_AD_STATE_CFG & ad->state)
+ pp_ad_cfg_write(ad);
+ if (PP_AD_STATE_INIT & ad->state)
+ pp_ad_init_write(ad);
+ if (PP_AD_STATE_DATA & ad->state) {
+ bl = ctl->mfd->bl_level;
+ ad->last_bl = bl;
+ if (ad->state & PP_AD_STATE_BL_LIN) {
+ bl = ad->bl_lin[bl >> ad->bl_bright_shift];
+ bl = bl << ad->bl_bright_shift;
+ }
+ pp_ad_input_write(ad, bl);
+ }
+ if ((PP_AD_STATE_VSYNC & ad->state) && ad->calc_itr)
+ ctl->add_vsync_handler(ctl, &ad->handle);
+ }
+
+ pp_sts = mdss_pp_res->pp_disp_sts[disp_num];
if (pp_sts.pa_sts & PP_STS_ENABLE) {
flags |= PP_FLAGS_DIRTY_PA;
- if (!(mdss_pp_res->pa_disp_cfg[mixer_num].flags
+ if (!(mdss_pp_res->pa_disp_cfg[disp_num].flags
& MDP_PP_OPS_DISABLE))
- mdss_pp_res->pa_disp_cfg[mixer_num].flags |=
+ mdss_pp_res->pa_disp_cfg[disp_num].flags |=
MDP_PP_OPS_WRITE;
}
if (pp_sts.pcc_sts & PP_STS_ENABLE) {
flags |= PP_FLAGS_DIRTY_PCC;
- if (!(mdss_pp_res->pcc_disp_cfg[mixer_num].ops
+ if (!(mdss_pp_res->pcc_disp_cfg[disp_num].ops
& MDP_PP_OPS_DISABLE))
- mdss_pp_res->pcc_disp_cfg[mixer_num].ops |=
+ mdss_pp_res->pcc_disp_cfg[disp_num].ops |=
MDP_PP_OPS_WRITE;
}
if (pp_sts.igc_sts & PP_STS_ENABLE) {
flags |= PP_FLAGS_DIRTY_IGC;
- if (!(mdss_pp_res->igc_disp_cfg[mixer_num].ops
+ if (!(mdss_pp_res->igc_disp_cfg[disp_num].ops
& MDP_PP_OPS_DISABLE))
- mdss_pp_res->igc_disp_cfg[mixer_num].ops |=
+ mdss_pp_res->igc_disp_cfg[disp_num].ops |=
MDP_PP_OPS_WRITE;
}
if (pp_sts.argc_sts & PP_STS_ENABLE) {
flags |= PP_FLAGS_DIRTY_ARGC;
- if (!(mdss_pp_res->argc_disp_cfg[mixer_num].flags
+ if (!(mdss_pp_res->argc_disp_cfg[disp_num].flags
& MDP_PP_OPS_DISABLE))
- mdss_pp_res->argc_disp_cfg[mixer_num].flags |=
+ mdss_pp_res->argc_disp_cfg[disp_num].flags |=
MDP_PP_OPS_WRITE;
}
if (pp_sts.enhist_sts & PP_STS_ENABLE) {
flags |= PP_FLAGS_DIRTY_ENHIST;
- if (!(mdss_pp_res->enhist_disp_cfg[mixer_num].ops
+ if (!(mdss_pp_res->enhist_disp_cfg[disp_num].ops
& MDP_PP_OPS_DISABLE))
- mdss_pp_res->enhist_disp_cfg[mixer_num].ops |=
+ mdss_pp_res->enhist_disp_cfg[disp_num].ops |=
MDP_PP_OPS_WRITE;
}
if (pp_sts.dither_sts & PP_STS_ENABLE) {
flags |= PP_FLAGS_DIRTY_DITHER;
- if (!(mdss_pp_res->dither_disp_cfg[mixer_num].flags
+ if (!(mdss_pp_res->dither_disp_cfg[disp_num].flags
& MDP_PP_OPS_DISABLE))
- mdss_pp_res->dither_disp_cfg[mixer_num].flags |=
+ mdss_pp_res->dither_disp_cfg[disp_num].flags |=
MDP_PP_OPS_WRITE;
}
if (pp_sts.gamut_sts & PP_STS_ENABLE) {
flags |= PP_FLAGS_DIRTY_GAMUT;
- if (!(mdss_pp_res->gamut_disp_cfg[mixer_num].flags
+ if (!(mdss_pp_res->gamut_disp_cfg[disp_num].flags
& MDP_PP_OPS_DISABLE))
- mdss_pp_res->gamut_disp_cfg[mixer_num].flags |=
+ mdss_pp_res->gamut_disp_cfg[disp_num].flags |=
MDP_PP_OPS_WRITE;
}
if (pp_sts.pgc_sts & PP_STS_ENABLE) {
flags |= PP_FLAGS_DIRTY_PGC;
- if (!(mdss_pp_res->pgc_disp_cfg[mixer_num].flags
+ if (!(mdss_pp_res->pgc_disp_cfg[disp_num].flags
& MDP_PP_OPS_DISABLE))
- mdss_pp_res->pgc_disp_cfg[mixer_num].flags |=
+ mdss_pp_res->pgc_disp_cfg[disp_num].flags |=
MDP_PP_OPS_WRITE;
}
- mdss_pp_res->pp_disp_flags[mixer_num] = flags;
+ mdss_pp_res->pp_disp_flags[disp_num] |= flags;
return 0;
}
@@ -1481,9 +1636,30 @@
MDSS_MDP_REG_WRITE(offset, (cfg->c2_data[i] & 0xFFF) | data);
}
+int mdss_mdp_limited_lut_igc_config(struct mdss_mdp_ctl *ctl)
+{
+ int ret = 0;
+ u32 copyback = 0;
+ u32 copy_from_kernel = 1;
+ struct mdp_igc_lut_data config;
+
+ if (!ctl)
+ return -EINVAL;
+
+ config.len = IGC_LUT_ENTRIES;
+ config.ops = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE;
+ config.block = (ctl->mfd->index) + MDP_LOGICAL_BLOCK_DISP_0;
+ config.c0_c1_data = igc_limited;
+ config.c2_data = igc_limited;
+
+ ret = mdss_mdp_igc_lut_config(ctl, &config, ©back,
+ copy_from_kernel);
+ return ret;
+}
+
int mdss_mdp_igc_lut_config(struct mdss_mdp_ctl *ctl,
struct mdp_igc_lut_data *config,
- u32 *copyback)
+ u32 *copyback, u32 copy_from_kernel)
{
int ret = 0;
u32 tbl_idx, igc_offset, disp_num, dspp_num = 0;
@@ -1496,6 +1672,9 @@
(config->block >= MDP_BLOCK_MAX))
return -EINVAL;
+ if (config->len != IGC_LUT_ENTRIES)
+ return -EINVAL;
+
mutex_lock(&mdss_pp_mutex);
disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0;
@@ -1535,15 +1714,25 @@
*copyback = 1;
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
} else {
- if (copy_from_user(&mdss_pp_res->igc_lut_c0c1[disp_num][0],
- config->c0_c1_data, config->len * sizeof(u32))) {
- ret = -EFAULT;
- goto igc_config_exit;
- }
- if (copy_from_user(&mdss_pp_res->igc_lut_c2[disp_num][0],
- config->c2_data, config->len * sizeof(u32))) {
- ret = -EFAULT;
- goto igc_config_exit;
+ if (copy_from_kernel) {
+ memcpy(&mdss_pp_res->igc_lut_c0c1[disp_num][0],
+ config->c0_c1_data, config->len * sizeof(u32));
+ memcpy(&mdss_pp_res->igc_lut_c2[disp_num][0],
+ config->c2_data, config->len * sizeof(u32));
+ } else {
+ if (copy_from_user(
+ &mdss_pp_res->igc_lut_c0c1[disp_num][0],
+ config->c0_c1_data,
+ config->len * sizeof(u32))) {
+ ret = -EFAULT;
+ goto igc_config_exit;
+ }
+ if (copy_from_user(
+ &mdss_pp_res->igc_lut_c2[disp_num][0],
+ config->c2_data, config->len * sizeof(u32))) {
+ ret = -EFAULT;
+ goto igc_config_exit;
+ }
}
mdss_pp_res->igc_disp_cfg[disp_num] = *config;
mdss_pp_res->igc_disp_cfg[disp_num].c0_c1_data =
@@ -2360,7 +2549,7 @@
hist_info = &mdss_pp_res->dspp_hist[dspp_num];
mutex_lock(&hist_info->hist_mutex);
for (j = 0; j < HIST_V_SIZE; j++)
- hist_concat[i] += hist_info->data[i];
+ hist_concat[j] += hist_info->data[j];
mutex_unlock(&hist_info->hist_mutex);
}
hist_data_addr = hist_concat;
@@ -2519,3 +2708,705 @@
}
};
}
+
+#define MDSS_AD_MAX_MIXERS 1
+static int mdss_ad_init_checks(struct msm_fb_data_type *mfd)
+{
+ u32 mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER];
+ u32 mixer_num;
+ u32 ret = -EINVAL;
+ int i = 0;
+ struct mdss_data_type *mdata = mfd_to_mdata(mfd);
+
+ if (!mfd || !mdata)
+ return ret;
+
+ if (mdata->nad_cfgs == 0) {
+ pr_debug("Assertive Display not supported by device");
+ return -ENODEV;
+ }
+
+ if (mfd->panel_info->type == MIPI_CMD_PANEL) {
+ pr_debug("Command panel not supported");
+ return -EINVAL;
+ }
+
+ mixer_num = mdss_mdp_get_ctl_mixers(mfd->index, mixer_id);
+ if (!mixer_num) {
+ pr_debug("no mixers connected, %d", mixer_num);
+ return -EHOSTDOWN;
+ }
+ if (mixer_num > MDSS_AD_MAX_MIXERS) {
+ pr_warn("too many mixers, not supported, %d", mixer_num);
+ return ret;
+ }
+
+ do {
+ if (mixer_id[i] >= mdata->nad_cfgs) {
+ pr_err("invalid mixer input, %d", mixer_id[i]);
+ return ret;
+ }
+ i++;
+ } while (i < mixer_num);
+
+ return mixer_id[0];
+}
+
+static int mdss_mdp_get_ad(struct msm_fb_data_type *mfd,
+ struct mdss_ad_info **ret_ad)
+{
+ int ad_num, ret = 0;
+ struct mdss_data_type *mdata;
+ struct mdss_ad_info *ad = NULL;
+ mdata = mfd_to_mdata(mfd);
+
+ ad_num = mdss_ad_init_checks(mfd);
+ if (ad_num >= 0)
+ ad = &mdata->ad_cfgs[ad_num];
+ else
+ ret = ad_num;
+ *ret_ad = ad;
+ return ret;
+}
+
+static int pp_update_ad_input(struct msm_fb_data_type *mfd)
+{
+ int ret;
+ struct mdss_ad_info *ad;
+ struct mdss_ad_input input;
+ struct mdss_mdp_ctl *ctl;
+
+ if (!mfd)
+ return -EINVAL;
+ ctl = mfd_to_ctl(mfd);
+ if (!ctl)
+ return -EINVAL;
+
+ ret = mdss_mdp_get_ad(mfd, &ad);
+ if (ret)
+ return ret;
+ if (!ad || ad->cfg.mode == MDSS_AD_MODE_AUTO_BL)
+ return -EINVAL;
+
+ pr_debug("backlight level changed (%d), trigger update to AD",
+ mfd->bl_level);
+ input.mode = ad->cfg.mode;
+ if (MDSS_AD_MODE_DATA_MATCH(ad->cfg.mode, MDSS_AD_INPUT_AMBIENT))
+ input.in.amb_light = ad->ad_data;
+ else
+ input.in.strength = ad->ad_data;
+ /* call to ad_input will trigger backlight read */
+ return mdss_mdp_ad_input(mfd, &input, 0);
+}
+
+int mdss_mdp_ad_config(struct msm_fb_data_type *mfd,
+ struct mdss_ad_init_cfg *init_cfg)
+{
+ struct mdss_ad_info *ad;
+ struct mdss_mdp_ctl *ctl;
+ int lin_ret = -1, inv_ret = -1, ret = 0;
+ u32 ratio_temp, shift = 0;
+
+ ret = mdss_mdp_get_ad(mfd, &ad);
+ if (ret)
+ return ret;
+
+ mutex_lock(&ad->lock);
+ if (init_cfg->ops & MDP_PP_AD_INIT) {
+ memcpy(&ad->init, &init_cfg->params.init,
+ sizeof(struct mdss_ad_init));
+ if (init_cfg->params.init.bl_lin_len == AD_BL_LIN_LEN) {
+ lin_ret = copy_from_user(&ad->bl_lin,
+ init_cfg->params.init.bl_lin,
+ AD_BL_LIN_LEN * sizeof(uint32_t));
+ inv_ret = copy_from_user(&ad->bl_lin_inv,
+ init_cfg->params.init.bl_lin_inv,
+ AD_BL_LIN_LEN * sizeof(uint32_t));
+ if (lin_ret || inv_ret)
+ ret = -ENOMEM;
+ ratio_temp = mfd->panel_info->bl_max / AD_BL_LIN_LEN;
+ while (ratio_temp > 0) {
+ ratio_temp = ratio_temp >> 1;
+ shift++;
+ }
+ ad->bl_bright_shift = shift;
+ } else if (init_cfg->params.init.bl_lin_len) {
+ ret = -EINVAL;
+ }
+ if (!lin_ret && !inv_ret)
+ ad->state |= PP_AD_STATE_BL_LIN;
+ else
+ ad->state &= !PP_AD_STATE_BL_LIN;
+
+ ad->sts |= PP_AD_STS_DIRTY_INIT;
+ } else if (init_cfg->ops & MDP_PP_AD_CFG) {
+ memcpy(&ad->cfg, &init_cfg->params.cfg,
+ sizeof(struct mdss_ad_cfg));
+ /*
+ * TODO: specify panel independent range of input from cfg,
+ * scale input backlight_scale to panel bl_max's range
+ */
+ ad->cfg.backlight_scale = mfd->panel_info->bl_max;
+ ad->sts |= PP_AD_STS_DIRTY_CFG;
+ }
+
+ if (!ret && (init_cfg->ops & MDP_PP_OPS_DISABLE)) {
+ ad->sts &= ~PP_STS_ENABLE;
+ mutex_unlock(&ad->lock);
+ cancel_work_sync(&ad->calc_work);
+ mutex_lock(&ad->lock);
+ ad->mfd = NULL;
+ } else if (!ret && (init_cfg->ops & MDP_PP_OPS_ENABLE)) {
+ ad->sts |= PP_STS_ENABLE;
+ ad->mfd = mfd;
+ }
+ mutex_unlock(&ad->lock);
+ ctl = mfd_to_ctl(mfd);
+ if (!ret)
+ mdss_mdp_pp_setup(ctl);
+ return ret;
+}
+
+int mdss_mdp_ad_input(struct msm_fb_data_type *mfd,
+ struct mdss_ad_input *input, int wait) {
+ int ret = 0;
+ struct mdss_ad_info *ad;
+ struct mdss_mdp_ctl *ctl;
+ u32 bl;
+
+ ret = mdss_mdp_get_ad(mfd, &ad);
+ if (ret)
+ return ret;
+
+ mutex_lock(&ad->lock);
+ if ((!PP_AD_STATE_IS_INITCFG(ad->state) &&
+ !PP_AD_STS_IS_DIRTY(ad->sts)) &&
+ !input->mode == MDSS_AD_MODE_CALIB) {
+ pr_warn("AD not initialized or configured.");
+ ret = -EPERM;
+ goto error;
+ }
+ switch (input->mode) {
+ case MDSS_AD_MODE_AUTO_BL:
+ case MDSS_AD_MODE_AUTO_STR:
+ if (!MDSS_AD_MODE_DATA_MATCH(ad->cfg.mode,
+ MDSS_AD_INPUT_AMBIENT)) {
+ ret = -EINVAL;
+ goto error;
+ }
+ ad->ad_data_mode = MDSS_AD_INPUT_AMBIENT;
+
+ ad->ad_data = input->in.amb_light;
+ ad->calc_itr = ad->cfg.stab_itr;
+ ad->sts |= PP_AD_STS_DIRTY_VSYNC;
+ ad->sts |= PP_AD_STS_DIRTY_DATA;
+ break;
+ case MDSS_AD_MODE_TARG_STR:
+ case MDSS_AD_MODE_MAN_STR:
+ if (!MDSS_AD_MODE_DATA_MATCH(ad->cfg.mode,
+ MDSS_AD_INPUT_STRENGTH)) {
+ ret = -EINVAL;
+ goto error;
+ }
+ ad->ad_data_mode = MDSS_AD_INPUT_STRENGTH;
+ ad->ad_data = input->in.strength;
+ ad->calc_itr = ad->cfg.stab_itr;
+ ad->sts |= PP_AD_STS_DIRTY_VSYNC;
+ ad->sts |= PP_AD_STS_DIRTY_DATA;
+ break;
+ case MDSS_AD_MODE_CALIB:
+ wait = 0;
+ if (mfd->calib_mode) {
+ bl = input->in.calib_bl;
+ if (bl >= AD_BL_LIN_LEN) {
+ pr_warn("calib_bl 255 max!");
+ break;
+ }
+ mutex_unlock(&ad->lock);
+ mutex_lock(&mfd->bl_lock);
+ MDSS_BRIGHT_TO_BL(bl, bl, mfd->panel_info->bl_max,
+ MDSS_MAX_BL_BRIGHTNESS);
+ mdss_fb_set_backlight(mfd, bl);
+ mutex_unlock(&mfd->bl_lock);
+ mutex_lock(&ad->lock);
+ } else {
+ pr_warn("should be in calib mode");
+ }
+ break;
+ default:
+ pr_warn("invalid default %d", input->mode);
+ ret = -EINVAL;
+ goto error;
+ }
+error:
+ mutex_unlock(&ad->lock);
+ if (!ret) {
+ if (wait) {
+ mutex_lock(&ad->lock);
+ init_completion(&ad->comp);
+ mutex_unlock(&ad->lock);
+ }
+ ctl = mfd_to_ctl(mfd);
+ mdss_mdp_pp_setup(ctl);
+ if (wait) {
+ ret = wait_for_completion_interruptible_timeout(
+ &ad->comp, HIST_WAIT_TIMEOUT(1));
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+ else if (ret > 0)
+ input->output = ad->last_str;
+ }
+ }
+ return ret;
+}
+
+static void pp_ad_input_write(struct mdss_ad_info *ad, u32 bl_lvl)
+{
+ char __iomem *base = ad->base;
+ switch (ad->cfg.mode) {
+ case MDSS_AD_MODE_AUTO_BL:
+ writel_relaxed(ad->ad_data, base + MDSS_MDP_REG_AD_AL);
+ break;
+ case MDSS_AD_MODE_AUTO_STR:
+ writel_relaxed(bl_lvl, base + MDSS_MDP_REG_AD_BL);
+ writel_relaxed(ad->ad_data, base + MDSS_MDP_REG_AD_AL);
+ break;
+ case MDSS_AD_MODE_TARG_STR:
+ writel_relaxed(bl_lvl, base + MDSS_MDP_REG_AD_BL);
+ writel_relaxed(ad->ad_data, base + MDSS_MDP_REG_AD_TARG_STR);
+ break;
+ case MDSS_AD_MODE_MAN_STR:
+ writel_relaxed(bl_lvl, base + MDSS_MDP_REG_AD_BL);
+ writel_relaxed(ad->ad_data, base + MDSS_MDP_REG_AD_STR_MAN);
+ break;
+ default:
+ pr_warn("Invalid mode! %d", ad->cfg.mode);
+ break;
+ }
+}
+
+static void pp_ad_init_write(struct mdss_ad_info *ad)
+{
+ u32 temp;
+ char __iomem *base = ad->base;
+ writel_relaxed(ad->init.i_control[0] & 0x1F,
+ base + MDSS_MDP_REG_AD_CON_CTRL_0);
+ writel_relaxed(ad->init.i_control[1] << 8,
+ base + MDSS_MDP_REG_AD_CON_CTRL_1);
+
+ temp = ad->init.white_lvl << 16;
+ temp |= ad->init.black_lvl & 0xFFFF;
+ writel_relaxed(temp, base + MDSS_MDP_REG_AD_BW_LVL);
+
+ writel_relaxed(ad->init.var, base + MDSS_MDP_REG_AD_VAR);
+
+ writel_relaxed(ad->init.limit_ampl, base + MDSS_MDP_REG_AD_AMP_LIM);
+
+ writel_relaxed(ad->init.i_dither, base + MDSS_MDP_REG_AD_DITH);
+
+ temp = ad->init.slope_max << 8;
+ temp |= ad->init.slope_min & 0xFF;
+ writel_relaxed(temp, base + MDSS_MDP_REG_AD_SLOPE);
+
+ writel_relaxed(ad->init.dither_ctl, base + MDSS_MDP_REG_AD_DITH_CTRL);
+
+ writel_relaxed(ad->init.format, base + MDSS_MDP_REG_AD_CTRL_0);
+ writel_relaxed(ad->init.auto_size, base + MDSS_MDP_REG_AD_CTRL_1);
+
+ temp = ad->init.frame_w << 16;
+ temp |= ad->init.frame_h & 0xFFFF;
+ writel_relaxed(temp, base + MDSS_MDP_REG_AD_FRAME_SIZE);
+
+ temp = ad->init.logo_v << 8;
+ temp |= ad->init.logo_h & 0xFF;
+ writel_relaxed(temp, base + MDSS_MDP_REG_AD_LOGO_POS);
+
+ pp_ad_cfg_lut(base + MDSS_MDP_REG_AD_LUT_FI, ad->init.asym_lut);
+ pp_ad_cfg_lut(base + MDSS_MDP_REG_AD_LUT_CC, ad->init.color_corr_lut);
+}
+
+#define MDSS_PP_AD_DEF_CALIB 0x6E
+static void pp_ad_cfg_write(struct mdss_ad_info *ad)
+{
+ char __iomem *base = ad->base;
+ u32 temp, temp_calib = MDSS_PP_AD_DEF_CALIB;
+ switch (ad->cfg.mode) {
+ case MDSS_AD_MODE_AUTO_BL:
+ temp = ad->cfg.backlight_max << 16;
+ temp |= ad->cfg.backlight_min & 0xFFFF;
+ writel_relaxed(temp, base + MDSS_MDP_REG_AD_BL_MINMAX);
+ writel_relaxed(ad->cfg.amb_light_min,
+ base + MDSS_MDP_REG_AD_AL_MIN);
+ temp = ad->cfg.filter[1] << 16;
+ temp |= ad->cfg.filter[0] & 0xFFFF;
+ writel_relaxed(temp, base + MDSS_MDP_REG_AD_AL_FILT);
+ case MDSS_AD_MODE_AUTO_STR:
+ pp_ad_cfg_lut(base + MDSS_MDP_REG_AD_LUT_AL,
+ ad->cfg.al_calib_lut);
+ writel_relaxed(ad->cfg.strength_limit,
+ base + MDSS_MDP_REG_AD_STR_LIM);
+ temp = ad->cfg.calib[3] << 16;
+ temp |= ad->cfg.calib[2] & 0xFFFF;
+ writel_relaxed(temp, base + MDSS_MDP_REG_AD_CALIB_CD);
+ writel_relaxed(ad->cfg.t_filter_recursion,
+ base + MDSS_MDP_REG_AD_TFILT_CTRL);
+ temp_calib = ad->cfg.calib[0] & 0xFFFF;
+ case MDSS_AD_MODE_TARG_STR:
+ temp = ad->cfg.calib[1] << 16;
+ temp |= temp_calib;
+ writel_relaxed(temp, base + MDSS_MDP_REG_AD_CALIB_AB);
+ case MDSS_AD_MODE_MAN_STR:
+ writel_relaxed(ad->cfg.backlight_scale,
+ base + MDSS_MDP_REG_AD_BL_MAX);
+ writel_relaxed(ad->cfg.mode, base + MDSS_MDP_REG_AD_MODE_SEL);
+ pr_debug("stab_itr = %d", ad->cfg.stab_itr);
+ break;
+ default:
+ break;
+ }
+}
+
+static void pp_ad_vsync_handler(struct mdss_mdp_ctl *ctl, ktime_t t)
+{
+ struct mdss_data_type *mdata = ctl->mdata;
+ struct mdss_ad_info *ad;
+
+ if (ctl->mixer_left && ctl->mixer_left->num < mdata->nad_cfgs) {
+ ad = &mdata->ad_cfgs[ctl->mixer_left->num];
+ queue_work(mdata->ad_calc_wq, &ad->calc_work);
+ }
+}
+
+#define MDSS_PP_AD_BYPASS_DEF 0x101
+static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd)
+{
+ int ret = 0;
+ struct mdss_ad_info *ad;
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
+ char __iomem *base;
+ u32 bypass = MDSS_PP_AD_BYPASS_DEF, bl;
+
+ ret = mdss_mdp_get_ad(mfd, &ad);
+ if (ret)
+ return ret;
+
+ base = ad->base;
+
+ mutex_lock(&ad->lock);
+ if (ad->sts != last_sts || ad->state != last_state) {
+ last_sts = ad->sts;
+ last_state = ad->state;
+ pr_debug("begining: ad->sts = 0x%08x, state = 0x%08x", ad->sts,
+ ad->state);
+ }
+ if (!PP_AD_STS_IS_DIRTY(ad->sts) &&
+ (ad->sts & PP_AD_STS_DIRTY_DATA)) {
+ /*
+ * Write inputs to regs when the data has been updated or
+ * Assertive Display is up and running as long as there are
+ * no updates to AD init or cfg
+ */
+ ad->sts &= ~PP_AD_STS_DIRTY_DATA;
+ ad->state |= PP_AD_STATE_DATA;
+ bl = 0;
+ if (MDSS_AD_RUNNING_AUTO_STR(ad) || ad->last_bl == 0) {
+ mutex_lock(&mfd->bl_lock);
+ bl = mfd->bl_level;
+ if (bl != ad->last_bl) {
+ ad->last_bl = bl;
+ ad->calc_itr = ad->cfg.stab_itr;
+ ad->sts |= PP_AD_STS_DIRTY_VSYNC;
+ }
+ if (ad->state & PP_AD_STATE_BL_LIN) {
+ bl = ad->bl_lin[bl >> ad->bl_bright_shift];
+ bl = bl << ad->bl_bright_shift;
+ }
+ mutex_unlock(&mfd->bl_lock);
+ }
+ pp_ad_input_write(ad, bl);
+ }
+
+ if (ad->sts & PP_AD_STS_DIRTY_CFG) {
+ ad->sts &= ~PP_AD_STS_DIRTY_CFG;
+ ad->state |= PP_AD_STATE_CFG;
+
+ pp_ad_cfg_write(ad);
+
+ if (!MDSS_AD_MODE_DATA_MATCH(ad->cfg.mode, ad->ad_data_mode)) {
+ ad->sts &= ~PP_AD_STS_DIRTY_DATA;
+ ad->state &= ~PP_AD_STATE_DATA;
+ pr_debug("Mode switched, data invalidated!");
+ }
+ }
+ if (ad->sts & PP_AD_STS_DIRTY_INIT) {
+ ad->sts &= ~PP_AD_STS_DIRTY_INIT;
+ ad->state |= PP_AD_STATE_INIT;
+ pp_ad_init_write(ad);
+ }
+
+ if ((ad->sts & PP_STS_ENABLE) && PP_AD_STATE_IS_READY(ad->state)) {
+ bypass = 0;
+ ret = 1;
+ ad->state |= PP_AD_STATE_RUN;
+ mutex_lock(&mfd->bl_lock);
+ mfd->mdp.update_ad_input = pp_update_ad_input;
+ mfd->ext_bl_ctrl = ad->cfg.bl_ctrl_mode;
+ mutex_unlock(&mfd->bl_lock);
+
+ } else {
+ if (ad->state & PP_AD_STATE_RUN) {
+ ret = 1;
+ /* Clear state and regs when going to off state*/
+ ad->sts = 0;
+ ad->sts |= PP_AD_STS_DIRTY_VSYNC;
+ ad->state &= !PP_AD_STATE_INIT;
+ ad->state &= !PP_AD_STATE_CFG;
+ ad->state &= !PP_AD_STATE_DATA;
+ ad->state &= !PP_AD_STATE_BL_LIN;
+ ad->bl_bright_shift = 0;
+ ad->ad_data = 0;
+ ad->ad_data_mode = 0;
+ ad->calc_itr = 0;
+ memset(&ad->bl_lin, 0, sizeof(uint32_t) *
+ AD_BL_LIN_LEN);
+ memset(&ad->bl_lin_inv, 0, sizeof(uint32_t) *
+ AD_BL_LIN_LEN);
+ memset(&ad->init, 0, sizeof(struct mdss_ad_init));
+ memset(&ad->cfg, 0, sizeof(struct mdss_ad_cfg));
+ mutex_lock(&mfd->bl_lock);
+ mfd->mdp.update_ad_input = NULL;
+ mfd->ext_bl_ctrl = 0;
+ mutex_unlock(&mfd->bl_lock);
+ }
+ ad->state &= ~PP_AD_STATE_RUN;
+ }
+ writel_relaxed(bypass, base);
+
+ if (PP_AD_STS_DIRTY_VSYNC & ad->sts) {
+ pr_debug("dirty vsync, calc_itr = %d", ad->calc_itr);
+ ad->sts &= ~PP_AD_STS_DIRTY_VSYNC;
+ if (!(PP_AD_STATE_VSYNC & ad->state) && ad->calc_itr &&
+ (ad->state & PP_AD_STATE_RUN)) {
+ ctl->add_vsync_handler(ctl, &ad->handle);
+ ad->state |= PP_AD_STATE_VSYNC;
+ } else if ((PP_AD_STATE_VSYNC & ad->state) &&
+ (!ad->calc_itr || !(PP_AD_STATE_RUN & ad->state))) {
+ ctl->remove_vsync_handler(ctl, &ad->handle);
+ ad->state &= ~PP_AD_STATE_VSYNC;
+ }
+ }
+
+ if (ad->sts != last_sts || ad->state != last_state) {
+ last_sts = ad->sts;
+ last_state = ad->state;
+ pr_debug("end: ad->sts = 0x%08x, state = 0x%08x", ad->sts,
+ ad->state);
+ }
+ mutex_unlock(&ad->lock);
+ return ret;
+}
+
+#define MDSS_PP_AD_SLEEP 10
+static void pp_ad_calc_worker(struct work_struct *work)
+{
+ struct mdss_ad_info *ad;
+ struct mdss_mdp_ctl *ctl;
+ struct msm_fb_data_type *mfd;
+ u32 bl, calc_done = 0;
+ ad = container_of(work, struct mdss_ad_info, calc_work);
+
+ mutex_lock(&ad->lock);
+ if (!ad->mfd || !(ad->sts & PP_STS_ENABLE)) {
+ mutex_unlock(&ad->lock);
+ return;
+ }
+ mfd = ad->mfd;
+ ctl = mfd_to_ctl(ad->mfd);
+
+ if (PP_AD_STATE_RUN & ad->state) {
+ /* Kick off calculation */
+ ad->calc_itr--;
+ writel_relaxed(1, ad->base + MDSS_MDP_REG_AD_START_CALC);
+ }
+ if (ad->state & PP_AD_STATE_RUN) {
+ do {
+ calc_done = readl_relaxed(ad->base +
+ MDSS_MDP_REG_AD_CALC_DONE);
+ if (!calc_done)
+ usleep(MDSS_PP_AD_SLEEP);
+ } while (!calc_done && (ad->state & PP_AD_STATE_RUN));
+ if (calc_done) {
+ ad->last_str = 0xFF & readl_relaxed(ad->base +
+ MDSS_MDP_REG_AD_STR_OUT);
+ if (MDSS_AD_RUNNING_AUTO_BL(ad)) {
+ bl = 0xFFFF & readl_relaxed(ad->base +
+ MDSS_MDP_REG_AD_BL_OUT);
+ if (ad->state & PP_AD_STATE_BL_LIN) {
+ bl = bl >> ad->bl_bright_shift;
+ bl = min_t(u32, bl,
+ MDSS_MAX_BL_BRIGHTNESS);
+ bl = ad->bl_lin_inv[bl];
+ bl = bl << ad->bl_bright_shift;
+ }
+ pr_debug("calc bl = %d", bl);
+ ad->last_str |= bl << 16;
+ mutex_lock(&ad->mfd->bl_lock);
+ mdss_fb_set_backlight(ad->mfd, bl);
+ mutex_unlock(&ad->mfd->bl_lock);
+ }
+ pr_debug("calc_str = %d, calc_itr %d",
+ ad->last_str & 0xFF,
+ ad->calc_itr);
+ } else {
+ ad->last_str = 0xFFFFFFFF;
+ }
+ }
+ complete(&ad->comp);
+
+ if (!ad->calc_itr) {
+ ad->state &= ~PP_AD_STATE_VSYNC;
+ ctl->remove_vsync_handler(ctl, &ad->handle);
+ }
+ mutex_unlock(&ad->lock);
+ mutex_lock(&mfd->lock);
+ mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, BIT(13 + ad->num));
+ mutex_unlock(&mfd->lock);
+
+ /* Trigger update notify to wake up those waiting for display updates */
+ mdss_fb_update_notify_update(mfd);
+}
+
+#define PP_AD_LUT_LEN 33
+static void pp_ad_cfg_lut(char __iomem *offset, u32 *data)
+{
+ int i;
+ u32 temp;
+
+ for (i = 0; i < PP_AD_LUT_LEN - 1; i += 2) {
+ temp = data[i+1] << 16;
+ temp |= (data[i] & 0xFFFF);
+ writel_relaxed(temp, offset + (i*2));
+ }
+ writel_relaxed(data[PP_AD_LUT_LEN - 1] << 16,
+ offset + ((PP_AD_LUT_LEN - 1) * 2));
+}
+
+int mdss_mdp_ad_addr_setup(struct mdss_data_type *mdata, u32 *ad_off)
+{
+ u32 i;
+ int rc = 0;
+
+ mdata->ad_cfgs = devm_kzalloc(&mdata->pdev->dev,
+ sizeof(struct mdss_ad_info) * mdata->nad_cfgs,
+ GFP_KERNEL);
+
+ if (!mdata->ad_cfgs) {
+ pr_err("unable to setup assertive display:devm_kzalloc fail\n");
+ return -ENOMEM;
+ }
+
+ mdata->ad_calc_wq = create_singlethread_workqueue("ad_calc_wq");
+ for (i = 0; i < mdata->nad_cfgs; i++) {
+ mdata->ad_cfgs[i].base = mdata->mdp_base + ad_off[i];
+ mdata->ad_cfgs[i].num = i;
+ mdata->ad_cfgs[i].calc_itr = 0;
+ mdata->ad_cfgs[i].last_str = 0xFFFFFFFF;
+ mutex_init(&mdata->ad_cfgs[i].lock);
+ mdata->ad_cfgs[i].handle.vsync_handler = pp_ad_vsync_handler;
+ INIT_WORK(&mdata->ad_cfgs[i].calc_work, pp_ad_calc_worker);
+ }
+ return rc;
+}
+
+static int is_valid_calib_addr(void *addr)
+{
+ int ret = 0;
+ unsigned int ptr;
+ ptr = (unsigned int) addr;
+ /* if request is outside the MDP reg-map or is not aligned 4 */
+ if (ptr == 0x0 || ptr > 0x5138 || ptr % 0x4)
+ goto end;
+ if (ptr >= 0x100 && ptr <= 0x5138) {
+ /* if ptr is in dspp range */
+ if (ptr >= 0x4600 && ptr <= 0x5138) {
+ /* if ptr is in dspp0 range*/
+ if (ptr >= 0x4600 && ptr <= 0x4938)
+ ptr -= 0x4600;
+ /* if ptr is in dspp1 range */
+ else if (ptr >= 0x4a00 && ptr <= 0x4d38)
+ ptr -= 0x4a00;
+ /* if ptr is in dspp2 range */
+ else if (ptr >= 0x4e00 && ptr <= 0x5138)
+ ptr -= 0x4e00;
+ /* if ptr is in pcc plane rgb coeff.range */
+ if (ptr >= 0x30 && ptr <= 0xe8)
+ ret = 1;
+ /* if ptr is in ARLUT red range */
+ else if (ptr >= 0x2b0 && ptr <= 0x2b8)
+ ret = 1;
+ /* if ptr is in PA range */
+ else if (ptr >= 0x238 && ptr <= 0x244)
+ ret = 1;
+ /* if ptr is in ARLUT green range */
+ else if (ptr >= 0x2c0 && ptr <= 0x2c8)
+ ret = 1;
+ /* if ptr is in ARLUT blue range or
+ gamut map table range */
+ else if (ptr >= 0x2d0 && ptr <= 0x338)
+ ret = 1;
+ /* if ptr is dspp0,dspp1,dspp2 op mode
+ register */
+ else if (ptr == 0)
+ ret = 1;
+ } else if (ptr >= 0x600 && ptr <= 0x608)
+ ret = 1;
+ else if (ptr >= 0x400 && ptr <= 0x408)
+ ret = 1;
+ else if ((ptr == 0x1830) || (ptr == 0x1c30) ||
+ (ptr == 0x1430) || (ptr == 0x1e38))
+ ret = 1;
+ else if ((ptr == 0x1e3c) || (ptr == 0x1e30))
+ ret = 1;
+ else if (ptr >= 0x3220 && ptr <= 0x3228)
+ ret = 1;
+ else if (ptr >= 0x3200 || ptr == 0x100)
+ ret = 1;
+ }
+end:
+ return ret;
+}
+
+int mdss_mdp_calib_config(struct mdp_calib_config_data *cfg, u32 *copyback)
+{
+ int ret = -1;
+ void *ptr = (void *) cfg->addr;
+
+ if (is_valid_calib_addr(ptr))
+ ret = 0;
+ else
+ return ret;
+ ptr = (void *)(((unsigned int) ptr) + (mdss_res->mdp_base));
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+
+ if (cfg->ops & MDP_PP_OPS_READ) {
+ cfg->data = readl_relaxed(ptr);
+ *copyback = 1;
+ ret = 0;
+ } else if (cfg->ops & MDP_PP_OPS_WRITE) {
+ writel_relaxed(cfg->data, ptr);
+ ret = 0;
+ }
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ return ret;
+}
+
+int mdss_mdp_calib_mode(struct msm_fb_data_type *mfd,
+ struct mdss_calib_cfg *cfg)
+{
+ if (!mdss_pp_res || !mfd)
+ return -EINVAL;
+ mutex_lock(&mdss_pp_mutex);
+ mfd->calib_mode = cfg->calib_mask;
+ mutex_unlock(&mdss_pp_mutex);
+ return 0;
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.c b/drivers/video/msm/mdss/mdss_mdp_rotator.c
index 016c973..f9894cc 100644
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.c
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.c
@@ -99,6 +99,7 @@
pr_debug("waiting for rot=%d to complete\n", rot->pipe->num);
mdss_mdp_display_wait4comp(ctl);
rot->busy = false;
+ mdss_mdp_smp_release(rot->pipe);
}
mutex_unlock(&rot->lock);
@@ -367,6 +368,7 @@
int mdss_mdp_rotator_release(u32 ndx)
{
+ int rc = 0;
struct mdss_mdp_rotator_session *rot;
mutex_lock(&rotator_lock);
rot = mdss_mdp_rotator_session_get(ndx);
@@ -374,11 +376,11 @@
mdss_mdp_rotator_finish(rot);
} else {
pr_warn("unknown session id=%x\n", ndx);
- return -ENOENT;
+ rc = -ENOENT;
}
mutex_unlock(&rotator_lock);
- return 0;
+ return rc;
}
int mdss_mdp_rotator_release_all(void)
diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c
index 60f05ca..539ed22 100644
--- a/drivers/video/msm/mdss/mdss_mdp_util.c
+++ b/drivers/video/msm/mdss/mdss_mdp_util.c
@@ -27,8 +27,7 @@
#include "mdss_fb.h"
#include "mdss_mdp.h"
#include "mdss_mdp_formats.h"
-
-#define DEFAULT_FRAME_RATE 60
+#include "mdss_debug.h"
enum {
MDP_INTR_VSYNC_INTF_0,
@@ -124,6 +123,7 @@
irqreturn_t mdss_mdp_isr(int irq, void *ptr)
{
+ struct mdss_data_type *mdata = ptr;
u32 isr, mask, hist_isr, hist_mask;
@@ -172,17 +172,25 @@
if (isr & MDSS_MDP_INTR_PING_PONG_2_RD_PTR)
mdss_mdp_intr_done(MDP_INTR_PING_PONG_2_RD_PTR);
- if (isr & MDSS_MDP_INTR_INTF_0_VSYNC)
+ if (isr & MDSS_MDP_INTR_INTF_0_VSYNC) {
mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_0);
+ mdss_misr_crc_collect(mdata, DISPLAY_MISR_EDP);
+ }
- if (isr & MDSS_MDP_INTR_INTF_1_VSYNC)
+ if (isr & MDSS_MDP_INTR_INTF_1_VSYNC) {
mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_1);
+ mdss_misr_crc_collect(mdata, DISPLAY_MISR_DSI0);
+ }
- if (isr & MDSS_MDP_INTR_INTF_2_VSYNC)
+ if (isr & MDSS_MDP_INTR_INTF_2_VSYNC) {
mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_2);
+ mdss_misr_crc_collect(mdata, DISPLAY_MISR_DSI1);
+ }
- if (isr & MDSS_MDP_INTR_INTF_3_VSYNC)
+ if (isr & MDSS_MDP_INTR_INTF_3_VSYNC) {
mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_3);
+ mdss_misr_crc_collect(mdata, DISPLAY_MISR_HDMI);
+ }
if (isr & MDSS_MDP_INTR_WB_0_DONE)
mdss_mdp_intr_done(MDP_INTR_WB_0);
@@ -225,7 +233,6 @@
struct mdss_mdp_format_params *fmt,
struct mdss_mdp_plane_sizes *ps)
{
- u32 stride_off;
if (fmt->is_yuv) {
ps->rau_cnt = DIV_ROUND_UP(w, 64);
ps->ystride[0] = 64 * 4;
@@ -238,6 +245,9 @@
ps->rau_h[1] = 4;
} else
ps->ystride[1] = 32 * 2;
+
+ /* account for both chroma components */
+ ps->ystride[1] <<= 1;
} else if (fmt->fetch_planes == MDSS_MDP_PLANE_INTERLEAVED) {
ps->rau_cnt = DIV_ROUND_UP(w, 32);
ps->ystride[0] = 32 * 4 * fmt->bpp;
@@ -249,11 +259,14 @@
return -EINVAL;
}
- stride_off = DIV_ROUND_UP(ps->rau_cnt, 8);
- ps->ystride[0] = ps->ystride[0] * ps->rau_cnt + stride_off;
- ps->ystride[1] = ps->ystride[1] * ps->rau_cnt + stride_off;
+ ps->ystride[0] *= ps->rau_cnt;
+ ps->ystride[1] *= ps->rau_cnt;
ps->num_planes = 2;
+ pr_debug("BWC rau_cnt=%d strides={%d,%d} heights={%d,%d}\n",
+ ps->rau_cnt, ps->ystride[0], ps->ystride[1],
+ ps->rau_h[0], ps->rau_h[1]);
+
return 0;
}
@@ -262,7 +275,7 @@
{
struct mdss_mdp_format_params *fmt;
int i, rc;
- u32 bpp, ystride0_off, ystride1_off;
+ u32 bpp;
if (ps == NULL)
return -EINVAL;
@@ -277,17 +290,23 @@
memset(ps, 0, sizeof(struct mdss_mdp_plane_sizes));
if (bwc_mode) {
+ u32 height, meta_size;
+
rc = mdss_mdp_get_rau_strides(w, h, fmt, ps);
if (rc)
return rc;
- ystride0_off = DIV_ROUND_UP(h, ps->rau_h[0]);
- ystride1_off = DIV_ROUND_UP(h, ps->rau_h[1]);
- ps->plane_size[0] = (ps->ystride[0] * ystride0_off) +
- (ps->ystride[1] * ystride1_off);
- ps->ystride[0] += ps->ystride[1];
+
+ height = DIV_ROUND_UP(h, ps->rau_h[0]);
+ meta_size = DIV_ROUND_UP(ps->rau_cnt, 8);
+ ps->ystride[1] += meta_size;
+ ps->ystride[0] += ps->ystride[1] + meta_size;
+ ps->plane_size[0] = ps->ystride[0] * height;
+
ps->ystride[1] = 2;
- ps->plane_size[1] = ps->rau_cnt * ps->ystride[1] *
- (ystride0_off + ystride1_off);
+ ps->plane_size[1] = 2 * ps->rau_cnt * height;
+
+ pr_debug("BWC data stride=%d size=%d meta size=%d\n",
+ ps->ystride[0], ps->plane_size[0], ps->plane_size[1]);
} else {
if (fmt->fetch_planes == MDSS_MDP_PLANE_INTERLEAVED) {
ps->num_planes = 1;
@@ -520,6 +539,11 @@
}
}
+ if (!*start) {
+ pr_err("start address is zero!\n");
+ return -ENOMEM;
+ }
+
if (!ret && (img->offset < data->len)) {
data->addr += img->offset;
data->len -= img->offset;
@@ -532,26 +556,3 @@
return ret;
}
-
-u32 mdss_get_panel_framerate(struct msm_fb_data_type *mfd)
-{
- u32 frame_rate = DEFAULT_FRAME_RATE;
- u32 pixel_total;
- struct mdss_panel_info *panel_info = mfd->panel_info;
-
- if (panel_info->type == MIPI_VIDEO_PANEL) {
- frame_rate = panel_info->mipi.frame_rate;
- } else {
- pixel_total = (panel_info->lcdc.h_back_porch +
- panel_info->lcdc.h_front_porch +
- panel_info->lcdc.h_pulse_width +
- panel_info->xres) *
- (panel_info->lcdc.v_back_porch +
- panel_info->lcdc.v_front_porch +
- panel_info->lcdc.v_pulse_width +
- panel_info->yres);
- if (pixel_total)
- frame_rate = panel_info->clk_rate / pixel_total;
- }
- return frame_rate;
-}
diff --git a/drivers/video/msm/mdss/mdss_mdp_wb.c b/drivers/video/msm/mdss/mdss_mdp_wb.c
index 7ccf1b9..0bb68f9 100644
--- a/drivers/video/msm/mdss/mdss_mdp_wb.c
+++ b/drivers/video/msm/mdss/mdss_mdp_wb.c
@@ -194,6 +194,7 @@
{
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_mdp_wb *wb = mfd_to_wb(mfd);
+ int rc = 0;
mutex_lock(&mdss_mdp_wb_buf_lock);
if (wb == NULL) {
@@ -202,7 +203,8 @@
mdp5_data->wb = wb;
} else if (mfd->index != wb->fb_ndx) {
pr_err("only one writeback intf supported at a time\n");
- return -EMLINK;
+ rc = -EMLINK;
+ goto error;
} else {
pr_debug("writeback already initialized\n");
}
@@ -217,8 +219,9 @@
init_waitqueue_head(&wb->wait_q);
mdp5_data->wb = wb;
+error:
mutex_unlock(&mdss_mdp_wb_buf_lock);
- return 0;
+ return rc;
}
static int mdss_mdp_wb_terminate(struct msm_fb_data_type *mfd)
@@ -248,8 +251,8 @@
if (wb->secure_pipe)
mdss_mdp_pipe_destroy(wb->secure_pipe);
mutex_unlock(&wb->lock);
-
- mdp5_data->ctl->is_secure = false;
+ if (mdp5_data->ctl)
+ mdp5_data->ctl->is_secure = false;
mdp5_data->wb = NULL;
mutex_unlock(&mdss_mdp_wb_buf_lock);
diff --git a/drivers/video/msm/mdss/mdss_panel.h b/drivers/video/msm/mdss/mdss_panel.h
index d230100..e8a6312 100644
--- a/drivers/video/msm/mdss/mdss_panel.h
+++ b/drivers/video/msm/mdss/mdss_panel.h
@@ -23,6 +23,8 @@
u16 type;
};
+#define DEFAULT_FRAME_RATE 60
+
/* panel type list */
#define NO_PANEL 0xffff /* No Panel */
#define MDDI_PANEL 1 /* MDDI */
@@ -55,8 +57,44 @@
MAX_PHYS_TARGET_NUM,
};
+/**
+ * enum mdss_intf_events - Different events generated by MDP core
+ *
+ * @MDSS_EVENT_RESET: MDP control path is being (re)initialized.
+ * @MDSS_EVENT_UNBLANK: Sent before first frame update from MDP is
+ * sent to panel.
+ * @MDSS_EVENT_PANEL_ON: After first frame update from MDP.
+ * @MDSS_EVENT_BLANK: MDP has no contents to display only blank screen
+ * is shown in panel. Sent before panel off.
+ * @MDSS_EVENT_PANEL_OFF: MDP has suspended frame updates, panel should be
+ * completely shutdown after this call.
+ * @MDSS_EVENT_CLOSE: MDP has tore down entire session.
+ * @MDSS_EVENT_SUSPEND: Propagation of power suspend event.
+ * @MDSS_EVENT_RESUME: Propagation of power resume event.
+ * @MDSS_EVENT_CHECK_PARAMS: Event generated when a panel reconfiguration is
+ * requested including when resolution changes.
+ * The event handler receives pointer to
+ * struct mdss_panel_info and should return one of:
+ * - negative if the configuration is invalid
+ * - 0 if there is no panel reconfig needed
+ * - 1 if reconfig is needed to take effect
+ * @MDSS_EVENT_CONT_SPLASH_BEGIN: Special event used to handle transition of
+ * display state from boot loader to panel driver.
+ * The event handler will disable the panel.
+ * @MDSS_EVENT_CONT_SPLASH_FINISH: Special event used to handle transition of
+ * display state from boot loader to panel driver.
+ * The event handler will enable the panel and
+ * vote for the display clocks.
+ * @MDSS_EVENT_FB_REGISTERED: Called after fb dev driver has been registered,
+ * panel driver gets ptr to struct fb_info which
+ * holds fb dev information.
+ * @MDSS_EVENT_PANEL_CLK_CTRL: panel clock control
+ - 0 clock disable
+ - 1 clock enable
+ * @MDSS_EVENT_DSI_CMDLIST_KOFF: kickoff sending dcs command from command list
+ */
enum mdss_intf_events {
- MDSS_EVENT_RESET,
+ MDSS_EVENT_RESET = 1,
MDSS_EVENT_UNBLANK,
MDSS_EVENT_PANEL_ON,
MDSS_EVENT_BLANK,
@@ -65,8 +103,11 @@
MDSS_EVENT_SUSPEND,
MDSS_EVENT_RESUME,
MDSS_EVENT_CHECK_PARAMS,
+ MDSS_EVENT_CONT_SPLASH_BEGIN,
MDSS_EVENT_CONT_SPLASH_FINISH,
MDSS_EVENT_FB_REGISTERED,
+ MDSS_EVENT_PANEL_CLK_CTRL,
+ MDSS_EVENT_DSI_CMDLIST_KOFF,
};
struct lcd_panel_info {
@@ -195,7 +236,7 @@
u32 out_format;
u32 vic; /* video identification code */
int bklt_ctrl; /* backlight ctrl */
- int pwm_gpio;
+ int pwm_pmic_gpio;
int pwm_lpg_chan;
int pwm_period;
@@ -214,12 +255,75 @@
void (*set_backlight) (struct mdss_panel_data *pdata, u32 bl_level);
unsigned char *mmss_cc_base;
- /* function entry chain */
+ /**
+ * event_handler() - callback handler for MDP core events
+ * @pdata: Pointer refering to the panel struct associated to this
+ * event. Can be used to retrieve panel info.
+ * @e: Event being generated, see enum mdss_intf_events
+ * @arg: Optional argument to pass some info from some events.
+ *
+ * Used to register handler to be used to propagate different events
+ * happening in MDP core driver. Panel driver can listen for any of
+ * these events to perform appropriate actions for panel initialization
+ * and teardown.
+ */
int (*event_handler) (struct mdss_panel_data *pdata, int e, void *arg);
struct mdss_panel_data *next;
};
+/**
+ * mdss_get_panel_framerate() - get panel frame rate based on panel information
+ * @panel_info: Pointer to panel info containing all panel information
+ */
+static inline u32 mdss_panel_get_framerate(struct mdss_panel_info *panel_info)
+{
+ u32 frame_rate, pixel_total;
+
+ if (panel_info == NULL)
+ return DEFAULT_FRAME_RATE;
+
+ switch (panel_info->type) {
+ case MIPI_VIDEO_PANEL:
+ case MIPI_CMD_PANEL:
+ frame_rate = panel_info->mipi.frame_rate;
+ break;
+ case WRITEBACK_PANEL:
+ frame_rate = DEFAULT_FRAME_RATE;
+ break;
+ default:
+ pixel_total = (panel_info->lcdc.h_back_porch +
+ panel_info->lcdc.h_front_porch +
+ panel_info->lcdc.h_pulse_width +
+ panel_info->xres) *
+ (panel_info->lcdc.v_back_porch +
+ panel_info->lcdc.v_front_porch +
+ panel_info->lcdc.v_pulse_width +
+ panel_info->yres);
+ if (pixel_total)
+ frame_rate = panel_info->clk_rate / pixel_total;
+ else
+ frame_rate = DEFAULT_FRAME_RATE;
+
+ break;
+ }
+ return frame_rate;
+}
+
+/*
+ * mdss_panel_get_vtotal() - return panel vertical height
+ * @pinfo: Pointer to panel info containing all panel information
+ *
+ * Returns the total height of the panel including any blanking regions
+ * which are not visible to user but used to calculate panel pixel clock.
+ */
+static inline int mdss_panel_get_vtotal(struct mdss_panel_info *pinfo)
+{
+ return pinfo->yres + pinfo->lcdc.v_back_porch +
+ pinfo->lcdc.v_front_porch +
+ pinfo->lcdc.v_pulse_width;
+}
+
int mdss_register_panel(struct platform_device *pdev,
struct mdss_panel_data *pdata);
#endif /* MDSS_PANEL_H */
diff --git a/drivers/video/msm/mdss/mhl_msc.c b/drivers/video/msm/mdss/mhl_msc.c
index 96e8b67..15811bb 100644
--- a/drivers/video/msm/mdss/mhl_msc.c
+++ b/drivers/video/msm/mdss/mhl_msc.c
@@ -169,6 +169,7 @@
cmd_env = vmalloc(sizeof(struct msc_cmd_envelope));
if (!cmd_env) {
pr_err("%s: out of memory!\n", __func__);
+ mutex_unlock(&msc_send_workqueue_mutex);
return -ENOMEM;
}
@@ -200,6 +201,22 @@
return 0;
}
+int mhl_msc_clear(struct mhl_tx_ctrl *mhl_ctrl)
+{
+ if (!mhl_ctrl)
+ return -EFAULT;
+
+ memset(mhl_ctrl->devcap, 0, 16);
+ mhl_ctrl->devcap_state = 0;
+ mhl_ctrl->path_en_state = 0;
+ mhl_ctrl->status[0] = 0;
+ mhl_ctrl->status[1] = 0;
+ mhl_ctrl->scrpd_busy = 0;
+ mhl_ctrl->wr_burst_pending = 0;
+
+ return 0;
+}
+
int mhl_msc_command_done(struct mhl_tx_ctrl *mhl_ctrl,
struct msc_command_struct *req)
{
@@ -540,7 +557,7 @@
* connected device bits
* changed and DEVCAP READY
*/
- if (((value ^ mhl_ctrl->devcap_state) &
+ if (((value ^ mhl_ctrl->status[offset]) &
MHL_STATUS_DCAP_RDY)) {
if (value & MHL_STATUS_DCAP_RDY) {
mhl_ctrl->devcap_state = 0;
@@ -562,7 +579,7 @@
* bit set
*/
tmds_en = mhl_check_tmds_enabled(mhl_ctrl);
- if ((value ^ mhl_ctrl->path_en_state)
+ if ((value ^ mhl_ctrl->status[offset])
& MHL_STATUS_PATH_ENABLED) {
if (value & MHL_STATUS_PATH_ENABLED) {
if (tmds_en &&
@@ -592,7 +609,7 @@
}
break;
}
- mhl_ctrl->path_en_state = value;
+ mhl_ctrl->status[offset] = value;
return 0;
}
diff --git a/drivers/video/msm/mdss/mhl_msc.h b/drivers/video/msm/mdss/mhl_msc.h
index 8a1fd39..3137f17 100644
--- a/drivers/video/msm/mdss/mhl_msc.h
+++ b/drivers/video/msm/mdss/mhl_msc.h
@@ -25,6 +25,8 @@
/******************************************************************/
/* the below APIs are implemented by the MSC functionality */
+int mhl_msc_clear(struct mhl_tx_ctrl *mhl_ctrl);
+
int mhl_msc_command_done(struct mhl_tx_ctrl *mhl_ctrl,
struct msc_command_struct *req);
diff --git a/drivers/video/msm/mdss/mhl_sii8334.c b/drivers/video/msm/mdss/mhl_sii8334.c
index a3a1a4e..3f03725 100644
--- a/drivers/video/msm/mdss/mhl_sii8334.c
+++ b/drivers/video/msm/mdss/mhl_sii8334.c
@@ -190,7 +190,7 @@
static irqreturn_t mhl_tx_isr(int irq, void *dev_id);
static void switch_mode(struct mhl_tx_ctrl *mhl_ctrl,
- enum mhl_st_type to_mode);
+ enum mhl_st_type to_mode, bool hpd_off);
static void mhl_init_reg_settings(struct mhl_tx_ctrl *mhl_ctrl,
bool mhl_disc_en);
@@ -361,7 +361,7 @@
pr_debug("%s:%u\n", __func__, __LINE__);
INIT_COMPLETION(mhl_ctrl->rgnd_done);
timeout = wait_for_completion_interruptible_timeout
- (&mhl_ctrl->rgnd_done, HZ/2);
+ (&mhl_ctrl->rgnd_done, HZ);
if (!timeout) {
/* most likely nothing plugged in USB */
/* USB HOST connected or already in USB mode */
@@ -377,6 +377,12 @@
{
int rc;
struct mhl_tx_ctrl *mhl_ctrl = data;
+ struct i2c_client *client = mhl_ctrl->i2c_handle;
+ unsigned long flags;
+
+ enable_irq(client->irq);
+ /* wait for i2c interrupt line to be activated */
+ msleep(300);
if (id) {
/* When MHL cable is disconnected we get a sii8334
@@ -396,11 +402,16 @@
mhl_ctrl->notify_usb_online = usb_notify_cb;
if (!mhl_ctrl->disc_enabled) {
+ spin_lock_irqsave(&mhl_ctrl->lock, flags);
+ mhl_ctrl->tx_powered_off = false;
+ spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
mhl_sii_reset_pin(mhl_ctrl, 0);
msleep(50);
mhl_sii_reset_pin(mhl_ctrl, 1);
- /* TX PR-guide requires a 100 ms wait here */
- msleep(100);
+ /* chipset PR recommends waiting for at least 100 ms
+ * the chipset needs longer to come out of D3 state.
+ */
+ msleep(300);
mhl_init_reg_settings(mhl_ctrl, true);
rc = mhl_sii_wait_for_rgnd(mhl_ctrl);
} else {
@@ -690,8 +701,8 @@
MHL_SII_PAGE3_WR(0x3C, 0x80);
- if (mhl_ctrl->cur_state != POWER_STATE_D3)
- MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT6 | BIT5 | BIT4, BIT4);
+ MHL_SII_REG_NAME_MOD(REG_INT_CTRL,
+ (BIT6 | BIT5 | BIT4), (BIT6 | BIT4));
/* Enable Auto Soft RESET */
MHL_SII_REG_NAME_WR(REG_SRST, 0x084);
@@ -703,9 +714,18 @@
}
-static void switch_mode(struct mhl_tx_ctrl *mhl_ctrl, enum mhl_st_type to_mode)
+static void switch_mode(struct mhl_tx_ctrl *mhl_ctrl, enum mhl_st_type to_mode,
+ bool hpd_off)
{
struct i2c_client *client = mhl_ctrl->i2c_handle;
+ unsigned long flags;
+ int rc;
+ struct msm_hdmi_mhl_ops *hdmi_mhl_ops = mhl_ctrl->hdmi_mhl_ops;
+
+ pr_debug("%s: tx pwr on\n", __func__);
+ spin_lock_irqsave(&mhl_ctrl->lock, flags);
+ mhl_ctrl->tx_powered_off = false;
+ spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
switch (to_mode) {
case POWER_STATE_D0_NO_MHL:
@@ -722,8 +742,11 @@
mhl_ctrl->cur_state = to_mode;
break;
case POWER_STATE_D3:
- if (mhl_ctrl->cur_state == POWER_STATE_D3)
+ if (mhl_ctrl->cur_state == POWER_STATE_D3) {
+ pr_debug("%s: mhl tx already in low power mode\n",
+ __func__);
break;
+ }
/* Force HPD to 0 when not in MHL mode. */
mhl_drive_hpd(mhl_ctrl, HPD_DOWN);
@@ -736,7 +759,12 @@
msleep(50);
if (!mhl_ctrl->disc_enabled)
MHL_SII_REG_NAME_MOD(REG_DISC_CTRL1, BIT1 | BIT0, 0x00);
- MHL_SII_PAGE3_MOD(0x003D, BIT0, 0x00);
+ if (hdmi_mhl_ops && hpd_off) {
+ rc = hdmi_mhl_ops->set_upstream_hpd(
+ mhl_ctrl->pdata->hdmi_pdev, 0);
+ pr_debug("%s: hdmi unset hpd %s\n", __func__,
+ rc ? "failed" : "passed");
+ }
mhl_ctrl->cur_state = POWER_STATE_D3;
break;
default:
@@ -744,6 +772,22 @@
}
}
+static bool is_mhl_powered(void *mhl_ctx)
+{
+ struct mhl_tx_ctrl *mhl_ctrl = (struct mhl_tx_ctrl *)mhl_ctx;
+ unsigned long flags;
+ bool r = false;
+
+ spin_lock_irqsave(&mhl_ctrl->lock, flags);
+ if (mhl_ctrl->tx_powered_off)
+ r = false;
+ else
+ r = true;
+ spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
+
+ pr_debug("%s: ret pwr state as %x\n", __func__, r);
+ return r;
+}
void mhl_tmds_ctrl(struct mhl_tx_ctrl *mhl_ctrl, uint8_t on)
{
@@ -759,6 +803,7 @@
void mhl_drive_hpd(struct mhl_tx_ctrl *mhl_ctrl, uint8_t to_state)
{
struct i2c_client *client = mhl_ctrl->i2c_handle;
+ unsigned long flags;
pr_debug("%s: To state=[0x%x]\n", __func__, to_state);
if (to_state == HPD_UP) {
@@ -769,10 +814,13 @@
* propogate to src let HPD float by clearing
* HPD OUT OVRRD EN
*/
- MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT4, 0x00);
+ spin_lock_irqsave(&mhl_ctrl->lock, flags);
+ mhl_ctrl->tx_powered_off = false;
+ spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
+ MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT4, 0);
} else {
/* Drive HPD to DOWN state */
- MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT4, BIT4);
+ MHL_SII_REG_NAME_MOD(REG_INT_CTRL, (BIT4 | BIT5), BIT4);
}
}
@@ -789,9 +837,7 @@
pr_err("%s: cur st not D0\n", __func__);
return;
}
- /* spin_lock_irqsave(&mhl_state_lock, flags); */
- switch_mode(mhl_ctrl, POWER_STATE_D0_MHL);
- /* spin_unlock_irqrestore(&mhl_state_lock, flags); */
+ switch_mode(mhl_ctrl, POWER_STATE_D0_MHL, true);
MHL_SII_REG_NAME_WR(REG_MHLTX_CTL1, 0x10);
MHL_SII_CBUS_WR(0x07, 0xF2);
@@ -823,19 +869,25 @@
static void mhl_msm_disconnection(struct mhl_tx_ctrl *mhl_ctrl)
{
struct i2c_client *client = mhl_ctrl->i2c_handle;
- /*
- * MHL TX CTL1
- * Disabling Tx termination
- */
- MHL_SII_PAGE3_WR(0x30, 0xD0);
- switch_mode(mhl_ctrl, POWER_STATE_D3);
+ /* disabling Tx termination */
+ MHL_SII_REG_NAME_WR(REG_MHLTX_CTL1, 0xD0);
+ switch_mode(mhl_ctrl, POWER_STATE_D3, true);
+ mhl_msc_clear(mhl_ctrl);
}
-static int mhl_msm_read_rgnd_int(struct mhl_tx_ctrl *mhl_ctrl)
+static int mhl_msm_read_rgnd_int(struct mhl_tx_ctrl *mhl_ctrl)
{
uint8_t rgnd_imp;
struct i2c_client *client = mhl_ctrl->i2c_handle;
+ struct msm_hdmi_mhl_ops *hdmi_mhl_ops = mhl_ctrl->hdmi_mhl_ops;
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&mhl_ctrl->lock, flags);
+ mhl_ctrl->tx_powered_off = false;
+ spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
+
/* DISC STATUS REG 2 */
rgnd_imp = (mhl_i2c_reg_read(client, TX_PAGE_3, 0x001C) &
(BIT1 | BIT0));
@@ -843,6 +895,12 @@
if (0x02 == rgnd_imp) {
pr_debug("%s: mhl sink\n", __func__);
+ if (hdmi_mhl_ops) {
+ rc = hdmi_mhl_ops->set_upstream_hpd(
+ mhl_ctrl->pdata->hdmi_pdev, 1);
+ pr_debug("%s: hdmi set hpd %s\n", __func__,
+ rc ? "failed" : "passed");
+ }
mhl_ctrl->mhl_mode = 1;
power_supply_changed(&mhl_ctrl->mhl_psy);
if (mhl_ctrl->notify_usb_online)
@@ -850,7 +908,7 @@
} else {
pr_debug("%s: non-mhl sink\n", __func__);
mhl_ctrl->mhl_mode = 0;
- switch_mode(mhl_ctrl, POWER_STATE_D3);
+ switch_mode(mhl_ctrl, POWER_STATE_D3, true);
}
complete(&mhl_ctrl->rgnd_done);
return mhl_ctrl->mhl_mode ?
@@ -904,9 +962,9 @@
}
-static void dev_detect_isr(struct mhl_tx_ctrl *mhl_ctrl)
+static int dev_detect_isr(struct mhl_tx_ctrl *mhl_ctrl)
{
- uint8_t status, reg ;
+ uint8_t status, reg;
struct i2c_client *client = mhl_ctrl->i2c_handle;
/* INTR_STATUS4 */
@@ -915,14 +973,14 @@
if ((0x00 == status) &&\
(mhl_ctrl->cur_state == POWER_STATE_D3)) {
- pr_err("%s: invalid intr\n", __func__);
- return;
+ pr_warn("%s: invalid intr\n", __func__);
+ return 0;
}
if (0xFF == status) {
- pr_debug("%s: invalid intr 0xff\n", __func__);
+ pr_warn("%s: invalid intr 0xff\n", __func__);
MHL_SII_REG_NAME_WR(REG_INTR4, status);
- return;
+ return 0;
}
if ((status & BIT0) && (mhl_ctrl->chip_rev_id < 1)) {
@@ -940,31 +998,36 @@
mhl_msm_connection(mhl_ctrl);
} else if (status & BIT3) {
pr_debug("%s: uUSB-a type dev detct\n", __func__);
+
/* Short RGND */
MHL_SII_REG_NAME_MOD(REG_DISC_STAT2, BIT0 | BIT1, 0x00);
mhl_msm_disconnection(mhl_ctrl);
power_supply_changed(&mhl_ctrl->mhl_psy);
if (mhl_ctrl->notify_usb_online)
mhl_ctrl->notify_usb_online(0);
+ return 0;
}
if (status & BIT5) {
/* clr intr - reg int4 */
pr_debug("%s: mhl discon: int4 st=%02X\n", __func__,
(int)status);
+ mhl_ctrl->mhl_det_discon = true;
+
reg = MHL_SII_REG_NAME_RD(REG_INTR4);
MHL_SII_REG_NAME_WR(REG_INTR4, reg);
mhl_msm_disconnection(mhl_ctrl);
power_supply_changed(&mhl_ctrl->mhl_psy);
if (mhl_ctrl->notify_usb_online)
mhl_ctrl->notify_usb_online(0);
+ return 0;
}
if ((mhl_ctrl->cur_state != POWER_STATE_D0_NO_MHL) &&\
(status & BIT6)) {
/* rgnd rdy Intr */
pr_debug("%s: rgnd ready intr\n", __func__);
- switch_mode(mhl_ctrl, POWER_STATE_D0_NO_MHL);
+ switch_mode(mhl_ctrl, POWER_STATE_D0_NO_MHL, true);
mhl_msm_read_rgnd_int(mhl_ctrl);
}
@@ -981,6 +1044,7 @@
release_usb_switch_open(mhl_ctrl);
}
MHL_SII_REG_NAME_WR(REG_INTR4, status);
+ return 0;
}
static void mhl_misc_isr(struct mhl_tx_ctrl *mhl_ctrl)
@@ -997,13 +1061,42 @@
MHL_SII_REG_NAME_WR(REG_INTR5, intr_5_stat);
}
+static void mhl_tx_down(struct mhl_tx_ctrl *mhl_ctrl)
+{
+ struct i2c_client *client = mhl_ctrl->i2c_handle;
+ unsigned long flags;
+ uint8_t reg;
+
+ switch_mode(mhl_ctrl, POWER_STATE_D3, true);
+
+ reg = MHL_SII_REG_NAME_RD(REG_INTR1);
+ MHL_SII_REG_NAME_WR(REG_INTR1, reg);
+
+ reg = MHL_SII_REG_NAME_RD(REG_INTR4);
+ MHL_SII_REG_NAME_WR(REG_INTR4, reg);
+
+ /* disable INTR1 and INTR4 */
+ MHL_SII_REG_NAME_MOD(REG_INTR1_MASK, BIT6, 0x0);
+ MHL_SII_REG_NAME_MOD(REG_INTR4_MASK,
+ (BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6), 0x0);
+
+ MHL_SII_PAGE1_MOD(0x003D, BIT0, 0x00);
+ spin_lock_irqsave(&mhl_ctrl->lock, flags);
+ mhl_ctrl->tx_powered_off = true;
+ spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
+ pr_debug("%s: disabled\n", __func__);
+ disable_irq_nosync(client->irq);
+}
static void mhl_hpd_stat_isr(struct mhl_tx_ctrl *mhl_ctrl)
{
- uint8_t intr_1_stat;
- uint8_t cbus_stat;
+ uint8_t intr_1_stat, cbus_stat, t;
+ unsigned long flags;
struct i2c_client *client = mhl_ctrl->i2c_handle;
+ if (!is_mhl_powered(mhl_ctrl))
+ return;
+
/* INTR STATUS 1 */
intr_1_stat = MHL_SII_PAGE0_RD(0x0071);
@@ -1012,6 +1105,7 @@
/* Clear interrupts */
MHL_SII_PAGE0_WR(0x0071, intr_1_stat);
+
if (BIT6 & intr_1_stat) {
/*
* HPD status change event is pending
@@ -1019,11 +1113,30 @@
* MSC REQ ABRT REASON
*/
cbus_stat = MHL_SII_CBUS_RD(0x0D);
- if (BIT6 & cbus_stat)
- mhl_drive_hpd(mhl_ctrl, HPD_UP);
- else
- mhl_drive_hpd(mhl_ctrl, HPD_DOWN);
+ pr_debug("%s: cbus_stat=[0x%02x] cur_pwr=[%u]\n",
+ __func__, cbus_stat, mhl_ctrl->cur_state);
+ spin_lock_irqsave(&mhl_ctrl->lock, flags);
+ t = mhl_ctrl->dwnstream_hpd;
+ pr_debug("%s: %u: dwnstrm_hpd=0x%02x\n",
+ __func__, __LINE__, mhl_ctrl->dwnstream_hpd);
+ spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
+
+ if (BIT6 & (cbus_stat ^ t)) {
+ u8 status = cbus_stat & BIT6;
+ mhl_drive_hpd(mhl_ctrl, status ? HPD_UP : HPD_DOWN);
+ if (!status && mhl_ctrl->mhl_det_discon) {
+ pr_debug("%s:%u: power_down\n",
+ __func__, __LINE__);
+ mhl_tx_down(mhl_ctrl);
+ }
+ spin_lock_irqsave(&mhl_ctrl->lock, flags);
+ mhl_ctrl->dwnstream_hpd = cbus_stat;
+ pr_debug("%s: %u: dwnstrm_hpd=0x%02x\n",
+ __func__, __LINE__, mhl_ctrl->dwnstream_hpd);
+ spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
+ mhl_ctrl->mhl_det_discon = false;
+ }
}
}
@@ -1176,6 +1289,7 @@
uint8_t cmd_data = 0x0;
int msc_msg_recved = 0;
int rc = -1;
+ unsigned long flags;
struct i2c_client *client = mhl_ctrl->i2c_handle;
regval = MHL_SII_REG_NAME_RD(REG_CBUS_INTR_STATUS);
@@ -1216,6 +1330,14 @@
intr = MHL_SII_REG_NAME_RD(REG_CBUS_SET_INT_0);
MHL_SII_REG_NAME_WR(REG_CBUS_SET_INT_0, intr);
mhl_msc_recv_set_int(mhl_ctrl, 0, intr);
+ if (intr & MHL_INT_DCAP_CHG) {
+ /* No need to go to low power mode */
+ spin_lock_irqsave(&mhl_ctrl->lock, flags);
+ mhl_ctrl->dwnstream_hpd = 0x00;
+ pr_debug("%s: %u: dwnstrm_hpd=0x%02x\n",
+ __func__, __LINE__, mhl_ctrl->dwnstream_hpd);
+ spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
+ }
pr_debug("%s: MHL_INT_0 = %02x\n", __func__, intr);
intr = MHL_SII_REG_NAME_RD(REG_CBUS_SET_INT_1);
@@ -1257,151 +1379,47 @@
}
-static void clear_all_intrs(struct i2c_client *client)
-{
- uint8_t regval = 0x00;
-
- pr_debug_intr("********* exiting isr mask check ?? *************\n");
- pr_debug_intr("int1 mask = %02X\n",
- (int) MHL_SII_REG_NAME_RD(REG_INTR1));
- pr_debug_intr("int3 mask = %02X\n",
- (int) MHL_SII_PAGE0_RD(0x0077));
- pr_debug_intr("int4 mask = %02X\n",
- (int) MHL_SII_REG_NAME_RD(REG_INTR4));
- pr_debug_intr("int5 mask = %02X\n",
- (int) MHL_SII_REG_NAME_RD(REG_INTR5));
- pr_debug_intr("cbus1 mask = %02X\n",
- (int) MHL_SII_CBUS_RD(0x0009));
- pr_debug_intr("cbus2 mask = %02X\n",
- (int) MHL_SII_CBUS_RD(0x001F));
- pr_debug_intr("********* end of isr mask check *************\n");
-
- regval = MHL_SII_REG_NAME_RD(REG_INTR1);
- pr_debug_intr("int1 st = %02X\n", (int)regval);
- MHL_SII_REG_NAME_WR(REG_INTR1, regval);
-
- regval = MHL_SII_REG_NAME_RD(REG_INTR2);
- pr_debug_intr("int2 st = %02X\n", (int)regval);
- MHL_SII_REG_NAME_WR(REG_INTR2, regval);
-
- regval = MHL_SII_PAGE0_RD(0x0073);
- pr_debug_intr("int3 st = %02X\n", (int)regval);
- MHL_SII_PAGE0_WR(0x0073, regval);
-
- regval = MHL_SII_REG_NAME_RD(REG_INTR4);
- pr_debug_intr("int4 st = %02X\n", (int)regval);
- MHL_SII_REG_NAME_WR(REG_INTR4, regval);
-
- regval = MHL_SII_REG_NAME_RD(REG_INTR5);
- pr_debug_intr("int5 st = %02X\n", (int)regval);
- MHL_SII_REG_NAME_WR(REG_INTR5, regval);
-
- regval = MHL_SII_CBUS_RD(0x0008);
- pr_debug_intr("cbusInt st = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x0008, regval);
-
- regval = MHL_SII_CBUS_RD(0x001E);
- pr_debug_intr("CBUS intR_2: %d\n", (int)regval);
- MHL_SII_CBUS_WR(0x001E, regval);
-
- regval = MHL_SII_CBUS_RD(0x00A0);
- pr_debug_intr("A0 int set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00A0, regval);
-
- regval = MHL_SII_CBUS_RD(0x00A1);
- pr_debug_intr("A1 int set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00A1, regval);
-
- regval = MHL_SII_CBUS_RD(0x00A2);
- pr_debug_intr("A2 int set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00A2, regval);
-
- regval = MHL_SII_CBUS_RD(0x00A3);
- pr_debug_intr("A3 int set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00A3, regval);
-
- regval = MHL_SII_CBUS_RD(0x00B0);
- pr_debug_intr("B0 st set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00B0, regval);
-
- regval = MHL_SII_CBUS_RD(0x00B1);
- pr_debug_intr("B1 st set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00B1, regval);
-
- regval = MHL_SII_CBUS_RD(0x00B2);
- pr_debug_intr("B2 st set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00B2, regval);
-
- regval = MHL_SII_CBUS_RD(0x00B3);
- pr_debug_intr("B3 st set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00B3, regval);
-
- regval = MHL_SII_CBUS_RD(0x00E0);
- pr_debug_intr("E0 st set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00E0, regval);
-
- regval = MHL_SII_CBUS_RD(0x00E1);
- pr_debug_intr("E1 st set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00E1, regval);
-
- regval = MHL_SII_CBUS_RD(0x00E2);
- pr_debug_intr("E2 st set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00E2, regval);
-
- regval = MHL_SII_CBUS_RD(0x00E3);
- pr_debug_intr("E3 st set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00E3, regval);
-
- regval = MHL_SII_CBUS_RD(0x00F0);
- pr_debug_intr("F0 int set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00F0, regval);
-
- regval = MHL_SII_CBUS_RD(0x00F1);
- pr_debug_intr("F1 int set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00F1, regval);
-
- regval = MHL_SII_CBUS_RD(0x00F2);
- pr_debug_intr("F2 int set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00F2, regval);
-
- regval = MHL_SII_CBUS_RD(0x00F3);
- pr_debug_intr("F3 int set = %02X\n", (int)regval);
- MHL_SII_CBUS_WR(0x00F3, regval);
- pr_debug_intr("********* end of exiting in isr *************\n");
-}
-
-
static irqreturn_t mhl_tx_isr(int irq, void *data)
{
+ int rc;
struct mhl_tx_ctrl *mhl_ctrl = (struct mhl_tx_ctrl *)data;
+ unsigned long flags;
+
pr_debug("%s: Getting Interrupts\n", __func__);
+ spin_lock_irqsave(&mhl_ctrl->lock, flags);
+ if (mhl_ctrl->tx_powered_off) {
+ pr_warn("%s: powered off\n", __func__);
+ spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
+ return IRQ_HANDLED;
+ }
+ spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
+
/*
* Check RGND, MHL_EST, CBUS_LOCKOUT, SCDT
* interrupts. In D3, we get only RGND
*/
- dev_detect_isr(mhl_ctrl);
+ rc = dev_detect_isr(mhl_ctrl);
+ if (rc)
+ pr_debug("%s: dev_detect_isr rc=[%d]\n", __func__, rc);
pr_debug("%s: cur pwr state is [0x%x]\n",
__func__, mhl_ctrl->cur_state);
- if (mhl_ctrl->cur_state == POWER_STATE_D0_MHL) {
- /*
- * If dev_detect_isr() didn't move the tx to D3
- * on disconnect, continue to check other
- * interrupt sources.
- */
- mhl_misc_isr(mhl_ctrl);
- /*
- * Check for any peer messages for DCAP_CHG, MSC etc
- * Dispatch to have the CBUS module working only
- * once connected.
- */
- mhl_cbus_isr(mhl_ctrl);
- mhl_hpd_stat_isr(mhl_ctrl);
- }
+ /*
+ * If dev_detect_isr() didn't move the tx to D3
+ * on disconnect, continue to check other
+ * interrupt sources.
+ */
+ mhl_misc_isr(mhl_ctrl);
- clear_all_intrs(mhl_ctrl->i2c_handle);
+ /*
+ * Check for any peer messages for DCAP_CHG, MSC etc
+ * Dispatch to have the CBUS module working only
+ * once connected.
+ */
+ mhl_cbus_isr(mhl_ctrl);
+ mhl_hpd_stat_isr(mhl_ctrl);
return IRQ_HANDLED;
}
@@ -1410,6 +1428,13 @@
{
uint8_t chip_rev_id = 0x00;
struct i2c_client *client = mhl_ctrl->i2c_handle;
+ unsigned long flags;
+
+
+ spin_lock_irqsave(&mhl_ctrl->lock, flags);
+ mhl_ctrl->dwnstream_hpd = 0;
+ mhl_ctrl->tx_powered_off = false;
+ spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
/* Reset the TX chip */
mhl_sii_reset_pin(mhl_ctrl, 0);
@@ -1428,7 +1453,9 @@
* MHL-USB handshake is implemented
*/
mhl_init_reg_settings(mhl_ctrl, true);
- switch_mode(mhl_ctrl, POWER_STATE_D3);
+ switch_mode(mhl_ctrl, POWER_STATE_D3, true);
+ pr_debug("%s:%u: power_down\n", __func__, __LINE__);
+ mhl_tx_down(mhl_ctrl);
return 0;
}
@@ -1710,6 +1737,7 @@
mhl_ctrl->cur_state = POWER_STATE_D0_MHL;
INIT_LIST_HEAD(&mhl_ctrl->list_cmd);
init_completion(&mhl_ctrl->msc_cmd_done);
+ spin_lock_init(&mhl_ctrl->lock);
mhl_ctrl->msc_send_workqueue = create_singlethread_workqueue
("mhl_msc_cmd_queue");
@@ -1813,7 +1841,7 @@
pr_debug("%s: i2c client addr is [%x]\n", __func__, client->addr);
if (mhl_ctrl->pdata->hdmi_pdev) {
rc = msm_hdmi_register_mhl(mhl_ctrl->pdata->hdmi_pdev,
- hdmi_mhl_ops);
+ hdmi_mhl_ops, mhl_ctrl);
if (rc) {
pr_err("%s: register with hdmi failed\n", __func__);
rc = -EPROBE_DEFER;
@@ -1905,6 +1933,72 @@
MODULE_DEVICE_TABLE(i2c, mhl_sii_i2c_id);
+#if defined(CONFIG_PM) || defined(CONFIG_PM_SLEEP)
+static int mhl_i2c_suspend_sub(struct i2c_client *client)
+{
+ enable_irq_wake(client->irq);
+ disable_irq(client->irq);
+ return 0;
+}
+
+static int mhl_i2c_resume_sub(struct i2c_client *client)
+{
+ disable_irq_wake(client->irq);
+ enable_irq(client->irq);
+ return 0;
+}
+#endif /* defined(CONFIG_PM) || defined(CONFIG_PM_SLEEP) */
+
+#if defined(CONFIG_PM) && !defined(CONFIG_PM_SLEEP)
+static int mhl_i2c_suspend(struct i2c_client *client, pm_message_t state)
+{
+ if (!client)
+ return -ENODEV;
+ pr_debug("%s: mhl suspend\n", __func__);
+ return mhl_i2c_suspend_sub(client);
+}
+
+static int mhl_i2c_resume(struct i2c_client *client)
+{
+ if (!client)
+ return -ENODEV;
+ pr_debug("%s: mhl resume\n", __func__);
+ return mhl_i2c_resume_sub(client);
+}
+#else
+#define mhl_i2c_suspend NULL
+#define mhl_i2c_resume NULL
+#endif /* defined(CONFIG_PM) && !defined(CONFIG_PM_SLEEP) */
+
+#ifdef CONFIG_PM_SLEEP
+static int mhl_i2c_pm_suspend(struct device *dev)
+{
+ struct i2c_client *client =
+ container_of(dev, struct i2c_client, dev);
+
+ if (!client)
+ return -ENODEV;
+ pr_debug("%s: mhl pm suspend\n", __func__);
+ return mhl_i2c_suspend_sub(client);
+
+}
+
+static int mhl_i2c_pm_resume(struct device *dev)
+{
+ struct i2c_client *client =
+ container_of(dev, struct i2c_client, dev);
+
+ if (!client)
+ return -ENODEV;
+ pr_debug("%s: mhl pm resume\n", __func__);
+ return mhl_i2c_resume_sub(client);
+}
+
+static const struct dev_pm_ops mhl_i2c_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mhl_i2c_pm_suspend, mhl_i2c_pm_resume)
+};
+#endif /* CONFIG_PM_SLEEP */
+
static struct of_device_id mhl_match_table[] = {
{.compatible = COMPATIBLE_NAME,},
{ },
@@ -1915,9 +2009,16 @@
.name = MHL_DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = mhl_match_table,
+#ifdef CONFIG_PM_SLEEP
+ .pm = &mhl_i2c_pm_ops,
+#endif /* CONFIG_PM_SLEEP */
},
.probe = mhl_i2c_probe,
.remove = mhl_i2c_remove,
+#if defined(CONFIG_PM) && !defined(CONFIG_PM_SLEEP)
+ .suspend = mhl_i2c_suspend,
+ .resume = mhl_i2c_resume,
+#endif /* defined(CONFIG_PM) && !defined(CONFIG_PM_SLEEP) */
.id_table = mhl_sii_i2c_id,
};
diff --git a/drivers/video/msm/mdss/msm_mdss_io_8974.c b/drivers/video/msm/mdss/msm_mdss_io_8974.c
index 2b07e43..aab67df 100644
--- a/drivers/video/msm/mdss/msm_mdss_io_8974.c
+++ b/drivers/video/msm/mdss/msm_mdss_io_8974.c
@@ -32,6 +32,7 @@
struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
struct device *dev = NULL;
+ int rc = 0;
if (!pdev) {
pr_err("%s: Invalid pdev\n", __func__);
@@ -39,32 +40,53 @@
}
dev = &pdev->dev;
+ ctrl_pdata->ahb_clk = clk_get(dev, "iface_clk");
+ if (IS_ERR(ctrl_pdata->ahb_clk)) {
+ rc = PTR_ERR(ctrl_pdata->ahb_clk);
+ pr_err("%s: Unable to get mdss ahb clk. rc=%d\n",
+ __func__, rc);
+ goto mdss_dsi_clk_err;
+ }
+
+ ctrl_pdata->axi_clk = clk_get(dev, "bus_clk");
+ if (IS_ERR(ctrl_pdata->axi_clk)) {
+ rc = PTR_ERR(ctrl_pdata->axi_clk);
+ pr_err("%s: Unable to get axi bus clk. rc=%d\n",
+ __func__, rc);
+ goto mdss_dsi_clk_err;
+ }
+
ctrl_pdata->byte_clk = clk_get(dev, "byte_clk");
if (IS_ERR(ctrl_pdata->byte_clk)) {
- pr_err("can't find dsi_byte_clk\n");
+ rc = PTR_ERR(ctrl_pdata->byte_clk);
+ pr_err("%s: can't find dsi_byte_clk. rc=%d\n",
+ __func__, rc);
ctrl_pdata->byte_clk = NULL;
goto mdss_dsi_clk_err;
}
ctrl_pdata->pixel_clk = clk_get(dev, "pixel_clk");
if (IS_ERR(ctrl_pdata->pixel_clk)) {
- pr_err("can't find dsi_pixel_clk\n");
+ rc = PTR_ERR(ctrl_pdata->pixel_clk);
+ pr_err("%s: can't find dsi_pixel_clk. rc=%d\n",
+ __func__, rc);
ctrl_pdata->pixel_clk = NULL;
goto mdss_dsi_clk_err;
}
ctrl_pdata->esc_clk = clk_get(dev, "core_clk");
if (IS_ERR(ctrl_pdata->esc_clk)) {
- pr_err("can't find dsi_esc_clk\n");
+ rc = PTR_ERR(ctrl_pdata->esc_clk);
+ pr_err("%s: can't find dsi_esc_clk. rc=%d\n",
+ __func__, rc);
ctrl_pdata->esc_clk = NULL;
goto mdss_dsi_clk_err;
}
- return 0;
-
mdss_dsi_clk_err:
- mdss_dsi_clk_deinit(ctrl_pdata);
- return -EPERM;
+ if (rc)
+ mdss_dsi_clk_deinit(ctrl_pdata);
+ return rc;
}
void mdss_dsi_clk_deinit(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
@@ -75,6 +97,10 @@
clk_put(ctrl_pdata->esc_clk);
if (ctrl_pdata->pixel_clk)
clk_put(ctrl_pdata->pixel_clk);
+ if (ctrl_pdata->axi_clk)
+ clk_put(ctrl_pdata->axi_clk);
+ if (ctrl_pdata->ahb_clk)
+ clk_put(ctrl_pdata->ahb_clk);
}
#define PREF_DIV_RATIO 27
@@ -156,6 +182,33 @@
return 0;
}
+int mdss_dsi_enable_bus_clocks(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
+{
+ int rc = 0;
+
+ rc = clk_prepare_enable(ctrl_pdata->ahb_clk);
+ if (rc) {
+ pr_err("%s: failed to enable ahb clock. rc=%d\n", __func__, rc);
+ goto error;
+ }
+
+ rc = clk_prepare_enable(ctrl_pdata->axi_clk);
+ if (rc) {
+ pr_err("%s: failed to enable ahb clock. rc=%d\n", __func__, rc);
+ clk_disable_unprepare(ctrl_pdata->ahb_clk);
+ goto error;
+ }
+
+error:
+ return rc;
+}
+
+void mdss_dsi_disable_bus_clocks(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
+{
+ clk_disable_unprepare(ctrl_pdata->axi_clk);
+ clk_disable_unprepare(ctrl_pdata->ahb_clk);
+}
+
void mdss_dsi_prepare_clocks(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
clk_prepare(ctrl_pdata->byte_clk);
@@ -170,13 +223,10 @@
clk_unprepare(ctrl_pdata->byte_clk);
}
-void mdss_dsi_clk_enable(struct mdss_panel_data *pdata)
+void mdss_dsi_clk_enable(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
- struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
u32 esc_clk_rate = 19200000;
- ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
- panel_data);
if (!ctrl_pdata) {
pr_err("%s: Invalid input data\n", __func__);
return;
@@ -206,12 +256,8 @@
ctrl_pdata->mdss_dsi_clk_on = 1;
}
-void mdss_dsi_clk_disable(struct mdss_panel_data *pdata)
+void mdss_dsi_clk_disable(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
- struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
-
- ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
- panel_data);
if (!ctrl_pdata) {
pr_err("%s: Invalid input data\n", __func__);
return;
@@ -241,42 +287,66 @@
wmb();
}
-void mdss_dsi_phy_enable(unsigned char *ctrl_base, int on)
+void mdss_dsi_phy_enable(struct mdss_dsi_ctrl_pdata *ctrl, int on)
{
+ static struct mdss_dsi_ctrl_pdata *left_ctrl;
+
+ if (ctrl == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return;
+ }
+
+ if (!left_ctrl
+ && ctrl->shared_pdata.broadcast_enable)
+ if ((ctrl->panel_data).panel_info.pdest
+ == DISPLAY_1)
+ left_ctrl = ctrl;
+
if (on) {
- MIPI_OUTP(ctrl_base + 0x03cc, 0x03);
+ MIPI_OUTP(ctrl->ctrl_base + 0x03cc, 0x03);
wmb();
usleep(100);
- MIPI_OUTP(ctrl_base + 0x0220, 0x006);
+ MIPI_OUTP(ctrl->ctrl_base + 0x0220, 0x006);
wmb();
usleep(100);
- MIPI_OUTP(ctrl_base + 0x0268, 0x001);
+ MIPI_OUTP(ctrl->ctrl_base + 0x0268, 0x001);
wmb();
usleep(100);
- MIPI_OUTP(ctrl_base + 0x0268, 0x000);
+ MIPI_OUTP(ctrl->ctrl_base + 0x0268, 0x000);
wmb();
usleep(100);
- MIPI_OUTP(ctrl_base + 0x0220, 0x007);
+ MIPI_OUTP(ctrl->ctrl_base + 0x0220, 0x007);
wmb();
- MIPI_OUTP(ctrl_base + 0x03cc, 0x01);
+ MIPI_OUTP(ctrl->ctrl_base + 0x03cc, 0x01);
wmb();
usleep(100);
/* MMSS_DSI_0_PHY_DSIPHY_CTRL_0 */
- MIPI_OUTP(ctrl_base + 0x0470, 0x07e);
- MIPI_OUTP(ctrl_base + 0x0470, 0x06e);
- MIPI_OUTP(ctrl_base + 0x0470, 0x06c);
- MIPI_OUTP(ctrl_base + 0x0470, 0x064);
- MIPI_OUTP(ctrl_base + 0x0470, 0x065);
- MIPI_OUTP(ctrl_base + 0x0470, 0x075);
- MIPI_OUTP(ctrl_base + 0x0470, 0x077);
- MIPI_OUTP(ctrl_base + 0x0470, 0x07f);
+ MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x07e);
+ MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x06e);
+ MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x06c);
+ MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x064);
+ MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x065);
+ MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x075);
+ MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x077);
+ MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x07f);
wmb();
-
} else {
- MIPI_OUTP(ctrl_base + 0x0220, 0x006);
- usleep(10);
- MIPI_OUTP(ctrl_base + 0x0470, 0x000);
+ if (left_ctrl &&
+ (ctrl->panel_data.panel_info.pdest
+ == DISPLAY_1))
+ return;
+
+ if (left_ctrl &&
+ (ctrl->panel_data.panel_info.pdest
+ == DISPLAY_2)) {
+ MIPI_OUTP(left_ctrl->ctrl_base + 0x0220, 0x006);
+ MIPI_OUTP(left_ctrl->ctrl_base + 0x0470, 0x000);
+ MIPI_OUTP(left_ctrl->ctrl_base + 0x0598, 0x000);
+ }
+ MIPI_OUTP(ctrl->ctrl_base + 0x0220, 0x006);
+ MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x000);
+ MIPI_OUTP(ctrl->ctrl_base + 0x0598, 0x000);
wmb();
}
}
diff --git a/fs/file.c b/fs/file.c
index ba3f605..2f989c3 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -476,6 +476,7 @@
spin_unlock(&files->file_lock);
return error;
}
+EXPORT_SYMBOL(alloc_fd);
int get_unused_fd(void)
{
diff --git a/include/linux/android_alarm.h b/include/linux/android_alarm.h
index cbfeafc..096f777 100644
--- a/include/linux/android_alarm.h
+++ b/include/linux/android_alarm.h
@@ -70,6 +70,7 @@
void alarm_start_range(struct alarm *alarm, ktime_t start, ktime_t end);
int alarm_try_to_cancel(struct alarm *alarm);
int alarm_cancel(struct alarm *alarm);
+void set_power_on_alarm(long secs);
ktime_t alarm_get_elapsed_realtime(void);
/* set rtc while preserving elapsed realtime */
diff --git a/include/linux/bluetooth-power.h b/include/linux/bluetooth-power.h
new file mode 100644
index 0000000..ba53a40
--- /dev/null
+++ b/include/linux/bluetooth-power.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2013, 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.
+ *
+ */
+
+#ifndef __LINUX_BLUETOOTH_POWER_H
+#define __LINUX_BLUETOOTH_POWER_H
+
+/*
+ * voltage regulator information required for configuring the
+ * bluetooth chipset
+ */
+struct bt_power_vreg_data {
+ /* voltage regulator handle */
+ struct regulator *reg;
+ /* regulator name */
+ const char *name;
+ /* voltage levels to be set */
+ unsigned int low_vol_level;
+ unsigned int high_vol_level;
+ /*
+ * is set voltage supported for this regulator?
+ * false => set voltage is not supported
+ * true => set voltage is supported
+ *
+ * Some regulators (like gpio-regulators, LVS (low voltage swtiches)
+ * PMIC regulators) dont have the capability to call
+ * regulator_set_voltage or regulator_set_optimum_mode
+ * Use this variable to indicate if its a such regulator or not
+ */
+ bool set_voltage_sup;
+ /* is this regulator enabled? */
+ bool is_enabled;
+};
+
+/*
+ * Platform data for the bluetooth power driver.
+ */
+struct bluetooth_power_platform_data {
+ /* Bluetooth reset gpio */
+ int bt_gpio_sys_rst;
+ /* VDDIO voltage regulator */
+ struct bt_power_vreg_data *bt_vdd_io;
+ /* VDD_PA voltage regulator */
+ struct bt_power_vreg_data *bt_vdd_pa;
+ /* VDD_LDOIN voltage regulator */
+ struct bt_power_vreg_data *bt_vdd_ldo;
+ /* Optional: chip power down gpio-regulator
+ * chip power down data is required when bluetooth module
+ * and other modules like wifi co-exist in a single chip and
+ * shares a common gpio to bring chip out of reset.
+ */
+ struct bt_power_vreg_data *bt_chip_pwd;
+ /* Optional: Bluetooth power setup function */
+ int (*bt_power_setup) (int);
+};
+
+#endif /* __LINUX_BLUETOOTH_POWER_H */
diff --git a/include/linux/coresight-stm.h b/include/linux/coresight-stm.h
index 3f35dd9..298fc9a 100644
--- a/include/linux/coresight-stm.h
+++ b/include/linux/coresight-stm.h
@@ -7,6 +7,7 @@
OST_ENTITY_TRACE_PRINTK = 0x02,
OST_ENTITY_TRACE_MARKER = 0x04,
OST_ENTITY_DEV_NODE = 0x08,
+ OST_ENTITY_DIAG = 0xEE,
OST_ENTITY_QVIEW = 0xFE,
OST_ENTITY_MAX = 0xFF,
};
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index 2f77d29..73b94af 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -286,7 +286,7 @@
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW,
- MSG_LVL_HIGH,
+ MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL,
@@ -725,7 +725,7 @@
/* LOG CODES */
#define LOG_0 0x0
-#define LOG_1 0x17F4
+#define LOG_1 0x17FA
#define LOG_2 0x0
#define LOG_3 0x0
#define LOG_4 0x4910
diff --git a/include/linux/dvb/dmx.h b/include/linux/dvb/dmx.h
index c219725..ce9e5b9 100644
--- a/include/linux/dvb/dmx.h
+++ b/include/linux/dvb/dmx.h
@@ -195,7 +195,7 @@
/* write pointer offset in bytes */
unsigned int write_offset;
- /* non-zero if data error occured */
+ /* non-zero if data error occurred */
int error;
};
@@ -229,7 +229,18 @@
DMX_EVENT_MARKER = 0x00000100,
/* New indexing entry is ready */
- DMX_EVENT_NEW_INDEX_ENTRY = 0x00000200
+ DMX_EVENT_NEW_INDEX_ENTRY = 0x00000200,
+
+ /*
+ * Section filter timer expired. This is notified
+ * when timeout is configured to section filter
+ * (dmx_sct_filter_params) and no sections were
+ * received for the given time.
+ */
+ DMX_EVENT_SECTION_TIMEOUT = 0x00000400,
+
+ /* Scrambling bits change between clear and scrambled */
+ DMX_EVENT_SCRAMBLING_STATUS_CHANGE = 0x00000800
};
enum dmx_oob_cmd {
@@ -248,7 +259,7 @@
/* Discontinuity indicator was set */
#define DMX_FILTER_DISCONTINUITY_INDICATOR 0x02
-/* PES legnth in PES header is not correct */
+/* PES length in PES header is not correct */
#define DMX_FILTER_PES_LENGTH_ERROR 0x04
@@ -404,7 +415,7 @@
/*
* The PID the index entry belongs to.
* In case of recording filter, multiple PIDs may exist in the same
- * filter through DMX_ADD_PID ioctl and each can be indexed seperatly.
+ * filter through DMX_ADD_PID ioctl and each can be indexed separately.
*/
__u16 pid;
@@ -415,7 +426,7 @@
__u64 match_tsp_num;
/*
- * The TS packet number in the recorded data preceeding
+ * The TS packet number in the recorded data preceding
* match_tsp_num and has PUSI set.
*/
__u64 last_pusi_tsp_num;
@@ -424,6 +435,23 @@
__u64 stc;
};
+/* Scrambling information associated with DMX_EVENT_SCRAMBLING_STATUS_CHANGE */
+struct dmx_scrambling_status_event_info {
+ /*
+ * The PID which its scrambling bit status changed.
+ * In case of recording filter, multiple PIDs may exist in the same
+ * filter through DMX_ADD_PID ioctl, each may have
+ * different scrambling bits status.
+ */
+ __u16 pid;
+
+ /* old value of scrambling bits */
+ __u8 old_value;
+
+ /* new value of scrambling bits */
+ __u8 new_value;
+};
+
/*
* Filter's event returned through DMX_GET_EVENT.
* poll with POLLPRI would block until events are available.
@@ -439,6 +467,7 @@
struct dmx_es_data_event_info es_data;
struct dmx_marker_event_info marker;
struct dmx_index_event_info index;
+ struct dmx_scrambling_status_event_info scrambling_status;
} params;
};
@@ -706,6 +735,59 @@
__u64 types;
};
+struct dmx_set_ts_insertion {
+ /*
+ * Unique identifier managed by the caller.
+ * This identifier can be used later to remove the
+ * insertion using DMX_ABORT_TS_INSERTION ioctl.
+ */
+ __u32 identifier;
+
+ /*
+ * Repetition time in msec, minimum allowed value is 25msec.
+ * 0 repetition time means one-shot insertion is done.
+ * Insertion done based on wall-clock.
+ */
+ __u32 repetition_time;
+
+ /*
+ * TS packets buffer to be inserted.
+ * The buffer is inserted as-is to the recording buffer
+ * without any modification.
+ * It is advised to set discontinuity flag in the very
+ * first TS packet in the buffer.
+ */
+ const __u8 *ts_packets;
+
+ /*
+ * Size in bytes of the TS packets buffer to be inserted.
+ * Should be in multiples of 188 or 192 bytes
+ * depending on recording filter output format.
+ */
+ size_t size;
+};
+
+struct dmx_abort_ts_insertion {
+ /*
+ * Identifier of the insertion buffer previously set
+ * using DMX_SET_TS_INSERTION.
+ */
+ __u32 identifier;
+};
+
+struct dmx_scrambling_bits {
+ /*
+ * The PID to return its scrambling bit value.
+ * In case of recording filter, multiple PIDs may exist in the same
+ * filter through DMX_ADD_PID ioctl, each may have different
+ * scrambling bits status.
+ */
+ __u16 pid;
+
+ /* Current value of scrambling bits: 0, 1, 2 or 3 */
+ __u8 value;
+};
+
#define DMX_START _IO('o', 41)
#define DMX_STOP _IO('o', 42)
#define DMX_SET_FILTER _IOW('o', 43, struct dmx_sct_filter_params)
@@ -734,5 +816,8 @@
#define DMX_GET_EVENTS_MASK _IOR('o', 67, struct dmx_events_mask)
#define DMX_PUSH_OOB_COMMAND _IOW('o', 68, struct dmx_oob_command)
#define DMX_SET_INDEXING_PARAMS _IOW('o', 69, struct dmx_indexing_params)
+#define DMX_SET_TS_INSERTION _IOW('o', 70, struct dmx_set_ts_insertion)
+#define DMX_ABORT_TS_INSERTION _IOW('o', 71, struct dmx_abort_ts_insertion)
+#define DMX_GET_SCRAMBLING_BITS _IOWR('o', 72, struct dmx_scrambling_bits)
#endif /*_DVBDMX_H_*/
diff --git a/include/linux/input/ft5x06_ts.h b/include/linux/input/ft5x06_ts.h
index a379d38..af8c332 100644
--- a/include/linux/input/ft5x06_ts.h
+++ b/include/linux/input/ft5x06_ts.h
@@ -3,7 +3,7 @@
* FocalTech ft5x06 TouchScreen driver header file.
*
* Copyright (c) 2010 Focal tech Ltd.
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -18,12 +18,28 @@
#ifndef __LINUX_FT5X06_TS_H__
#define __LINUX_FT5X06_TS_H__
+#define FT5X06_ID 0x55
+#define FT5X16_ID 0x0A
+#define FT5X36_ID 0x14
+#define FT6X06_ID 0x06
+
struct ft5x06_ts_platform_data {
- unsigned long irqflags;
+ u32 irqflags;
+ u32 irq_gpio;
+ u32 irq_gpio_flags;
+ u32 reset_gpio;
+ u32 reset_gpio_flags;
+ u32 family_id;
u32 x_max;
u32 y_max;
- u32 irq_gpio;
- u32 reset_gpio;
+ u32 x_min;
+ u32 y_min;
+ u32 panel_minx;
+ u32 panel_miny;
+ u32 panel_maxx;
+ u32 panel_maxy;
+ bool no_force_update;
+ bool i2c_pull_up;
int (*power_init) (bool);
int (*power_on) (bool);
};
diff --git a/include/linux/input/gen_vkeys.h b/include/linux/input/gen_vkeys.h
index ce29351..a58158d 100644
--- a/include/linux/input/gen_vkeys.h
+++ b/include/linux/input/gen_vkeys.h
@@ -19,5 +19,6 @@
int panel_maxy;
int *keycodes;
int num_keys;
+ int y_offset;
};
#endif
diff --git a/include/linux/input/synaptics_dsx.h b/include/linux/input/synaptics_dsx.h
index f90f59e..73016d6 100644
--- a/include/linux/input/synaptics_dsx.h
+++ b/include/linux/input/synaptics_dsx.h
@@ -35,7 +35,6 @@
* struct synaptics_rmi4_platform_data - rmi4 platform data
* @x_flip: x flip flag
* @y_flip: y flip flag
- * @regulator_en: regulator enable flag
* @irq_gpio: attention interrupt gpio
* @irq_flags: flags used by the irq
* @reset_gpio: reset gpio
@@ -47,7 +46,6 @@
struct synaptics_rmi4_platform_data {
bool x_flip;
bool y_flip;
- bool regulator_en;
bool i2c_pull_up;
unsigned irq_gpio;
u32 irq_flags;
diff --git a/include/linux/mfd/pm8xxx/batterydata-lib.h b/include/linux/mfd/pm8xxx/batterydata-lib.h
index df9569b..644eede 100644
--- a/include/linux/mfd/pm8xxx/batterydata-lib.h
+++ b/include/linux/mfd/pm8xxx/batterydata-lib.h
@@ -18,10 +18,10 @@
#define FCC_CC_COLS 5
#define FCC_TEMP_COLS 8
-#define PC_CC_ROWS 29
+#define PC_CC_ROWS 31
#define PC_CC_COLS 13
-#define PC_TEMP_ROWS 29
+#define PC_TEMP_ROWS 31
#define PC_TEMP_COLS 8
#define MAX_SINGLE_LUT_COLS 20
@@ -74,6 +74,8 @@
BATT_PALLADIUM,
BATT_DESAY,
BATT_OEM,
+ BATT_QRD_4V35_2000MAH,
+ BATT_QRD_4V2_1300MAH,
};
/**
@@ -114,6 +116,8 @@
extern struct bms_battery_data palladium_1500_data;
extern struct bms_battery_data desay_5200_data;
extern struct bms_battery_data oem_batt_data;
+extern struct bms_battery_data QRD_4v35_2000mAh_data;
+extern struct bms_battery_data qrd_4v2_1300mah_data;
int interpolate_fcc(struct single_row_lut *fcc_temp_lut, int batt_temp);
int interpolate_scalingfactor(struct sf_lut *sf_lut, int row_entry, int pc);
diff --git a/include/linux/mfd/pm8xxx/pm8921-bms.h b/include/linux/mfd/pm8xxx/pm8921-bms.h
index a19c0b6..7562166 100644
--- a/include/linux/mfd/pm8xxx/pm8921-bms.h
+++ b/include/linux/mfd/pm8xxx/pm8921-bms.h
@@ -37,6 +37,12 @@
* is considered empty(mV)
* @enable_fcc_learning: if set the driver will learn full charge
* capacity of the battery upon end of charge
+ * @min_fcc_learning_soc: minimum SOC as which CC counting for FCC
+ * learning can start
+ * @min_fcc_ocv_pc: minimum PC (lookup(OCV)) at which CC counting
+ * for FCC learning can start
+ * @max_fcc_learning_samples: Maximum number of FCC measurement cycles to be
+ * used for FCC update
* @normal_voltage_calc_ms: The period of soc calculation in ms when battery
* voltage higher than cutoff voltage
* @low_voltage_calc_ms: The period of soc calculation in ms when battery
@@ -66,6 +72,9 @@
unsigned int alarm_low_mv;
unsigned int alarm_high_mv;
int enable_fcc_learning;
+ int min_fcc_learning_soc;
+ int min_fcc_ocv_pc;
+ int max_fcc_learning_samples;
int shutdown_soc_valid_limit;
int ignore_shutdown_soc;
int adjust_soc_low_threshold;
diff --git a/include/linux/mfd/pm8xxx/pwm.h b/include/linux/mfd/pm8xxx/pwm.h
index 6d95e3a..47ebe06 100644
--- a/include/linux/mfd/pm8xxx/pwm.h
+++ b/include/linux/mfd/pm8xxx/pwm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, 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
@@ -32,6 +32,9 @@
#define PM_PWM_LUT_NO_TABLE 0x100
+#define PM_PWM_BANK_LO 0x1000
+#define PM_PWM_BANK_HI 0x2000
+
/**
* PWM frequency/period control
*
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index 3a9b1b9..e688bd9 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -76,21 +76,25 @@
WCD9XXX_IRQ_EAR_PA_OCPL_FAULT,
WCD9XXX_IRQ_HPH_L_PA_STARTUP,
WCD9XXX_IRQ_HPH_R_PA_STARTUP,
- WCD9XXX_IRQ_EAR_PA_STARTUP,
- WCD9XXX_IRQ_RESERVED_0,
+ WCD9320_IRQ_EAR_PA_STARTUP,
+ WCD9306_IRQ_MBHC_JACK_SWITCH = WCD9320_IRQ_EAR_PA_STARTUP,
+ WCD9310_NUM_IRQS,
+ WCD9XXX_IRQ_RESERVED_0 = WCD9310_NUM_IRQS,
WCD9XXX_IRQ_RESERVED_1,
/* INTR_REG 3 */
WCD9XXX_IRQ_MAD_AUDIO,
WCD9XXX_IRQ_MAD_BEACON,
WCD9XXX_IRQ_MAD_ULTRASOUND,
WCD9XXX_IRQ_SPEAKER_CLIPPING,
- WCD9XXX_IRQ_MBHC_JACK_SWITCH,
+ WCD9320_IRQ_MBHC_JACK_SWITCH,
+ WCD9XXX_IRQ_VBAT_MONITOR_ATTACK,
+ WCD9XXX_IRQ_VBAT_MONITOR_RELEASE,
WCD9XXX_NUM_IRQS,
};
enum {
- TABLA_NUM_IRQS = WCD9XXX_NUM_IRQS,
- SITAR_NUM_IRQS = WCD9XXX_NUM_IRQS,
+ TABLA_NUM_IRQS = WCD9310_NUM_IRQS,
+ SITAR_NUM_IRQS = WCD9310_NUM_IRQS,
TAIKO_NUM_IRQS = WCD9XXX_NUM_IRQS,
TAPAN_NUM_IRQS = WCD9XXX_NUM_IRQS,
};
@@ -150,6 +154,13 @@
#define WCD9XXX_CH(xport, xshift) \
{.port = xport, .shift = xshift}
+enum wcd9xxx_chipid_major {
+ TABLA_MAJOR = cpu_to_le16(0x100),
+ SITAR_MAJOR = cpu_to_le16(0x101),
+ TAIKO_MAJOR = cpu_to_le16(0x102),
+ TAPAN_MAJOR = cpu_to_le16(0x103),
+};
+
struct wcd9xxx_codec_type {
u16 id_major;
u16 id_minor;
diff --git a/include/linux/mfd/wcd9xxx/pdata.h b/include/linux/mfd/wcd9xxx/pdata.h
index c6e4ab3..b7ba6fb 100644
--- a/include/linux/mfd/wcd9xxx/pdata.h
+++ b/include/linux/mfd/wcd9xxx/pdata.h
@@ -126,6 +126,7 @@
u8 bias2_cap_mode;
u8 bias3_cap_mode;
u8 bias4_cap_mode;
+ bool bias2_is_headset_only;
};
struct wcd9xxx_ocp_setting {
diff --git a/include/linux/mfd/wcd9xxx/wcd9304_registers.h b/include/linux/mfd/wcd9xxx/wcd9304_registers.h
index a7f9e4a..8e5e23a 100644
--- a/include/linux/mfd/wcd9xxx/wcd9304_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9304_registers.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -473,17 +473,17 @@
#define SITAR_A_CDC_TX3_DMIC_CTL (0x235)
#define SITAR_A_CDC_TX3_DMIC_CTL__POR (0x00000000)
-#define SITAR_A_CDC_TX4_VOL_CTL_TIMER (0x239)
+#define SITAR_A_CDC_TX4_VOL_CTL_TIMER (0x238)
#define SITAR_A_CDC_TX4_VOL_CTL_TIMER__POR (0x00000000)
-#define SITAR_A_CDC_TX4_VOL_CTL_GAIN (0x23A)
+#define SITAR_A_CDC_TX4_VOL_CTL_GAIN (0x239)
#define SITAR_A_CDC_TX4_VOL_CTL_GAIN__POR (0x00000000)
-#define SITAR_A_CDC_TX4_VOL_CTL_CFG (0x23B)
+#define SITAR_A_CDC_TX4_VOL_CTL_CFG (0x23A)
#define SITAR_A_CDC_TX4_VOL_CTL_CFG__POR (0x00000000)
-#define SITAR_A_CDC_TX4_MUX_CTL (0x23C)
+#define SITAR_A_CDC_TX4_MUX_CTL (0x23B)
#define SITAR_A_CDC_TX4_MUX_CTL__POR (0x00000008)
-#define SITAR_A_CDC_TX4_CLK_FS_CTL (0x23D)
+#define SITAR_A_CDC_TX4_CLK_FS_CTL (0x23C)
#define SITAR_A_CDC_TX4_CLK_FS_CTL__POR (0x00000003)
-#define SITAR_A_CDC_TX4_DMIC_CTL (0x23E)
+#define SITAR_A_CDC_TX4_DMIC_CTL (0x23D)
#define SITAR_A_CDC_TX4_DMIC_CTL__POR (0x00000000)
#define SITAR_A_CDC_TX5_VOL_CTL_TIMER (0x240)
diff --git a/include/linux/mhl_8334.h b/include/linux/mhl_8334.h
index d8eb494..7297ff3 100644
--- a/include/linux/mhl_8334.h
+++ b/include/linux/mhl_8334.h
@@ -146,6 +146,7 @@
struct completion msc_cmd_done;
uint8_t devcap[16];
uint8_t devcap_state;
+ uint8_t status[2];
uint8_t path_en_state;
void *hdmi_mhl_ops;
struct work_struct mhl_msc_send_work;
@@ -158,6 +159,10 @@
int scrpd_busy;
int wr_burst_pending;
struct completion req_write_done;
+ spinlock_t lock;
+ bool tx_powered_off;
+ uint8_t dwnstream_hpd;
+ bool mhl_det_discon;
};
int mhl_i2c_reg_read(struct i2c_client *client,
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 9a4e61d..2cb297e 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -13,6 +13,7 @@
#include <linux/device.h>
#include <linux/mmc/core.h>
#include <linux/mod_devicetable.h>
+#include <linux/notifier.h>
struct mmc_cid {
unsigned int manfid;
@@ -173,7 +174,8 @@
wide_bus:1,
high_power:1,
high_speed:1,
- disable_cd:1;
+ disable_cd:1,
+ async_intr_sup:1;
};
struct sdio_cis {
@@ -276,7 +278,6 @@
* percentage of sectors that should issue check for
* BKOPS need
* @bkops_stats: BKOPS statistics
- * @poll_for_completion: Poll on BKOPS completion
* @cancel_delayed_work: A flag to indicate if the delayed work
* should be cancelled
* @sectors_changed: number of sectors written or
@@ -294,10 +295,6 @@
* is idle.
*/
#define MMC_IDLE_BKOPS_TIME_MS 200
- struct work_struct poll_for_completion;
-/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
-#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS (4 * 60 * 1000) /* in ms */
-#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000 /* in ms */
bool cancel_delayed_work;
unsigned int sectors_changed;
/*
@@ -388,6 +385,8 @@
struct device_attribute rpm_attrib;
unsigned int idle_timeout;
+ struct notifier_block reboot_notify;
+ bool issue_long_pon;
};
/*
@@ -608,6 +607,7 @@
void (*remove)(struct mmc_card *);
int (*suspend)(struct mmc_card *);
int (*resume)(struct mmc_card *);
+ void (*shutdown)(struct mmc_card *);
};
extern int mmc_register_driver(struct mmc_driver *);
@@ -619,4 +619,5 @@
struct mmc_card *card);
extern void mmc_blk_init_packed_statistics(struct mmc_card *card);
extern void mmc_blk_disable_wr_packing(struct mmc_queue *mq);
+extern int mmc_send_long_pon(struct mmc_card *card);
#endif /* LINUX_MMC_CARD_H */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 995f8a2..f548721 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -146,6 +146,7 @@
extern int mmc_stop_bkops(struct mmc_card *);
extern int mmc_read_bkops_status(struct mmc_card *);
+extern bool mmc_card_is_prog_state(struct mmc_card *);
extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
struct mmc_async_req *, int *);
extern int mmc_interrupt_hpi(struct mmc_card *);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 9eef3a0..dbafdfc 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -82,6 +82,12 @@
#define MMC_SET_DRIVER_TYPE_D 3
};
+/* states to represent load on the host */
+enum mmc_load {
+ MMC_LOAD_HIGH,
+ MMC_LOAD_LOW,
+};
+
struct mmc_host_ops {
/*
* 'enable' is called when the host is claimed and 'disable' is called
@@ -140,6 +146,7 @@
void (*hw_reset)(struct mmc_host *host);
unsigned long (*get_max_frequency)(struct mmc_host *host);
unsigned long (*get_min_frequency)(struct mmc_host *host);
+ int (*notify_load)(struct mmc_host *, enum mmc_load);
int (*stop_request)(struct mmc_host *host);
unsigned int (*get_xfer_remain)(struct mmc_host *host);
};
@@ -272,6 +279,7 @@
#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */
#define MMC_CAP2_DETECT_ON_ERR (1 << 8) /* On I/O err check card removal */
#define MMC_CAP2_HC_ERASE_SZ (1 << 9) /* High-capacity erase size */
+#define MMC_CAP2_CD_ACTIVE_HIGH (1 << 10) /* Card-detect signal active high */
#define MMC_CAP2_PACKED_RD (1 << 12) /* Allow packed read */
#define MMC_CAP2_PACKED_WR (1 << 13) /* Allow packed write */
@@ -285,6 +293,8 @@
#define MMC_CAP2_STOP_REQUEST (1 << 18) /* Allow stop ongoing request */
/* Use runtime PM framework provided by MMC core */
#define MMC_CAP2_CORE_RUNTIME_PM (1 << 19)
+/* Allows Asynchronous SDIO irq while card is in 4-bit mode */
+#define MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE (1 << 20)
mmc_pm_flag_t pm_caps; /* supported pm features */
int clk_requests; /* internal reference counter */
@@ -401,11 +411,13 @@
bool initialized;
bool in_progress;
struct delayed_work work;
+ enum mmc_load state;
} clk_scaling;
unsigned long private[0] ____cacheline_aligned;
};
extern struct mmc_host *mmc_alloc_host(int extra, struct device *);
+extern bool mmc_host_may_gate_card(struct mmc_card *);
extern int mmc_add_host(struct mmc_host *);
extern void mmc_remove_host(struct mmc_host *);
extern void mmc_free_host(struct mmc_host *);
diff --git a/include/linux/mmc/sdio.h b/include/linux/mmc/sdio.h
index 58e52d4..961a4e1 100644
--- a/include/linux/mmc/sdio.h
+++ b/include/linux/mmc/sdio.h
@@ -162,6 +162,10 @@
#define SDIO_DTSx_SET_TYPE_A (1 << SDIO_DRIVE_DTSx_SHIFT)
#define SDIO_DTSx_SET_TYPE_C (2 << SDIO_DRIVE_DTSx_SHIFT)
#define SDIO_DTSx_SET_TYPE_D (3 << SDIO_DRIVE_DTSx_SHIFT)
+
+#define SDIO_CCCR_INTERRUPT_EXTENSION 0x16
+#define SDIO_SUPPORT_ASYNC_INTR (1<<0)
+#define SDIO_ENABLE_ASYNC_INTR (1<<1)
/*
* Function Basic Registers (FBR)
*/
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index ca7a586..d63232a 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -72,11 +72,9 @@
#ifdef CONFIG_CMA
bool is_cma_pageblock(struct page *page);
# define is_migrate_cma(migratetype) unlikely((migratetype) == MIGRATE_CMA)
-# define cma_wmark_pages(zone) zone->min_cma_pages
#else
# define is_cma_pageblock(page) false
# define is_migrate_cma(migratetype) false
-# define cma_wmark_pages(zone) 0
#endif
#define for_each_migratetype_order(order, type) \
@@ -385,11 +383,6 @@
seqlock_t span_seqlock;
#endif
#ifdef CONFIG_CMA
- /*
- * CMA needs to increase watermark levels during the allocation
- * process to make sure that the system is not starved.
- */
- unsigned long min_cma_pages;
bool cma_alloc;
#endif
struct free_area free_area[MAX_ORDER];
diff --git a/include/linux/msm_audio_acdb.h b/include/linux/msm_audio_acdb.h
index 3d159c4..a741107 100644
--- a/include/linux/msm_audio_acdb.h
+++ b/include/linux/msm_audio_acdb.h
@@ -72,6 +72,13 @@
uint16_t gain;
};
+enum msm_spkr_prot_states {
+ MSM_SPKR_PROT_CALIBRATED,
+ MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS,
+ MSM_SPKR_PROT_DISABLED,
+ MSM_SPKR_PROT_NOT_CALIBRATED
+};
+
struct msm_spk_prot_cfg {
int r0;
int t0;
diff --git a/include/linux/msm_audio_ion.h b/include/linux/msm_audio_ion.h
index 83e5dff..38b27bf 100644
--- a/include/linux/msm_audio_ion.h
+++ b/include/linux/msm_audio_ion.h
@@ -13,7 +13,12 @@
#ifndef _LINUX_MSM_AUDIO_ION_H
#define _LINUX_MSM_AUDIO_ION_H
-
+#ifdef CONFIG_SND_SOC_QDSP6V2
+#include <sound/q6asm-v2.h>
+#else
+#include <sound/q6asm.h>
+#endif
+#include <sound/pcm.h>
#include <linux/msm_ion.h>
@@ -26,9 +31,11 @@
unsigned long *ionflag, size_t bufsz,
ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr);
int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle);
-
+int msm_audio_ion_mmap(struct audio_buffer *substream,
+ struct vm_area_struct *vma);
bool msm_audio_ion_is_smmu_available(void);
+int msm_audio_ion_cache_operations(struct audio_buffer *abuff, int cache_op);
#ifdef CONFIG_SND_SOC_QDSP6V2
struct ion_client *msm_audio_ion_client_create(unsigned int heap_mask,
diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h
index 2ad040e..ae88807 100644
--- a/include/linux/msm_kgsl.h
+++ b/include/linux/msm_kgsl.h
@@ -769,7 +769,7 @@
struct kgsl_perfcounter_read_group {
unsigned int groupid;
unsigned int countable;
- uint64_t value;
+ unsigned long long value;
};
struct kgsl_perfcounter_read {
@@ -781,6 +781,27 @@
#define IOCTL_KGSL_PERFCOUNTER_READ \
_IOWR(KGSL_IOC_TYPE, 0x3B, struct kgsl_perfcounter_read)
+/*
+ * struct kgsl_gpumem_sync_cache_bulk - argument to
+ * IOCTL_KGSL_GPUMEM_SYNC_CACHE_BULK
+ * @id_list: list of GPU buffer ids of the buffers to sync
+ * @count: number of GPU buffer ids in id_list
+ * @op: a mask of KGSL_GPUMEM_CACHE_* values
+ *
+ * Sync the cache for memory headed to and from the GPU. Certain
+ * optimizations can be made on the cache operation based on the total
+ * size of the working set of memory to be managed.
+ */
+struct kgsl_gpumem_sync_cache_bulk {
+ unsigned int *id_list;
+ unsigned int count;
+ unsigned int op;
+/* private: reserved for future use */
+ unsigned int __pad[2]; /* For future binary compatibility */
+};
+
+#define IOCTL_KGSL_GPUMEM_SYNC_CACHE_BULK \
+ _IOWR(KGSL_IOC_TYPE, 0x3C, struct kgsl_gpumem_sync_cache_bulk)
#ifdef __KERNEL__
#ifdef CONFIG_MSM_KGSL_DRM
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index f9e483c..18921a0 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -50,7 +50,7 @@
#define MSMFB_HISTOGRAM_START _IOR(MSMFB_IOCTL_MAGIC, 144, \
struct mdp_histogram_start_req)
#define MSMFB_HISTOGRAM_STOP _IOR(MSMFB_IOCTL_MAGIC, 145, unsigned int)
-#define MSMFB_NOTIFY_UPDATE _IOW(MSMFB_IOCTL_MAGIC, 146, unsigned int)
+#define MSMFB_NOTIFY_UPDATE _IOWR(MSMFB_IOCTL_MAGIC, 146, unsigned int)
#define MSMFB_OVERLAY_3D _IOWR(MSMFB_IOCTL_MAGIC, 147, \
struct msmfb_overlay_3d)
@@ -78,6 +78,7 @@
#define MSMFB_METADATA_GET _IOW(MSMFB_IOCTL_MAGIC, 166, struct msmfb_metadata)
#define MSMFB_WRITEBACK_SET_MIRRORING_HINT _IOW(MSMFB_IOCTL_MAGIC, 167, \
unsigned int)
+#define MSMFB_ASYNC_BLIT _IOW(MSMFB_IOCTL_MAGIC, 168, unsigned int)
#define FB_TYPE_3D_PANEL 0x10101010
#define MDP_IMGTYPE2_START 0x10000
@@ -89,6 +90,12 @@
};
enum {
+ NOTIFY_TYPE_NO_UPDATE,
+ NOTIFY_TYPE_SUSPEND,
+ NOTIFY_TYPE_UPDATE,
+};
+
+enum {
MDP_RGB_565, /* RGB 565 planer */
MDP_XRGB_8888, /* RGB 888 padded */
MDP_Y_CBCR_H2V2, /* Y and CbCr, pseudo planer w/ Cb is in MSB */
@@ -436,6 +443,31 @@
uint32_t *b;
};
+enum {
+ DISPLAY_MISR_EDP,
+ DISPLAY_MISR_DSI0,
+ DISPLAY_MISR_DSI1,
+ DISPLAY_MISR_HDMI,
+ DISPLAY_MISR_LCDC,
+ DISPLAY_MISR_ATV,
+ DISPLAY_MISR_DSI_CMD,
+ DISPLAY_MISR_MAX
+};
+
+enum {
+ MISR_OP_NONE,
+ MISR_OP_SFM,
+ MISR_OP_MFM,
+ MISR_OP_BM,
+ MISR_OP_MAX
+};
+
+struct mdp_misr {
+ uint32_t block_id;
+ uint32_t frame_count;
+ uint32_t crc_op_mode;
+ uint32_t crc_value[32];
+};
/*
@@ -574,10 +606,14 @@
uint32_t data;
};
+#define MDSS_MAX_BL_BRIGHTNESS 255
+#define AD_BL_LIN_LEN (MDSS_MAX_BL_BRIGHTNESS + 1)
+
#define MDSS_AD_MODE_AUTO_BL 0x0
#define MDSS_AD_MODE_AUTO_STR 0x1
#define MDSS_AD_MODE_TARG_STR 0x3
#define MDSS_AD_MODE_MAN_STR 0x7
+#define MDSS_AD_MODE_CALIB 0xF
#define MDP_PP_AD_INIT 0x10
#define MDP_PP_AD_CFG 0x20
@@ -600,8 +636,13 @@
uint16_t frame_h;
uint8_t logo_v;
uint8_t logo_h;
+ uint32_t bl_lin_len;
+ uint32_t *bl_lin;
+ uint32_t *bl_lin_inv;
};
+#define MDSS_AD_BL_CTRL_MODE_EN 1
+#define MDSS_AD_BL_CTRL_MODE_DIS 0
struct mdss_ad_cfg {
uint32_t mode;
uint32_t al_calib_lut[33];
@@ -613,6 +654,8 @@
uint16_t calib[4];
uint8_t strength_limit;
uint8_t t_filter_recursion;
+ uint16_t stab_itr;
+ uint32_t bl_ctrl_mode;
};
/* ops uses standard MDP_PP_* flags */
@@ -630,7 +673,15 @@
union {
uint32_t amb_light;
uint32_t strength;
+ uint32_t calib_bl;
} in;
+ uint32_t output;
+};
+
+#define MDSS_CALIB_MODE_BL 0x1
+struct mdss_calib_cfg {
+ uint32_t ops;
+ uint32_t calib_mask;
};
enum {
@@ -645,6 +696,7 @@
mdp_op_calib_cfg,
mdp_op_ad_cfg,
mdp_op_ad_input,
+ mdp_op_calib_mode,
mdp_op_max,
};
@@ -670,6 +722,7 @@
struct mdp_gamut_cfg_data gamut_cfg_data;
struct mdp_calib_config_data calib_cfg;
struct mdss_ad_init_cfg ad_init_cfg;
+ struct mdss_calib_cfg mdss_calib_cfg;
struct mdss_ad_input ad_input;
} data;
};
@@ -682,6 +735,7 @@
metadata_op_vic,
metadata_op_wb_format,
metadata_op_get_caps,
+ metadata_op_crc,
metadata_op_max
};
@@ -706,6 +760,7 @@
uint32_t op;
uint32_t flags;
union {
+ struct mdp_misr misr_request;
struct mdp_blend_cfg blend_cfg;
struct mdp_mixer_cfg mixer_cfg;
uint32_t panel_frame_rate;
@@ -714,7 +769,7 @@
} data;
};
-#define MDP_MAX_FENCE_FD 10
+#define MDP_MAX_FENCE_FD 32
#define MDP_BUF_SYNC_FLAG_WAIT 1
struct mdp_buf_sync {
@@ -724,6 +779,12 @@
int *rel_fen_fd;
};
+struct mdp_async_blit_req_list {
+ struct mdp_buf_sync sync;
+ uint32_t count;
+ struct mdp_blit_req req[];
+};
+
#define MDP_DISPLAY_COMMIT_OVERLAY 1
struct mdp_buf_fence {
uint32_t flags;
diff --git a/include/linux/msm_thermal.h b/include/linux/msm_thermal.h
index f14cc52..2c1fa11 100644
--- a/include/linux/msm_thermal.h
+++ b/include/linux/msm_thermal.h
@@ -20,6 +20,7 @@
int32_t limit_temp_degC;
int32_t temp_hysteresis_degC;
uint32_t freq_step;
+ uint32_t freq_control_mask;
int32_t core_limit_temp_degC;
int32_t core_temp_hysteresis_degC;
uint32_t core_control_mask;
diff --git a/include/linux/netfilter_ipv4/ipt_NATTYPE.h b/include/linux/netfilter_ipv4/ipt_NATTYPE.h
index 88311c9..594b62a 100644
--- a/include/linux/netfilter_ipv4/ipt_NATTYPE.h
+++ b/include/linux/netfilter_ipv4/ipt_NATTYPE.h
@@ -21,7 +21,8 @@
u_int16_t type;
};
-extern bool nattype_refresh_timer(unsigned long nattype);
+extern bool nattype_refresh_timer(unsigned long nattype,
+unsigned long timeout_value);
#endif /*_IPT_NATTYPE_H_target*/
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index f551e75..6d7d178 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -3069,4 +3069,32 @@
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U = 1<<3,
};
+/**
+ * enum nl80211_connect_failed_reason - connection request failed reasons
+ * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be
+ * handled by the AP is reached.
+ * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL.
+ */
+enum nl80211_connect_failed_reason {
+ NL80211_CONN_FAIL_MAX_CLIENTS,
+ NL80211_CONN_FAIL_BLOCKED_CLIENT,
+};
+
+/**
+ * enum nl80211_acl_policy - access control policy
+ *
+ * Access control policy is applied on a MAC list set by
+ * %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to
+ * be used with %NL80211_ATTR_ACL_POLICY.
+ *
+ * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are
+ * listed in ACL, i.e. allow all the stations which are not listed
+ * in ACL to authenticate.
+ * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed
+ * in ACL, i.e. deny all the stations which are not listed in ACL.
+ */
+enum nl80211_acl_policy {
+ NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED,
+ NL80211_ACL_POLICY_DENY_UNLESS_LISTED,
+};
#endif /* __LINUX_NL80211_H */
diff --git a/include/linux/of_coresight.h b/include/linux/of_coresight.h
index 0943dda..eb20e80 100644
--- a/include/linux/of_coresight.h
+++ b/include/linux/of_coresight.h
@@ -13,7 +13,7 @@
#ifndef __LINUX_OF_CORESIGHT_H
#define __LINUX_OF_CORESIGHT_H
-#ifdef CONFIG_OF
+#ifdef CONFIG_OF_CORESIGHT
extern struct coresight_platform_data *of_get_coresight_platform_data(
struct device *dev, struct device_node *node);
extern struct coresight_cti_data *of_get_coresight_cti_data(
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 6e30ca2..1de9aaa 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -127,6 +127,7 @@
POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
POWER_SUPPLY_PROP_SCOPE,
POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
+ POWER_SUPPLY_PROP_RESISTANCE,
/* Properties of type `const char *' */
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
diff --git a/include/linux/qpnp-misc.h b/include/linux/qpnp-misc.h
index b241e5d..ee614a4 100644
--- a/include/linux/qpnp-misc.h
+++ b/include/linux/qpnp-misc.h
@@ -30,7 +30,7 @@
int qpnp_misc_irqs_available(struct device *consumer_dev);
#else
-static int qpnp_misc_irq_available(struct device *consumer_dev)
+static int qpnp_misc_irqs_available(struct device *consumer_dev)
{
return 0;
}
diff --git a/include/linux/qpnp/power-on.h b/include/linux/qpnp/power-on.h
index 85dbce9..5e87259 100644
--- a/include/linux/qpnp/power-on.h
+++ b/include/linux/qpnp/power-on.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -15,10 +15,38 @@
#include <linux/errno.h>
+/**
+ * enum pon_trigger_source: List of PON trigger sources
+ * %PON_SMPL: PON triggered by SMPL - Sudden Momentary Power Loss
+ * %PON_RTC: PON triggered by RTC alarm
+ * %PON_DC_CHG: PON triggered by insertion of DC charger
+ * %PON_USB_CHG: PON triggered by insertion of USB
+ * %PON_PON1: PON triggered by other PMIC (multi-PMIC option)
+ * %PON_CBLPWR_N: PON triggered by power-cable insertion
+ * %PON_KPDPWR_N: PON triggered by long press of the power-key
+ */
+enum pon_trigger_source {
+ PON_SMPL = 1,
+ PON_RTC,
+ PON_DC_CHG,
+ PON_USB_CHG,
+ PON_PON1,
+ PON_CBLPWR_N,
+ PON_KPDPWR_N,
+};
+
#ifdef CONFIG_QPNP_POWER_ON
int qpnp_pon_system_pwr_off(bool reset);
+int qpnp_pon_is_warm_reset(void);
+int qpnp_pon_trigger_config(enum pon_trigger_source pon_src, bool enable);
#else
static int qpnp_pon_system_pwr_off(bool reset) { return -ENODEV; }
+static inline int qpnp_pon_is_warm_reset(void) { return -ENODEV; }
+static inline int qpnp_pon_trigger_config(enum pon_trigger_source pon_src,
+ bool enable)
+{
+ return -ENODEV;
+}
#endif
#endif
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index 15e5dc9..dfb156f 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -197,15 +197,18 @@
/**
* enum qpnp_adc_scale_fn_type - Scaling function for pm8941 pre calibrated
* digital data relative to ADC reference.
- * %ADC_SCALE_DEFAULT: Default scaling to convert raw adc code to voltage.
- * %ADC_SCALE_BATT_THERM: Conversion to temperature based on btm parameters.
- * %ADC_SCALE_THERM_100K_PULLUP: Returns temperature in degC.
+ * %SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
+ * %SCALE_BATT_THERM: Conversion to temperature(decidegC) based on btm
+ * parameters.
+ * %SCALE_THERM_100K_PULLUP: Returns temperature in degC.
* Uses a mapping table with 100K pullup.
- * %ADC_SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
- * %ADC_SCALE_XOTHERM: Returns XO thermistor voltage in degree's Centigrade.
- * %ADC_SCALE_THERM_150K_PULLUP: Returns temperature in degC.
+ * %SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
+ * %SCALE_XOTHERM: Returns XO thermistor voltage in degree's Centigrade.
+ * %SCALE_THERM_150K_PULLUP: Returns temperature in degC.
* Uses a mapping table with 150K pullup.
- * %ADC_SCALE_NONE: Do not use this scaling type.
+ * %SCALE_QRD_BATT_THERM: Conversion to temperature(decidegC) based on
+ * btm parameters.
+ * %SCALE_NONE: Do not use this scaling type.
*/
enum qpnp_adc_scale_fn_type {
SCALE_DEFAULT = 0,
@@ -214,6 +217,7 @@
SCALE_PMIC_THERM,
SCALE_XOTHERM,
SCALE_THERM_150K_PULLUP,
+ SCALE_QRD_BATT_THERM,
SCALE_NONE,
};
@@ -598,6 +602,34 @@
QPNP_ADC_TM_CH_SELECT_NONE
};
+enum qpnp_comp_scheme_type {
+ COMP_ID_GF = 0,
+ COMP_ID_SMIC,
+ COMP_ID_TSMC,
+ COMP_ID_NUM,
+};
+
+enum qpnp_iadc_rev {
+ QPNP_IADC_VER_3_0 = 0x1,
+ QPNP_IADC_VER_3_1 = 0x3,
+};
+
+#define QPNP_VBAT_SNS_COEFF_1_TYPEA 3000
+#define QPNP_VBAT_SNS_COEFF_2_TYPEA 45810000
+#define QPNP_VBAT_SNS_COEFF_3 100000
+#define QPNP_VBAT_SNS_COEFF_1_TYPEB 3500
+#define QPNP_VBAT_SNS_COEFF_2_TYPEB 80000000
+
+#define QPNP_COEFF_1 969000
+#define QPNP_COEFF_2 34
+#define QPNP_COEFF_3_TYPEA 1700000
+#define QPNP_COEFF_3_TYPEB 1000000
+#define QPNP_COEFF_4 100
+#define QPNP_COEFF_5 15000
+#define QPNP_COEFF_6 100000
+#define QPNP_COEFF_7 21700
+#define QPNP_COEFF_8 100000000
+
/**
* struct qpnp_adc_tm_config - Represent ADC Thermal Monitor configuration.
* @channel: ADC channel for which thermal monitoring is requested.
@@ -1039,7 +1071,7 @@
/**
* qpnp_adc_scale_batt_therm() - Scales the pre-calibrated digital output
* of an ADC to the ADC reference and compensates for the
- * gain and offset. Returns the temperature in degC.
+ * gain and offset. Returns the temperature in decidegC.
* @adc_code: pre-calibrated digital ouput of the ADC.
* @adc_prop: adc properties of the pm8xxx adc such as bit resolution,
* reference voltage.
@@ -1052,6 +1084,21 @@
const struct qpnp_vadc_chan_properties *chan_prop,
struct qpnp_vadc_result *chan_rslt);
/**
+ * qpnp_adc_scale_qrd_batt_therm() - Scales the pre-calibrated digital output
+ * of an ADC to the ADC reference and compensates for the
+ * gain and offset. Returns the temperature in decidegC.
+ * @adc_code: pre-calibrated digital ouput of the ADC.
+ * @adc_prop: adc properties of the pm8xxx adc such as bit resolution,
+ * reference voltage.
+ * @chan_prop: individual channel properties to compensate the i/p scaling,
+ * slope and offset.
+ * @chan_rslt: physical result to be stored.
+ */
+int32_t qpnp_adc_scale_qrd_batt_therm(int32_t adc_code,
+ const struct qpnp_adc_properties *adc_prop,
+ const struct qpnp_vadc_chan_properties *chan_prop,
+ struct qpnp_vadc_result *chan_rslt);
+/**
* qpnp_adc_scale_batt_id() - Scales the pre-calibrated digital output
* of an ADC to the ADC reference and compensates for the
* gain and offset.
@@ -1233,6 +1280,11 @@
*/
int32_t qpnp_vadc_iadc_sync_complete_request(
enum qpnp_vadc_channels channel, struct qpnp_vadc_result *result);
+/**
+ * qpnp_vadc_sns_comp_result() - Compensate vbatt readings based on temperature
+ * @result: Voltage in uV that needs compensation.
+ */
+int32_t qpnp_vbat_sns_comp_result(int64_t *result);
#else
static inline int32_t qpnp_vadc_read(uint32_t channel,
struct qpnp_vadc_result *result)
@@ -1257,6 +1309,11 @@
const struct qpnp_vadc_chan_properties *chan_prop,
struct qpnp_vadc_result *chan_rslt)
{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_qrd_batt_therm(int32_t adc_code,
+ const struct qpnp_adc_properties *adc_prop,
+ const struct qpnp_vadc_chan_properties *chan_prop,
+ struct qpnp_vadc_result *chan_rslt);
+{ return -ENXIO; }
static inline int32_t qpnp_adc_scale_batt_id(int32_t adc_code,
const struct qpnp_adc_properties *adc_prop,
const struct qpnp_vadc_chan_properties *chan_prop,
@@ -1312,6 +1369,8 @@
enum qpnp_vadc_channels channel,
struct qpnp_vadc_result *result)
{ return -ENXIO; }
+static inline int32_t qpnp_vbat_sns_comp_result(int64_t *result)
+{ return -ENXIO; }
#endif
/* Public API */
@@ -1365,6 +1424,7 @@
* @result: 0 on success.
*/
int32_t qpnp_iadc_calibrate_for_trim(void);
+int32_t qpnp_iadc_comp_result(int64_t *result);
#else
static inline int32_t qpnp_iadc_read(enum qpnp_iadc_channels channel,
struct qpnp_iadc_result *result)
@@ -1382,6 +1442,8 @@
{ return -ENXIO; }
static inline int32_t qpnp_iadc_calibrate_for_trim(void)
{ return -ENXIO; }
+static inline int32_t qpnp_iadc_comp_result(int64_t *result, int32_t sign)
+{ return -ENXIO; }
#endif
/* Public API */
diff --git a/include/linux/regulator/cpr-regulator.h b/include/linux/regulator/cpr-regulator.h
index b6fc091..6387913 100644
--- a/include/linux/regulator/cpr-regulator.h
+++ b/include/linux/regulator/cpr-regulator.h
@@ -35,7 +35,6 @@
CPR_CORNER_SVS = 1,
CPR_CORNER_NORMAL,
CPR_CORNER_TURBO,
- CPR_CORNER_SUPER_TURBO,
CPR_CORNER_MAX,
};
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 67889bf..952bcb1 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -2726,6 +2726,8 @@
#endif /* CONFIG_SMP */
+extern struct atomic_notifier_head migration_notifier_head;
+
extern long sched_setaffinity(pid_t pid, const struct cpumask *new_mask);
extern long sched_getaffinity(pid_t pid, struct cpumask *mask);
diff --git a/include/linux/spmi.h b/include/linux/spmi.h
index d179eab..e8e932e 100644
--- a/include/linux/spmi.h
+++ b/include/linux/spmi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -54,7 +54,7 @@
struct spmi_controller {
struct device dev;
unsigned int nr;
- struct list_head list;
+ struct completion dev_released;
int (*cmd)(struct spmi_controller *, u8 opcode, u8 sid);
int (*read_cmd)(struct spmi_controller *,
u8 opcode, u8 sid, u16 addr, u8 bc, u8 *buf);
diff --git a/include/linux/sync.h b/include/linux/sync.h
index 31ba6ec..f4ac10e 100644
--- a/include/linux/sync.h
+++ b/include/linux/sync.h
@@ -101,7 +101,7 @@
struct sync_timeline {
struct kref kref;
const struct sync_timeline_ops *ops;
- char name[32];
+ char name[64];
/* protected by child_list_lock */
bool destroyed;
diff --git a/include/linux/topology.h b/include/linux/topology.h
index 92a89f0..c2d9c17 100644
--- a/include/linux/topology.h
+++ b/include/linux/topology.h
@@ -118,7 +118,7 @@
#define SD_MC_INIT (struct sched_domain) { \
.min_interval = 1, \
.max_interval = 4, \
- .busy_factor = 64, \
+ .busy_factor = 1, \
.imbalance_pct = 125, \
.cache_nice_tries = 1, \
.busy_idx = 2, \
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 5e73bd9..f9729c4 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -222,6 +222,7 @@
bool enable_lpm_on_dev_suspend;
bool core_clk_always_on_workaround;
bool delay_lpm_on_disconnect;
+ bool delay_lpm_hndshk_on_disconnect;
bool dp_manual_pullup;
bool enable_sec_phy;
struct msm_bus_scale_pdata *bus_scale_table;
@@ -422,6 +423,9 @@
u32 standalone_latency;
bool pool_64_bit_align;
bool enable_hbm;
+ bool disable_park_mode;
+ bool consider_ipa_handshake;
+ bool ahb_async_bridge_bypass;
};
struct msm_usb_host_platform_data {
@@ -461,10 +465,21 @@
int (*notify)(void *, int, void (*)(int online));
void *ctxt;
};
-
+#ifdef CONFIG_USB_BAM
+bool msm_bam_lpm_ok(void);
+void msm_bam_set_hsic_host_dev(struct device *dev);
+void msm_bam_wait_for_hsic_prod_granted(void);
+bool msm_bam_hsic_lpm_ok(void);
+void msm_bam_hsic_notify_on_resume(void);
+#else
+static inline bool msm_bam_lpm_ok(void) { return true; }
+static inline void msm_bam_set_hsic_host_dev(struct device *dev) {}
+static inline void msm_bam_wait_for_hsic_prod_granted(void) {}
+static inline bool msm_bam_hsic_lpm_ok(void) { return true; }
+static inline void msm_bam_hsic_notify_on_resume(void) {}
+#endif
#ifdef CONFIG_USB_CI13XXX_MSM
void msm_hw_bam_disable(bool bam_disable);
-
#else
static inline void msm_hw_bam_disable(bool bam_disable)
{
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 74b09cb..c6da2c3 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -700,6 +700,9 @@
#define V4L2_QCOM_BUF_FLAG_CODECCONFIG 0x4000
#define V4L2_QCOM_BUF_FLAG_EOSEQ 0x8000
#define V4L2_QCOM_BUF_TIMESTAMP_INVALID 0x10000
+#define V4L2_QCOM_BUF_FLAG_IDRFRAME 0x20000 /* Image is a IDR-frame */
+#define V4L2_QCOM_BUF_FLAG_DECODEONLY 0x40000
+#define V4L2_QCOM_BUF_DATA_CORRUPT 0x80000
/*
* O V E R L A Y P R E V I E W
@@ -1668,13 +1671,6 @@
/* MPEG-class control IDs specific to the Samsung MFC 5.1 driver as defined by V4L2 */
#define V4L2_CID_MPEG_MFC51_BASE (V4L2_CTRL_CLASS_MPEG | 0x1100)
-#define V4L2_CID_MPEG_QCOM_BASE (V4L2_CTRL_CLASS_MPEG | 0x2100)
-
-#define V4L2_CID_MPEG_QCOM_SET_PERF_LEVEL (V4L2_CID_MPEG_QCOM_BASE + 0)
-enum v3l2_mpeg_qcom_perf_level {
- V4L2_CID_MPEG_QCOM_PERF_LEVEL_PERFORMANCE = 0,
- V4L2_CID_MPEG_QCOM_PERF_LEVEL_TURBO = 1,
-};
#define V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY (V4L2_CID_MPEG_MFC51_BASE+0)
#define V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE (V4L2_CID_MPEG_MFC51_BASE+1)
@@ -1837,6 +1833,7 @@
V4L2_MPEG_VIDC_INDEX_EXTRADATA_INPUT_CROP,
V4L2_MPEG_VIDC_INDEX_EXTRADATA_DIGITAL_ZOOM,
V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO,
+ V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP
};
#define V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO \
(V4L2_CID_MPEG_MSM_VIDC_BASE + 23)
@@ -1847,9 +1844,11 @@
#define V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL (V4L2_CID_MPEG_MSM_VIDC_BASE + 26)
enum v4l2_mpeg_vidc_perf_level {
- V4L2_CID_MPEG_VIDC_PERF_LEVEL_PERFORMANCE = 0,
- V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO = 1,
+ V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL = 0,
+ V4L2_CID_MPEG_VIDC_PERF_LEVEL_PERFORMANCE = 1,
+ V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO = 2,
};
+
#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_GOB \
(V4L2_CID_MPEG_MSM_VIDC_BASE+27)
#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_DELIVERY_MODE \
diff --git a/include/linux/wcnss_wlan.h b/include/linux/wcnss_wlan.h
index 2319c48..2ba585e 100644
--- a/include/linux/wcnss_wlan.h
+++ b/include/linux/wcnss_wlan.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, 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
@@ -33,6 +33,8 @@
#define WCNSS_WLAN_IRQ_INVALID -1
#define HAVE_WCNSS_SUSPEND_RESUME_NOTIFY 1
#define HAVE_WCNSS_RESET_INTR 1
+#define HAVE_WCNSS_CAL_DOWNLOAD 1
+#define HAVE_WCNSS_RX_BUFF_COUNT 1
struct device *wcnss_wlan_get_device(void);
struct resource *wcnss_wlan_get_memory_map(struct device *dev);
@@ -65,7 +67,10 @@
void wcnss_resume_notify(void);
void wcnss_riva_log_debug_regs(void);
void wcnss_pronto_log_debug_regs(void);
-int wcnss_cold_boot_done(void);
+int wcnss_device_ready(void);
+void wcnss_riva_dump_pmic_regs(void);
+int wcnss_xo_auto_detect_enabled(void);
+u32 wcnss_get_wlan_rx_buff_count(void);
#define wcnss_wlan_get_drvdata(dev) dev_get_drvdata(dev)
#define wcnss_wlan_set_drvdata(dev, data) dev_set_drvdata((dev), (data))
diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h
index bce6af3..31798d6 100644
--- a/include/media/msm_cam_sensor.h
+++ b/include/media/msm_cam_sensor.h
@@ -108,10 +108,10 @@
SUB_MODULE_EEPROM,
SUB_MODULE_LED_FLASH,
SUB_MODULE_STROBE_FLASH,
- SUB_MODULE_CSIPHY,
- SUB_MODULE_CSIPHY_3D,
SUB_MODULE_CSID,
SUB_MODULE_CSID_3D,
+ SUB_MODULE_CSIPHY,
+ SUB_MODULE_CSIPHY_3D,
SUB_MODULE_MAX,
};
@@ -184,6 +184,18 @@
uint16_t delay;
};
+struct msm_camera_i2c_array_write_config {
+ struct msm_camera_i2c_reg_setting conf_array;
+ uint16_t slave_addr;
+};
+
+struct msm_camera_i2c_read_config {
+ uint16_t slave_addr;
+ uint16_t reg_addr;
+ enum msm_camera_i2c_data_type data_type;
+ uint16_t *data;
+};
+
struct msm_camera_csid_vc_cfg {
uint8_t cid;
uint8_t dt;
@@ -207,6 +219,7 @@
uint8_t settle_cnt;
uint16_t lane_mask;
uint8_t combo_mode;
+ uint8_t csid_core;
};
struct msm_camera_csi2_params {
@@ -288,8 +301,8 @@
enum eeprom_cfg_type_t {
CFG_EEPROM_GET_INFO,
- CFG_EEPROM_GET_DATA,
- CFG_EEPROM_READ_DATA,
+ CFG_EEPROM_GET_CAL_DATA,
+ CFG_EEPROM_READ_CAL_DATA,
CFG_EEPROM_WRITE_DATA,
};
struct eeprom_get_t {
@@ -319,7 +332,9 @@
enum msm_sensor_cfg_type_t {
CFG_SET_SLAVE_INFO,
+ CFG_SLAVE_READ_I2C,
CFG_WRITE_I2C_ARRAY,
+ CFG_SLAVE_WRITE_I2C_ARRAY,
CFG_WRITE_I2C_SEQ_ARRAY,
CFG_POWER_UP,
CFG_POWER_DOWN,
diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h
index afd5a42..b4b3bfc 100644
--- a/include/media/msm_camera.h
+++ b/include/media/msm_camera.h
@@ -1396,6 +1396,7 @@
uint8_t settle_cnt;
uint16_t lane_mask;
uint8_t combo_mode;
+ uint8_t csid_core;
};
struct msm_camera_csi2_params {
diff --git a/include/media/msm_gemini.h b/include/media/msm_gemini.h
index 0167335..2209758 100644
--- a/include/media/msm_gemini.h
+++ b/include/media/msm_gemini.h
@@ -51,10 +51,19 @@
#define MSM_GMN_IOCTL_TEST_DUMP_REGION \
_IOW(MSM_GMN_IOCTL_MAGIC, 15, unsigned long)
+#define MSM_GMN_IOCTL_SET_MODE \
+ _IOW(MSM_GMN_IOCTL_MAGIC, 16, enum msm_gmn_out_mode)
+
#define MSM_GEMINI_MODE_REALTIME_ENCODE 0
#define MSM_GEMINI_MODE_OFFLINE_ENCODE 1
#define MSM_GEMINI_MODE_REALTIME_ROTATION 2
#define MSM_GEMINI_MODE_OFFLINE_ROTATION 3
+
+enum msm_gmn_out_mode {
+ MSM_GMN_OUTMODE_FRAGMENTED,
+ MSM_GMN_OUTMODE_SINGLE
+};
+
struct msm_gemini_ctrl_cmd {
uint32_t type;
uint32_t len;
diff --git a/include/media/msm_media_info.h b/include/media/msm_media_info.h
index 993a4ab..65831db 100644
--- a/include/media/msm_media_info.h
+++ b/include/media/msm_media_info.h
@@ -7,6 +7,7 @@
enum color_fmts {
COLOR_FMT_NV12,
+ COLOR_FMT_NV21,
};
static inline unsigned int VENUS_Y_STRIDE(int color_fmt, int width)
@@ -16,6 +17,7 @@
goto invalid_input;
switch (color_fmt) {
+ case COLOR_FMT_NV21:
case COLOR_FMT_NV12:
alignment = 128;
stride = MSM_MEDIA_ALIGN(width, alignment);
@@ -34,6 +36,7 @@
goto invalid_input;
switch (color_fmt) {
+ case COLOR_FMT_NV21:
case COLOR_FMT_NV12:
alignment = 128;
stride = MSM_MEDIA_ALIGN(width, alignment);
@@ -52,6 +55,7 @@
goto invalid_input;
switch (color_fmt) {
+ case COLOR_FMT_NV21:
case COLOR_FMT_NV12:
alignment = 32;
sclines = MSM_MEDIA_ALIGN(height, alignment);
@@ -70,6 +74,7 @@
goto invalid_input;
switch (color_fmt) {
+ case COLOR_FMT_NV21:
case COLOR_FMT_NV12:
alignment = 16;
sclines = MSM_MEDIA_ALIGN(((height + 1) >> 1), alignment);
@@ -96,6 +101,7 @@
y_sclines = VENUS_Y_SCANLINES(color_fmt, height);
uv_sclines = VENUS_UV_SCANLINES(color_fmt, height);
switch (color_fmt) {
+ case COLOR_FMT_NV21:
case COLOR_FMT_NV12:
uv_alignment = 4096;
y_plane = y_stride * y_sclines;
diff --git a/include/media/msm_vidc.h b/include/media/msm_vidc.h
index f632ad6..2164275 100644
--- a/include/media/msm_vidc.h
+++ b/include/media/msm_vidc.h
@@ -73,6 +73,16 @@
unsigned int aspect_height;
};
+struct msm_vidc_mpeg2_seqdisp_payload {
+ unsigned int video_format;
+ bool color_descp;
+ unsigned int color_primaries;
+ unsigned int transfer_char;
+ unsigned int matrix_coeffs;
+ unsigned int disp_width;
+ unsigned int disp_height;
+};
+
struct msm_vidc_panscan_window {
unsigned int panscan_height_offset;
unsigned int panscan_width_offset;
@@ -94,6 +104,7 @@
EXTRADATA_FRAME_RATE = 0x00000007,
EXTRADATA_PANSCAN_WINDOW = 0x00000008,
EXTRADATA_RECOVERY_POINT_SEI = 0x00000009,
+ EXTRADATA_MPEG2_SEQDISP = 0x0000000D,
EXTRADATA_MULTISLICE_INFO = 0x7F100000,
EXTRADATA_NUM_CONCEALED_MB = 0x7F100001,
EXTRADATA_INDEX = 0x7F100002,
diff --git a/include/media/msmb_camera.h b/include/media/msmb_camera.h
index 21a1c44..388b308 100644
--- a/include/media/msmb_camera.h
+++ b/include/media/msmb_camera.h
@@ -14,6 +14,9 @@
#define MSM_CAM_V4L2_IOCTL_CMD_ACK \
_IOW('V', BASE_VIDIOC_PRIVATE + 32, struct v4l2_event)
+#define MSM_CAM_V4L2_IOCTL_NOTIFY_ERROR \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 33, struct v4l2_event)
+
#define QCAMERA_DEVICE_GROUP_ID 1
#define QCAMERA_VNODE_GROUP_ID 2
#define MSM_CAMERA_NAME "msm_camera"
diff --git a/include/media/msmb_isp.h b/include/media/msmb_isp.h
index 6fb1a65..4d36688 100644
--- a/include/media/msmb_isp.h
+++ b/include/media/msmb_isp.h
@@ -64,6 +64,7 @@
EVERY_8FRAME,
EVERY_16FRAME,
EVERY_32FRAME,
+ SKIP_ALL,
MAX_SKIP,
};
@@ -160,18 +161,20 @@
ENABLE_STREAM_BUF_DIVERT,
DISABLE_STREAM_BUF_DIVERT,
UPDATE_STREAM_FRAMEDROP_PATTERN,
+ UPDATE_STREAM_AXI_CONFIG,
+};
+
+struct msm_vfe_axi_stream_cfg_update_info {
+ uint32_t stream_handle;
+ uint32_t output_format;
+ enum msm_vfe_frame_skip_pattern skip_pattern;
+ struct msm_vfe_axi_plane_cfg plane_cfg[MAX_PLANES_PER_STREAM];
};
struct msm_vfe_axi_stream_update_cmd {
- uint32_t stream_handle;
+ uint32_t num_streams;
enum msm_vfe_axi_stream_update_type update_type;
- enum msm_vfe_frame_skip_pattern skip_pattern;
-};
-
-enum msm_vfe_stats_pipeline_policy {
- STATS_COMP_ALL,
- STATS_COMP_NONE,
- MAX_STATS_POLICY,
+ struct msm_vfe_axi_stream_cfg_update_info update_info[MAX_NUM_STREAM];
};
enum msm_isp_stats_type {
@@ -193,11 +196,11 @@
uint32_t session_id;
uint32_t stream_id;
enum msm_isp_stats_type stats_type;
+ uint32_t composite_flag;
uint32_t framedrop_pattern;
uint32_t irq_subsample_pattern;
uint32_t buffer_offset;
uint32_t stream_handle;
- uint8_t comp_flag;
};
struct msm_vfe_stats_stream_release_cmd {
@@ -209,12 +212,6 @@
uint8_t enable;
};
-struct msm_vfe_stats_comp_policy_cfg {
- enum msm_vfe_stats_pipeline_policy stats_pipeline_policy;
- uint32_t comp_framedrop_pattern;
- uint32_t comp_irq_subsample_pattern;
-};
-
enum msm_vfe_reg_cfg_type {
VFE_WRITE,
VFE_WRITE_MB,
@@ -319,7 +316,7 @@
#define ISP_EVENT_EOF (ISP_EVENT_BASE + ISP_EOF)
#define ISP_EVENT_BUF_DIVERT (ISP_BUF_EVENT_BASE)
#define ISP_EVENT_STATS_NOTIFY (ISP_STATS_EVENT_BASE)
-
+#define ISP_EVENT_COMP_STATS_NOTIFY (ISP_EVENT_STATS_NOTIFY + MSM_ISP_STATS_MAX)
/* The msm_v4l2_event_data structure should match the
* v4l2_event.u.data field.
* should not exceed 64 bytes */
@@ -328,6 +325,7 @@
uint32_t session_id;
uint32_t stream_id;
uint32_t handle;
+ uint32_t output_format;
int8_t buf_idx;
};
struct msm_isp_stats_event {
@@ -346,6 +344,8 @@
*which use monotonic clock
*/
struct timeval timestamp;
+ /* Monotonic timestamp since bootup */
+ struct timeval mono_timestamp;
/* if pix is a src frame_id is from camif */
uint32_t frame_id;
union {
@@ -373,6 +373,9 @@
#define V4L2_PIX_FMT_QGBRG12 v4l2_fourcc('Q', 'G', 'B', '2')
#define V4L2_PIX_FMT_QGRBG12 v4l2_fourcc('Q', 'G', 'R', '2')
#define V4L2_PIX_FMT_QRGGB12 v4l2_fourcc('Q', 'R', 'G', '2')
+#define V4L2_PIX_FMT_NV14 v4l2_fourcc('N', 'V', '1', '4')
+#define V4L2_PIX_FMT_NV41 v4l2_fourcc('N', 'V', '4', '1')
+#define V4L2_PIX_FMT_META v4l2_fourcc('Q', 'M', 'E', 'T')
#define VIDIOC_MSM_VFE_REG_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_vfe_cfg_cmd2)
@@ -412,10 +415,6 @@
_IOWR('V', BASE_VIDIOC_PRIVATE+11, \
struct msm_vfe_stats_stream_release_cmd)
-#define VIDIOC_MSM_ISP_CFG_STATS_COMP_POLICY \
- _IOWR('V', BASE_VIDIOC_PRIVATE+12, \
- struct msm_vfe_stats_comp_policy_cfg)
-
#define VIDIOC_MSM_ISP_UPDATE_STREAM \
_IOWR('V', BASE_VIDIOC_PRIVATE+13, struct msm_vfe_axi_stream_update_cmd)
diff --git a/include/media/msmb_ispif.h b/include/media/msmb_ispif.h
index c9eb12a..61dfd52 100644
--- a/include/media/msmb_ispif.h
+++ b/include/media/msmb_ispif.h
@@ -1,8 +1,10 @@
#ifndef MSM_CAM_ISPIF_H
#define MSM_CAM_ISPIF_H
-#define CSID_VERSION_V2 0x02000011
-#define CSID_VERSION_V3 0x30000000
+#define CSID_VERSION_V20 0x02000011
+#define CSID_VERSION_V22 0x02001000
+#define CSID_VERSION_V30 0x30000000
+#define CSID_VERSION_V3 0x30000000
enum msm_ispif_vfe_intf {
VFE0,
diff --git a/include/media/msmb_pproc.h b/include/media/msmb_pproc.h
index d7064a0..8e9aedf 100644
--- a/include/media/msmb_pproc.h
+++ b/include/media/msmb_pproc.h
@@ -18,6 +18,11 @@
MSM_CPP_REALTIME_FRAME,
};
+enum msm_vpe_frame_type {
+ MSM_VPE_OFFLINE_FRAME,
+ MSM_VPE_REALTIME_FRAME,
+};
+
struct msm_cpp_frame_strip_info {
int scale_v_en;
int scale_h_en;
@@ -108,9 +113,10 @@
struct timeval in_time, out_time;
void *cookie;
int32_t *status;
-
+ int32_t duplicate_output;
+ uint32_t duplicate_identity;
struct msm_cpp_buffer_info_t input_buffer_info;
- struct msm_cpp_buffer_info_t output_buffer_info;
+ struct msm_cpp_buffer_info_t output_buffer_info[2];
};
struct cpp_hw_info {
@@ -118,6 +124,57 @@
uint32_t cpp_hw_caps;
};
+struct msm_vpe_frame_strip_info {
+ uint32_t src_w;
+ uint32_t src_h;
+ uint32_t dst_w;
+ uint32_t dst_h;
+ uint32_t src_x;
+ uint32_t src_y;
+ uint32_t phase_step_x;
+ uint32_t phase_step_y;
+ uint32_t phase_init_x;
+ uint32_t phase_init_y;
+};
+
+struct msm_vpe_buffer_info_t {
+ int fd;
+ uint32_t index;
+ uint32_t offset;
+ uint8_t native_buff;
+ uint8_t processed_divert;
+};
+
+struct msm_vpe_stream_buff_info_t {
+ uint32_t identity;
+ uint32_t num_buffs;
+ struct msm_vpe_buffer_info_t *buffer_info;
+};
+
+struct msm_vpe_frame_info_t {
+ int32_t frame_id;
+ struct timeval timestamp;
+ uint32_t inst_id;
+ uint32_t identity;
+ uint32_t client_id;
+ enum msm_vpe_frame_type frame_type;
+ struct msm_vpe_frame_strip_info strip_info;
+ int src_fd;
+ int dst_fd;
+ struct ion_handle *src_ion_handle;
+ struct ion_handle *dest_ion_handle;
+ unsigned long src_phyaddr;
+ unsigned long dest_phyaddr;
+ unsigned long src_chroma_plane_offset;
+ unsigned long dest_chroma_plane_offset;
+ struct timeval in_time, out_time;
+ void *cookie;
+
+ struct msm_vpe_buffer_info_t input_buffer_info;
+ struct msm_vpe_buffer_info_t output_buffer_info;
+};
+
+
#define VIDIOC_MSM_CPP_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_camera_v4l2_ioctl_t)
@@ -142,7 +199,27 @@
#define VIDIOC_MSM_CPP_DEQUEUE_STREAM_BUFF_INFO \
_IOWR('V', BASE_VIDIOC_PRIVATE + 7, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_VPE_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 8, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_VPE_TRANSACTION_SETUP \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 9, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_VPE_GET_EVENTPAYLOAD \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 10, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_VPE_GET_INST_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 11, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_VPE_ENQUEUE_STREAM_BUFF_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 12, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_VPE_DEQUEUE_STREAM_BUFF_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 13, struct msm_camera_v4l2_ioctl_t)
+
#define V4L2_EVENT_CPP_FRAME_DONE (V4L2_EVENT_PRIVATE_START + 0)
+#define V4L2_EVENT_VPE_FRAME_DONE (V4L2_EVENT_PRIVATE_START + 1)
struct msm_camera_v4l2_ioctl_t {
uint32_t id;
diff --git a/include/media/radio-iris-commands.h b/include/media/radio-iris-commands.h
index d41baa9..0b3331a 100644
--- a/include/media/radio-iris-commands.h
+++ b/include/media/radio-iris-commands.h
@@ -71,6 +71,7 @@
V4L2_CID_PRIVATE_CF0TH12,
V4L2_CID_PRIVATE_SINRFIRSTSTAGE,
V4L2_CID_PRIVATE_RMSSIFIRSTSTAGE,
+ V4L2_CID_PRIVATE_RXREPEATCOUNT,
/*using private CIDs under userclass*/
V4L2_CID_PRIVATE_IRIS_READ_DEFAULT = 0x00980928,
diff --git a/include/media/radio-iris.h b/include/media/radio-iris.h
index 53602c5..d6151c0 100644
--- a/include/media/radio-iris.h
+++ b/include/media/radio-iris.h
@@ -73,6 +73,9 @@
#define CF0TH12_BYTE4_OFFSET 11
#define MAX_SINR_FIRSTSTAGE 127
#define MAX_RMSSI_FIRSTSTAGE 127
+#define RDS_PS0_XFR_MODE 0x01
+#define RDS_PS0_LEN 6
+#define RX_REPEATE_BYTE_OFFSET 5
/* HCI timeouts */
#define RADIO_HCI_TIMEOUT (10000) /* 10 seconds */
diff --git a/include/media/tavarua.h b/include/media/tavarua.h
index 881b851..fea09dd 100644
--- a/include/media/tavarua.h
+++ b/include/media/tavarua.h
@@ -18,6 +18,7 @@
#define BYTES_PER_BLOCK (3)
#define MAX_PS_LENGTH (96)
#define MAX_RT_LENGTH (64)
+#define RX_STATIONS0_LEN (15)
#define XFRDAT0 (0x20)
#define XFRDAT1 (0x21)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 42c8823..35c57c0 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -1,6 +1,6 @@
/*
BlueZ - Bluetooth protocol stack for Linux
- Copyright (c) 2000-2001, 2010-2012 The Linux Foundation. All rights reserved.
+ Copyright (c) 2000-2001, 2010-2013 The Linux Foundation. All rights reserved.
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
@@ -1145,6 +1145,11 @@
__le16 opcode;
} __packed;
+#define HCI_EV_HARDWARE_ERROR 0x10
+struct hci_ev_hardware_error {
+ __u8 hw_err_code;
+} __packed;
+
#define HCI_EV_ROLE_CHANGE 0x12
struct hci_ev_role_change {
__u8 status;
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index dc29eb9..fb2b57a 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -416,6 +416,26 @@
size_t probe_resp_len;
};
+struct mac_address {
+ u8 addr[ETH_ALEN];
+};
+
+/**
+ * struct cfg80211_acl_data - Access control list data
+ *
+ * @acl_policy: ACL policy to be applied on the station's
+ * entry specified by mac_addr
+ * @n_acl_entries: Number of MAC address entries passed
+ * @mac_addrs: List of MAC addresses of stations to be used for ACL
+ */
+struct cfg80211_acl_data {
+ enum nl80211_acl_policy acl_policy;
+ int n_acl_entries;
+
+ /* Keep it last */
+ struct mac_address mac_addrs[];
+};
+
/**
* struct cfg80211_ap_settings - AP configuration
*
@@ -432,6 +452,8 @@
* @privacy: the BSS uses privacy
* @auth_type: Authentication type (algorithm)
* @inactivity_timeout: time in seconds to determine station's inactivity.
+ * @acl: ACL configuration used by the drivers which has support for
+ * MAC address based access control
*/
struct cfg80211_ap_settings {
struct cfg80211_beacon_data beacon;
@@ -444,6 +466,7 @@
bool privacy;
enum nl80211_auth_type auth_type;
int inactivity_timeout;
+ const struct cfg80211_acl_data *acl;
};
/**
@@ -1559,6 +1582,13 @@
* later passes to cfg80211_probe_status().
*
* @set_noack_map: Set the NoAck Map for the TIDs.
+ * @set_mac_acl: Sets MAC address control list in AP and P2P GO mode.
+ * Parameters include ACL policy, an array of MAC address of stations
+ * and the number of MAC addresses. If there is already a list in driver
+ * this new list replaces the existing one. Driver has to clear its ACL
+ * when number of MAC addresses entries is passed as 0. Drivers which
+ * advertise the support for MAC based ACL have to implement this callback.
+ *
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -1757,6 +1787,9 @@
struct ieee80211_channel *(*get_channel)(struct wiphy *wiphy);
int (*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_update_ft_ies_params *ftie);
+
+ int (*set_mac_acl)(struct wiphy *wiphy, struct net_device *dev,
+ const struct cfg80211_acl_data *params);
};
/*
@@ -1924,10 +1957,6 @@
bool beacon_int_infra_match;
};
-struct mac_address {
- u8 addr[ETH_ALEN];
-};
-
struct ieee80211_txrx_stypes {
u16 tx, rx;
};
@@ -2068,6 +2097,9 @@
* @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features.
* @ht_capa_mod_mask: Specify what ht_cap values can be over-ridden.
* If null, then none can be over-ridden.
+ *
+ * @max_acl_mac_addrs: Maximum number of MAC addresses that the device
+ * supports for ACL.
*/
struct wiphy {
/* assign these fields before you register the wiphy */
@@ -2089,6 +2121,8 @@
/* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */
u16 interface_modes;
+ u16 max_acl_mac_addrs;
+
u32 flags, features;
u32 ap_sme_capa;
diff --git a/include/sound/Kbuild b/include/sound/Kbuild
index edadaa9..60847b0 100644
--- a/include/sound/Kbuild
+++ b/include/sound/Kbuild
@@ -12,3 +12,4 @@
header-y += compress_params.h
header-y += compress_offload.h
header-y += lsm_params.h
+header-y += voice_params.h
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index fa4dedc..0d3b5a0 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -634,8 +634,6 @@
* when a new port is added here. */
#define PRIMARY_I2S_RX 0 /* index = 0 */
#define PRIMARY_I2S_TX 1 /* index = 1 */
-#define PCM_RX 2 /* index = 2 */
-#define PCM_TX 3 /* index = 3 */
#define SECONDARY_I2S_RX 4 /* index = 4 */
#define SECONDARY_I2S_TX 5 /* index = 5 */
#define MI2S_RX 6 /* index = 6 */
@@ -784,26 +782,6 @@
#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX 0x4008
/* SLIMbus Tx port on channel 4. */
#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX 0x4009
-/* SLIMbus Rx port on channel 0. */
-#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX 0x4000
-/* SLIMbus Tx port on channel 0. */
-#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX 0x4001
-/* SLIMbus Rx port on channel 1. */
-#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX 0x4002
-/* SLIMbus Tx port on channel 1. */
-#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX 0x4003
-/* SLIMbus Rx port on channel 2. */
-#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX 0x4004
-/* SLIMbus Tx port on channel 2. */
-#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX 0x4005
-/* SLIMbus Rx port on channel 3. */
-#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX 0x4006
-/* SLIMbus Tx port on channel 3. */
-#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX 0x4007
-/* SLIMbus Rx port on channel 4. */
-#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX 0x4008
-/* SLIMbus Tx port on channel 4. */
-#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX 0x4009
/* SLIMbus Rx port on channel 5. */
#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX 0x400a
/* SLIMbus Tx port on channel 5. */
@@ -2259,7 +2237,7 @@
*/
#define ADSP_MEMORY_MAP_PHYSICAL_MEMORY 0
-
+#define NULL_COPP_TOPOLOGY 0x00010312
#define DEFAULT_COPP_TOPOLOGY 0x00010be3
#define DEFAULT_POPP_TOPOLOGY 0x00010be4
#define VPM_TX_SM_ECNS_COPP_TOPOLOGY 0x00010F71
@@ -6538,6 +6516,11 @@
#define AANC_HW_BLOCK_VERSION_1 (1)
#define AANC_HW_BLOCK_VERSION_2 (2)
+/*Clip bank selection*/
+#define AFE_API_VERSION_CLIP_BANK_SEL_CFG 0x1
+#define AFE_CLIP_MAX_BANKS 4
+#define AFE_PARAM_ID_CLIP_BANK_SEL_CFG 0x00010242
+
struct afe_param_aanc_port_cfg {
/* Minor version used for tracking the version of the module's
* source port configuration.
@@ -6572,6 +6555,18 @@
uint32_t aanc_hw_version;
} __packed;
+struct afe_param_id_clip_bank_sel {
+ /* Minor version used for tracking the version of the module's
+ * hw version
+ */
+ uint32_t minor_version;
+
+ /* Number of banks to be read */
+ uint32_t num_banks;
+
+ uint32_t bank_map[AFE_CLIP_MAX_BANKS];
+} __packed;
+
/* ERROR CODES */
/* Success. The operation completed with no errors. */
#define ADSP_EOK 0x00000000
@@ -6804,6 +6799,8 @@
AFE_SLIMBUS_SLAVE_CONFIG,
AFE_CDC_REGISTERS_CONFIG,
AFE_AANC_VERSION,
+ AFE_CDC_CLIP_REGISTERS_CONFIG,
+ AFE_CLIP_BANK_SEL,
AFE_MAX_CONFIG_TYPES,
};
@@ -6925,4 +6922,16 @@
/* Dolby DAP topology */
#define DOLBY_ADM_COPP_TOPOLOGY_ID 0x0001033B
+struct afe_svc_cmd_set_clip_bank_selection {
+ struct apr_hdr hdr;
+ struct afe_svc_cmd_set_param param;
+ struct afe_port_param_data_v2 pdata;
+ struct afe_param_id_clip_bank_sel bank_sel;
+} __packed;
+
+/* Ultrasound supported formats */
+#define US_POINT_EPOS_FORMAT 0x00012310
+#define US_RAW_FORMAT 0x0001127C
+#define US_PROX_FORMAT 0x0001272B
+
#endif /*_APR_AUDIO_V2_H_ */
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 40b0e1e..a7933a4 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -1262,7 +1262,7 @@
#define MPEG4_MULTI_AAC 0x00010D86
#define US_POINT_EPOS_FORMAT 0x00012310
#define US_RAW_FORMAT 0x0001127C
-#define US_PROX_FORMAT 0x00012721
+#define US_PROX_FORMAT 0x0001272B
#define MULTI_CHANNEL_PCM 0x00010C66
#define ASM_ENCDEC_SBCRATE 0x00010C13
@@ -1453,6 +1453,23 @@
u32 read_format;
} __attribute__((packed));
+#define ASM_STREAM_CMD_OPEN_LOOPBACK 0x00010D6E
+struct asm_stream_cmd_open_loopback {
+ struct apr_hdr hdr;
+ u32 mode_flags;
+/* Mode flags.
+ * Bit 0-31: reserved; client should set these bits to 0
+ */
+ u16 src_endpointype;
+ /* Endpoint type. 0 = Tx Matrix */
+ u16 sink_endpointype;
+ /* Endpoint type. 0 = Rx Matrix */
+ u32 postprocopo_id;
+/* Postprocessor topology ID. Specifies the topology of
+ * postprocessing algorithms.
+ */
+} __packed;
+
#define ADM_CMD_CONNECT_AFE_PORT 0x00010320
#define ADM_CMD_DISCONNECT_AFE_PORT 0x00010321
@@ -1909,5 +1926,4 @@
int srs_ss3d_open(int port_id, int srs_tech_id, void *srs_params);
/* SRS Studio Sound 3D end */
-
#endif /*_APR_AUDIO_H_*/
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 028e683..8984405 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -447,6 +447,7 @@
#endif
#endif
struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */
+ struct snd_kcontrol *vol_kctl; /* volume controls */
};
struct snd_pcm {
@@ -1128,4 +1129,28 @@
unsigned long private_value,
struct snd_pcm_chmap **info_ret);
+/*
+ * PCM Volume control API
+ */
+/* array element of volume */
+struct snd_pcm_volume_elem {
+ int volume;
+};
+
+/* pp information; retrieved via snd_kcontrol_chip() */
+struct snd_pcm_volume {
+ struct snd_pcm *pcm; /* assigned PCM instance */
+ int stream; /* PLAYBACK or CAPTURE */
+ struct snd_kcontrol *kctl;
+ const struct snd_pcm_volume_elem *volume;
+ int max_length;
+ void *private_data; /* optional: private data pointer */
+};
+
+int snd_pcm_add_volume_ctls(struct snd_pcm *pcm, int stream,
+ const struct snd_pcm_volume_elem *volume,
+ int max_length,
+ unsigned long private_value,
+ struct snd_pcm_volume **info_ret);
+
#endif /* __SOUND_PCM_H */
diff --git a/include/sound/q6adm-v2.h b/include/sound/q6adm-v2.h
index 4bea1e1..795bb99 100644
--- a/include/sound/q6adm-v2.h
+++ b/include/sound/q6adm-v2.h
@@ -47,13 +47,15 @@
int adm_memory_unmap_regions(int port_id, uint32_t *buf_add, uint32_t *bufsz,
uint32_t bufcnt);
-int adm_close(int port);
+int adm_close(int port, bool perf_mode);
int adm_matrix_map(int session_id, int path, int num_copps,
- unsigned int *port_id, int copp_id);
+ unsigned int *port_id, int copp_id, bool perf_mode);
int adm_connect_afe_port(int mode, int session_id, int port_id);
+void adm_ec_ref_rx_id(int port_id);
+
int adm_get_copp_id(int port_id);
void adm_set_multi_ch_map(char *channel_map);
diff --git a/include/sound/q6afe-v2.h b/include/sound/q6afe-v2.h
index 2a740f4..2e4c7c1 100644
--- a/include/sound/q6afe-v2.h
+++ b/include/sound/q6afe-v2.h
@@ -41,8 +41,8 @@
enum {
IDX_PRIMARY_I2S_RX = 0,
IDX_PRIMARY_I2S_TX = 1,
- IDX_PCM_RX = 2,
- IDX_PCM_TX = 3,
+ IDX_AFE_PORT_ID_PRIMARY_PCM_RX = 2,
+ IDX_AFE_PORT_ID_PRIMARY_PCM_TX = 3,
IDX_SECONDARY_I2S_RX = 4,
IDX_SECONDARY_I2S_TX = 5,
IDX_MI2S_RX = 6,
@@ -166,7 +166,7 @@
int afe_port_start(u16 port_id, union afe_port_config *afe_config,
u32 rate);
int afe_spk_prot_feed_back_cfg(int src_port, int dst_port,
- int l_ch, int r_ch);
+ int l_ch, int r_ch, u32 enable);
int afe_spk_prot_get_calib_data(struct afe_spkr_prot_get_vi_calib *calib);
int afe_port_stop_nowait(int port_id);
int afe_apply_gain(u16 port_id, u16 gain);
@@ -197,6 +197,8 @@
enum afe_mad_type afe_port_get_mad_type(u16 port_id);
int afe_set_config(enum afe_config_type config_type, void *config_data,
int arg);
+void afe_clear_config(enum afe_config_type config);
+bool afe_has_config(enum afe_config_type config);
void afe_set_aanc_info(struct aanc_data *aanc_info);
#endif /* __Q6AFE_V2_H__ */
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index 0dd14e6..2138689 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -70,7 +70,6 @@
#define COMPRESSED_IO 0x0040
#define NT_MODE 0x0400
-
#define NO_TIMESTAMP 0xFF00
#define SET_TIMESTAMP 0x0000
@@ -79,6 +78,19 @@
#define SESSION_MAX 0x08
+/* payload structure bytes */
+#define READDONE_IDX_STATUS 0
+#define READDONE_IDX_BUFADD_LSW 1
+#define READDONE_IDX_BUFADD_MSW 2
+#define READDONE_IDX_MEMMAP_HDL 3
+#define READDONE_IDX_SIZE 4
+#define READDONE_IDX_OFFSET 5
+#define READDONE_IDX_LSW_TS 6
+#define READDONE_IDX_MSW_TS 7
+#define READDONE_IDX_FLAGS 8
+#define READDONE_IDX_NUMFRAMES 9
+#define READDONE_IDX_SEQ_ID 10
+
#define SOFT_PAUSE_PERIOD 30 /* ramp up/down for 30ms */
#define SOFT_PAUSE_STEP 2000 /* Step value 2ms or 2000us */
enum {
@@ -153,6 +165,8 @@
wait_queue_head_t cmd_wait;
wait_queue_head_t time_wait;
bool perf_mode;
+ /* audio cache operations fptr*/
+ int (*fptr_cache_ops)(struct audio_buffer *abuff, int cache_op);
};
void q6asm_audio_client_free(struct audio_client *ac);
diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h
index 406407d..41f875b 100644
--- a/include/sound/q6asm.h
+++ b/include/sound/q6asm.h
@@ -207,6 +207,8 @@
uint32_t rd_format,
uint32_t wr_format);
+int q6asm_open_loopack(struct audio_client *ac);
+
int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
uint32_t lsw_ts, uint32_t flags);
int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
diff --git a/include/sound/q6lsm.h b/include/sound/q6lsm.h
index e9ca91d..d7a01a0 100644
--- a/include/sound/q6lsm.h
+++ b/include/sound/q6lsm.h
@@ -124,6 +124,7 @@
int q6lsm_start(struct lsm_client *client, bool wait);
int q6lsm_stop(struct lsm_client *client, bool wait);
int q6lsm_snd_model_buf_alloc(struct lsm_client *client, uint32_t len);
+int q6lsm_snd_model_buf_free(struct lsm_client *client);
int q6lsm_close(struct lsm_client *client);
int q6lsm_register_sound_model(struct lsm_client *client,
enum lsm_detection_mode mode, u16 minkeyword,
diff --git a/include/sound/voice_params.h b/include/sound/voice_params.h
new file mode 100644
index 0000000..43e3b9d
--- /dev/null
+++ b/include/sound/voice_params.h
@@ -0,0 +1,14 @@
+#ifndef __VOICE_PARAMS_H__
+#define __VOICE_PARAMS_H__
+
+#include <linux/types.h>
+#include <sound/asound.h>
+
+enum voice_lch_mode {
+ VOICE_LCH_START = 1,
+ VOICE_LCH_STOP
+};
+
+#define SNDRV_VOICE_IOCTL_LCH _IOW('U', 0x00, enum voice_lch_mode)
+
+#endif
diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h
index ea83664..951e6ca 100644
--- a/include/trace/events/cpufreq_interactive.h
+++ b/include/trace/events/cpufreq_interactive.h
@@ -28,13 +28,7 @@
__entry->actualfreq)
);
-DEFINE_EVENT(set, cpufreq_interactive_up,
- TP_PROTO(u32 cpu_id, unsigned long targfreq,
- unsigned long actualfreq),
- TP_ARGS(cpu_id, targfreq, actualfreq)
-);
-
-DEFINE_EVENT(set, cpufreq_interactive_down,
+DEFINE_EVENT(set, cpufreq_interactive_setspeed,
TP_PROTO(u32 cpu_id, unsigned long targfreq,
unsigned long actualfreq),
TP_ARGS(cpu_id, targfreq, actualfreq)
@@ -42,44 +36,50 @@
DECLARE_EVENT_CLASS(loadeval,
TP_PROTO(unsigned long cpu_id, unsigned long load,
- unsigned long curfreq, unsigned long targfreq),
- TP_ARGS(cpu_id, load, curfreq, targfreq),
+ unsigned long curtarg, unsigned long curactual,
+ unsigned long newtarg),
+ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg),
TP_STRUCT__entry(
__field(unsigned long, cpu_id )
__field(unsigned long, load )
- __field(unsigned long, curfreq )
- __field(unsigned long, targfreq )
+ __field(unsigned long, curtarg )
+ __field(unsigned long, curactual )
+ __field(unsigned long, newtarg )
),
TP_fast_assign(
__entry->cpu_id = cpu_id;
__entry->load = load;
- __entry->curfreq = curfreq;
- __entry->targfreq = targfreq;
+ __entry->curtarg = curtarg;
+ __entry->curactual = curactual;
+ __entry->newtarg = newtarg;
),
- TP_printk("cpu=%lu load=%lu cur=%lu targ=%lu",
- __entry->cpu_id, __entry->load, __entry->curfreq,
- __entry->targfreq)
+ TP_printk("cpu=%lu load=%lu cur=%lu actual=%lu targ=%lu",
+ __entry->cpu_id, __entry->load, __entry->curtarg,
+ __entry->curactual, __entry->newtarg)
);
DEFINE_EVENT(loadeval, cpufreq_interactive_target,
TP_PROTO(unsigned long cpu_id, unsigned long load,
- unsigned long curfreq, unsigned long targfreq),
- TP_ARGS(cpu_id, load, curfreq, targfreq)
+ unsigned long curtarg, unsigned long curactual,
+ unsigned long newtarg),
+ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg)
);
DEFINE_EVENT(loadeval, cpufreq_interactive_already,
TP_PROTO(unsigned long cpu_id, unsigned long load,
- unsigned long curfreq, unsigned long targfreq),
- TP_ARGS(cpu_id, load, curfreq, targfreq)
+ unsigned long curtarg, unsigned long curactual,
+ unsigned long newtarg),
+ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg)
);
DEFINE_EVENT(loadeval, cpufreq_interactive_notyet,
TP_PROTO(unsigned long cpu_id, unsigned long load,
- unsigned long curfreq, unsigned long targfreq),
- TP_ARGS(cpu_id, load, curfreq, targfreq)
+ unsigned long curtarg, unsigned long curactual,
+ unsigned long newtarg),
+ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg)
);
TRACE_EVENT(cpufreq_interactive_boost,
diff --git a/include/trace/events/kmem.h b/include/trace/events/kmem.h
index a1da44f..f36e4d5 100644
--- a/include/trace/events/kmem.h
+++ b/include/trace/events/kmem.h
@@ -314,7 +314,7 @@
TP_ARGS(client_name, heap_name, len, mask, flags),
TP_STRUCT__entry(
- __field(const char *, client_name)
+ __array(char, client_name, 64)
__field(const char *, heap_name)
__field(size_t, len)
__field(unsigned int, mask)
@@ -322,7 +322,7 @@
),
TP_fast_assign(
- __entry->client_name = client_name;
+ strlcpy(__entry->client_name, client_name, 64);
__entry->heap_name = heap_name;
__entry->len = len;
__entry->mask = mask;
@@ -495,6 +495,110 @@
TP_ARGS(mode)
);
+DECLARE_EVENT_CLASS(ion_alloc_pages,
+
+ TP_PROTO(gfp_t gfp_flags,
+ unsigned int order),
+
+ TP_ARGS(gfp_flags, order),
+
+ TP_STRUCT__entry(
+ __field(gfp_t, gfp_flags)
+ __field(unsigned int, order)
+ ),
+
+ TP_fast_assign(
+ __entry->gfp_flags = gfp_flags;
+ __entry->order = order;
+ ),
+
+ TP_printk("gfp_flags=%s order=%d",
+ show_gfp_flags(__entry->gfp_flags),
+ __entry->order)
+ );
+
+DEFINE_EVENT(ion_alloc_pages, alloc_pages_iommu_start,
+ TP_PROTO(gfp_t gfp_flags,
+ unsigned int order),
+
+ TP_ARGS(gfp_flags, order)
+ );
+
+DEFINE_EVENT(ion_alloc_pages, alloc_pages_iommu_end,
+ TP_PROTO(gfp_t gfp_flags,
+ unsigned int order),
+
+ TP_ARGS(gfp_flags, order)
+ );
+
+DEFINE_EVENT(ion_alloc_pages, alloc_pages_iommu_fail,
+ TP_PROTO(gfp_t gfp_flags,
+ unsigned int order),
+
+ TP_ARGS(gfp_flags, order)
+ );
+
+DEFINE_EVENT(ion_alloc_pages, alloc_pages_sys_start,
+ TP_PROTO(gfp_t gfp_flags,
+ unsigned int order),
+
+ TP_ARGS(gfp_flags, order)
+ );
+
+DEFINE_EVENT(ion_alloc_pages, alloc_pages_sys_end,
+ TP_PROTO(gfp_t gfp_flags,
+ unsigned int order),
+
+ TP_ARGS(gfp_flags, order)
+ );
+
+DEFINE_EVENT(ion_alloc_pages, alloc_pages_sys_fail,
+ TP_PROTO(gfp_t gfp_flags,
+ unsigned int order),
+
+ TP_ARGS(gfp_flags, order)
+
+ );
+
+DECLARE_EVENT_CLASS(smmu_map,
+
+ TP_PROTO(unsigned int va,
+ phys_addr_t pa,
+ unsigned int chunk_size,
+ size_t len),
+
+ TP_ARGS(va, pa, chunk_size, len),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, va)
+ __field(phys_addr_t, pa)
+ __field(unsigned int, chunk_size)
+ __field(size_t, len)
+ ),
+
+ TP_fast_assign(
+ __entry->va = va;
+ __entry->pa = pa;
+ __entry->chunk_size = chunk_size;
+ __entry->len = len;
+ ),
+
+ TP_printk("v_addr=%p p_addr=%pa chunk_size=0x%x len=%zu",
+ (void *)__entry->va,
+ &__entry->pa,
+ __entry->chunk_size,
+ __entry->len)
+ );
+
+DEFINE_EVENT(smmu_map, iommu_map_range,
+ TP_PROTO(unsigned int va,
+ phys_addr_t pa,
+ unsigned int chunk_size,
+ size_t len),
+
+ TP_ARGS(va, pa, chunk_size, len)
+ );
+
#endif /* _TRACE_KMEM_H */
/* This part must be outside protection */
diff --git a/include/trace/events/workqueue.h b/include/trace/events/workqueue.h
index 7d49729..82f61f4 100644
--- a/include/trace/events/workqueue.h
+++ b/include/trace/events/workqueue.h
@@ -54,7 +54,7 @@
__entry->function = work->func;
__entry->workqueue = cwq->wq;
__entry->req_cpu = req_cpu;
- __entry->cpu = cwq->gcwq->cpu;
+ __entry->cpu = cwq->pool->gcwq->cpu;
),
TP_printk("work struct=%p function=%pf workqueue=%p req_cpu=%u cpu=%u",
diff --git a/include/video/msm_hdmi_modes.h b/include/video/msm_hdmi_modes.h
index a15272b..ced6acb 100644
--- a/include/video/msm_hdmi_modes.h
+++ b/include/video/msm_hdmi_modes.h
@@ -122,9 +122,10 @@
#define HDMI_EVFRMT_END HDMI_VFRMT_4096x2160p24_16_9
/* VESA DMT TIMINGS */
-#define HDMI_VFRMT_2560x1600p60_16_9 (HDMI_EVFRMT_END + 1)
+#define HDMI_VFRMT_1024x768p60_4_3 (HDMI_EVFRMT_END + 1)
#define HDMI_VFRMT_1280x1024p60_5_4 (HDMI_EVFRMT_END + 2)
-#define VESA_DMT_VFRMT_END HDMI_VFRMT_1280x1024p60_5_4
+#define HDMI_VFRMT_2560x1600p60_16_9 (HDMI_EVFRMT_END + 3)
+#define VESA_DMT_VFRMT_END HDMI_VFRMT_2560x1600p60_16_9
#define HDMI_VFRMT_MAX (VESA_DMT_VFRMT_END + 1)
#define HDMI_VFRMT_FORCE_32BIT 0x7FFFFFFF
@@ -183,6 +184,9 @@
#define HDMI_VFRMT_1920x1080p30_16_9_TIMING \
{HDMI_VFRMT_1920x1080p30_16_9, 1920, 88, 44, 148, false, \
1080, 4, 5, 36, false, 74250, 30000, false, true}
+#define HDMI_VFRMT_1024x768p60_4_3_TIMING \
+ {HDMI_VFRMT_1024x768p60_4_3, 1024, 24, 136, 160, false, \
+ 768, 2, 6, 29, false, 65000, 60000, false, true}
#define HDMI_VFRMT_1280x1024p60_5_4_TIMING \
{HDMI_VFRMT_1280x1024p60_5_4, 1280, 48, 112, 248, false, \
1024, 1, 3, 38, false, 108000, 60000, false, true}
@@ -249,6 +253,7 @@
}
if (type & MSM_HDMI_MODES_DVI) {
+ MSM_HDMI_MODES_SET_TIMING(lut, HDMI_VFRMT_1024x768p60_4_3);
MSM_HDMI_MODES_SET_TIMING(lut, HDMI_VFRMT_1280x1024p60_5_4);
MSM_HDMI_MODES_SET_TIMING(lut, HDMI_VFRMT_2560x1600p60_16_9);
}
@@ -326,8 +331,9 @@
case HDMI_VFRMT_3840x2160p25_16_9: return "3840x2160 p25 16/9";
case HDMI_VFRMT_3840x2160p24_16_9: return "3840x2160 p24 16/9";
case HDMI_VFRMT_4096x2160p24_16_9: return "4096x2160 p24 16/9";
+ case HDMI_VFRMT_1024x768p60_4_3: return "1024x768 p60 4/3";
+ case HDMI_VFRMT_1280x1024p60_5_4: return "1280x1024 p60 5/4";
case HDMI_VFRMT_2560x1600p60_16_9: return "2560x1600 p60 16/9";
- case HDMI_VFRMT_1280x1024p60_5_4: return "1280x1042 p60 5/4";
default: return "???";
}
}
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
index e6a2e35..edd656c 100644
--- a/kernel/hrtimer.c
+++ b/kernel/hrtimer.c
@@ -224,13 +224,10 @@
raw_spin_unlock(&base->cpu_base->lock);
raw_spin_lock(&new_base->cpu_base->lock);
- this_cpu = smp_processor_id();
-
- if (cpu != this_cpu && (hrtimer_check_target(timer, new_base)
- || !cpu_online(cpu))) {
+ if (cpu != this_cpu && hrtimer_check_target(timer, new_base)) {
+ cpu = this_cpu;
raw_spin_unlock(&new_base->cpu_base->lock);
raw_spin_lock(&base->cpu_base->lock);
- cpu = smp_processor_id();
timer->base = base;
goto again;
}
diff --git a/kernel/pid.c b/kernel/pid.c
index 9f08dfa..7acf590 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -430,6 +430,7 @@
{
return find_task_by_pid_ns(vnr, current->nsproxy->pid_ns);
}
+EXPORT_SYMBOL_GPL(find_task_by_vpid);
struct pid *get_task_pid(struct task_struct *task, enum pid_type type)
{
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index a2bad88..3513fef 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -87,6 +87,8 @@
#define CREATE_TRACE_POINTS
#include <trace/events/sched.h>
+ATOMIC_NOTIFIER_HEAD(migration_notifier_head);
+
void start_bandwidth_timer(struct hrtimer *period_timer, ktime_t period)
{
unsigned long delta;
@@ -1589,15 +1591,17 @@
try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
{
unsigned long flags;
- int cpu, success = 0;
+ int cpu, src_cpu, success = 0;
smp_wmb();
raw_spin_lock_irqsave(&p->pi_lock, flags);
+ src_cpu = task_cpu(p);
+ cpu = src_cpu;
+
if (!(p->state & state))
goto out;
success = 1; /* we're going to change ->state */
- cpu = task_cpu(p);
if (p->on_rq && ttwu_remote(p, wake_flags))
goto stat;
@@ -1634,7 +1638,7 @@
p->sched_class->task_waking(p);
cpu = select_task_rq(p, SD_BALANCE_WAKE, wake_flags);
- if (task_cpu(p) != cpu) {
+ if (src_cpu != cpu) {
wake_flags |= WF_MIGRATED;
set_task_cpu(p, cpu);
}
@@ -1646,6 +1650,9 @@
out:
raw_spin_unlock_irqrestore(&p->pi_lock, flags);
+ if (src_cpu != cpu && task_notify_on_migrate(p))
+ atomic_notifier_call_chain(&migration_notifier_head,
+ cpu, (void *)src_cpu);
return success;
}
@@ -4757,6 +4764,7 @@
delayacct_blkio_end();
return ret;
}
+EXPORT_SYMBOL(io_schedule_timeout);
/**
* sys_sched_get_priority_max - return maximum RT priority.
@@ -5067,6 +5075,7 @@
static int __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu)
{
struct rq *rq_dest, *rq_src;
+ bool moved = false;
int ret = 0;
if (unlikely(!cpu_active(dest_cpu)))
@@ -5093,12 +5102,16 @@
set_task_cpu(p, dest_cpu);
enqueue_task(rq_dest, p, 0);
check_preempt_curr(rq_dest, p, 0);
+ moved = true;
}
done:
ret = 1;
fail:
double_rq_unlock(rq_src, rq_dest);
raw_spin_unlock(&p->pi_lock);
+ if (moved && task_notify_on_migrate(p))
+ atomic_notifier_call_chain(&migration_notifier_head,
+ dest_cpu, (void *)src_cpu);
return ret;
}
@@ -5877,6 +5890,7 @@
{
struct rq *rq = cpu_rq(cpu);
struct sched_domain *tmp;
+ unsigned long next_balance = rq->next_balance;
/* Remove the sched domains which do not contribute to scheduling. */
for (tmp = sd; tmp; ) {
@@ -5901,6 +5915,17 @@
sd->child = NULL;
}
+ for (tmp = sd; tmp; ) {
+ unsigned long interval;
+
+ interval = msecs_to_jiffies(tmp->balance_interval);
+ if (time_after(next_balance, tmp->last_balance + interval))
+ next_balance = tmp->last_balance + interval;
+
+ tmp = tmp->parent;
+ }
+ rq->next_balance = next_balance;
+
sched_domain_debug(sd, cpu);
rq_attach_root(rq, rd);
@@ -7730,6 +7755,24 @@
sched_move_task(task);
}
+static u64 cpu_notify_on_migrate_read_u64(struct cgroup *cgrp,
+ struct cftype *cft)
+{
+ struct task_group *tg = cgroup_tg(cgrp);
+
+ return tg->notify_on_migrate;
+}
+
+static int cpu_notify_on_migrate_write_u64(struct cgroup *cgrp,
+ struct cftype *cft, u64 notify)
+{
+ struct task_group *tg = cgroup_tg(cgrp);
+
+ tg->notify_on_migrate = (notify > 0);
+
+ return 0;
+}
+
#ifdef CONFIG_FAIR_GROUP_SCHED
static int cpu_shares_write_u64(struct cgroup *cgrp, struct cftype *cftype,
u64 shareval)
@@ -8001,6 +8044,11 @@
#endif /* CONFIG_RT_GROUP_SCHED */
static struct cftype cpu_files[] = {
+ {
+ .name = "notify_on_migrate",
+ .read_u64 = cpu_notify_on_migrate_read_u64,
+ .write_u64 = cpu_notify_on_migrate_write_u64,
+ },
#ifdef CONFIG_FAIR_GROUP_SCHED
{
.name = "shares",
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 7f4a46c..103730d 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -2130,7 +2130,7 @@
WARN_ON(task_rq(p) != rq);
- if (cfs_rq->nr_running > 1) {
+ if (rq->cfs.h_nr_running > 1) {
u64 slice = sched_slice(cfs_rq, se);
u64 ran = se->sum_exec_runtime - se->prev_sum_exec_runtime;
s64 delta = slice - ran;
@@ -2154,8 +2154,7 @@
/*
* called from enqueue/dequeue and updates the hrtick when the
- * current task is from our class and nr_running is low enough
- * to matter.
+ * current task is from our class.
*/
static void hrtick_update(struct rq *rq)
{
@@ -2164,8 +2163,7 @@
if (!hrtick_enabled(rq) || curr->sched_class != &fair_sched_class)
return;
- if (cfs_rq_of(&curr->se)->nr_running < sched_nr_latency)
- hrtick_start_fair(rq, curr);
+ hrtick_start_fair(rq, curr);
}
#else /* !CONFIG_SCHED_HRTICK */
static inline void
@@ -3103,6 +3101,8 @@
unsigned int loop_max;
};
+static DEFINE_PER_CPU(bool, dbs_boost_needed);
+
/*
* move_task - move a task from one runqueue to another runqueue.
* Both runqueues must be locked.
@@ -3113,6 +3113,8 @@
set_task_cpu(p, env->dst_cpu);
activate_task(env->dst_rq, p, 0);
check_preempt_curr(env->dst_rq, p, 0);
+ if (task_notify_on_migrate(p))
+ per_cpu(dbs_boost_needed, env->dst_cpu) = true;
}
/*
@@ -4543,9 +4545,15 @@
*/
sd->nr_balance_failed = sd->cache_nice_tries+1;
}
- } else
+ } else {
sd->nr_balance_failed = 0;
-
+ if (per_cpu(dbs_boost_needed, this_cpu)) {
+ per_cpu(dbs_boost_needed, this_cpu) = false;
+ atomic_notifier_call_chain(&migration_notifier_head,
+ this_cpu,
+ (void *)cpu_of(busiest));
+ }
+ }
if (likely(!active_balance)) {
/* We were unbalanced, so reset the balancing interval */
sd->balance_interval = sd->min_interval;
@@ -4700,6 +4708,12 @@
out_unlock:
busiest_rq->active_balance = 0;
raw_spin_unlock_irq(&busiest_rq->lock);
+ if (per_cpu(dbs_boost_needed, target_cpu)) {
+ per_cpu(dbs_boost_needed, target_cpu) = false;
+ atomic_notifier_call_chain(&migration_notifier_head,
+ target_cpu,
+ (void *)cpu_of(busiest_rq));
+ }
return 0;
}
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 451bd4f..5370bcb 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -104,6 +104,8 @@
struct task_group {
struct cgroup_subsys_state css;
+ bool notify_on_migrate;
+
#ifdef CONFIG_FAIR_GROUP_SCHED
/* schedulable entities of this group on each cpu */
struct sched_entity **se;
@@ -554,6 +556,11 @@
return autogroup_task_group(p, tg);
}
+static inline bool task_notify_on_migrate(struct task_struct *p)
+{
+ return task_group(p)->notify_on_migrate;
+}
+
/* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */
static inline void set_task_rq(struct task_struct *p, unsigned int cpu)
{
@@ -579,7 +586,10 @@
{
return NULL;
}
-
+static inline bool task_notify_on_migrate(struct task_struct *p)
+{
+ return false;
+}
#endif /* CONFIG_CGROUP_SCHED */
static inline void __set_task_cpu(struct task_struct *p, unsigned int cpu)
diff --git a/kernel/signal.c b/kernel/signal.c
index 17afcaf..7a06651 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -482,6 +482,9 @@
if (force_default || ka->sa.sa_handler != SIG_IGN)
ka->sa.sa_handler = SIG_DFL;
ka->sa.sa_flags = 0;
+#ifdef SA_RESTORER
+ ka->sa.sa_restorer = NULL;
+#endif
sigemptyset(&ka->sa.sa_mask);
ka++;
}
diff --git a/kernel/timer.c b/kernel/timer.c
index 24c5d20..cf7217a 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -1679,12 +1679,12 @@
boot_done = 1;
base = &boot_tvec_bases;
}
+ spin_lock_init(&base->lock);
tvec_base_done[cpu] = 1;
} else {
base = per_cpu(tvec_bases, cpu);
}
- spin_lock_init(&base->lock);
for (j = 0; j < TVN_SIZE; j++) {
INIT_LIST_HEAD(base->tv5.vec + j);
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 5abf42f..f1a6e9e 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -46,11 +46,12 @@
enum {
/* global_cwq flags */
- GCWQ_MANAGE_WORKERS = 1 << 0, /* need to manage workers */
- GCWQ_MANAGING_WORKERS = 1 << 1, /* managing workers */
- GCWQ_DISASSOCIATED = 1 << 2, /* cpu can't serve workers */
- GCWQ_FREEZING = 1 << 3, /* freeze in progress */
- GCWQ_HIGHPRI_PENDING = 1 << 4, /* highpri works on queue */
+ GCWQ_DISASSOCIATED = 1 << 0, /* cpu can't serve workers */
+ GCWQ_FREEZING = 1 << 1, /* freeze in progress */
+
+ /* pool flags */
+ POOL_MANAGE_WORKERS = 1 << 0, /* need to manage workers */
+ POOL_MANAGING_WORKERS = 1 << 1, /* managing workers */
/* worker flags */
WORKER_STARTED = 1 << 0, /* started */
@@ -72,6 +73,8 @@
TRUSTEE_RELEASE = 3, /* release workers */
TRUSTEE_DONE = 4, /* trustee is done */
+ NR_WORKER_POOLS = 2, /* # worker pools per gcwq */
+
BUSY_WORKER_HASH_ORDER = 6, /* 64 pointers */
BUSY_WORKER_HASH_SIZE = 1 << BUSY_WORKER_HASH_ORDER,
BUSY_WORKER_HASH_MASK = BUSY_WORKER_HASH_SIZE - 1,
@@ -91,6 +94,7 @@
* all cpus. Give -20.
*/
RESCUER_NICE_LEVEL = -20,
+ HIGHPRI_NICE_LEVEL = -20,
};
/*
@@ -115,6 +119,7 @@
*/
struct global_cwq;
+struct worker_pool;
/*
* The poor guys doing the actual heavy lifting. All on-duty workers
@@ -131,7 +136,7 @@
struct cpu_workqueue_struct *current_cwq; /* L: current_work's cwq */
struct list_head scheduled; /* L: scheduled works */
struct task_struct *task; /* I: worker task */
- struct global_cwq *gcwq; /* I: the associated gcwq */
+ struct worker_pool *pool; /* I: the associated pool */
/* 64 bytes boundary on 64bit, 32 on 32bit */
unsigned long last_active; /* L: last active timestamp */
unsigned int flags; /* X: flags */
@@ -139,6 +144,22 @@
struct work_struct rebind_work; /* L: rebind worker to cpu */
};
+struct worker_pool {
+ struct global_cwq *gcwq; /* I: the owning gcwq */
+ unsigned int flags; /* X: flags */
+
+ struct list_head worklist; /* L: list of pending works */
+ int nr_workers; /* L: total number of workers */
+ int nr_idle; /* L: currently idle ones */
+
+ struct list_head idle_list; /* X: list of idle workers */
+ struct timer_list idle_timer; /* L: worker idle timeout */
+ struct timer_list mayday_timer; /* L: SOS timer for workers */
+
+ struct ida worker_ida; /* L: for worker IDs */
+ struct worker *first_idle; /* L: first idle worker */
+};
+
/*
* Global per-cpu workqueue. There's one and only one for each cpu
* and all works are queued and processed here regardless of their
@@ -146,27 +167,18 @@
*/
struct global_cwq {
spinlock_t lock; /* the gcwq lock */
- struct list_head worklist; /* L: list of pending works */
unsigned int cpu; /* I: the associated cpu */
unsigned int flags; /* L: GCWQ_* flags */
- int nr_workers; /* L: total number of workers */
- int nr_idle; /* L: currently idle ones */
-
- /* workers are chained either in the idle_list or busy_hash */
- struct list_head idle_list; /* X: list of idle workers */
+ /* workers are chained either in busy_hash or pool idle_list */
struct hlist_head busy_hash[BUSY_WORKER_HASH_SIZE];
/* L: hash of busy workers */
- struct timer_list idle_timer; /* L: worker idle timeout */
- struct timer_list mayday_timer; /* L: SOS timer for dworkers */
-
- struct ida worker_ida; /* L: for worker IDs */
+ struct worker_pool pools[2]; /* normal and highpri pools */
struct task_struct *trustee; /* L: for gcwq shutdown */
unsigned int trustee_state; /* L: trustee state */
wait_queue_head_t trustee_wait; /* trustee wait */
- struct worker *first_idle; /* L: first idle worker */
} ____cacheline_aligned_in_smp;
/*
@@ -175,7 +187,7 @@
* aligned at two's power of the number of flag bits.
*/
struct cpu_workqueue_struct {
- struct global_cwq *gcwq; /* I: the associated gcwq */
+ struct worker_pool *pool; /* I: the associated pool */
struct workqueue_struct *wq; /* I: the owning workqueue */
int work_color; /* L: current color */
int flush_color; /* L: flushing color */
@@ -264,6 +276,10 @@
#define CREATE_TRACE_POINTS
#include <trace/events/workqueue.h>
+#define for_each_worker_pool(pool, gcwq) \
+ for ((pool) = &(gcwq)->pools[0]; \
+ (pool) < &(gcwq)->pools[NR_WORKER_POOLS]; (pool)++)
+
#define for_each_busy_worker(worker, i, pos, gcwq) \
for (i = 0; i < BUSY_WORKER_HASH_SIZE; i++) \
hlist_for_each_entry(worker, pos, &gcwq->busy_hash[i], hentry)
@@ -444,7 +460,7 @@
* try_to_wake_up(). Put it in a separate cacheline.
*/
static DEFINE_PER_CPU(struct global_cwq, global_cwq);
-static DEFINE_PER_CPU_SHARED_ALIGNED(atomic_t, gcwq_nr_running);
+static DEFINE_PER_CPU_SHARED_ALIGNED(atomic_t, pool_nr_running[NR_WORKER_POOLS]);
/*
* Global cpu workqueue and nr_running counter for unbound gcwq. The
@@ -452,10 +468,17 @@
* workers have WORKER_UNBOUND set.
*/
static struct global_cwq unbound_global_cwq;
-static atomic_t unbound_gcwq_nr_running = ATOMIC_INIT(0); /* always 0 */
+static atomic_t unbound_pool_nr_running[NR_WORKER_POOLS] = {
+ [0 ... NR_WORKER_POOLS - 1] = ATOMIC_INIT(0), /* always 0 */
+};
static int worker_thread(void *__worker);
+static int worker_pool_pri(struct worker_pool *pool)
+{
+ return pool - pool->gcwq->pools;
+}
+
static struct global_cwq *get_gcwq(unsigned int cpu)
{
if (cpu != WORK_CPU_UNBOUND)
@@ -464,12 +487,15 @@
return &unbound_global_cwq;
}
-static atomic_t *get_gcwq_nr_running(unsigned int cpu)
+static atomic_t *get_pool_nr_running(struct worker_pool *pool)
{
+ int cpu = pool->gcwq->cpu;
+ int idx = worker_pool_pri(pool);
+
if (cpu != WORK_CPU_UNBOUND)
- return &per_cpu(gcwq_nr_running, cpu);
+ return &per_cpu(pool_nr_running, cpu)[idx];
else
- return &unbound_gcwq_nr_running;
+ return &unbound_pool_nr_running[idx];
}
static struct cpu_workqueue_struct *get_cwq(unsigned int cpu,
@@ -555,7 +581,7 @@
if (data & WORK_STRUCT_CWQ)
return ((struct cpu_workqueue_struct *)
- (data & WORK_STRUCT_WQ_DATA_MASK))->gcwq;
+ (data & WORK_STRUCT_WQ_DATA_MASK))->pool->gcwq;
cpu = data >> WORK_STRUCT_FLAG_BITS;
if (cpu == WORK_CPU_NONE)
@@ -566,60 +592,62 @@
}
/*
- * Policy functions. These define the policies on how the global
- * worker pool is managed. Unless noted otherwise, these functions
- * assume that they're being called with gcwq->lock held.
+ * Policy functions. These define the policies on how the global worker
+ * pools are managed. Unless noted otherwise, these functions assume that
+ * they're being called with gcwq->lock held.
*/
-static bool __need_more_worker(struct global_cwq *gcwq)
+static bool __need_more_worker(struct worker_pool *pool)
{
- return !atomic_read(get_gcwq_nr_running(gcwq->cpu)) ||
- gcwq->flags & GCWQ_HIGHPRI_PENDING;
+ return !atomic_read(get_pool_nr_running(pool));
}
/*
* Need to wake up a worker? Called from anything but currently
* running workers.
+ *
+ * Note that, because unbound workers never contribute to nr_running, this
+ * function will always return %true for unbound gcwq as long as the
+ * worklist isn't empty.
*/
-static bool need_more_worker(struct global_cwq *gcwq)
+static bool need_more_worker(struct worker_pool *pool)
{
- return !list_empty(&gcwq->worklist) && __need_more_worker(gcwq);
+ return !list_empty(&pool->worklist) && __need_more_worker(pool);
}
/* Can I start working? Called from busy but !running workers. */
-static bool may_start_working(struct global_cwq *gcwq)
+static bool may_start_working(struct worker_pool *pool)
{
- return gcwq->nr_idle;
+ return pool->nr_idle;
}
/* Do I need to keep working? Called from currently running workers. */
-static bool keep_working(struct global_cwq *gcwq)
+static bool keep_working(struct worker_pool *pool)
{
- atomic_t *nr_running = get_gcwq_nr_running(gcwq->cpu);
+ atomic_t *nr_running = get_pool_nr_running(pool);
- return !list_empty(&gcwq->worklist) &&
- (atomic_read(nr_running) <= 1 ||
- gcwq->flags & GCWQ_HIGHPRI_PENDING);
+ return !list_empty(&pool->worklist) && atomic_read(nr_running) <= 1;
}
/* Do we need a new worker? Called from manager. */
-static bool need_to_create_worker(struct global_cwq *gcwq)
+static bool need_to_create_worker(struct worker_pool *pool)
{
- return need_more_worker(gcwq) && !may_start_working(gcwq);
+ return need_more_worker(pool) && !may_start_working(pool);
}
/* Do I need to be the manager? */
-static bool need_to_manage_workers(struct global_cwq *gcwq)
+static bool need_to_manage_workers(struct worker_pool *pool)
{
- return need_to_create_worker(gcwq) || gcwq->flags & GCWQ_MANAGE_WORKERS;
+ return need_to_create_worker(pool) ||
+ (pool->flags & POOL_MANAGE_WORKERS);
}
/* Do we have too many workers and should some go away? */
-static bool too_many_workers(struct global_cwq *gcwq)
+static bool too_many_workers(struct worker_pool *pool)
{
- bool managing = gcwq->flags & GCWQ_MANAGING_WORKERS;
- int nr_idle = gcwq->nr_idle + managing; /* manager is considered idle */
- int nr_busy = gcwq->nr_workers - nr_idle;
+ bool managing = pool->flags & POOL_MANAGING_WORKERS;
+ int nr_idle = pool->nr_idle + managing; /* manager is considered idle */
+ int nr_busy = pool->nr_workers - nr_idle;
return nr_idle > 2 && (nr_idle - 2) * MAX_IDLE_WORKERS_RATIO >= nr_busy;
}
@@ -629,26 +657,26 @@
*/
/* Return the first worker. Safe with preemption disabled */
-static struct worker *first_worker(struct global_cwq *gcwq)
+static struct worker *first_worker(struct worker_pool *pool)
{
- if (unlikely(list_empty(&gcwq->idle_list)))
+ if (unlikely(list_empty(&pool->idle_list)))
return NULL;
- return list_first_entry(&gcwq->idle_list, struct worker, entry);
+ return list_first_entry(&pool->idle_list, struct worker, entry);
}
/**
* wake_up_worker - wake up an idle worker
- * @gcwq: gcwq to wake worker for
+ * @pool: worker pool to wake worker from
*
- * Wake up the first idle worker of @gcwq.
+ * Wake up the first idle worker of @pool.
*
* CONTEXT:
* spin_lock_irq(gcwq->lock).
*/
-static void wake_up_worker(struct global_cwq *gcwq)
+static void wake_up_worker(struct worker_pool *pool)
{
- struct worker *worker = first_worker(gcwq);
+ struct worker *worker = first_worker(pool);
if (likely(worker))
wake_up_process(worker->task);
@@ -670,7 +698,7 @@
struct worker *worker = kthread_data(task);
if (!(worker->flags & WORKER_NOT_RUNNING))
- atomic_inc(get_gcwq_nr_running(cpu));
+ atomic_inc(get_pool_nr_running(worker->pool));
}
/**
@@ -692,8 +720,8 @@
unsigned int cpu)
{
struct worker *worker = kthread_data(task), *to_wakeup = NULL;
- struct global_cwq *gcwq = get_gcwq(cpu);
- atomic_t *nr_running = get_gcwq_nr_running(cpu);
+ struct worker_pool *pool = worker->pool;
+ atomic_t *nr_running = get_pool_nr_running(pool);
if (worker->flags & WORKER_NOT_RUNNING)
return NULL;
@@ -712,8 +740,8 @@
* could be manipulating idle_list, so dereferencing idle_list
* without gcwq lock is safe.
*/
- if (atomic_dec_and_test(nr_running) && !list_empty(&gcwq->worklist))
- to_wakeup = first_worker(gcwq);
+ if (atomic_dec_and_test(nr_running) && !list_empty(&pool->worklist))
+ to_wakeup = first_worker(pool);
return to_wakeup ? to_wakeup->task : NULL;
}
@@ -733,7 +761,7 @@
static inline void worker_set_flags(struct worker *worker, unsigned int flags,
bool wakeup)
{
- struct global_cwq *gcwq = worker->gcwq;
+ struct worker_pool *pool = worker->pool;
WARN_ON_ONCE(worker->task != current);
@@ -744,12 +772,12 @@
*/
if ((flags & WORKER_NOT_RUNNING) &&
!(worker->flags & WORKER_NOT_RUNNING)) {
- atomic_t *nr_running = get_gcwq_nr_running(gcwq->cpu);
+ atomic_t *nr_running = get_pool_nr_running(pool);
if (wakeup) {
if (atomic_dec_and_test(nr_running) &&
- !list_empty(&gcwq->worklist))
- wake_up_worker(gcwq);
+ !list_empty(&pool->worklist))
+ wake_up_worker(pool);
} else
atomic_dec(nr_running);
}
@@ -769,7 +797,7 @@
*/
static inline void worker_clr_flags(struct worker *worker, unsigned int flags)
{
- struct global_cwq *gcwq = worker->gcwq;
+ struct worker_pool *pool = worker->pool;
unsigned int oflags = worker->flags;
WARN_ON_ONCE(worker->task != current);
@@ -783,7 +811,7 @@
*/
if ((flags & WORKER_NOT_RUNNING) && (oflags & WORKER_NOT_RUNNING))
if (!(worker->flags & WORKER_NOT_RUNNING))
- atomic_inc(get_gcwq_nr_running(gcwq->cpu));
+ atomic_inc(get_pool_nr_running(pool));
}
/**
@@ -867,43 +895,6 @@
}
/**
- * gcwq_determine_ins_pos - find insertion position
- * @gcwq: gcwq of interest
- * @cwq: cwq a work is being queued for
- *
- * A work for @cwq is about to be queued on @gcwq, determine insertion
- * position for the work. If @cwq is for HIGHPRI wq, the work is
- * queued at the head of the queue but in FIFO order with respect to
- * other HIGHPRI works; otherwise, at the end of the queue. This
- * function also sets GCWQ_HIGHPRI_PENDING flag to hint @gcwq that
- * there are HIGHPRI works pending.
- *
- * CONTEXT:
- * spin_lock_irq(gcwq->lock).
- *
- * RETURNS:
- * Pointer to inserstion position.
- */
-static inline struct list_head *gcwq_determine_ins_pos(struct global_cwq *gcwq,
- struct cpu_workqueue_struct *cwq)
-{
- struct work_struct *twork;
-
- if (likely(!(cwq->wq->flags & WQ_HIGHPRI)))
- return &gcwq->worklist;
-
- list_for_each_entry(twork, &gcwq->worklist, entry) {
- struct cpu_workqueue_struct *tcwq = get_work_cwq(twork);
-
- if (!(tcwq->wq->flags & WQ_HIGHPRI))
- break;
- }
-
- gcwq->flags |= GCWQ_HIGHPRI_PENDING;
- return &twork->entry;
-}
-
-/**
* insert_work - insert a work into gcwq
* @cwq: cwq @work belongs to
* @work: work to insert
@@ -920,7 +911,7 @@
struct work_struct *work, struct list_head *head,
unsigned int extra_flags)
{
- struct global_cwq *gcwq = cwq->gcwq;
+ struct worker_pool *pool = cwq->pool;
/* we own @work, set data and link */
set_work_cwq(work, cwq, extra_flags);
@@ -940,8 +931,8 @@
*/
smp_mb();
- if (__need_more_worker(gcwq))
- wake_up_worker(gcwq);
+ if (__need_more_worker(pool))
+ wake_up_worker(pool);
}
/*
@@ -1040,7 +1031,7 @@
if (likely(cwq->nr_active < cwq->max_active)) {
trace_workqueue_activate_work(work);
cwq->nr_active++;
- worklist = gcwq_determine_ins_pos(gcwq, cwq);
+ worklist = &cwq->pool->worklist;
} else {
work_flags |= WORK_STRUCT_DELAYED;
worklist = &cwq->delayed_works;
@@ -1189,7 +1180,8 @@
*/
static void worker_enter_idle(struct worker *worker)
{
- struct global_cwq *gcwq = worker->gcwq;
+ struct worker_pool *pool = worker->pool;
+ struct global_cwq *gcwq = pool->gcwq;
BUG_ON(worker->flags & WORKER_IDLE);
BUG_ON(!list_empty(&worker->entry) &&
@@ -1197,22 +1189,27 @@
/* can't use worker_set_flags(), also called from start_worker() */
worker->flags |= WORKER_IDLE;
- gcwq->nr_idle++;
+ pool->nr_idle++;
worker->last_active = jiffies;
/* idle_list is LIFO */
- list_add(&worker->entry, &gcwq->idle_list);
+ list_add(&worker->entry, &pool->idle_list);
if (likely(!(worker->flags & WORKER_ROGUE))) {
- if (too_many_workers(gcwq) && !timer_pending(&gcwq->idle_timer))
- mod_timer(&gcwq->idle_timer,
+ if (too_many_workers(pool) && !timer_pending(&pool->idle_timer))
+ mod_timer(&pool->idle_timer,
jiffies + IDLE_WORKER_TIMEOUT);
} else
wake_up_all(&gcwq->trustee_wait);
- /* sanity check nr_running */
- WARN_ON_ONCE(gcwq->nr_workers == gcwq->nr_idle &&
- atomic_read(get_gcwq_nr_running(gcwq->cpu)));
+ /*
+ * Sanity check nr_running. Because trustee releases gcwq->lock
+ * between setting %WORKER_ROGUE and zapping nr_running, the
+ * warning may trigger spuriously. Check iff trustee is idle.
+ */
+ WARN_ON_ONCE(gcwq->trustee_state == TRUSTEE_DONE &&
+ pool->nr_workers == pool->nr_idle &&
+ atomic_read(get_pool_nr_running(pool)));
}
/**
@@ -1226,11 +1223,11 @@
*/
static void worker_leave_idle(struct worker *worker)
{
- struct global_cwq *gcwq = worker->gcwq;
+ struct worker_pool *pool = worker->pool;
BUG_ON(!(worker->flags & WORKER_IDLE));
worker_clr_flags(worker, WORKER_IDLE);
- gcwq->nr_idle--;
+ pool->nr_idle--;
list_del_init(&worker->entry);
}
@@ -1267,7 +1264,7 @@
static bool worker_maybe_bind_and_lock(struct worker *worker)
__acquires(&gcwq->lock)
{
- struct global_cwq *gcwq = worker->gcwq;
+ struct global_cwq *gcwq = worker->pool->gcwq;
struct task_struct *task = worker->task;
while (true) {
@@ -1309,7 +1306,7 @@
static void worker_rebind_fn(struct work_struct *work)
{
struct worker *worker = container_of(work, struct worker, rebind_work);
- struct global_cwq *gcwq = worker->gcwq;
+ struct global_cwq *gcwq = worker->pool->gcwq;
if (worker_maybe_bind_and_lock(worker))
worker_clr_flags(worker, WORKER_REBIND);
@@ -1334,10 +1331,10 @@
/**
* create_worker - create a new workqueue worker
- * @gcwq: gcwq the new worker will belong to
+ * @pool: pool the new worker will belong to
* @bind: whether to set affinity to @cpu or not
*
- * Create a new worker which is bound to @gcwq. The returned worker
+ * Create a new worker which is bound to @pool. The returned worker
* can be started by calling start_worker() or destroyed using
* destroy_worker().
*
@@ -1347,16 +1344,18 @@
* RETURNS:
* Pointer to the newly created worker.
*/
-static struct worker *create_worker(struct global_cwq *gcwq, bool bind)
+static struct worker *create_worker(struct worker_pool *pool, bool bind)
{
+ struct global_cwq *gcwq = pool->gcwq;
bool on_unbound_cpu = gcwq->cpu == WORK_CPU_UNBOUND;
+ const char *pri = worker_pool_pri(pool) ? "H" : "";
struct worker *worker = NULL;
int id = -1;
spin_lock_irq(&gcwq->lock);
- while (ida_get_new(&gcwq->worker_ida, &id)) {
+ while (ida_get_new(&pool->worker_ida, &id)) {
spin_unlock_irq(&gcwq->lock);
- if (!ida_pre_get(&gcwq->worker_ida, GFP_KERNEL))
+ if (!ida_pre_get(&pool->worker_ida, GFP_KERNEL))
goto fail;
spin_lock_irq(&gcwq->lock);
}
@@ -1366,20 +1365,22 @@
if (!worker)
goto fail;
- worker->gcwq = gcwq;
+ worker->pool = pool;
worker->id = id;
if (!on_unbound_cpu)
worker->task = kthread_create_on_node(worker_thread,
- worker,
- cpu_to_node(gcwq->cpu),
- "kworker/%u:%d", gcwq->cpu, id);
+ worker, cpu_to_node(gcwq->cpu),
+ "kworker/%u:%d%s", gcwq->cpu, id, pri);
else
worker->task = kthread_create(worker_thread, worker,
- "kworker/u:%d", id);
+ "kworker/u:%d%s", id, pri);
if (IS_ERR(worker->task))
goto fail;
+ if (worker_pool_pri(pool))
+ set_user_nice(worker->task, HIGHPRI_NICE_LEVEL);
+
/*
* A rogue worker will become a regular one if CPU comes
* online later on. Make sure every worker has
@@ -1397,7 +1398,7 @@
fail:
if (id >= 0) {
spin_lock_irq(&gcwq->lock);
- ida_remove(&gcwq->worker_ida, id);
+ ida_remove(&pool->worker_ida, id);
spin_unlock_irq(&gcwq->lock);
}
kfree(worker);
@@ -1416,7 +1417,7 @@
static void start_worker(struct worker *worker)
{
worker->flags |= WORKER_STARTED;
- worker->gcwq->nr_workers++;
+ worker->pool->nr_workers++;
worker_enter_idle(worker);
wake_up_process(worker->task);
}
@@ -1432,7 +1433,8 @@
*/
static void destroy_worker(struct worker *worker)
{
- struct global_cwq *gcwq = worker->gcwq;
+ struct worker_pool *pool = worker->pool;
+ struct global_cwq *gcwq = pool->gcwq;
int id = worker->id;
/* sanity check frenzy */
@@ -1440,9 +1442,9 @@
BUG_ON(!list_empty(&worker->scheduled));
if (worker->flags & WORKER_STARTED)
- gcwq->nr_workers--;
+ pool->nr_workers--;
if (worker->flags & WORKER_IDLE)
- gcwq->nr_idle--;
+ pool->nr_idle--;
list_del_init(&worker->entry);
worker->flags |= WORKER_DIE;
@@ -1453,29 +1455,30 @@
kfree(worker);
spin_lock_irq(&gcwq->lock);
- ida_remove(&gcwq->worker_ida, id);
+ ida_remove(&pool->worker_ida, id);
}
-static void idle_worker_timeout(unsigned long __gcwq)
+static void idle_worker_timeout(unsigned long __pool)
{
- struct global_cwq *gcwq = (void *)__gcwq;
+ struct worker_pool *pool = (void *)__pool;
+ struct global_cwq *gcwq = pool->gcwq;
spin_lock_irq(&gcwq->lock);
- if (too_many_workers(gcwq)) {
+ if (too_many_workers(pool)) {
struct worker *worker;
unsigned long expires;
/* idle_list is kept in LIFO order, check the last one */
- worker = list_entry(gcwq->idle_list.prev, struct worker, entry);
+ worker = list_entry(pool->idle_list.prev, struct worker, entry);
expires = worker->last_active + IDLE_WORKER_TIMEOUT;
if (time_before(jiffies, expires))
- mod_timer(&gcwq->idle_timer, expires);
+ mod_timer(&pool->idle_timer, expires);
else {
/* it's been idle for too long, wake up manager */
- gcwq->flags |= GCWQ_MANAGE_WORKERS;
- wake_up_worker(gcwq);
+ pool->flags |= POOL_MANAGE_WORKERS;
+ wake_up_worker(pool);
}
}
@@ -1492,7 +1495,7 @@
return false;
/* mayday mayday mayday */
- cpu = cwq->gcwq->cpu;
+ cpu = cwq->pool->gcwq->cpu;
/* WORK_CPU_UNBOUND can't be set in cpumask, use cpu 0 instead */
if (cpu == WORK_CPU_UNBOUND)
cpu = 0;
@@ -1501,37 +1504,38 @@
return true;
}
-static void gcwq_mayday_timeout(unsigned long __gcwq)
+static void gcwq_mayday_timeout(unsigned long __pool)
{
- struct global_cwq *gcwq = (void *)__gcwq;
+ struct worker_pool *pool = (void *)__pool;
+ struct global_cwq *gcwq = pool->gcwq;
struct work_struct *work;
spin_lock_irq(&gcwq->lock);
- if (need_to_create_worker(gcwq)) {
+ if (need_to_create_worker(pool)) {
/*
* We've been trying to create a new worker but
* haven't been successful. We might be hitting an
* allocation deadlock. Send distress signals to
* rescuers.
*/
- list_for_each_entry(work, &gcwq->worklist, entry)
+ list_for_each_entry(work, &pool->worklist, entry)
send_mayday(work);
}
spin_unlock_irq(&gcwq->lock);
- mod_timer(&gcwq->mayday_timer, jiffies + MAYDAY_INTERVAL);
+ mod_timer(&pool->mayday_timer, jiffies + MAYDAY_INTERVAL);
}
/**
* maybe_create_worker - create a new worker if necessary
- * @gcwq: gcwq to create a new worker for
+ * @pool: pool to create a new worker for
*
- * Create a new worker for @gcwq if necessary. @gcwq is guaranteed to
+ * Create a new worker for @pool if necessary. @pool is guaranteed to
* have at least one idle worker on return from this function. If
* creating a new worker takes longer than MAYDAY_INTERVAL, mayday is
- * sent to all rescuers with works scheduled on @gcwq to resolve
+ * sent to all rescuers with works scheduled on @pool to resolve
* possible allocation deadlock.
*
* On return, need_to_create_worker() is guaranteed to be false and
@@ -1546,52 +1550,54 @@
* false if no action was taken and gcwq->lock stayed locked, true
* otherwise.
*/
-static bool maybe_create_worker(struct global_cwq *gcwq)
+static bool maybe_create_worker(struct worker_pool *pool)
__releases(&gcwq->lock)
__acquires(&gcwq->lock)
{
- if (!need_to_create_worker(gcwq))
+ struct global_cwq *gcwq = pool->gcwq;
+
+ if (!need_to_create_worker(pool))
return false;
restart:
spin_unlock_irq(&gcwq->lock);
/* if we don't make progress in MAYDAY_INITIAL_TIMEOUT, call for help */
- mod_timer(&gcwq->mayday_timer, jiffies + MAYDAY_INITIAL_TIMEOUT);
+ mod_timer(&pool->mayday_timer, jiffies + MAYDAY_INITIAL_TIMEOUT);
while (true) {
struct worker *worker;
- worker = create_worker(gcwq, true);
+ worker = create_worker(pool, true);
if (worker) {
- del_timer_sync(&gcwq->mayday_timer);
+ del_timer_sync(&pool->mayday_timer);
spin_lock_irq(&gcwq->lock);
start_worker(worker);
- BUG_ON(need_to_create_worker(gcwq));
+ BUG_ON(need_to_create_worker(pool));
return true;
}
- if (!need_to_create_worker(gcwq))
+ if (!need_to_create_worker(pool))
break;
__set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(CREATE_COOLDOWN);
- if (!need_to_create_worker(gcwq))
+ if (!need_to_create_worker(pool))
break;
}
- del_timer_sync(&gcwq->mayday_timer);
+ del_timer_sync(&pool->mayday_timer);
spin_lock_irq(&gcwq->lock);
- if (need_to_create_worker(gcwq))
+ if (need_to_create_worker(pool))
goto restart;
return true;
}
/**
* maybe_destroy_worker - destroy workers which have been idle for a while
- * @gcwq: gcwq to destroy workers for
+ * @pool: pool to destroy workers for
*
- * Destroy @gcwq workers which have been idle for longer than
+ * Destroy @pool workers which have been idle for longer than
* IDLE_WORKER_TIMEOUT.
*
* LOCKING:
@@ -1602,19 +1608,19 @@
* false if no action was taken and gcwq->lock stayed locked, true
* otherwise.
*/
-static bool maybe_destroy_workers(struct global_cwq *gcwq)
+static bool maybe_destroy_workers(struct worker_pool *pool)
{
bool ret = false;
- while (too_many_workers(gcwq)) {
+ while (too_many_workers(pool)) {
struct worker *worker;
unsigned long expires;
- worker = list_entry(gcwq->idle_list.prev, struct worker, entry);
+ worker = list_entry(pool->idle_list.prev, struct worker, entry);
expires = worker->last_active + IDLE_WORKER_TIMEOUT;
if (time_before(jiffies, expires)) {
- mod_timer(&gcwq->idle_timer, expires);
+ mod_timer(&pool->idle_timer, expires);
break;
}
@@ -1647,23 +1653,24 @@
*/
static bool manage_workers(struct worker *worker)
{
- struct global_cwq *gcwq = worker->gcwq;
+ struct worker_pool *pool = worker->pool;
+ struct global_cwq *gcwq = pool->gcwq;
bool ret = false;
- if (gcwq->flags & GCWQ_MANAGING_WORKERS)
+ if (pool->flags & POOL_MANAGING_WORKERS)
return ret;
- gcwq->flags &= ~GCWQ_MANAGE_WORKERS;
- gcwq->flags |= GCWQ_MANAGING_WORKERS;
+ pool->flags &= ~POOL_MANAGE_WORKERS;
+ pool->flags |= POOL_MANAGING_WORKERS;
/*
* Destroy and then create so that may_start_working() is true
* on return.
*/
- ret |= maybe_destroy_workers(gcwq);
- ret |= maybe_create_worker(gcwq);
+ ret |= maybe_destroy_workers(pool);
+ ret |= maybe_create_worker(pool);
- gcwq->flags &= ~GCWQ_MANAGING_WORKERS;
+ pool->flags &= ~POOL_MANAGING_WORKERS;
/*
* The trustee might be waiting to take over the manager
@@ -1720,10 +1727,9 @@
{
struct work_struct *work = list_first_entry(&cwq->delayed_works,
struct work_struct, entry);
- struct list_head *pos = gcwq_determine_ins_pos(cwq->gcwq, cwq);
trace_workqueue_activate_work(work);
- move_linked_works(work, pos, NULL);
+ move_linked_works(work, &cwq->pool->worklist, NULL);
__clear_bit(WORK_STRUCT_DELAYED_BIT, work_data_bits(work));
cwq->nr_active++;
}
@@ -1796,7 +1802,8 @@
__acquires(&gcwq->lock)
{
struct cpu_workqueue_struct *cwq = get_work_cwq(work);
- struct global_cwq *gcwq = cwq->gcwq;
+ struct worker_pool *pool = worker->pool;
+ struct global_cwq *gcwq = pool->gcwq;
struct hlist_head *bwh = busy_worker_head(gcwq, work);
bool cpu_intensive = cwq->wq->flags & WQ_CPU_INTENSIVE;
work_func_t f = work->func;
@@ -1836,27 +1843,19 @@
list_del_init(&work->entry);
/*
- * If HIGHPRI_PENDING, check the next work, and, if HIGHPRI,
- * wake up another worker; otherwise, clear HIGHPRI_PENDING.
- */
- if (unlikely(gcwq->flags & GCWQ_HIGHPRI_PENDING)) {
- struct work_struct *nwork = list_first_entry(&gcwq->worklist,
- struct work_struct, entry);
-
- if (!list_empty(&gcwq->worklist) &&
- get_work_cwq(nwork)->wq->flags & WQ_HIGHPRI)
- wake_up_worker(gcwq);
- else
- gcwq->flags &= ~GCWQ_HIGHPRI_PENDING;
- }
-
- /*
* CPU intensive works don't participate in concurrency
* management. They're the scheduler's responsibility.
*/
if (unlikely(cpu_intensive))
worker_set_flags(worker, WORKER_CPU_INTENSIVE, true);
+ /*
+ * Unbound gcwq isn't concurrency managed and work items should be
+ * executed ASAP. Wake up another worker if necessary.
+ */
+ if ((worker->flags & WORKER_UNBOUND) && need_more_worker(pool))
+ wake_up_worker(pool);
+
spin_unlock_irq(&gcwq->lock);
work_clear_pending(work);
@@ -1929,7 +1928,8 @@
static int worker_thread(void *__worker)
{
struct worker *worker = __worker;
- struct global_cwq *gcwq = worker->gcwq;
+ struct worker_pool *pool = worker->pool;
+ struct global_cwq *gcwq = pool->gcwq;
/* tell the scheduler that this is a workqueue worker */
worker->task->flags |= PF_WQ_WORKER;
@@ -1946,11 +1946,11 @@
worker_leave_idle(worker);
recheck:
/* no more worker necessary? */
- if (!need_more_worker(gcwq))
+ if (!need_more_worker(pool))
goto sleep;
/* do we need to manage? */
- if (unlikely(!may_start_working(gcwq)) && manage_workers(worker))
+ if (unlikely(!may_start_working(pool)) && manage_workers(worker))
goto recheck;
/*
@@ -1969,7 +1969,7 @@
do {
struct work_struct *work =
- list_first_entry(&gcwq->worklist,
+ list_first_entry(&pool->worklist,
struct work_struct, entry);
if (likely(!(*work_data_bits(work) & WORK_STRUCT_LINKED))) {
@@ -1981,11 +1981,11 @@
move_linked_works(work, &worker->scheduled, NULL);
process_scheduled_works(worker);
}
- } while (keep_working(gcwq));
+ } while (keep_working(pool));
worker_set_flags(worker, WORKER_PREP, false);
sleep:
- if (unlikely(need_to_manage_workers(gcwq)) && manage_workers(worker))
+ if (unlikely(need_to_manage_workers(pool)) && manage_workers(worker))
goto recheck;
/*
@@ -2043,14 +2043,15 @@
for_each_mayday_cpu(cpu, wq->mayday_mask) {
unsigned int tcpu = is_unbound ? WORK_CPU_UNBOUND : cpu;
struct cpu_workqueue_struct *cwq = get_cwq(tcpu, wq);
- struct global_cwq *gcwq = cwq->gcwq;
+ struct worker_pool *pool = cwq->pool;
+ struct global_cwq *gcwq = pool->gcwq;
struct work_struct *work, *n;
__set_current_state(TASK_RUNNING);
mayday_clear_cpu(cpu, wq->mayday_mask);
/* migrate to the target cpu if possible */
- rescuer->gcwq = gcwq;
+ rescuer->pool = pool;
worker_maybe_bind_and_lock(rescuer);
/*
@@ -2058,7 +2059,7 @@
* process'em.
*/
BUG_ON(!list_empty(&rescuer->scheduled));
- list_for_each_entry_safe(work, n, &gcwq->worklist, entry)
+ list_for_each_entry_safe(work, n, &pool->worklist, entry)
if (get_work_cwq(work) == cwq)
move_linked_works(work, scheduled, &n);
@@ -2069,8 +2070,8 @@
* regular worker; otherwise, we end up with 0 concurrency
* and stalling the execution.
*/
- if (keep_working(gcwq))
- wake_up_worker(gcwq);
+ if (keep_working(pool))
+ wake_up_worker(pool);
spin_unlock_irq(&gcwq->lock);
}
@@ -2195,7 +2196,7 @@
for_each_cwq_cpu(cpu, wq) {
struct cpu_workqueue_struct *cwq = get_cwq(cpu, wq);
- struct global_cwq *gcwq = cwq->gcwq;
+ struct global_cwq *gcwq = cwq->pool->gcwq;
spin_lock_irq(&gcwq->lock);
@@ -2411,9 +2412,9 @@
struct cpu_workqueue_struct *cwq = get_cwq(cpu, wq);
bool drained;
- spin_lock_irq(&cwq->gcwq->lock);
+ spin_lock_irq(&cwq->pool->gcwq->lock);
drained = !cwq->nr_active && list_empty(&cwq->delayed_works);
- spin_unlock_irq(&cwq->gcwq->lock);
+ spin_unlock_irq(&cwq->pool->gcwq->lock);
if (drained)
continue;
@@ -2453,7 +2454,7 @@
*/
smp_rmb();
cwq = get_work_cwq(work);
- if (unlikely(!cwq || gcwq != cwq->gcwq))
+ if (unlikely(!cwq || gcwq != cwq->pool->gcwq))
goto already_gone;
} else if (wait_executing) {
worker = find_worker_executing_work(gcwq, work);
@@ -2971,13 +2972,6 @@
if (flags & WQ_MEM_RECLAIM)
flags |= WQ_RESCUER;
- /*
- * Unbound workqueues aren't concurrency managed and should be
- * dispatched to workers immediately.
- */
- if (flags & WQ_UNBOUND)
- flags |= WQ_HIGHPRI;
-
max_active = max_active ?: WQ_DFL_ACTIVE;
max_active = wq_clamp_max_active(max_active, flags, wq->name);
@@ -2998,9 +2992,10 @@
for_each_cwq_cpu(cpu, wq) {
struct cpu_workqueue_struct *cwq = get_cwq(cpu, wq);
struct global_cwq *gcwq = get_gcwq(cpu);
+ int pool_idx = (bool)(flags & WQ_HIGHPRI);
BUG_ON((unsigned long)cwq & WORK_STRUCT_FLAG_MASK);
- cwq->gcwq = gcwq;
+ cwq->pool = &gcwq->pools[pool_idx];
cwq->wq = wq;
cwq->flush_color = -1;
cwq->max_active = max_active;
@@ -3304,9 +3299,30 @@
__ret1 < 0 ? -1 : 0; \
})
+static bool gcwq_is_managing_workers(struct global_cwq *gcwq)
+{
+ struct worker_pool *pool;
+
+ for_each_worker_pool(pool, gcwq)
+ if (pool->flags & POOL_MANAGING_WORKERS)
+ return true;
+ return false;
+}
+
+static bool gcwq_has_idle_workers(struct global_cwq *gcwq)
+{
+ struct worker_pool *pool;
+
+ for_each_worker_pool(pool, gcwq)
+ if (!list_empty(&pool->idle_list))
+ return true;
+ return false;
+}
+
static int __cpuinit trustee_thread(void *__gcwq)
{
struct global_cwq *gcwq = __gcwq;
+ struct worker_pool *pool;
struct worker *worker;
struct work_struct *work;
struct hlist_node *pos;
@@ -3322,13 +3338,15 @@
* cancelled.
*/
BUG_ON(gcwq->cpu != smp_processor_id());
- rc = trustee_wait_event(!(gcwq->flags & GCWQ_MANAGING_WORKERS));
+ rc = trustee_wait_event(!gcwq_is_managing_workers(gcwq));
BUG_ON(rc < 0);
- gcwq->flags |= GCWQ_MANAGING_WORKERS;
+ for_each_worker_pool(pool, gcwq) {
+ pool->flags |= POOL_MANAGING_WORKERS;
- list_for_each_entry(worker, &gcwq->idle_list, entry)
- worker->flags |= WORKER_ROGUE;
+ list_for_each_entry(worker, &pool->idle_list, entry)
+ worker->flags |= WORKER_ROGUE;
+ }
for_each_busy_worker(worker, i, pos, gcwq)
worker->flags |= WORKER_ROGUE;
@@ -3349,10 +3367,12 @@
* keep_working() are always true as long as the worklist is
* not empty.
*/
- atomic_set(get_gcwq_nr_running(gcwq->cpu), 0);
+ for_each_worker_pool(pool, gcwq)
+ atomic_set(get_pool_nr_running(pool), 0);
spin_unlock_irq(&gcwq->lock);
- del_timer_sync(&gcwq->idle_timer);
+ for_each_worker_pool(pool, gcwq)
+ del_timer_sync(&pool->idle_timer);
spin_lock_irq(&gcwq->lock);
/*
@@ -3374,29 +3394,38 @@
* may be frozen works in freezable cwqs. Don't declare
* completion while frozen.
*/
- while (gcwq->nr_workers != gcwq->nr_idle ||
- gcwq->flags & GCWQ_FREEZING ||
- gcwq->trustee_state == TRUSTEE_IN_CHARGE) {
- int nr_works = 0;
+ while (true) {
+ bool busy = false;
- list_for_each_entry(work, &gcwq->worklist, entry) {
- send_mayday(work);
- nr_works++;
- }
+ for_each_worker_pool(pool, gcwq)
+ busy |= pool->nr_workers != pool->nr_idle;
- list_for_each_entry(worker, &gcwq->idle_list, entry) {
- if (!nr_works--)
- break;
- wake_up_process(worker->task);
- }
+ if (!busy && !(gcwq->flags & GCWQ_FREEZING) &&
+ gcwq->trustee_state != TRUSTEE_IN_CHARGE)
+ break;
- if (need_to_create_worker(gcwq)) {
- spin_unlock_irq(&gcwq->lock);
- worker = create_worker(gcwq, false);
- spin_lock_irq(&gcwq->lock);
- if (worker) {
- worker->flags |= WORKER_ROGUE;
- start_worker(worker);
+ for_each_worker_pool(pool, gcwq) {
+ int nr_works = 0;
+
+ list_for_each_entry(work, &pool->worklist, entry) {
+ send_mayday(work);
+ nr_works++;
+ }
+
+ list_for_each_entry(worker, &pool->idle_list, entry) {
+ if (!nr_works--)
+ break;
+ wake_up_process(worker->task);
+ }
+
+ if (need_to_create_worker(pool)) {
+ spin_unlock_irq(&gcwq->lock);
+ worker = create_worker(pool, false);
+ spin_lock_irq(&gcwq->lock);
+ if (worker) {
+ worker->flags |= WORKER_ROGUE;
+ start_worker(worker);
+ }
}
}
@@ -3411,11 +3440,18 @@
* all workers till we're canceled.
*/
do {
- rc = trustee_wait_event(!list_empty(&gcwq->idle_list));
- while (!list_empty(&gcwq->idle_list))
- destroy_worker(list_first_entry(&gcwq->idle_list,
- struct worker, entry));
- } while (gcwq->nr_workers && rc >= 0);
+ rc = trustee_wait_event(gcwq_has_idle_workers(gcwq));
+
+ i = 0;
+ for_each_worker_pool(pool, gcwq) {
+ while (!list_empty(&pool->idle_list)) {
+ worker = list_first_entry(&pool->idle_list,
+ struct worker, entry);
+ destroy_worker(worker);
+ }
+ i |= pool->nr_workers;
+ }
+ } while (i && rc >= 0);
/*
* At this point, either draining has completed and no worker
@@ -3424,7 +3460,8 @@
* Tell the remaining busy ones to rebind once it finishes the
* currently scheduled works by scheduling the rebind_work.
*/
- WARN_ON(!list_empty(&gcwq->idle_list));
+ for_each_worker_pool(pool, gcwq)
+ WARN_ON(!list_empty(&pool->idle_list));
for_each_busy_worker(worker, i, pos, gcwq) {
struct work_struct *rebind_work = &worker->rebind_work;
@@ -3449,7 +3486,8 @@
}
/* relinquish manager role */
- gcwq->flags &= ~GCWQ_MANAGING_WORKERS;
+ for_each_worker_pool(pool, gcwq)
+ pool->flags &= ~POOL_MANAGING_WORKERS;
/* notify completion */
gcwq->trustee = NULL;
@@ -3491,8 +3529,10 @@
unsigned int cpu = (unsigned long)hcpu;
struct global_cwq *gcwq = get_gcwq(cpu);
struct task_struct *new_trustee = NULL;
- struct worker *uninitialized_var(new_worker);
+ struct worker *new_workers[NR_WORKER_POOLS] = { };
+ struct worker_pool *pool;
unsigned long flags;
+ int i;
action &= ~CPU_TASKS_FROZEN;
@@ -3505,12 +3545,12 @@
kthread_bind(new_trustee, cpu);
/* fall through */
case CPU_UP_PREPARE:
- BUG_ON(gcwq->first_idle);
- new_worker = create_worker(gcwq, false);
- if (!new_worker) {
- if (new_trustee)
- kthread_stop(new_trustee);
- return NOTIFY_BAD;
+ i = 0;
+ for_each_worker_pool(pool, gcwq) {
+ BUG_ON(pool->first_idle);
+ new_workers[i] = create_worker(pool, false);
+ if (!new_workers[i++])
+ goto err_destroy;
}
}
@@ -3527,8 +3567,11 @@
wait_trustee_state(gcwq, TRUSTEE_IN_CHARGE);
/* fall through */
case CPU_UP_PREPARE:
- BUG_ON(gcwq->first_idle);
- gcwq->first_idle = new_worker;
+ i = 0;
+ for_each_worker_pool(pool, gcwq) {
+ BUG_ON(pool->first_idle);
+ pool->first_idle = new_workers[i++];
+ }
break;
case CPU_DYING:
@@ -3545,8 +3588,10 @@
gcwq->trustee_state = TRUSTEE_BUTCHER;
/* fall through */
case CPU_UP_CANCELED:
- destroy_worker(gcwq->first_idle);
- gcwq->first_idle = NULL;
+ for_each_worker_pool(pool, gcwq) {
+ destroy_worker(pool->first_idle);
+ pool->first_idle = NULL;
+ }
break;
case CPU_DOWN_FAILED:
@@ -3563,18 +3608,32 @@
* Put the first_idle in and request a real manager to
* take a look.
*/
- spin_unlock_irq(&gcwq->lock);
- kthread_bind(gcwq->first_idle->task, cpu);
- spin_lock_irq(&gcwq->lock);
- gcwq->flags |= GCWQ_MANAGE_WORKERS;
- start_worker(gcwq->first_idle);
- gcwq->first_idle = NULL;
+ for_each_worker_pool(pool, gcwq) {
+ spin_unlock_irq(&gcwq->lock);
+ kthread_bind(pool->first_idle->task, cpu);
+ spin_lock_irq(&gcwq->lock);
+ pool->flags |= POOL_MANAGE_WORKERS;
+ start_worker(pool->first_idle);
+ pool->first_idle = NULL;
+ }
break;
}
spin_unlock_irqrestore(&gcwq->lock, flags);
return notifier_from_errno(0);
+
+err_destroy:
+ if (new_trustee)
+ kthread_stop(new_trustee);
+
+ spin_lock_irqsave(&gcwq->lock, flags);
+ for (i = 0; i < NR_WORKER_POOLS; i++)
+ if (new_workers[i])
+ destroy_worker(new_workers[i]);
+ spin_unlock_irqrestore(&gcwq->lock, flags);
+
+ return NOTIFY_BAD;
}
#ifdef CONFIG_SMP
@@ -3733,6 +3792,7 @@
for_each_gcwq_cpu(cpu) {
struct global_cwq *gcwq = get_gcwq(cpu);
+ struct worker_pool *pool;
struct workqueue_struct *wq;
spin_lock_irq(&gcwq->lock);
@@ -3754,7 +3814,8 @@
cwq_activate_first_delayed(cwq);
}
- wake_up_worker(gcwq);
+ for_each_worker_pool(pool, gcwq)
+ wake_up_worker(pool);
spin_unlock_irq(&gcwq->lock);
}
@@ -3775,24 +3836,29 @@
/* initialize gcwqs */
for_each_gcwq_cpu(cpu) {
struct global_cwq *gcwq = get_gcwq(cpu);
+ struct worker_pool *pool;
spin_lock_init(&gcwq->lock);
- INIT_LIST_HEAD(&gcwq->worklist);
gcwq->cpu = cpu;
gcwq->flags |= GCWQ_DISASSOCIATED;
- INIT_LIST_HEAD(&gcwq->idle_list);
for (i = 0; i < BUSY_WORKER_HASH_SIZE; i++)
INIT_HLIST_HEAD(&gcwq->busy_hash[i]);
- init_timer_deferrable(&gcwq->idle_timer);
- gcwq->idle_timer.function = idle_worker_timeout;
- gcwq->idle_timer.data = (unsigned long)gcwq;
+ for_each_worker_pool(pool, gcwq) {
+ pool->gcwq = gcwq;
+ INIT_LIST_HEAD(&pool->worklist);
+ INIT_LIST_HEAD(&pool->idle_list);
- setup_timer(&gcwq->mayday_timer, gcwq_mayday_timeout,
- (unsigned long)gcwq);
+ init_timer_deferrable(&pool->idle_timer);
+ pool->idle_timer.function = idle_worker_timeout;
+ pool->idle_timer.data = (unsigned long)pool;
- ida_init(&gcwq->worker_ida);
+ setup_timer(&pool->mayday_timer, gcwq_mayday_timeout,
+ (unsigned long)pool);
+
+ ida_init(&pool->worker_ida);
+ }
gcwq->trustee_state = TRUSTEE_DONE;
init_waitqueue_head(&gcwq->trustee_wait);
@@ -3801,15 +3867,20 @@
/* create the initial worker */
for_each_online_gcwq_cpu(cpu) {
struct global_cwq *gcwq = get_gcwq(cpu);
- struct worker *worker;
+ struct worker_pool *pool;
if (cpu != WORK_CPU_UNBOUND)
gcwq->flags &= ~GCWQ_DISASSOCIATED;
- worker = create_worker(gcwq, true);
- BUG_ON(!worker);
- spin_lock_irq(&gcwq->lock);
- start_worker(worker);
- spin_unlock_irq(&gcwq->lock);
+
+ for_each_worker_pool(pool, gcwq) {
+ struct worker *worker;
+
+ worker = create_worker(pool, true);
+ BUG_ON(!worker);
+ spin_lock_irq(&gcwq->lock);
+ start_worker(worker);
+ spin_unlock_irq(&gcwq->lock);
+ }
}
system_wq = alloc_workqueue("events", 0, 0);
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 69b9521..8a93508 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -785,6 +785,10 @@
set_pageblock_migratetype(page, MIGRATE_CMA);
__free_pages(page, pageblock_order);
totalram_pages += pageblock_nr_pages;
+#ifdef CONFIG_HIGHMEM
+ if (PageHighMem(page))
+ totalhigh_pages += pageblock_nr_pages;
+#endif
}
#endif
@@ -1657,6 +1661,7 @@
long min = mark;
long lowmem_reserve = z->lowmem_reserve[classzone_idx];
int o;
+ long free_cma = 0;
free_pages -= (1 << order) - 1;
if (alloc_flags & ALLOC_HIGH)
@@ -1666,9 +1671,10 @@
#ifdef CONFIG_CMA
/* If allocation can't use CMA areas don't use free CMA pages */
if (!(alloc_flags & ALLOC_CMA))
- free_pages -= zone_page_state(z, NR_FREE_CMA_PAGES);
+ free_cma = zone_page_state(z, NR_FREE_CMA_PAGES);
#endif
- if (free_pages <= min + lowmem_reserve)
+
+ if (free_pages - free_cma <= min + lowmem_reserve)
return false;
for (o = 0; o < order; o++) {
/* At the next order, this order's pages become unavailable */
@@ -5199,10 +5205,6 @@
zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + (tmp >> 2);
zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + (tmp >> 1);
- zone->watermark[WMARK_MIN] += cma_wmark_pages(zone);
- zone->watermark[WMARK_LOW] += cma_wmark_pages(zone);
- zone->watermark[WMARK_HIGH] += cma_wmark_pages(zone);
-
setup_zone_migrate_reserve(zone);
spin_unlock_irqrestore(&zone->lock, flags);
}
@@ -5820,54 +5822,6 @@
return ret > 0 ? 0 : ret;
}
-/*
- * Update zone's cma pages counter used for watermark level calculation.
- */
-static inline void __update_cma_watermarks(struct zone *zone, int count)
-{
- unsigned long flags;
- spin_lock_irqsave(&zone->lock, flags);
- zone->min_cma_pages += count;
- spin_unlock_irqrestore(&zone->lock, flags);
- setup_per_zone_wmarks();
-}
-
-/*
- * Trigger memory pressure bump to reclaim some pages in order to be able to
- * allocate 'count' pages in single page units. Does similar work as
- *__alloc_pages_slowpath() function.
- */
-static int __reclaim_pages(struct zone *zone, gfp_t gfp_mask, int count)
-{
- enum zone_type high_zoneidx = gfp_zone(gfp_mask);
- struct zonelist *zonelist = node_zonelist(0, gfp_mask);
- int did_some_progress = 0;
- int order = 1;
-
- /*
- * Increase level of watermarks to force kswapd do his job
- * to stabilise at new watermark level.
- */
- __update_cma_watermarks(zone, count);
-
- /* Obey watermarks as if the page was being allocated */
- while (!zone_watermark_ok(zone, 0, low_wmark_pages(zone), 0, 0)) {
- wake_all_kswapd(order, zonelist, high_zoneidx, zone_idx(zone));
-
- did_some_progress = __perform_reclaim(gfp_mask, order, zonelist,
- NULL);
- if (!did_some_progress) {
- /* Exhausted what can be done so it's blamo time */
- out_of_memory(zonelist, gfp_mask, order, NULL, false);
- }
- }
-
- /* Restore original watermark levels. */
- __update_cma_watermarks(zone, -count);
-
- return count;
-}
-
/**
* alloc_contig_range() -- tries to allocate given range of pages
* @start: start PFN to allocate
@@ -5968,11 +5922,6 @@
goto done;
}
- /*
- * Reclaim enough pages to make sure that contiguous allocation
- * will not starve the system.
- */
- __reclaim_pages(zone, GFP_HIGHUSER_MOVABLE, end-start);
/* Grab isolated pages from freelists. */
outer_end = isolate_freepages_range(outer_start, end);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index d243646..2dba071 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -2426,6 +2426,21 @@
}
}
+static inline void hci_hardware_error_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_hardware_error *ev = (void *) skb->data;
+
+ BT_ERR("hdev=%p, hw_err_code = %u", hdev, ev->hw_err_code);
+
+ if (hdev && hdev->dev_type == HCI_BREDR) {
+ hci_dev_lock_bh(hdev);
+ mgmt_powered(hdev->id, 1);
+ hci_dev_unlock_bh(hdev);
+ }
+
+}
+
static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_role_change *ev = (void *) skb->data;
@@ -3555,6 +3570,10 @@
hci_cmd_status_evt(hdev, skb);
break;
+ case HCI_EV_HARDWARE_ERROR:
+ hci_hardware_error_evt(hdev, skb);
+ break;
+
case HCI_EV_ROLE_CHANGE:
hci_role_change_evt(hdev, skb);
break;
diff --git a/net/ipv4/netfilter/ipt_NATTYPE.c b/net/ipv4/netfilter/ipt_NATTYPE.c
index 2bb18ca..f181be4 100644
--- a/net/ipv4/netfilter/ipt_NATTYPE.c
+++ b/net/ipv4/netfilter/ipt_NATTYPE.c
@@ -58,6 +58,7 @@
struct ipt_nattype {
struct list_head list;
struct timer_list timeout;
+ unsigned long timeout_value;
unsigned char is_valid;
unsigned short proto; /* Protocol: TCP or UDP */
struct nf_nat_ipv4_range range; /* LAN side source information */
@@ -102,7 +103,7 @@
* nattype_refresh_timer()
* Refresh the timer for this object.
*/
-bool nattype_refresh_timer(unsigned long nat_type)
+bool nattype_refresh_timer(unsigned long nat_type, unsigned long timeout_value)
{
struct ipt_nattype *nte = (struct ipt_nattype *)nat_type;
if (!nte)
@@ -113,7 +114,8 @@
return false;
}
if (del_timer(&nte->timeout)) {
- nte->timeout.expires = jiffies + NATTYPE_TIMEOUT * HZ;
+ nte->timeout_value = timeout_value - jiffies;
+ nte->timeout.expires = timeout_value;
add_timer(&nte->timeout);
spin_unlock_bh(&nattype_lock);
return true;
@@ -222,7 +224,8 @@
* nattype_compare
* Compare two entries, return true if relevant fields are the same.
*/
-static bool nattype_compare(struct ipt_nattype *n1, struct ipt_nattype *n2)
+static bool nattype_compare(struct ipt_nattype *n1, struct ipt_nattype *n2,
+ const struct ipt_nattype_info *info)
{
/*
* Protocol compare.
@@ -261,19 +264,15 @@
}
/*
- * Destination compare
+ * Destination Comapre for Address Restricted Cone NAT.
*/
- if (n1->dest_addr != n2->dest_addr) {
+ if ((info->type == TYPE_ADDRESS_RESTRICTED) &&
+ (n1->dest_addr != n2->dest_addr)) {
DEBUGP("nattype_compare: dest_addr mismatch: %pI4:%pI4\n",
- &n1->dest_addr, &n2->dest_addr);
+ &n1->dest_addr, &n2->dest_addr);
return false;
}
- if (n1->dest_port != n2->dest_port) {
- DEBUGP("nattype_compare: dest_port mismatch: %d:%d\n",
- ntohs(n1->dest_port), ntohs(n2->dest_port));
- return false;
- }
return true;
}
@@ -316,6 +315,15 @@
}
/*
+ * Refresh the timer, if we fail, break
+ * out and forward fail as though we never
+ * found the entry.
+ */
+ if (!nattype_refresh_timer((unsigned long)nte,
+ jiffies + nte->timeout_value))
+ break;
+
+ /*
* Expand the ingress conntrack to include the reply as source
*/
DEBUGP("Expand ingress conntrack=%p, type=%d, src[%pI4:%d]\n",
@@ -344,11 +352,21 @@
enum ip_conntrack_info ctinfo;
const struct ipt_nattype_info *info = par->targinfo;
uint16_t nat_port;
+ enum ip_conntrack_dir dir;
+
if (par->hooknum != NF_INET_FORWARD)
return XT_CONTINUE;
/*
+ * Egress packet, create a new rule in our list. If conntrack does
+ * not have an entry, skip this packet.
+ */
+ ct = nf_ct_get(skb, &ctinfo);
+ if (!ct)
+ return XT_CONTINUE;
+
+ /*
* Ingress packet, refresh the timer if we find an entry.
*/
if (info->mode == MODE_FORWARD_IN) {
@@ -366,7 +384,8 @@
* out and forward fail as though we never
* found the entry.
*/
- if (!nattype_refresh_timer((unsigned long)nte))
+ if (!nattype_refresh_timer((unsigned long)nte,
+ ct->timeout.expires))
break;
/*
* The entry is found and refreshed, the
@@ -382,15 +401,9 @@
return XT_CONTINUE;
}
- /*
- * Egress packet, create a new rule in our list. If conntrack does
- * not have an entry, skip this packet.
- */
- ct = nf_ct_get(skb, &ctinfo);
- if (!ct || (ctinfo == IP_CT_NEW && ctinfo == IP_CT_RELATED))
- return XT_CONTINUE;
+ dir = CTINFO2DIR(ctinfo);
- nat_port = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.all;
+ nat_port = ct->tuplehash[!dir].tuple.dst.u.all;
/*
* Allocate a new entry
@@ -439,7 +452,7 @@
*/
spin_lock_bh(&nattype_lock);
list_for_each_entry(nte2, &nattype_list, list) {
- if (!nattype_compare(nte, nte2))
+ if (!nattype_compare(nte, nte2, info))
continue;
spin_unlock_bh(&nattype_lock);
/*
@@ -447,7 +460,9 @@
* entry as this one is timed out and will be removed
* from the list shortly.
*/
- if (!nattype_refresh_timer((unsigned long)nte2))
+ nte2->timeout_value = ct->timeout.expires - jiffies;
+ if (!nattype_refresh_timer((unsigned long)nte2,
+ ct->timeout.expires))
break;
/*
* Found and refreshed an existing entry. Its values
@@ -463,7 +478,8 @@
/*
* Add the new entry to the list.
*/
- nte->timeout.expires = jiffies + (NATTYPE_TIMEOUT * HZ);
+ nte->timeout_value = ct->timeout.expires - jiffies;
+ nte->timeout.expires = ct->timeout.expires;
add_timer(&nte->timeout);
list_add(&nte->list, &nattype_list);
ct->nattype_entry = (unsigned long)nte;
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 13925ac..d7eafd5 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -1101,7 +1101,7 @@
/* Refresh the NAT type entry. */
#if defined(CONFIG_IP_NF_TARGET_NATTYPE_MODULE)
- (void)nattype_refresh_timer(ct->nattype_entry);
+ (void)nattype_refresh_timer(ct->nattype_entry, ct->timeout.expires);
#endif
acct:
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index ca7e835..6239080 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1229,6 +1229,11 @@
ct->timeout.expires = jiffies + timeout * HZ;
add_timer(&ct->timeout);
+/* Refresh the NAT type entry. */
+#if defined(CONFIG_IP_NF_TARGET_NATTYPE_MODULE)
+ (void)nattype_refresh_timer(ct->nattype_entry, ct->timeout.expires);
+#endif
+
return 0;
}
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 59debb7..2356791 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -137,6 +137,8 @@
static DEFINE_RWLOCK(nl_table_lock);
static atomic_t nl_table_users = ATOMIC_INIT(0);
+#define nl_deref_protected(X) rcu_dereference_protected(X, lockdep_is_held(&nl_table_lock));
+
static ATOMIC_NOTIFIER_HEAD(netlink_chain);
static inline u32 netlink_group_mask(u32 group)
@@ -330,6 +332,11 @@
struct hlist_node *node;
unsigned long mask;
unsigned int i;
+ struct listeners *listeners;
+
+ listeners = nl_deref_protected(tbl->listeners);
+ if (!listeners)
+ return;
for (i = 0; i < NLGRPLONGS(tbl->groups); i++) {
mask = 0;
@@ -337,7 +344,7 @@
if (i < NLGRPLONGS(nlk_sk(sk)->ngroups))
mask |= nlk_sk(sk)->groups[i];
}
- tbl->listeners->masks[i] = mask;
+ listeners->masks[i] = mask;
}
/* this function is only called with the netlink table "grabbed", which
* makes sure updates are visible before bind or setsockopt return. */
@@ -518,7 +525,11 @@
if (netlink_is_kernel(sk)) {
BUG_ON(nl_table[sk->sk_protocol].registered == 0);
if (--nl_table[sk->sk_protocol].registered == 0) {
- kfree(nl_table[sk->sk_protocol].listeners);
+ struct listeners *old;
+
+ old = nl_deref_protected(nl_table[sk->sk_protocol].listeners);
+ RCU_INIT_POINTER(nl_table[sk->sk_protocol].listeners, NULL);
+ kfree_rcu(old, rcu);
nl_table[sk->sk_protocol].module = NULL;
nl_table[sk->sk_protocol].registered = 0;
}
@@ -948,7 +959,7 @@
rcu_read_lock();
listeners = rcu_dereference(nl_table[sk->sk_protocol].listeners);
- if (group - 1 < nl_table[sk->sk_protocol].groups)
+ if (listeners && group - 1 < nl_table[sk->sk_protocol].groups)
res = test_bit(group - 1, listeners->masks);
rcu_read_unlock();
@@ -1579,7 +1590,7 @@
new = kzalloc(sizeof(*new) + NLGRPSZ(groups), GFP_ATOMIC);
if (!new)
return -ENOMEM;
- old = rcu_dereference_protected(tbl->listeners, 1);
+ old = nl_deref_protected(tbl->listeners);
memcpy(new->masks, old->masks, NLGRPSZ(tbl->groups));
rcu_assign_pointer(tbl->listeners, new);
diff --git a/net/wireless/core.c b/net/wireless/core.c
index ccdfed8..674c1fa 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -505,6 +505,11 @@
ETH_ALEN)))
return -EINVAL;
+ if (WARN_ON(wiphy->max_acl_mac_addrs &&
+ (!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME) ||
+ !rdev->ops->set_mac_acl)))
+ return -EINVAL;
+
if (wiphy->addresses)
memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index e1fa62e..6ed6d3e 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -210,6 +210,8 @@
[NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 },
[NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, },
[NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN },
+ [NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 },
+ [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
[NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 },
[NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, },
[NL80211_ATTR_SPLIT_WIPHY_DUMP] = { .type = NLA_FLAG, },
@@ -1066,6 +1068,11 @@
sizeof(*dev->wiphy.ht_capa_mod_mask),
dev->wiphy.ht_capa_mod_mask);
+ if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
+ dev->wiphy.max_acl_mac_addrs)
+ NLA_PUT_U32(msg, NL80211_ATTR_MAC_ACL_MAX,
+ dev->wiphy.max_acl_mac_addrs);
+
return genlmsg_end(msg, hdr);
nla_put_failure:
@@ -2105,6 +2112,97 @@
return err;
}
+/* This function returns an error or the number of nested attributes */
+static int validate_acl_mac_addrs(struct nlattr *nl_attr)
+{
+ struct nlattr *attr;
+ int n_entries = 0, tmp;
+
+ nla_for_each_nested(attr, nl_attr, tmp) {
+ if (nla_len(attr) != ETH_ALEN)
+ return -EINVAL;
+
+ n_entries++;
+ }
+
+ return n_entries;
+}
+
+/*
+ * This function parses ACL information and allocates memory for ACL data.
+ * On successful return, the calling function is responsible to free the
+ * ACL buffer returned by this function.
+ */
+static struct cfg80211_acl_data *parse_acl_data(struct wiphy *wiphy,
+ struct genl_info *info)
+{
+ enum nl80211_acl_policy acl_policy;
+ struct nlattr *attr;
+ struct cfg80211_acl_data *acl;
+ int i = 0, n_entries, tmp;
+
+ if (!wiphy->max_acl_mac_addrs)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ if (!info->attrs[NL80211_ATTR_ACL_POLICY])
+ return ERR_PTR(-EINVAL);
+
+ acl_policy = nla_get_u32(info->attrs[NL80211_ATTR_ACL_POLICY]);
+ if (acl_policy != NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED &&
+ acl_policy != NL80211_ACL_POLICY_DENY_UNLESS_LISTED)
+ return ERR_PTR(-EINVAL);
+
+ if (!info->attrs[NL80211_ATTR_MAC_ADDRS])
+ return ERR_PTR(-EINVAL);
+
+ n_entries = validate_acl_mac_addrs(info->attrs[NL80211_ATTR_MAC_ADDRS]);
+ if (n_entries < 0)
+ return ERR_PTR(n_entries);
+
+ if (n_entries > wiphy->max_acl_mac_addrs)
+ return ERR_PTR(-ENOTSUPP);
+
+ acl = kzalloc(sizeof(*acl) + (sizeof(struct mac_address) * n_entries),
+ GFP_KERNEL);
+ if (!acl)
+ return ERR_PTR(-ENOMEM);
+
+ nla_for_each_nested(attr, info->attrs[NL80211_ATTR_MAC_ADDRS], tmp) {
+ memcpy(acl->mac_addrs[i].addr, nla_data(attr), ETH_ALEN);
+ i++;
+ }
+
+ acl->n_acl_entries = n_entries;
+ acl->acl_policy = acl_policy;
+
+ return acl;
+}
+
+static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct cfg80211_acl_data *acl;
+ int err;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EOPNOTSUPP;
+
+ if (!dev->ieee80211_ptr->beacon_interval)
+ return -EINVAL;
+
+ acl = parse_acl_data(&rdev->wiphy, info);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+
+ err = rdev->ops->set_mac_acl(&rdev->wiphy, dev, acl);
+
+ kfree(acl);
+
+ return err;
+}
+
static int nl80211_parse_beacon(struct genl_info *info,
struct cfg80211_beacon_data *bcn)
{
@@ -2251,9 +2349,18 @@
info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
}
+ if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
+ params.acl = parse_acl_data(&rdev->wiphy, info);
+ if (IS_ERR(params.acl))
+ return PTR_ERR(params.acl);
+ }
+
err = rdev->ops->start_ap(&rdev->wiphy, dev, ¶ms);
if (!err)
wdev->beacon_interval = params.beacon_interval;
+
+ kfree(params.acl);
+
return err;
}
@@ -7063,6 +7170,14 @@
NL80211_FLAG_NEED_RTNL,
},
{
+ .cmd = NL80211_CMD_SET_MAC_ACL,
+ .doit = nl80211_set_mac_acl,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
.cmd = NL80211_CMD_UPDATE_FT_IES,
.doit = nl80211_update_ft_ies,
.policy = nl80211_policy,
diff --git a/scripts/build-all.py b/scripts/build-all.py
index 3cecbe2..4789af7 100755
--- a/scripts/build-all.py
+++ b/scripts/build-all.py
@@ -88,7 +88,6 @@
r'[fm]sm[0-9]*_defconfig',
r'apq*_defconfig',
r'qsd*_defconfig',
- r'msmzinc*_defconfig',
)
for p in arch_pats:
for n in glob.glob('arch/arm/configs/' + p):
diff --git a/scripts/dtc/Makefile b/scripts/dtc/Makefile
index 6d1c6bb..2a48022 100644
--- a/scripts/dtc/Makefile
+++ b/scripts/dtc/Makefile
@@ -27,3 +27,5 @@
# dependencies on generated files need to be listed explicitly
$(obj)/dtc-lexer.lex.o: $(obj)/dtc-parser.tab.h
+# generated files need to be cleaned explicitly
+clean-files := dtc-lexer.lex.c dtc-parser.tab.c dtc-parser.tab.h
diff --git a/scripts/dtc/Makefile.dtc b/scripts/dtc/Makefile.dtc
index 6ddf9ec..bece49b 100644
--- a/scripts/dtc/Makefile.dtc
+++ b/scripts/dtc/Makefile.dtc
@@ -3,7 +3,16 @@
# This is not a complete Makefile of itself. Instead, it is designed to
# be easily embeddable into other systems of Makefiles.
#
-DTC_SRCS = dtc.c flattree.c fstree.c data.c livetree.c treesource.c srcpos.c \
- checks.c
+DTC_SRCS = \
+ checks.c \
+ data.c \
+ dtc.c \
+ flattree.c \
+ fstree.c \
+ livetree.c \
+ srcpos.c \
+ treesource.c \
+ util.c
+
DTC_GEN_SRCS = dtc-lexer.lex.c dtc-parser.tab.c
DTC_OBJS = $(DTC_SRCS:%.c=%.o) $(DTC_GEN_SRCS:%.c=%.o)
diff --git a/scripts/dtc/checks.c b/scripts/dtc/checks.c
index a662a00..ee96a25 100644
--- a/scripts/dtc/checks.c
+++ b/scripts/dtc/checks.c
@@ -31,12 +31,6 @@
#define TRACE(c, fmt, ...) do { } while (0)
#endif
-enum checklevel {
- IGNORE = 0,
- WARN = 1,
- ERROR = 2,
-};
-
enum checkstatus {
UNCHECKED = 0,
PREREQ,
@@ -57,14 +51,14 @@
node_check_fn node_fn;
prop_check_fn prop_fn;
void *data;
- enum checklevel level;
+ bool warn, error;
enum checkstatus status;
int inprogress;
int num_prereqs;
struct check **prereq;
};
-#define CHECK(nm, tfn, nfn, pfn, d, lvl, ...) \
+#define CHECK_ENTRY(nm, tfn, nfn, pfn, d, w, e, ...) \
static struct check *nm##_prereqs[] = { __VA_ARGS__ }; \
static struct check nm = { \
.name = #nm, \
@@ -72,20 +66,37 @@
.node_fn = (nfn), \
.prop_fn = (pfn), \
.data = (d), \
- .level = (lvl), \
+ .warn = (w), \
+ .error = (e), \
.status = UNCHECKED, \
.num_prereqs = ARRAY_SIZE(nm##_prereqs), \
.prereq = nm##_prereqs, \
};
+#define WARNING(nm, tfn, nfn, pfn, d, ...) \
+ CHECK_ENTRY(nm, tfn, nfn, pfn, d, true, false, __VA_ARGS__)
+#define ERROR(nm, tfn, nfn, pfn, d, ...) \
+ CHECK_ENTRY(nm, tfn, nfn, pfn, d, false, true, __VA_ARGS__)
+#define CHECK(nm, tfn, nfn, pfn, d, ...) \
+ CHECK_ENTRY(nm, tfn, nfn, pfn, d, false, false, __VA_ARGS__)
-#define TREE_CHECK(nm, d, lvl, ...) \
- CHECK(nm, check_##nm, NULL, NULL, d, lvl, __VA_ARGS__)
-#define NODE_CHECK(nm, d, lvl, ...) \
- CHECK(nm, NULL, check_##nm, NULL, d, lvl, __VA_ARGS__)
-#define PROP_CHECK(nm, d, lvl, ...) \
- CHECK(nm, NULL, NULL, check_##nm, d, lvl, __VA_ARGS__)
-#define BATCH_CHECK(nm, lvl, ...) \
- CHECK(nm, NULL, NULL, NULL, NULL, lvl, __VA_ARGS__)
+#define TREE_WARNING(nm, d, ...) \
+ WARNING(nm, check_##nm, NULL, NULL, d, __VA_ARGS__)
+#define TREE_ERROR(nm, d, ...) \
+ ERROR(nm, check_##nm, NULL, NULL, d, __VA_ARGS__)
+#define TREE_CHECK(nm, d, ...) \
+ CHECK(nm, check_##nm, NULL, NULL, d, __VA_ARGS__)
+#define NODE_WARNING(nm, d, ...) \
+ WARNING(nm, NULL, check_##nm, NULL, d, __VA_ARGS__)
+#define NODE_ERROR(nm, d, ...) \
+ ERROR(nm, NULL, check_##nm, NULL, d, __VA_ARGS__)
+#define NODE_CHECK(nm, d, ...) \
+ CHECK(nm, NULL, check_##nm, NULL, d, __VA_ARGS__)
+#define PROP_WARNING(nm, d, ...) \
+ WARNING(nm, NULL, NULL, check_##nm, d, __VA_ARGS__)
+#define PROP_ERROR(nm, d, ...) \
+ ERROR(nm, NULL, NULL, check_##nm, d, __VA_ARGS__)
+#define PROP_CHECK(nm, d, ...) \
+ CHECK(nm, NULL, NULL, check_##nm, d, __VA_ARGS__)
#ifdef __GNUC__
static inline void check_msg(struct check *c, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
@@ -95,13 +106,13 @@
va_list ap;
va_start(ap, fmt);
- if ((c->level < WARN) || (c->level <= quiet))
- return; /* Suppress message */
-
- fprintf(stderr, "%s (%s): ",
- (c->level == ERROR) ? "ERROR" : "Warning", c->name);
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
+ if ((c->warn && (quiet < 1))
+ || (c->error && (quiet < 2))) {
+ fprintf(stderr, "%s (%s): ",
+ (c->error) ? "ERROR" : "Warning", c->name);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ }
}
#define FAIL(c, ...) \
@@ -167,7 +178,7 @@
out:
c->inprogress = 0;
- if ((c->status != PASSED) && (c->level == ERROR))
+ if ((c->status != PASSED) && (c->error))
error = 1;
return error;
}
@@ -176,6 +187,13 @@
* Utility check functions
*/
+/* A check which always fails, for testing purposes only */
+static inline void check_always_fail(struct check *c, struct node *dt)
+{
+ FAIL(c, "always_fail check");
+}
+TREE_CHECK(always_fail, NULL);
+
static void check_is_string(struct check *c, struct node *root,
struct node *node)
{
@@ -190,8 +208,10 @@
FAIL(c, "\"%s\" property in %s is not a string",
propname, node->fullpath);
}
-#define CHECK_IS_STRING(nm, propname, lvl) \
- CHECK(nm, NULL, check_is_string, NULL, (propname), (lvl))
+#define WARNING_IF_NOT_STRING(nm, propname) \
+ WARNING(nm, NULL, check_is_string, NULL, (propname))
+#define ERROR_IF_NOT_STRING(nm, propname) \
+ ERROR(nm, NULL, check_is_string, NULL, (propname))
static void check_is_cell(struct check *c, struct node *root,
struct node *node)
@@ -207,8 +227,10 @@
FAIL(c, "\"%s\" property in %s is not a single cell",
propname, node->fullpath);
}
-#define CHECK_IS_CELL(nm, propname, lvl) \
- CHECK(nm, NULL, check_is_cell, NULL, (propname), (lvl))
+#define WARNING_IF_NOT_CELL(nm, propname) \
+ WARNING(nm, NULL, check_is_cell, NULL, (propname))
+#define ERROR_IF_NOT_CELL(nm, propname) \
+ ERROR(nm, NULL, check_is_cell, NULL, (propname))
/*
* Structural check functions
@@ -227,20 +249,24 @@
FAIL(c, "Duplicate node name %s",
child->fullpath);
}
-NODE_CHECK(duplicate_node_names, NULL, ERROR);
+NODE_ERROR(duplicate_node_names, NULL);
static void check_duplicate_property_names(struct check *c, struct node *dt,
struct node *node)
{
struct property *prop, *prop2;
- for_each_property(node, prop)
- for (prop2 = prop->next; prop2; prop2 = prop2->next)
+ for_each_property(node, prop) {
+ for (prop2 = prop->next; prop2; prop2 = prop2->next) {
+ if (prop2->deleted)
+ continue;
if (streq(prop->name, prop2->name))
FAIL(c, "Duplicate property name %s in %s",
prop->name, node->fullpath);
+ }
+ }
}
-NODE_CHECK(duplicate_property_names, NULL, ERROR);
+NODE_ERROR(duplicate_property_names, NULL);
#define LOWERCASE "abcdefghijklmnopqrstuvwxyz"
#define UPPERCASE "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -256,7 +282,7 @@
FAIL(c, "Bad character '%c' in node %s",
node->name[n], node->fullpath);
}
-NODE_CHECK(node_name_chars, PROPNODECHARS "@", ERROR);
+NODE_ERROR(node_name_chars, PROPNODECHARS "@");
static void check_node_name_format(struct check *c, struct node *dt,
struct node *node)
@@ -265,7 +291,7 @@
FAIL(c, "Node %s has multiple '@' characters in name",
node->fullpath);
}
-NODE_CHECK(node_name_format, NULL, ERROR, &node_name_chars);
+NODE_ERROR(node_name_format, NULL, &node_name_chars);
static void check_property_name_chars(struct check *c, struct node *dt,
struct node *node, struct property *prop)
@@ -276,7 +302,7 @@
FAIL(c, "Bad character '%c' in property name \"%s\", node %s",
prop->name[n], prop->name, node->fullpath);
}
-PROP_CHECK(property_name_chars, PROPNODECHARS, ERROR);
+PROP_ERROR(property_name_chars, PROPNODECHARS);
#define DESCLABEL_FMT "%s%s%s%s%s"
#define DESCLABEL_ARGS(node,prop,mark) \
@@ -331,8 +357,8 @@
for_each_marker_of_type(m, LABEL)
check_duplicate_label(c, dt, m->ref, node, prop, m);
}
-CHECK(duplicate_label, NULL, check_duplicate_label_node,
- check_duplicate_label_prop, NULL, ERROR);
+ERROR(duplicate_label, NULL, check_duplicate_label_node,
+ check_duplicate_label_prop, NULL);
static void check_explicit_phandles(struct check *c, struct node *root,
struct node *node, struct property *prop)
@@ -391,7 +417,7 @@
node->phandle = phandle;
}
-PROP_CHECK(explicit_phandles, NULL, ERROR);
+PROP_ERROR(explicit_phandles, NULL);
static void check_name_properties(struct check *c, struct node *root,
struct node *node)
@@ -420,8 +446,8 @@
free(prop);
}
}
-CHECK_IS_STRING(name_is_string, "name", ERROR);
-NODE_CHECK(name_properties, NULL, ERROR, &name_is_string);
+ERROR_IF_NOT_STRING(name_is_string, "name");
+NODE_ERROR(name_properties, NULL, &name_is_string);
/*
* Reference fixup functions
@@ -448,7 +474,7 @@
*((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle);
}
}
-CHECK(phandle_references, NULL, NULL, fixup_phandle_references, NULL, ERROR,
+ERROR(phandle_references, NULL, NULL, fixup_phandle_references, NULL,
&duplicate_node_names, &explicit_phandles);
static void fixup_path_references(struct check *c, struct node *dt,
@@ -473,19 +499,19 @@
strlen(path) + 1);
}
}
-CHECK(path_references, NULL, NULL, fixup_path_references, NULL, ERROR,
+ERROR(path_references, NULL, NULL, fixup_path_references, NULL,
&duplicate_node_names);
/*
* Semantic checks
*/
-CHECK_IS_CELL(address_cells_is_cell, "#address-cells", WARN);
-CHECK_IS_CELL(size_cells_is_cell, "#size-cells", WARN);
-CHECK_IS_CELL(interrupt_cells_is_cell, "#interrupt-cells", WARN);
+WARNING_IF_NOT_CELL(address_cells_is_cell, "#address-cells");
+WARNING_IF_NOT_CELL(size_cells_is_cell, "#size-cells");
+WARNING_IF_NOT_CELL(interrupt_cells_is_cell, "#interrupt-cells");
-CHECK_IS_STRING(device_type_is_string, "device_type", WARN);
-CHECK_IS_STRING(model_is_string, "model", WARN);
-CHECK_IS_STRING(status_is_string, "status", WARN);
+WARNING_IF_NOT_STRING(device_type_is_string, "device_type");
+WARNING_IF_NOT_STRING(model_is_string, "model");
+WARNING_IF_NOT_STRING(status_is_string, "status");
static void fixup_addr_size_cells(struct check *c, struct node *dt,
struct node *node)
@@ -503,8 +529,8 @@
if (prop)
node->size_cells = propval_cell(prop);
}
-CHECK(addr_size_cells, NULL, fixup_addr_size_cells, NULL, NULL, WARN,
- &address_cells_is_cell, &size_cells_is_cell);
+WARNING(addr_size_cells, NULL, fixup_addr_size_cells, NULL, NULL,
+ &address_cells_is_cell, &size_cells_is_cell);
#define node_addr_cells(n) \
(((n)->addr_cells == -1) ? 2 : (n)->addr_cells)
@@ -538,7 +564,7 @@
"(#address-cells == %d, #size-cells == %d)",
node->fullpath, prop->val.len, addr_cells, size_cells);
}
-NODE_CHECK(reg_format, NULL, WARN, &addr_size_cells);
+NODE_WARNING(reg_format, NULL, &addr_size_cells);
static void check_ranges_format(struct check *c, struct node *dt,
struct node *node)
@@ -579,7 +605,7 @@
p_addr_cells, c_addr_cells, c_size_cells);
}
}
-NODE_CHECK(ranges_format, NULL, WARN, &addr_size_cells);
+NODE_WARNING(ranges_format, NULL, &addr_size_cells);
/*
* Style checks
@@ -606,7 +632,7 @@
FAIL(c, "Relying on default #size-cells value for %s",
node->fullpath);
}
-NODE_CHECK(avoid_default_addr_size, NULL, WARN, &addr_size_cells);
+NODE_WARNING(avoid_default_addr_size, NULL, &addr_size_cells);
static void check_obsolete_chosen_interrupt_controller(struct check *c,
struct node *dt)
@@ -623,7 +649,7 @@
FAIL(c, "/chosen has obsolete \"interrupt-controller\" "
"property");
}
-TREE_CHECK(obsolete_chosen_interrupt_controller, NULL, WARN);
+TREE_WARNING(obsolete_chosen_interrupt_controller, NULL);
static struct check *check_table[] = {
&duplicate_node_names, &duplicate_property_names,
@@ -642,8 +668,71 @@
&avoid_default_addr_size,
&obsolete_chosen_interrupt_controller,
+
+ &always_fail,
};
+static void enable_warning_error(struct check *c, bool warn, bool error)
+{
+ int i;
+
+ /* Raising level, also raise it for prereqs */
+ if ((warn && !c->warn) || (error && !c->error))
+ for (i = 0; i < c->num_prereqs; i++)
+ enable_warning_error(c->prereq[i], warn, error);
+
+ c->warn = c->warn || warn;
+ c->error = c->error || error;
+}
+
+static void disable_warning_error(struct check *c, bool warn, bool error)
+{
+ int i;
+
+ /* Lowering level, also lower it for things this is the prereq
+ * for */
+ if ((warn && c->warn) || (error && c->error)) {
+ for (i = 0; i < ARRAY_SIZE(check_table); i++) {
+ struct check *cc = check_table[i];
+ int j;
+
+ for (j = 0; j < cc->num_prereqs; j++)
+ if (cc->prereq[j] == c)
+ disable_warning_error(cc, warn, error);
+ }
+ }
+
+ c->warn = c->warn && !warn;
+ c->error = c->error && !error;
+}
+
+void parse_checks_option(bool warn, bool error, const char *optarg)
+{
+ int i;
+ const char *name = optarg;
+ bool enable = true;
+
+ if ((strncmp(optarg, "no-", 3) == 0)
+ || (strncmp(optarg, "no_", 3) == 0)) {
+ name = optarg + 3;
+ enable = false;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(check_table); i++) {
+ struct check *c = check_table[i];
+
+ if (streq(c->name, name)) {
+ if (enable)
+ enable_warning_error(c, warn, error);
+ else
+ disable_warning_error(c, warn, error);
+ return;
+ }
+ }
+
+ die("Unrecognized check name \"%s\"\n", name);
+}
+
void process_checks(int force, struct boot_info *bi)
{
struct node *dt = bi->dt;
@@ -653,7 +742,7 @@
for (i = 0; i < ARRAY_SIZE(check_table); i++) {
struct check *c = check_table[i];
- if (c->level != IGNORE)
+ if (c->warn || c->error)
error = error || run_check(c, dt);
}
diff --git a/scripts/dtc/data.c b/scripts/dtc/data.c
index fe555e8..4a40c5b 100644
--- a/scripts/dtc/data.c
+++ b/scripts/dtc/data.c
@@ -68,40 +68,6 @@
return d;
}
-static char get_oct_char(const char *s, int *i)
-{
- char x[4];
- char *endx;
- long val;
-
- x[3] = '\0';
- strncpy(x, s + *i, 3);
-
- val = strtol(x, &endx, 8);
-
- assert(endx > x);
-
- (*i) += endx - x;
- return val;
-}
-
-static char get_hex_char(const char *s, int *i)
-{
- char x[3];
- char *endx;
- long val;
-
- x[2] = '\0';
- strncpy(x, s + *i, 2);
-
- val = strtol(x, &endx, 16);
- if (!(endx > x))
- die("\\x used with no following hex digits\n");
-
- (*i) += endx - x;
- return val;
-}
-
struct data data_copy_escape_string(const char *s, int len)
{
int i = 0;
@@ -114,53 +80,10 @@
while (i < len) {
char c = s[i++];
- if (c != '\\') {
- q[d.len++] = c;
- continue;
- }
+ if (c == '\\')
+ c = get_escape_char(s, &i);
- c = s[i++];
- assert(c);
- switch (c) {
- case 'a':
- q[d.len++] = '\a';
- break;
- case 'b':
- q[d.len++] = '\b';
- break;
- case 't':
- q[d.len++] = '\t';
- break;
- case 'n':
- q[d.len++] = '\n';
- break;
- case 'v':
- q[d.len++] = '\v';
- break;
- case 'f':
- q[d.len++] = '\f';
- break;
- case 'r':
- q[d.len++] = '\r';
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- i--; /* need to re-read the first digit as
- * part of the octal value */
- q[d.len++] = get_oct_char(s, &i);
- break;
- case 'x':
- q[d.len++] = get_hex_char(s, &i);
- break;
- default:
- q[d.len++] = c;
- }
+ q[d.len++] = c;
}
q[d.len++] = '\0';
@@ -245,11 +168,33 @@
return d;
}
-struct data data_append_cell(struct data d, cell_t word)
+struct data data_append_integer(struct data d, uint64_t value, int bits)
{
- cell_t beword = cpu_to_fdt32(word);
+ uint8_t value_8;
+ uint16_t value_16;
+ uint32_t value_32;
+ uint64_t value_64;
- return data_append_data(d, &beword, sizeof(beword));
+ switch (bits) {
+ case 8:
+ value_8 = value;
+ return data_append_data(d, &value_8, 1);
+
+ case 16:
+ value_16 = cpu_to_fdt16(value);
+ return data_append_data(d, &value_16, 2);
+
+ case 32:
+ value_32 = cpu_to_fdt32(value);
+ return data_append_data(d, &value_32, 4);
+
+ case 64:
+ value_64 = cpu_to_fdt64(value);
+ return data_append_data(d, &value_64, 8);
+
+ default:
+ die("Invalid literal size (%d)\n", bits);
+ }
}
struct data data_append_re(struct data d, const struct fdt_reserve_entry *re)
@@ -262,11 +207,14 @@
return data_append_data(d, &bere, sizeof(bere));
}
+struct data data_append_cell(struct data d, cell_t word)
+{
+ return data_append_integer(d, word, sizeof(word) * 8);
+}
+
struct data data_append_addr(struct data d, uint64_t addr)
{
- uint64_t beaddr = cpu_to_fdt64(addr);
-
- return data_append_data(d, &beaddr, sizeof(beaddr));
+ return data_append_integer(d, addr, sizeof(addr) * 8);
}
struct data data_append_byte(struct data d, uint8_t byte)
diff --git a/scripts/dtc/dtc-lexer.l b/scripts/dtc/dtc-lexer.l
index e866ea5..254d5af 100644
--- a/scripts/dtc/dtc-lexer.l
+++ b/scripts/dtc/dtc-lexer.l
@@ -29,6 +29,7 @@
PATHCHAR ({PROPNODECHAR}|[/])
LABEL [a-zA-Z_][a-zA-Z0-9_]*
STRING \"([^\\"]|\\.)*\"
+CHAR_LITERAL '([^']|\\')*'
WS [[:space:]]
COMMENT "/*"([^*]|\*+[^*/])*\*+"/"
LINECOMMENT "//".*\n
@@ -70,6 +71,27 @@
push_input_file(name);
}
+<*>^"#"(line)?{WS}+[0-9]+{WS}+{STRING}({WS}+[0-9]+)? {
+ char *line, *tmp, *fn;
+ /* skip text before line # */
+ line = yytext;
+ while (!isdigit(*line))
+ line++;
+ /* skip digits in line # */
+ tmp = line;
+ while (!isspace(*tmp))
+ tmp++;
+ /* "NULL"-terminate line # */
+ *tmp = '\0';
+ /* start of filename */
+ fn = strchr(tmp + 1, '"') + 1;
+ /* strip trailing " from filename */
+ tmp = strchr(fn, '"');
+ *tmp = 0;
+ /* -1 since #line is the number of the next line */
+ srcpos_set_line(xstrdup(fn), atoi(line) - 1);
+ }
+
<*><<EOF>> {
if (!pop_input_file()) {
yyterminate();
@@ -96,6 +118,26 @@
return DT_MEMRESERVE;
}
+<*>"/bits/" {
+ DPRINT("Keyword: /bits/\n");
+ BEGIN_DEFAULT();
+ return DT_BITS;
+ }
+
+<*>"/delete-property/" {
+ DPRINT("Keyword: /delete-property/\n");
+ DPRINT("<PROPNODENAME>\n");
+ BEGIN(PROPNODENAME);
+ return DT_DEL_PROP;
+ }
+
+<*>"/delete-node/" {
+ DPRINT("Keyword: /delete-node/\n");
+ DPRINT("<PROPNODENAME>\n");
+ BEGIN(PROPNODENAME);
+ return DT_DEL_NODE;
+ }
+
<*>{LABEL}: {
DPRINT("Label: %s\n", yytext);
yylval.labelref = xstrdup(yytext);
@@ -103,12 +145,19 @@
return DT_LABEL;
}
-<V1>[0-9]+|0[xX][0-9a-fA-F]+ {
+<V1>([0-9]+|0[xX][0-9a-fA-F]+)(U|L|UL|LL|ULL)? {
yylval.literal = xstrdup(yytext);
DPRINT("Literal: '%s'\n", yylval.literal);
return DT_LITERAL;
}
+<*>{CHAR_LITERAL} {
+ yytext[yyleng-1] = '\0';
+ yylval.literal = xstrdup(yytext+1);
+ DPRINT("Character literal: %s\n", yylval.literal);
+ return DT_CHAR_LITERAL;
+ }
+
<*>\&{LABEL} { /* label reference */
DPRINT("Ref: %s\n", yytext+1);
yylval.labelref = xstrdup(yytext+1);
@@ -134,9 +183,10 @@
return ']';
}
-<PROPNODENAME>{PROPNODECHAR}+ {
+<PROPNODENAME>\\?{PROPNODECHAR}+ {
DPRINT("PropNodeName: %s\n", yytext);
- yylval.propnodename = xstrdup(yytext);
+ yylval.propnodename = xstrdup((yytext[0] == '\\') ?
+ yytext + 1 : yytext);
BEGIN_DEFAULT();
return DT_PROPNODENAME;
}
@@ -150,6 +200,15 @@
<*>{COMMENT}+ /* eat C-style comments */
<*>{LINECOMMENT}+ /* eat C++-style comments */
+<*>"<<" { return DT_LSHIFT; };
+<*>">>" { return DT_RSHIFT; };
+<*>"<=" { return DT_LE; };
+<*>">=" { return DT_GE; };
+<*>"==" { return DT_EQ; };
+<*>"!=" { return DT_NE; };
+<*>"&&" { return DT_AND; };
+<*>"||" { return DT_OR; };
+
<*>. {
DPRINT("Char: %c (\\x%02x)\n", yytext[0],
(unsigned)yytext[0]);
diff --git a/scripts/dtc/dtc-lexer.lex.c_shipped b/scripts/dtc/dtc-lexer.lex.c_shipped
index 8bbe128..a6c5fcd 100644
--- a/scripts/dtc/dtc-lexer.lex.c_shipped
+++ b/scripts/dtc/dtc-lexer.lex.c_shipped
@@ -1,5 +1,6 @@
+#line 2 "dtc-lexer.lex.c"
-#line 3 "scripts/dtc/dtc-lexer.lex.c_shipped"
+#line 4 "dtc-lexer.lex.c"
#define YY_INT_ALIGNED short int
@@ -53,7 +54,6 @@
typedef unsigned char flex_uint8_t;
typedef unsigned short int flex_uint16_t;
typedef unsigned int flex_uint32_t;
-#endif /* ! C99 */
/* Limits of integral types. */
#ifndef INT8_MIN
@@ -84,6 +84,8 @@
#define UINT32_MAX (4294967295U)
#endif
+#endif /* ! C99 */
+
#endif /* ! FLEXINT_H */
#ifdef __cplusplus
@@ -140,7 +142,15 @@
/* Size of default input buffer. */
#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
#endif
/* The state buf must be large enough to hold one state per character in the main buffer.
@@ -362,8 +372,8 @@
*yy_cp = '\0'; \
(yy_c_buf_p) = yy_cp;
-#define YY_NUM_RULES 17
-#define YY_END_OF_BUFFER 18
+#define YY_NUM_RULES 30
+#define YY_END_OF_BUFFER 31
/* This struct is not used in this scanner,
but its presence is necessary. */
struct yy_trans_info
@@ -371,19 +381,25 @@
flex_int32_t yy_verify;
flex_int32_t yy_nxt;
};
-static yyconst flex_int16_t yy_accept[94] =
+static yyconst flex_int16_t yy_accept[161] =
{ 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 18, 16, 13, 13, 16, 16, 16, 16, 16, 16,
- 16, 10, 11, 11, 6, 6, 13, 0, 2, 0,
- 7, 0, 0, 0, 0, 0, 0, 0, 5, 0,
- 9, 9, 11, 11, 6, 0, 7, 0, 0, 0,
- 0, 15, 0, 0, 0, 0, 6, 0, 14, 0,
- 0, 0, 0, 0, 8, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 3, 12,
- 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
- 0, 4, 0
+ 31, 29, 18, 18, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 15, 16,
+ 16, 29, 16, 10, 10, 18, 26, 0, 3, 0,
+ 27, 12, 0, 0, 11, 0, 0, 0, 0, 0,
+ 0, 0, 21, 23, 25, 24, 22, 0, 9, 28,
+ 0, 0, 0, 14, 14, 16, 16, 16, 10, 10,
+ 10, 0, 12, 0, 11, 0, 0, 0, 20, 0,
+ 0, 0, 0, 0, 0, 0, 0, 16, 10, 10,
+ 10, 0, 19, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 16, 13, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 16, 6, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 17, 0, 0, 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
+ 0, 0, 5, 8, 0, 0, 0, 0, 7, 0
} ;
static yyconst flex_int32_t yy_ec[256] =
@@ -391,17 +407,17 @@
1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
2, 2, 2, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 2, 1, 4, 5, 1, 1, 6, 1, 1,
- 1, 7, 5, 5, 8, 5, 9, 10, 11, 12,
- 12, 12, 12, 12, 12, 12, 12, 13, 1, 1,
- 1, 1, 5, 5, 14, 14, 14, 14, 14, 14,
- 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
- 15, 15, 15, 15, 15, 15, 15, 16, 15, 15,
- 1, 17, 18, 1, 15, 1, 14, 19, 20, 21,
+ 1, 2, 4, 5, 6, 1, 1, 7, 8, 1,
+ 1, 9, 10, 10, 11, 10, 12, 13, 14, 15,
+ 15, 15, 15, 15, 15, 15, 15, 16, 1, 17,
+ 18, 19, 10, 10, 20, 20, 20, 20, 20, 20,
+ 21, 21, 21, 21, 21, 22, 21, 21, 21, 21,
+ 21, 21, 21, 21, 23, 21, 21, 24, 21, 21,
+ 1, 25, 26, 1, 21, 1, 20, 27, 28, 29,
- 22, 14, 15, 15, 23, 15, 15, 24, 25, 26,
- 15, 15, 15, 27, 28, 29, 30, 31, 15, 16,
- 15, 15, 32, 1, 33, 1, 1, 1, 1, 1,
+ 30, 20, 21, 21, 31, 21, 21, 32, 33, 34,
+ 35, 36, 21, 37, 38, 39, 40, 41, 21, 24,
+ 42, 21, 43, 44, 45, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@@ -418,112 +434,163 @@
1, 1, 1, 1, 1
} ;
-static yyconst flex_int32_t yy_meta[34] =
+static yyconst flex_int32_t yy_meta[46] =
{ 0,
- 1, 1, 1, 1, 2, 1, 2, 2, 3, 4,
- 4, 4, 5, 6, 7, 7, 1, 1, 6, 6,
- 6, 6, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 8, 1
+ 1, 1, 1, 1, 1, 2, 3, 1, 2, 2,
+ 2, 4, 5, 5, 5, 6, 1, 1, 1, 7,
+ 8, 8, 8, 8, 1, 1, 7, 7, 7, 7,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 3, 1, 1
} ;
-static yyconst flex_int16_t yy_base[106] =
+static yyconst flex_int16_t yy_base[175] =
{ 0,
- 0, 0, 237, 236, 25, 0, 47, 0, 30, 71,
- 244, 247, 82, 84, 84, 211, 95, 229, 218, 0,
- 111, 247, 0, 84, 83, 95, 106, 86, 247, 237,
- 0, 230, 231, 234, 207, 209, 212, 220, 247, 206,
- 247, 218, 0, 106, 116, 0, 0, 0, 223, 89,
- 226, 219, 199, 206, 200, 204, 0, 190, 213, 212,
- 202, 91, 178, 161, 247, 172, 144, 150, 140, 130,
- 140, 124, 128, 120, 138, 137, 123, 122, 247, 247,
- 134, 114, 132, 86, 135, 125, 90, 136, 247, 97,
- 29, 247, 247, 153, 156, 161, 165, 170, 176, 180,
+ 0, 388, 381, 40, 41, 386, 71, 385, 34, 44,
+ 390, 395, 60, 62, 371, 112, 111, 111, 111, 104,
+ 370, 106, 371, 342, 124, 119, 0, 144, 395, 0,
+ 123, 0, 159, 153, 165, 167, 395, 130, 395, 382,
+ 395, 0, 372, 122, 395, 157, 374, 379, 350, 21,
+ 346, 349, 395, 395, 395, 395, 395, 362, 395, 395,
+ 181, 346, 342, 395, 359, 0, 191, 343, 190, 351,
+ 350, 0, 0, 0, 173, 362, 177, 367, 357, 329,
+ 335, 328, 337, 331, 206, 329, 334, 327, 395, 338,
+ 170, 314, 346, 345, 318, 325, 343, 158, 316, 212,
- 187, 195, 200, 205, 212
+ 322, 319, 320, 395, 340, 336, 308, 305, 314, 304,
+ 295, 138, 208, 220, 395, 292, 305, 265, 264, 254,
+ 201, 222, 285, 275, 273, 270, 236, 235, 225, 115,
+ 395, 395, 252, 216, 216, 217, 214, 230, 209, 220,
+ 213, 239, 211, 217, 216, 209, 229, 395, 240, 225,
+ 206, 169, 395, 395, 116, 106, 99, 54, 395, 395,
+ 254, 260, 268, 272, 276, 282, 289, 293, 301, 309,
+ 313, 319, 327, 335
} ;
-static yyconst flex_int16_t yy_def[106] =
+static yyconst flex_int16_t yy_def[175] =
{ 0,
- 93, 1, 1, 1, 1, 5, 93, 7, 1, 1,
- 93, 93, 93, 93, 94, 95, 93, 96, 17, 97,
- 96, 93, 98, 99, 93, 93, 93, 94, 93, 94,
- 100, 93, 101, 102, 93, 93, 93, 96, 93, 93,
- 93, 96, 98, 99, 93, 103, 100, 104, 101, 101,
- 102, 93, 93, 93, 93, 93, 103, 104, 93, 93,
- 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
- 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
- 93, 93, 93, 93, 93, 105, 93, 105, 93, 105,
- 93, 93, 0, 93, 93, 93, 93, 93, 93, 93,
+ 160, 1, 1, 1, 1, 5, 160, 7, 1, 1,
+ 160, 160, 160, 160, 160, 161, 162, 163, 160, 160,
+ 160, 160, 164, 160, 160, 160, 165, 164, 160, 166,
+ 167, 166, 166, 160, 160, 160, 160, 161, 160, 161,
+ 160, 168, 160, 163, 160, 163, 169, 170, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 164, 160, 160,
+ 160, 160, 160, 160, 164, 166, 167, 166, 160, 160,
+ 160, 171, 168, 172, 163, 169, 169, 170, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 166, 160, 160,
+ 171, 172, 160, 160, 160, 160, 160, 160, 160, 160,
- 93, 93, 93, 93, 93
+ 160, 160, 166, 160, 160, 160, 160, 160, 160, 160,
+ 160, 173, 160, 166, 160, 160, 160, 160, 160, 160,
+ 173, 160, 173, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 174, 160, 160, 160, 174, 160, 174, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160, 160, 0,
+ 160, 160, 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160
} ;
-static yyconst flex_int16_t yy_nxt[281] =
+static yyconst flex_int16_t yy_nxt[441] =
{ 0,
- 12, 13, 14, 15, 12, 16, 12, 12, 17, 12,
- 12, 12, 12, 18, 18, 18, 12, 12, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 12, 12, 19, 20, 20, 20, 92, 21, 25,
- 26, 26, 22, 21, 21, 21, 21, 12, 13, 14,
- 15, 23, 16, 23, 23, 19, 23, 23, 23, 12,
- 24, 24, 24, 12, 12, 24, 24, 24, 24, 24,
- 24, 24, 24, 24, 24, 24, 24, 24, 12, 12,
- 25, 26, 26, 27, 27, 27, 27, 29, 43, 29,
- 43, 43, 45, 45, 45, 50, 39, 59, 46, 93,
+ 12, 13, 14, 15, 16, 12, 17, 18, 12, 12,
+ 12, 19, 12, 12, 12, 12, 20, 21, 22, 23,
+ 23, 23, 23, 23, 12, 12, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 12, 24, 12, 25, 34, 35, 35, 25,
+ 81, 26, 26, 27, 27, 27, 34, 35, 35, 82,
+ 28, 36, 36, 36, 36, 159, 29, 28, 28, 28,
+ 28, 12, 13, 14, 15, 16, 30, 17, 18, 30,
+ 30, 30, 26, 30, 30, 30, 12, 20, 21, 22,
+ 31, 31, 31, 31, 31, 32, 12, 31, 31, 31,
- 30, 33, 30, 34, 45, 45, 45, 27, 27, 68,
- 43, 91, 43, 43, 69, 35, 87, 36, 39, 37,
- 42, 42, 42, 39, 42, 45, 45, 45, 89, 42,
- 42, 42, 42, 85, 85, 86, 85, 85, 86, 89,
- 84, 90, 83, 82, 81, 80, 79, 78, 77, 76,
- 75, 74, 90, 28, 28, 28, 28, 28, 28, 28,
- 28, 31, 31, 31, 38, 38, 38, 38, 41, 73,
- 41, 43, 72, 43, 71, 43, 43, 44, 33, 44,
- 44, 44, 44, 47, 69, 47, 47, 49, 49, 49,
- 49, 49, 49, 49, 49, 51, 51, 51, 51, 51,
+ 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 12, 24, 12, 39, 41, 45, 47,
+ 53, 54, 48, 56, 57, 61, 61, 47, 66, 45,
+ 48, 66, 66, 66, 39, 46, 40, 49, 59, 50,
+ 158, 51, 122, 52, 157, 49, 46, 50, 136, 63,
+ 137, 52, 156, 43, 40, 62, 65, 65, 65, 59,
+ 61, 61, 123, 65, 75, 69, 69, 69, 36, 36,
+ 65, 65, 65, 65, 70, 71, 72, 69, 69, 69,
+ 45, 46, 61, 61, 109, 77, 70, 71, 93, 110,
+ 68, 70, 71, 85, 85, 85, 66, 46, 155, 66,
- 51, 51, 51, 57, 70, 57, 58, 58, 58, 67,
- 58, 58, 88, 88, 88, 88, 88, 88, 88, 88,
- 34, 66, 65, 64, 63, 62, 61, 60, 52, 50,
- 39, 56, 39, 55, 54, 53, 52, 50, 48, 93,
- 40, 39, 32, 93, 19, 19, 11, 93, 93, 93,
- 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
- 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
- 93, 93, 93, 93, 93, 93, 93, 93, 93, 93
+ 66, 66, 69, 69, 69, 122, 59, 100, 100, 61,
+ 61, 70, 71, 100, 100, 148, 112, 154, 85, 85,
+ 85, 61, 61, 129, 129, 123, 129, 129, 135, 135,
+ 135, 142, 142, 148, 143, 149, 153, 135, 135, 135,
+ 142, 142, 160, 143, 152, 151, 150, 146, 145, 144,
+ 141, 140, 139, 149, 38, 38, 38, 38, 38, 38,
+ 38, 38, 42, 138, 134, 133, 42, 42, 44, 44,
+ 44, 44, 44, 44, 44, 44, 58, 58, 58, 58,
+ 64, 132, 64, 66, 131, 130, 66, 160, 66, 66,
+ 67, 128, 127, 67, 67, 67, 67, 73, 126, 73,
+
+ 73, 76, 76, 76, 76, 76, 76, 76, 76, 78,
+ 78, 78, 78, 78, 78, 78, 78, 91, 125, 91,
+ 92, 124, 92, 92, 120, 92, 92, 121, 121, 121,
+ 121, 121, 121, 121, 121, 147, 147, 147, 147, 147,
+ 147, 147, 147, 119, 118, 117, 116, 115, 47, 114,
+ 110, 113, 111, 108, 107, 106, 48, 105, 104, 89,
+ 103, 102, 101, 99, 98, 97, 96, 95, 94, 79,
+ 77, 90, 89, 88, 59, 87, 86, 59, 84, 83,
+ 80, 79, 77, 74, 160, 60, 59, 55, 37, 160,
+ 33, 25, 26, 25, 11, 160, 160, 160, 160, 160,
+
+ 160, 160, 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160, 160, 160
} ;
-static yyconst flex_int16_t yy_chk[281] =
+static yyconst flex_int16_t yy_chk[441] =
{ 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 5, 5, 5, 5, 91, 5, 9,
- 9, 9, 5, 5, 5, 5, 5, 7, 7, 7,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 4, 9, 9, 9, 10,
+ 50, 4, 5, 5, 5, 5, 10, 10, 10, 50,
+ 5, 13, 13, 14, 14, 158, 5, 5, 5, 5,
+ 5, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 10, 10, 10, 13, 13, 14, 14, 15, 24, 28,
- 24, 24, 25, 25, 25, 50, 24, 50, 25, 90,
- 15, 17, 28, 17, 26, 26, 26, 27, 27, 62,
- 44, 87, 44, 44, 62, 17, 84, 17, 44, 17,
- 21, 21, 21, 21, 21, 45, 45, 45, 86, 21,
- 21, 21, 21, 83, 83, 83, 85, 85, 85, 88,
- 82, 86, 81, 78, 77, 76, 75, 74, 73, 72,
- 71, 70, 88, 94, 94, 94, 94, 94, 94, 94,
- 94, 95, 95, 95, 96, 96, 96, 96, 97, 69,
- 97, 98, 68, 98, 67, 98, 98, 99, 66, 99,
- 99, 99, 99, 100, 64, 100, 100, 101, 101, 101,
- 101, 101, 101, 101, 101, 102, 102, 102, 102, 102,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 16, 17, 18, 19,
+ 20, 20, 19, 22, 22, 25, 25, 26, 31, 44,
+ 26, 31, 31, 31, 38, 18, 16, 19, 31, 19,
+ 157, 19, 112, 19, 156, 26, 44, 26, 130, 26,
+ 130, 26, 155, 17, 38, 25, 28, 28, 28, 28,
+ 33, 33, 112, 28, 46, 34, 34, 34, 36, 36,
+ 28, 28, 28, 28, 34, 34, 34, 35, 35, 35,
+ 75, 46, 61, 61, 98, 77, 35, 35, 77, 98,
+ 33, 91, 91, 61, 61, 61, 67, 75, 152, 67,
- 102, 102, 102, 103, 63, 103, 104, 104, 104, 61,
- 104, 104, 105, 105, 105, 105, 105, 105, 105, 105,
- 60, 59, 58, 56, 55, 54, 53, 52, 51, 49,
- 42, 40, 38, 37, 36, 35, 34, 33, 32, 30,
- 19, 18, 16, 11, 4, 3, 93, 93, 93, 93,
- 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
- 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
- 93, 93, 93, 93, 93, 93, 93, 93, 93, 93
+ 67, 67, 69, 69, 69, 121, 67, 85, 85, 113,
+ 113, 69, 69, 100, 100, 143, 100, 151, 85, 85,
+ 85, 114, 114, 122, 122, 121, 129, 129, 135, 135,
+ 135, 138, 138, 147, 138, 143, 150, 129, 129, 129,
+ 142, 142, 149, 142, 146, 145, 144, 141, 140, 139,
+ 137, 136, 134, 147, 161, 161, 161, 161, 161, 161,
+ 161, 161, 162, 133, 128, 127, 162, 162, 163, 163,
+ 163, 163, 163, 163, 163, 163, 164, 164, 164, 164,
+ 165, 126, 165, 166, 125, 124, 166, 123, 166, 166,
+ 167, 120, 119, 167, 167, 167, 167, 168, 118, 168,
+
+ 168, 169, 169, 169, 169, 169, 169, 169, 169, 170,
+ 170, 170, 170, 170, 170, 170, 170, 171, 117, 171,
+ 172, 116, 172, 172, 111, 172, 172, 173, 173, 173,
+ 173, 173, 173, 173, 173, 174, 174, 174, 174, 174,
+ 174, 174, 174, 110, 109, 108, 107, 106, 105, 103,
+ 102, 101, 99, 97, 96, 95, 94, 93, 92, 90,
+ 88, 87, 86, 84, 83, 82, 81, 80, 79, 78,
+ 76, 71, 70, 68, 65, 63, 62, 58, 52, 51,
+ 49, 48, 47, 43, 40, 24, 23, 21, 15, 11,
+ 8, 6, 3, 2, 160, 160, 160, 160, 160, 160,
+
+ 160, 160, 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160, 160, 160
} ;
static yy_state_type yy_last_accepting_state;
@@ -540,6 +607,7 @@
#define YY_MORE_ADJ 0
#define YY_RESTORE_YY_MORE_OFFSET
char *yytext;
+#line 1 "dtc-lexer.l"
/*
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
*
@@ -561,6 +629,10 @@
*/
#define YY_NO_INPUT 1
+
+
+
+#line 38 "dtc-lexer.l"
#include "dtc.h"
#include "srcpos.h"
#include "dtc-parser.tab.h"
@@ -588,6 +660,7 @@
static void push_input_file(const char *filename);
static int pop_input_file(void);
+#line 664 "dtc-lexer.lex.c"
#define INITIAL 0
#define INCLUDE 1
@@ -670,7 +743,12 @@
/* Amount of stuff to slurp up with each read. */
#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
#endif
/* Copy whatever the last rule matched to the standard output. */
@@ -689,7 +767,7 @@
if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
{ \
int c = '*'; \
- unsigned n; \
+ size_t n; \
for ( n = 0; n < max_size && \
(c = getc( yyin )) != EOF && c != '\n'; ++n ) \
buf[n] = (char) c; \
@@ -761,6 +839,9 @@
#endif
#define YY_RULE_SETUP \
+ if ( yyleng > 0 ) \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \
+ (yytext[yyleng - 1] == '\n'); \
YY_USER_ACTION
/** The main scanner function which does all the work.
@@ -771,6 +852,10 @@
register char *yy_cp, *yy_bp;
register int yy_act;
+#line 67 "dtc-lexer.l"
+
+#line 858 "dtc-lexer.lex.c"
+
if ( !(yy_init) )
{
(yy_init) = 1;
@@ -810,6 +895,7 @@
yy_bp = yy_cp;
yy_current_state = (yy_start);
+ yy_current_state += YY_AT_BOL();
yy_match:
do
{
@@ -822,13 +908,13 @@
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
- if ( yy_current_state >= 94 )
+ if ( yy_current_state >= 161 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
++yy_cp;
}
- while ( yy_current_state != 93 );
+ while ( yy_current_state != 160 );
yy_cp = (yy_last_accepting_cpos);
yy_current_state = (yy_last_accepting_state);
@@ -851,26 +937,54 @@
case 1:
/* rule 1 can match eol */
YY_RULE_SETUP
+#line 68 "dtc-lexer.l"
{
char *name = strchr(yytext, '\"') + 1;
yytext[yyleng-1] = '\0';
push_input_file(name);
}
YY_BREAK
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+#line 74 "dtc-lexer.l"
+{
+ char *line, *tmp, *fn;
+ /* skip text before line # */
+ line = yytext;
+ while (!isdigit(*line))
+ line++;
+ /* skip digits in line # */
+ tmp = line;
+ while (!isspace(*tmp))
+ tmp++;
+ /* "NULL"-terminate line # */
+ *tmp = '\0';
+ /* start of filename */
+ fn = strchr(tmp + 1, '"') + 1;
+ /* strip trailing " from filename */
+ tmp = strchr(fn, '"');
+ *tmp = 0;
+ /* -1 since #line is the number of the next line */
+ srcpos_set_line(xstrdup(fn), atoi(line) - 1);
+ }
+ YY_BREAK
case YY_STATE_EOF(INITIAL):
case YY_STATE_EOF(INCLUDE):
case YY_STATE_EOF(BYTESTRING):
case YY_STATE_EOF(PROPNODENAME):
case YY_STATE_EOF(V1):
+#line 95 "dtc-lexer.l"
{
if (!pop_input_file()) {
yyterminate();
}
}
YY_BREAK
-case 2:
-/* rule 2 can match eol */
+case 3:
+/* rule 3 can match eol */
YY_RULE_SETUP
+#line 101 "dtc-lexer.l"
{
DPRINT("String: %s\n", yytext);
yylval.data = data_copy_escape_string(yytext+1,
@@ -878,8 +992,9 @@
return DT_STRING;
}
YY_BREAK
-case 3:
+case 4:
YY_RULE_SETUP
+#line 108 "dtc-lexer.l"
{
DPRINT("Keyword: /dts-v1/\n");
dts_version = 1;
@@ -887,16 +1002,47 @@
return DT_V1;
}
YY_BREAK
-case 4:
+case 5:
YY_RULE_SETUP
+#line 115 "dtc-lexer.l"
{
DPRINT("Keyword: /memreserve/\n");
BEGIN_DEFAULT();
return DT_MEMRESERVE;
}
YY_BREAK
-case 5:
+case 6:
YY_RULE_SETUP
+#line 121 "dtc-lexer.l"
+{
+ DPRINT("Keyword: /bits/\n");
+ BEGIN_DEFAULT();
+ return DT_BITS;
+ }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 127 "dtc-lexer.l"
+{
+ DPRINT("Keyword: /delete-property/\n");
+ DPRINT("<PROPNODENAME>\n");
+ BEGIN(PROPNODENAME);
+ return DT_DEL_PROP;
+ }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 134 "dtc-lexer.l"
+{
+ DPRINT("Keyword: /delete-node/\n");
+ DPRINT("<PROPNODENAME>\n");
+ BEGIN(PROPNODENAME);
+ return DT_DEL_NODE;
+ }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 141 "dtc-lexer.l"
{
DPRINT("Label: %s\n", yytext);
yylval.labelref = xstrdup(yytext);
@@ -904,24 +1050,38 @@
return DT_LABEL;
}
YY_BREAK
-case 6:
+case 10:
YY_RULE_SETUP
+#line 148 "dtc-lexer.l"
{
yylval.literal = xstrdup(yytext);
DPRINT("Literal: '%s'\n", yylval.literal);
return DT_LITERAL;
}
YY_BREAK
-case 7:
+case 11:
+/* rule 11 can match eol */
YY_RULE_SETUP
+#line 154 "dtc-lexer.l"
+{
+ yytext[yyleng-1] = '\0';
+ yylval.literal = xstrdup(yytext+1);
+ DPRINT("Character literal: %s\n", yylval.literal);
+ return DT_CHAR_LITERAL;
+ }
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 161 "dtc-lexer.l"
{ /* label reference */
DPRINT("Ref: %s\n", yytext+1);
yylval.labelref = xstrdup(yytext+1);
return DT_REF;
}
YY_BREAK
-case 8:
+case 13:
YY_RULE_SETUP
+#line 167 "dtc-lexer.l"
{ /* new-style path reference */
yytext[yyleng-1] = '\0';
DPRINT("Ref: %s\n", yytext+2);
@@ -929,55 +1089,104 @@
return DT_REF;
}
YY_BREAK
-case 9:
+case 14:
YY_RULE_SETUP
+#line 174 "dtc-lexer.l"
{
yylval.byte = strtol(yytext, NULL, 16);
DPRINT("Byte: %02x\n", (int)yylval.byte);
return DT_BYTE;
}
YY_BREAK
-case 10:
+case 15:
YY_RULE_SETUP
+#line 180 "dtc-lexer.l"
{
DPRINT("/BYTESTRING\n");
BEGIN_DEFAULT();
return ']';
}
YY_BREAK
-case 11:
+case 16:
YY_RULE_SETUP
+#line 186 "dtc-lexer.l"
{
DPRINT("PropNodeName: %s\n", yytext);
- yylval.propnodename = xstrdup(yytext);
+ yylval.propnodename = xstrdup((yytext[0] == '\\') ?
+ yytext + 1 : yytext);
BEGIN_DEFAULT();
return DT_PROPNODENAME;
}
YY_BREAK
-case 12:
+case 17:
YY_RULE_SETUP
+#line 194 "dtc-lexer.l"
{
DPRINT("Binary Include\n");
return DT_INCBIN;
}
YY_BREAK
-case 13:
-/* rule 13 can match eol */
+case 18:
+/* rule 18 can match eol */
YY_RULE_SETUP
+#line 199 "dtc-lexer.l"
/* eat whitespace */
YY_BREAK
-case 14:
-/* rule 14 can match eol */
+case 19:
+/* rule 19 can match eol */
YY_RULE_SETUP
+#line 200 "dtc-lexer.l"
/* eat C-style comments */
YY_BREAK
-case 15:
-/* rule 15 can match eol */
+case 20:
+/* rule 20 can match eol */
YY_RULE_SETUP
+#line 201 "dtc-lexer.l"
/* eat C++-style comments */
YY_BREAK
-case 16:
+case 21:
YY_RULE_SETUP
+#line 203 "dtc-lexer.l"
+{ return DT_LSHIFT; };
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 204 "dtc-lexer.l"
+{ return DT_RSHIFT; };
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 205 "dtc-lexer.l"
+{ return DT_LE; };
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 206 "dtc-lexer.l"
+{ return DT_GE; };
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 207 "dtc-lexer.l"
+{ return DT_EQ; };
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 208 "dtc-lexer.l"
+{ return DT_NE; };
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 209 "dtc-lexer.l"
+{ return DT_AND; };
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 210 "dtc-lexer.l"
+{ return DT_OR; };
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 212 "dtc-lexer.l"
{
DPRINT("Char: %c (\\x%02x)\n", yytext[0],
(unsigned)yytext[0]);
@@ -993,10 +1202,12 @@
return yytext[0];
}
YY_BREAK
-case 17:
+case 30:
YY_RULE_SETUP
+#line 227 "dtc-lexer.l"
ECHO;
YY_BREAK
+#line 1211 "dtc-lexer.lex.c"
case YY_END_OF_BUFFER:
{
@@ -1275,6 +1486,7 @@
register char *yy_cp;
yy_current_state = (yy_start);
+ yy_current_state += YY_AT_BOL();
for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
{
@@ -1287,7 +1499,7 @@
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
- if ( yy_current_state >= 94 )
+ if ( yy_current_state >= 161 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
@@ -1315,11 +1527,11 @@
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
- if ( yy_current_state >= 94 )
+ if ( yy_current_state >= 161 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
- yy_is_jam = (yy_current_state == 93);
+ yy_is_jam = (yy_current_state == 160);
return yy_is_jam ? 0 : yy_current_state;
}
@@ -1394,6 +1606,8 @@
*(yy_c_buf_p) = '\0'; /* preserve yytext */
(yy_hold_char) = *++(yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n');
+
return c;
}
#endif /* ifndef YY_NO_INPUT */
@@ -1712,8 +1926,8 @@
/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
* scan from a @e copy of @a bytes.
- * @param bytes the byte buffer to scan
- * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
*
* @return the newly allocated buffer state object.
*/
@@ -1952,6 +2166,10 @@
#define YYTABLES_NAME "yytables"
+#line 227 "dtc-lexer.l"
+
+
+
static void push_input_file(const char *filename)
{
assert(filename);
@@ -1963,6 +2181,7 @@
yypush_buffer_state(yy_create_buffer(yyin,YY_BUF_SIZE));
}
+
static int pop_input_file(void)
{
if (srcfile_pop() == 0)
diff --git a/scripts/dtc/dtc-parser.tab.c_shipped b/scripts/dtc/dtc-parser.tab.c_shipped
index b05921e..4af5590 100644
--- a/scripts/dtc/dtc-parser.tab.c_shipped
+++ b/scripts/dtc/dtc-parser.tab.c_shipped
@@ -1,9 +1,10 @@
-/* A Bison parser, made by GNU Bison 2.4.3. */
+
+/* A Bison parser, made by GNU Bison 2.4.1. */
/* Skeleton implementation for Bison's Yacc-like parsers in C
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
- 2009, 2010 Free Software Foundation, Inc.
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -45,7 +46,7 @@
#define YYBISON 1
/* Bison version. */
-#define YYBISON_VERSION "2.4.3"
+#define YYBISON_VERSION "2.4.1"
/* Skeleton name. */
#define YYSKELETON_NAME "yacc.c"
@@ -66,6 +67,8 @@
/* Copy the first part of user declarations. */
+/* Line 189 of yacc.c */
+#line 21 "dtc-parser.y"
#include <stdio.h>
@@ -82,12 +85,15 @@
extern int treesource_error;
static unsigned long long eval_literal(const char *s, int base, int bits);
+static unsigned char eval_char_literal(const char *s);
+/* Line 189 of yacc.c */
+#line 93 "dtc-parser.tab.c"
/* Enabling traces. */
#ifndef YYDEBUG
-# define YYDEBUG 1
+# define YYDEBUG 0
#endif
/* Enabling verbose error messages. */
@@ -112,14 +118,26 @@
enum yytokentype {
DT_V1 = 258,
DT_MEMRESERVE = 259,
- DT_PROPNODENAME = 260,
- DT_LITERAL = 261,
- DT_BASE = 262,
- DT_BYTE = 263,
- DT_STRING = 264,
- DT_LABEL = 265,
- DT_REF = 266,
- DT_INCBIN = 267
+ DT_LSHIFT = 260,
+ DT_RSHIFT = 261,
+ DT_LE = 262,
+ DT_GE = 263,
+ DT_EQ = 264,
+ DT_NE = 265,
+ DT_AND = 266,
+ DT_OR = 267,
+ DT_BITS = 268,
+ DT_DEL_PROP = 269,
+ DT_DEL_NODE = 270,
+ DT_PROPNODENAME = 271,
+ DT_LITERAL = 272,
+ DT_CHAR_LITERAL = 273,
+ DT_BASE = 274,
+ DT_BYTE = 275,
+ DT_STRING = 276,
+ DT_LABEL = 277,
+ DT_REF = 278,
+ DT_INCBIN = 279
};
#endif
@@ -129,6 +147,8 @@
typedef union YYSTYPE
{
+/* Line 214 of yacc.c */
+#line 40 "dtc-parser.y"
char *propnodename;
char *literal;
@@ -137,16 +157,22 @@
uint8_t byte;
struct data data;
- uint64_t addr;
- cell_t cell;
+ struct {
+ struct data data;
+ int bits;
+ } array;
+
struct property *prop;
struct property *proplist;
struct node *node;
struct node *nodelist;
struct reserve_info *re;
+ uint64_t integer;
+/* Line 214 of yacc.c */
+#line 176 "dtc-parser.tab.c"
} YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
@@ -157,6 +183,8 @@
/* Copy the second part of user declarations. */
+/* Line 264 of yacc.c */
+#line 188 "dtc-parser.tab.c"
#ifdef short
# undef short
@@ -206,7 +234,7 @@
#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
#ifndef YY_
-# if defined YYENABLE_NLS && YYENABLE_NLS
+# if YYENABLE_NLS
# if ENABLE_NLS
# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
# define YY_(msgid) dgettext ("bison-runtime", msgid)
@@ -371,20 +399,20 @@
/* YYFINAL -- State number of the termination state. */
#define YYFINAL 4
/* YYLAST -- Last index in YYTABLE. */
-#define YYLAST 56
+#define YYLAST 133
/* YYNTOKENS -- Number of terminals. */
-#define YYNTOKENS 25
+#define YYNTOKENS 48
/* YYNNTS -- Number of nonterminals. */
-#define YYNNTS 16
+#define YYNNTS 28
/* YYNRULES -- Number of rules. */
-#define YYNRULES 39
+#define YYNRULES 79
/* YYNRULES -- Number of states. */
-#define YYNSTATES 67
+#define YYNSTATES 141
/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
#define YYUNDEFTOK 2
-#define YYMAXUTOK 267
+#define YYMAXUTOK 279
#define YYTRANSLATE(YYX) \
((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
@@ -395,16 +423,16 @@
0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 22, 24, 2, 2, 23, 2, 2, 14, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 13,
- 18, 17, 19, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 47, 2, 2, 2, 45, 41, 2,
+ 33, 35, 44, 42, 34, 43, 2, 26, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 38, 25,
+ 36, 29, 30, 37, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 20, 2, 21, 2, 2, 2, 2, 2, 2,
+ 2, 31, 2, 32, 40, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 15, 2, 16, 2, 2, 2, 2,
+ 2, 2, 2, 27, 39, 28, 46, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@@ -418,45 +446,68 @@
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
- 5, 6, 7, 8, 9, 10, 11, 12
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24
};
#if YYDEBUG
/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
YYRHS. */
-static const yytype_uint8 yyprhs[] =
+static const yytype_uint16 yyprhs[] =
{
- 0, 0, 3, 8, 9, 12, 17, 20, 22, 25,
- 29, 33, 39, 40, 43, 48, 51, 54, 57, 62,
- 67, 70, 80, 86, 89, 90, 93, 96, 97, 100,
- 103, 106, 108, 109, 112, 115, 116, 119, 122, 125
+ 0, 0, 3, 8, 9, 12, 17, 20, 23, 27,
+ 31, 36, 42, 43, 46, 51, 54, 58, 61, 64,
+ 68, 73, 76, 86, 92, 95, 96, 99, 102, 106,
+ 108, 111, 114, 117, 119, 121, 125, 127, 129, 135,
+ 137, 141, 143, 147, 149, 153, 155, 159, 161, 165,
+ 167, 171, 175, 177, 181, 185, 189, 193, 197, 201,
+ 203, 207, 211, 213, 217, 221, 225, 227, 229, 232,
+ 235, 238, 239, 242, 245, 246, 249, 252, 255, 259
};
/* YYRHS -- A `-1'-separated list of the rules' RHS. */
static const yytype_int8 yyrhs[] =
{
- 26, 0, -1, 3, 13, 27, 30, -1, -1, 28,
- 27, -1, 4, 29, 29, 13, -1, 10, 28, -1,
- 6, -1, 14, 31, -1, 30, 14, 31, -1, 30,
- 11, 31, -1, 15, 32, 39, 16, 13, -1, -1,
- 32, 33, -1, 5, 17, 34, 13, -1, 5, 13,
- -1, 10, 33, -1, 35, 9, -1, 35, 18, 36,
- 19, -1, 35, 20, 38, 21, -1, 35, 11, -1,
- 35, 12, 22, 9, 23, 29, 23, 29, 24, -1,
- 35, 12, 22, 9, 24, -1, 34, 10, -1, -1,
- 34, 23, -1, 35, 10, -1, -1, 36, 37, -1,
- 36, 11, -1, 36, 10, -1, 6, -1, -1, 38,
- 8, -1, 38, 10, -1, -1, 40, 39, -1, 40,
- 33, -1, 5, 31, -1, 10, 40, -1
+ 49, 0, -1, 3, 25, 50, 52, -1, -1, 51,
+ 50, -1, 4, 59, 59, 25, -1, 22, 51, -1,
+ 26, 53, -1, 52, 26, 53, -1, 52, 23, 53,
+ -1, 52, 15, 23, 25, -1, 27, 54, 74, 28,
+ 25, -1, -1, 54, 55, -1, 16, 29, 56, 25,
+ -1, 16, 25, -1, 14, 16, 25, -1, 22, 55,
+ -1, 57, 21, -1, 57, 58, 30, -1, 57, 31,
+ 73, 32, -1, 57, 23, -1, 57, 24, 33, 21,
+ 34, 59, 34, 59, 35, -1, 57, 24, 33, 21,
+ 35, -1, 56, 22, -1, -1, 56, 34, -1, 57,
+ 22, -1, 13, 17, 36, -1, 36, -1, 58, 59,
+ -1, 58, 23, -1, 58, 22, -1, 17, -1, 18,
+ -1, 33, 60, 35, -1, 61, -1, 62, -1, 62,
+ 37, 60, 38, 61, -1, 63, -1, 62, 12, 63,
+ -1, 64, -1, 63, 11, 64, -1, 65, -1, 64,
+ 39, 65, -1, 66, -1, 65, 40, 66, -1, 67,
+ -1, 66, 41, 67, -1, 68, -1, 67, 9, 68,
+ -1, 67, 10, 68, -1, 69, -1, 68, 36, 69,
+ -1, 68, 30, 69, -1, 68, 7, 69, -1, 68,
+ 8, 69, -1, 69, 5, 70, -1, 69, 6, 70,
+ -1, 70, -1, 70, 42, 71, -1, 70, 43, 71,
+ -1, 71, -1, 71, 44, 72, -1, 71, 26, 72,
+ -1, 71, 45, 72, -1, 72, -1, 59, -1, 43,
+ 72, -1, 46, 72, -1, 47, 72, -1, -1, 73,
+ 20, -1, 73, 22, -1, -1, 75, 74, -1, 75,
+ 55, -1, 16, 53, -1, 15, 16, 25, -1, 22,
+ 75, -1
};
/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
static const yytype_uint16 yyrline[] =
{
- 0, 86, 86, 95, 98, 105, 109, 117, 124, 128,
- 132, 145, 153, 156, 163, 167, 171, 179, 183, 187,
- 191, 195, 212, 222, 230, 233, 237, 245, 248, 252,
- 257, 264, 272, 275, 279, 287, 290, 294, 302, 306
+ 0, 109, 109, 118, 121, 128, 132, 140, 144, 148,
+ 158, 172, 180, 183, 190, 194, 198, 202, 210, 214,
+ 218, 222, 226, 243, 253, 261, 264, 268, 275, 290,
+ 295, 315, 329, 336, 340, 344, 351, 355, 356, 360,
+ 361, 365, 366, 370, 371, 375, 376, 380, 381, 385,
+ 386, 387, 391, 392, 393, 394, 395, 399, 400, 401,
+ 405, 406, 407, 411, 412, 413, 414, 418, 419, 420,
+ 421, 426, 429, 433, 441, 444, 448, 456, 460, 464
};
#endif
@@ -465,13 +516,19 @@
First, the terminals, then, starting at YYNTOKENS, nonterminals. */
static const char *const yytname[] =
{
- "$end", "error", "$undefined", "DT_V1", "DT_MEMRESERVE",
- "DT_PROPNODENAME", "DT_LITERAL", "DT_BASE", "DT_BYTE", "DT_STRING",
- "DT_LABEL", "DT_REF", "DT_INCBIN", "';'", "'/'", "'{'", "'}'", "'='",
- "'<'", "'>'", "'['", "']'", "'('", "','", "')'", "$accept", "sourcefile",
- "memreserves", "memreserve", "addr", "devicetree", "nodedef", "proplist",
- "propdef", "propdata", "propdataprefix", "celllist", "cellval",
- "bytestring", "subnodes", "subnode", 0
+ "$end", "error", "$undefined", "DT_V1", "DT_MEMRESERVE", "DT_LSHIFT",
+ "DT_RSHIFT", "DT_LE", "DT_GE", "DT_EQ", "DT_NE", "DT_AND", "DT_OR",
+ "DT_BITS", "DT_DEL_PROP", "DT_DEL_NODE", "DT_PROPNODENAME", "DT_LITERAL",
+ "DT_CHAR_LITERAL", "DT_BASE", "DT_BYTE", "DT_STRING", "DT_LABEL",
+ "DT_REF", "DT_INCBIN", "';'", "'/'", "'{'", "'}'", "'='", "'>'", "'['",
+ "']'", "'('", "','", "')'", "'<'", "'?'", "':'", "'|'", "'^'", "'&'",
+ "'+'", "'-'", "'*'", "'%'", "'~'", "'!'", "$accept", "sourcefile",
+ "memreserves", "memreserve", "devicetree", "nodedef", "proplist",
+ "propdef", "propdata", "propdataprefix", "arrayprefix", "integer_prim",
+ "integer_expr", "integer_trinary", "integer_or", "integer_and",
+ "integer_bitor", "integer_bitxor", "integer_bitand", "integer_eq",
+ "integer_rela", "integer_shift", "integer_add", "integer_mul",
+ "integer_unary", "bytestring", "subnodes", "subnode", 0
};
#endif
@@ -481,27 +538,37 @@
static const yytype_uint16 yytoknum[] =
{
0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
- 265, 266, 267, 59, 47, 123, 125, 61, 60, 62,
- 91, 93, 40, 44, 41
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 59, 47, 123, 125, 61,
+ 62, 91, 93, 40, 44, 41, 60, 63, 58, 124,
+ 94, 38, 43, 45, 42, 37, 126, 33
};
# endif
/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
static const yytype_uint8 yyr1[] =
{
- 0, 25, 26, 27, 27, 28, 28, 29, 30, 30,
- 30, 31, 32, 32, 33, 33, 33, 34, 34, 34,
- 34, 34, 34, 34, 35, 35, 35, 36, 36, 36,
- 36, 37, 38, 38, 38, 39, 39, 39, 40, 40
+ 0, 48, 49, 50, 50, 51, 51, 52, 52, 52,
+ 52, 53, 54, 54, 55, 55, 55, 55, 56, 56,
+ 56, 56, 56, 56, 56, 57, 57, 57, 58, 58,
+ 58, 58, 58, 59, 59, 59, 60, 61, 61, 62,
+ 62, 63, 63, 64, 64, 65, 65, 66, 66, 67,
+ 67, 67, 68, 68, 68, 68, 68, 69, 69, 69,
+ 70, 70, 70, 71, 71, 71, 71, 72, 72, 72,
+ 72, 73, 73, 73, 74, 74, 74, 75, 75, 75
};
/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
static const yytype_uint8 yyr2[] =
{
- 0, 2, 4, 0, 2, 4, 2, 1, 2, 3,
- 3, 5, 0, 2, 4, 2, 2, 2, 4, 4,
- 2, 9, 5, 2, 0, 2, 2, 0, 2, 2,
- 2, 1, 0, 2, 2, 0, 2, 2, 2, 2
+ 0, 2, 4, 0, 2, 4, 2, 2, 3, 3,
+ 4, 5, 0, 2, 4, 2, 3, 2, 2, 3,
+ 4, 2, 9, 5, 2, 0, 2, 2, 3, 1,
+ 2, 2, 2, 1, 1, 3, 1, 1, 5, 1,
+ 3, 1, 3, 1, 3, 1, 3, 1, 3, 1,
+ 3, 3, 1, 3, 3, 3, 3, 3, 3, 1,
+ 3, 3, 1, 3, 3, 3, 1, 1, 2, 2,
+ 2, 0, 2, 2, 0, 2, 2, 2, 3, 2
};
/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
@@ -509,41 +576,59 @@
means the default is an error. */
static const yytype_uint8 yydefact[] =
{
- 0, 0, 0, 3, 1, 0, 0, 0, 3, 7,
- 0, 6, 0, 2, 4, 0, 12, 8, 0, 0,
- 5, 35, 10, 9, 0, 0, 13, 0, 35, 15,
- 24, 38, 16, 39, 0, 37, 36, 0, 0, 11,
- 23, 14, 25, 17, 26, 20, 0, 27, 32, 0,
- 0, 0, 0, 31, 30, 29, 18, 28, 33, 34,
- 19, 0, 22, 0, 0, 0, 21
+ 0, 0, 0, 3, 1, 0, 0, 0, 3, 33,
+ 34, 0, 0, 6, 0, 2, 4, 0, 0, 0,
+ 67, 0, 36, 37, 39, 41, 43, 45, 47, 49,
+ 52, 59, 62, 66, 0, 12, 7, 0, 0, 0,
+ 68, 69, 70, 35, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 5, 74, 0, 9, 8, 40, 0,
+ 42, 44, 46, 48, 50, 51, 55, 56, 54, 53,
+ 57, 58, 60, 61, 64, 63, 65, 0, 0, 0,
+ 0, 13, 0, 74, 10, 0, 0, 0, 15, 25,
+ 77, 17, 79, 0, 76, 75, 38, 16, 78, 0,
+ 0, 11, 24, 14, 26, 0, 18, 27, 21, 0,
+ 71, 29, 0, 0, 0, 0, 32, 31, 19, 30,
+ 28, 0, 72, 73, 20, 0, 23, 0, 0, 0,
+ 22
};
/* YYDEFGOTO[NTERM-NUM]. */
static const yytype_int8 yydefgoto[] =
{
- -1, 2, 7, 8, 10, 13, 17, 21, 26, 37,
- 38, 50, 57, 51, 27, 28
+ -1, 2, 7, 8, 15, 36, 64, 91, 109, 110,
+ 122, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 125, 92, 93
};
/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
STATE-NUM. */
-#define YYPACT_NINF -12
+#define YYPACT_NINF -78
static const yytype_int8 yypact[] =
{
- 10, -11, 18, -1, -12, 22, -1, 15, -1, -12,
- 22, -12, 20, 1, -12, 17, -12, -12, 20, 20,
- -12, 6, -12, -12, 21, 6, -12, 23, 6, -12,
- -12, -12, -12, -12, 28, -12, -12, -6, 13, -12,
- -12, -12, -12, -12, -12, -12, 24, -12, -12, 33,
- -5, 0, -4, -12, -12, -12, -12, -12, -12, -12,
- -12, 22, -12, 25, 22, 19, -12
+ 22, 11, 51, 10, -78, 23, 10, 2, 10, -78,
+ -78, -9, 23, -78, 30, 38, -78, -9, -9, -9,
+ -78, 35, -78, -6, 52, 29, 48, 49, 33, 3,
+ 71, 36, 0, -78, 64, -78, -78, 68, 30, 30,
+ -78, -78, -78, -78, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -78, 44, 67, -78, -78, 52, 55,
+ 29, 48, 49, 33, 3, 3, 71, 71, 71, 71,
+ 36, 36, 0, 0, -78, -78, -78, 78, 79, 42,
+ 44, -78, 69, 44, -78, -9, 73, 74, -78, -78,
+ -78, -78, -78, 75, -78, -78, -78, -78, -78, -7,
+ -1, -78, -78, -78, -78, 84, -78, -78, -78, 63,
+ -78, -78, 32, 66, 82, -3, -78, -78, -78, -78,
+ -78, 46, -78, -78, -78, 23, -78, 70, 23, 72,
+ -78
};
/* YYPGOTO[NTERM-NUM]. */
static const yytype_int8 yypgoto[] =
{
- -12, -12, 36, 39, -10, -12, 8, -12, 12, -12,
- -12, -12, -12, -12, 27, 31
+ -78, -78, 97, 100, -78, -37, -78, -77, -78, -78,
+ -78, -5, 65, 13, -78, 76, 77, 62, 80, 83,
+ 34, 20, 26, 28, -14, -78, 18, 24
};
/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
@@ -553,35 +638,59 @@
#define YYTABLE_NINF -1
static const yytype_uint8 yytable[] =
{
- 15, 53, 3, 5, 40, 54, 55, 41, 58, 6,
- 59, 24, 18, 1, 56, 19, 25, 42, 4, 61,
- 62, 60, 43, 44, 45, 46, 22, 23, 9, 12,
- 20, 47, 31, 48, 29, 16, 16, 32, 30, 34,
- 35, 39, 52, 66, 14, 11, 49, 0, 64, 0,
- 0, 63, 0, 0, 65, 36, 33
+ 12, 66, 67, 40, 41, 42, 44, 34, 9, 10,
+ 52, 53, 115, 101, 5, 112, 104, 132, 113, 133,
+ 116, 117, 118, 119, 11, 1, 60, 114, 14, 134,
+ 120, 45, 6, 54, 17, 121, 3, 18, 19, 55,
+ 9, 10, 50, 51, 61, 62, 84, 85, 86, 9,
+ 10, 4, 100, 37, 126, 127, 11, 35, 87, 88,
+ 89, 38, 128, 46, 39, 11, 90, 98, 47, 35,
+ 43, 99, 76, 77, 78, 79, 56, 57, 58, 59,
+ 135, 136, 80, 81, 74, 75, 82, 83, 48, 63,
+ 49, 65, 94, 95, 96, 97, 124, 103, 107, 108,
+ 111, 123, 130, 131, 138, 16, 13, 140, 106, 71,
+ 69, 105, 0, 0, 102, 0, 0, 129, 0, 0,
+ 68, 0, 0, 70, 0, 0, 0, 0, 72, 0,
+ 137, 0, 73, 139
};
-static const yytype_int8 yycheck[] =
+static const yytype_int16 yycheck[] =
{
- 10, 6, 13, 4, 10, 10, 11, 13, 8, 10,
- 10, 5, 11, 3, 19, 14, 10, 23, 0, 23,
- 24, 21, 9, 10, 11, 12, 18, 19, 6, 14,
- 13, 18, 24, 20, 13, 15, 15, 25, 17, 16,
- 28, 13, 9, 24, 8, 6, 22, -1, 23, -1,
- -1, 61, -1, -1, 64, 28, 25
+ 5, 38, 39, 17, 18, 19, 12, 12, 17, 18,
+ 7, 8, 13, 90, 4, 22, 93, 20, 25, 22,
+ 21, 22, 23, 24, 33, 3, 26, 34, 26, 32,
+ 31, 37, 22, 30, 43, 36, 25, 46, 47, 36,
+ 17, 18, 9, 10, 44, 45, 60, 61, 62, 17,
+ 18, 0, 89, 15, 22, 23, 33, 27, 14, 15,
+ 16, 23, 30, 11, 26, 33, 22, 25, 39, 27,
+ 35, 29, 52, 53, 54, 55, 5, 6, 42, 43,
+ 34, 35, 56, 57, 50, 51, 58, 59, 40, 25,
+ 41, 23, 25, 38, 16, 16, 33, 28, 25, 25,
+ 25, 17, 36, 21, 34, 8, 6, 35, 95, 47,
+ 45, 93, -1, -1, 90, -1, -1, 122, -1, -1,
+ 44, -1, -1, 46, -1, -1, -1, -1, 48, -1,
+ 135, -1, 49, 138
};
/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
symbol of state STATE-NUM. */
static const yytype_uint8 yystos[] =
{
- 0, 3, 26, 13, 0, 4, 10, 27, 28, 6,
- 29, 28, 14, 30, 27, 29, 15, 31, 11, 14,
- 13, 32, 31, 31, 5, 10, 33, 39, 40, 13,
- 17, 31, 33, 40, 16, 33, 39, 34, 35, 13,
- 10, 13, 23, 9, 10, 11, 12, 18, 20, 22,
- 36, 38, 9, 6, 10, 11, 19, 37, 8, 10,
- 21, 23, 24, 29, 23, 29, 24
+ 0, 3, 49, 25, 0, 4, 22, 50, 51, 17,
+ 18, 33, 59, 51, 26, 52, 50, 43, 46, 47,
+ 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 59, 27, 53, 15, 23, 26,
+ 72, 72, 72, 35, 12, 37, 11, 39, 40, 41,
+ 9, 10, 7, 8, 30, 36, 5, 6, 42, 43,
+ 26, 44, 45, 25, 54, 23, 53, 53, 63, 60,
+ 64, 65, 66, 67, 68, 68, 69, 69, 69, 69,
+ 70, 70, 71, 71, 72, 72, 72, 14, 15, 16,
+ 22, 55, 74, 75, 25, 38, 16, 16, 25, 29,
+ 53, 55, 75, 28, 55, 74, 61, 25, 25, 56,
+ 57, 25, 22, 25, 34, 13, 21, 22, 23, 24,
+ 31, 36, 58, 17, 33, 73, 22, 23, 30, 59,
+ 36, 21, 20, 22, 32, 34, 35, 59, 34, 59,
+ 35
};
#define yyerrok (yyerrstatus = 0)
@@ -596,18 +705,9 @@
/* Like YYERROR except do call yyerror. This remains here temporarily
to ease the transition to the new meaning of YYERROR, for GCC.
- Once GCC version 2 has supplanted version 1, this can go. However,
- YYFAIL appears to be in use. Nevertheless, it is formally deprecated
- in Bison 2.4.2's NEWS entry, where a plan to phase it out is
- discussed. */
+ Once GCC version 2 has supplanted version 1, this can go. */
#define YYFAIL goto yyerrlab
-#if defined YYFAIL
- /* This is here to suppress warnings from the GCC cpp's
- -Wunused-macros. Normally we don't worry about that warning, but
- some users do, and we want to make it easy for users to remove
- YYFAIL uses, which will produce warnings from Bison 2.5. */
-#endif
#define YYRECOVERING() (!!yyerrstatus)
@@ -664,7 +764,7 @@
we won't break user code: when these are the locations we know. */
#ifndef YY_LOCATION_PRINT
-# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+# if YYLTYPE_IS_TRIVIAL
# define YY_LOCATION_PRINT(File, Loc) \
fprintf (File, "%d.%d-%d.%d", \
(Loc).first_line, (Loc).first_column, \
@@ -1403,6 +1503,8 @@
{
case 2:
+/* Line 1455 of yacc.c */
+#line 110 "dtc-parser.y"
{
the_boot_info = build_boot_info((yyvsp[(3) - (4)].re), (yyvsp[(4) - (4)].node),
guess_boot_cpuid((yyvsp[(4) - (4)].node)));
@@ -1411,6 +1513,8 @@
case 3:
+/* Line 1455 of yacc.c */
+#line 118 "dtc-parser.y"
{
(yyval.re) = NULL;
;}
@@ -1418,6 +1522,8 @@
case 4:
+/* Line 1455 of yacc.c */
+#line 122 "dtc-parser.y"
{
(yyval.re) = chain_reserve_entry((yyvsp[(1) - (2)].re), (yyvsp[(2) - (2)].re));
;}
@@ -1425,13 +1531,17 @@
case 5:
+/* Line 1455 of yacc.c */
+#line 129 "dtc-parser.y"
{
- (yyval.re) = build_reserve_entry((yyvsp[(2) - (4)].addr), (yyvsp[(3) - (4)].addr));
+ (yyval.re) = build_reserve_entry((yyvsp[(2) - (4)].integer), (yyvsp[(3) - (4)].integer));
;}
break;
case 6:
+/* Line 1455 of yacc.c */
+#line 133 "dtc-parser.y"
{
add_label(&(yyvsp[(2) - (2)].re)->labels, (yyvsp[(1) - (2)].labelref));
(yyval.re) = (yyvsp[(2) - (2)].re);
@@ -1440,27 +1550,26 @@
case 7:
- {
- (yyval.addr) = eval_literal((yyvsp[(1) - (1)].literal), 0, 64);
- ;}
- break;
-
- case 8:
-
+/* Line 1455 of yacc.c */
+#line 141 "dtc-parser.y"
{
(yyval.node) = name_node((yyvsp[(2) - (2)].node), "");
;}
break;
- case 9:
+ case 8:
+/* Line 1455 of yacc.c */
+#line 145 "dtc-parser.y"
{
(yyval.node) = merge_nodes((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
;}
break;
- case 10:
+ case 9:
+/* Line 1455 of yacc.c */
+#line 149 "dtc-parser.y"
{
struct node *target = get_node_by_ref((yyvsp[(1) - (3)].node), (yyvsp[(2) - (3)].labelref));
@@ -1472,8 +1581,26 @@
;}
break;
+ case 10:
+
+/* Line 1455 of yacc.c */
+#line 159 "dtc-parser.y"
+ {
+ struct node *target = get_node_by_ref((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].labelref));
+
+ if (!target)
+ print_error("label or path, '%s', not found", (yyvsp[(3) - (4)].labelref));
+ else
+ delete_node(target);
+
+ (yyval.node) = (yyvsp[(1) - (4)].node);
+ ;}
+ break;
+
case 11:
+/* Line 1455 of yacc.c */
+#line 173 "dtc-parser.y"
{
(yyval.node) = build_node((yyvsp[(2) - (5)].proplist), (yyvsp[(3) - (5)].nodelist));
;}
@@ -1481,6 +1608,8 @@
case 12:
+/* Line 1455 of yacc.c */
+#line 180 "dtc-parser.y"
{
(yyval.proplist) = NULL;
;}
@@ -1488,6 +1617,8 @@
case 13:
+/* Line 1455 of yacc.c */
+#line 184 "dtc-parser.y"
{
(yyval.proplist) = chain_property((yyvsp[(2) - (2)].prop), (yyvsp[(1) - (2)].proplist));
;}
@@ -1495,6 +1626,8 @@
case 14:
+/* Line 1455 of yacc.c */
+#line 191 "dtc-parser.y"
{
(yyval.prop) = build_property((yyvsp[(1) - (4)].propnodename), (yyvsp[(3) - (4)].data));
;}
@@ -1502,6 +1635,8 @@
case 15:
+/* Line 1455 of yacc.c */
+#line 195 "dtc-parser.y"
{
(yyval.prop) = build_property((yyvsp[(1) - (2)].propnodename), empty_data);
;}
@@ -1509,62 +1644,85 @@
case 16:
+/* Line 1455 of yacc.c */
+#line 199 "dtc-parser.y"
+ {
+ (yyval.prop) = build_property_delete((yyvsp[(2) - (3)].propnodename));
+ ;}
+ break;
+
+ case 17:
+
+/* Line 1455 of yacc.c */
+#line 203 "dtc-parser.y"
{
add_label(&(yyvsp[(2) - (2)].prop)->labels, (yyvsp[(1) - (2)].labelref));
(yyval.prop) = (yyvsp[(2) - (2)].prop);
;}
break;
- case 17:
+ case 18:
+/* Line 1455 of yacc.c */
+#line 211 "dtc-parser.y"
{
(yyval.data) = data_merge((yyvsp[(1) - (2)].data), (yyvsp[(2) - (2)].data));
;}
break;
- case 18:
-
- {
- (yyval.data) = data_merge((yyvsp[(1) - (4)].data), (yyvsp[(3) - (4)].data));
- ;}
- break;
-
case 19:
+/* Line 1455 of yacc.c */
+#line 215 "dtc-parser.y"
{
- (yyval.data) = data_merge((yyvsp[(1) - (4)].data), (yyvsp[(3) - (4)].data));
+ (yyval.data) = data_merge((yyvsp[(1) - (3)].data), (yyvsp[(2) - (3)].array).data);
;}
break;
case 20:
+/* Line 1455 of yacc.c */
+#line 219 "dtc-parser.y"
{
- (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), REF_PATH, (yyvsp[(2) - (2)].labelref));
+ (yyval.data) = data_merge((yyvsp[(1) - (4)].data), (yyvsp[(3) - (4)].data));
;}
break;
case 21:
+/* Line 1455 of yacc.c */
+#line 223 "dtc-parser.y"
+ {
+ (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), REF_PATH, (yyvsp[(2) - (2)].labelref));
+ ;}
+ break;
+
+ case 22:
+
+/* Line 1455 of yacc.c */
+#line 227 "dtc-parser.y"
{
FILE *f = srcfile_relative_open((yyvsp[(4) - (9)].data).val, NULL);
struct data d;
- if ((yyvsp[(6) - (9)].addr) != 0)
- if (fseek(f, (yyvsp[(6) - (9)].addr), SEEK_SET) != 0)
+ if ((yyvsp[(6) - (9)].integer) != 0)
+ if (fseek(f, (yyvsp[(6) - (9)].integer), SEEK_SET) != 0)
print_error("Couldn't seek to offset %llu in \"%s\": %s",
- (unsigned long long)(yyvsp[(6) - (9)].addr),
+ (unsigned long long)(yyvsp[(6) - (9)].integer),
(yyvsp[(4) - (9)].data).val,
strerror(errno));
- d = data_copy_file(f, (yyvsp[(8) - (9)].addr));
+ d = data_copy_file(f, (yyvsp[(8) - (9)].integer));
(yyval.data) = data_merge((yyvsp[(1) - (9)].data), d);
fclose(f);
;}
break;
- case 22:
+ case 23:
+/* Line 1455 of yacc.c */
+#line 244 "dtc-parser.y"
{
FILE *f = srcfile_relative_open((yyvsp[(4) - (5)].data).val, NULL);
struct data d = empty_data;
@@ -1576,122 +1734,383 @@
;}
break;
- case 23:
-
- {
- (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref));
- ;}
- break;
-
case 24:
+/* Line 1455 of yacc.c */
+#line 254 "dtc-parser.y"
{
- (yyval.data) = empty_data;
+ (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref));
;}
break;
case 25:
+/* Line 1455 of yacc.c */
+#line 261 "dtc-parser.y"
{
- (yyval.data) = (yyvsp[(1) - (2)].data);
+ (yyval.data) = empty_data;
;}
break;
case 26:
+/* Line 1455 of yacc.c */
+#line 265 "dtc-parser.y"
{
- (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref));
+ (yyval.data) = (yyvsp[(1) - (2)].data);
;}
break;
case 27:
+/* Line 1455 of yacc.c */
+#line 269 "dtc-parser.y"
{
- (yyval.data) = empty_data;
+ (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref));
;}
break;
case 28:
+/* Line 1455 of yacc.c */
+#line 276 "dtc-parser.y"
{
- (yyval.data) = data_append_cell((yyvsp[(1) - (2)].data), (yyvsp[(2) - (2)].cell));
+ (yyval.array).data = empty_data;
+ (yyval.array).bits = eval_literal((yyvsp[(2) - (3)].literal), 0, 7);
+
+ if (((yyval.array).bits != 8) &&
+ ((yyval.array).bits != 16) &&
+ ((yyval.array).bits != 32) &&
+ ((yyval.array).bits != 64))
+ {
+ print_error("Only 8, 16, 32 and 64-bit elements"
+ " are currently supported");
+ (yyval.array).bits = 32;
+ }
;}
break;
case 29:
+/* Line 1455 of yacc.c */
+#line 291 "dtc-parser.y"
{
- (yyval.data) = data_append_cell(data_add_marker((yyvsp[(1) - (2)].data), REF_PHANDLE,
- (yyvsp[(2) - (2)].labelref)), -1);
+ (yyval.array).data = empty_data;
+ (yyval.array).bits = 32;
;}
break;
case 30:
+/* Line 1455 of yacc.c */
+#line 296 "dtc-parser.y"
{
- (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref));
+ if ((yyvsp[(1) - (2)].array).bits < 64) {
+ uint64_t mask = (1ULL << (yyvsp[(1) - (2)].array).bits) - 1;
+ /*
+ * Bits above mask must either be all zero
+ * (positive within range of mask) or all one
+ * (negative and sign-extended). The second
+ * condition is true if when we set all bits
+ * within the mask to one (i.e. | in the
+ * mask), all bits are one.
+ */
+ if (((yyvsp[(2) - (2)].integer) > mask) && (((yyvsp[(2) - (2)].integer) | mask) != -1ULL))
+ print_error(
+ "integer value out of range "
+ "%016lx (%d bits)", (yyvsp[(1) - (2)].array).bits);
+ }
+
+ (yyval.array).data = data_append_integer((yyvsp[(1) - (2)].array).data, (yyvsp[(2) - (2)].integer), (yyvsp[(1) - (2)].array).bits);
;}
break;
case 31:
+/* Line 1455 of yacc.c */
+#line 316 "dtc-parser.y"
{
- (yyval.cell) = eval_literal((yyvsp[(1) - (1)].literal), 0, 32);
+ uint64_t val = ~0ULL >> (64 - (yyvsp[(1) - (2)].array).bits);
+
+ if ((yyvsp[(1) - (2)].array).bits == 32)
+ (yyvsp[(1) - (2)].array).data = data_add_marker((yyvsp[(1) - (2)].array).data,
+ REF_PHANDLE,
+ (yyvsp[(2) - (2)].labelref));
+ else
+ print_error("References are only allowed in "
+ "arrays with 32-bit elements.");
+
+ (yyval.array).data = data_append_integer((yyvsp[(1) - (2)].array).data, val, (yyvsp[(1) - (2)].array).bits);
;}
break;
case 32:
+/* Line 1455 of yacc.c */
+#line 330 "dtc-parser.y"
{
- (yyval.data) = empty_data;
+ (yyval.array).data = data_add_marker((yyvsp[(1) - (2)].array).data, LABEL, (yyvsp[(2) - (2)].labelref));
;}
break;
case 33:
+/* Line 1455 of yacc.c */
+#line 337 "dtc-parser.y"
{
- (yyval.data) = data_append_byte((yyvsp[(1) - (2)].data), (yyvsp[(2) - (2)].byte));
+ (yyval.integer) = eval_literal((yyvsp[(1) - (1)].literal), 0, 64);
;}
break;
case 34:
+/* Line 1455 of yacc.c */
+#line 341 "dtc-parser.y"
{
- (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref));
+ (yyval.integer) = eval_char_literal((yyvsp[(1) - (1)].literal));
;}
break;
case 35:
+/* Line 1455 of yacc.c */
+#line 345 "dtc-parser.y"
+ {
+ (yyval.integer) = (yyvsp[(2) - (3)].integer);
+ ;}
+ break;
+
+ case 38:
+
+/* Line 1455 of yacc.c */
+#line 356 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (5)].integer) ? (yyvsp[(3) - (5)].integer) : (yyvsp[(5) - (5)].integer); ;}
+ break;
+
+ case 40:
+
+/* Line 1455 of yacc.c */
+#line 361 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) || (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 42:
+
+/* Line 1455 of yacc.c */
+#line 366 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) && (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 44:
+
+/* Line 1455 of yacc.c */
+#line 371 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) | (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 46:
+
+/* Line 1455 of yacc.c */
+#line 376 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) ^ (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 48:
+
+/* Line 1455 of yacc.c */
+#line 381 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) & (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 50:
+
+/* Line 1455 of yacc.c */
+#line 386 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) == (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 51:
+
+/* Line 1455 of yacc.c */
+#line 387 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) != (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 53:
+
+/* Line 1455 of yacc.c */
+#line 392 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) < (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 54:
+
+/* Line 1455 of yacc.c */
+#line 393 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) > (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 55:
+
+/* Line 1455 of yacc.c */
+#line 394 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) <= (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 56:
+
+/* Line 1455 of yacc.c */
+#line 395 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) >= (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 57:
+
+/* Line 1455 of yacc.c */
+#line 399 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) << (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 58:
+
+/* Line 1455 of yacc.c */
+#line 400 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) >> (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 60:
+
+/* Line 1455 of yacc.c */
+#line 405 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) + (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 61:
+
+/* Line 1455 of yacc.c */
+#line 406 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) - (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 63:
+
+/* Line 1455 of yacc.c */
+#line 411 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) * (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 64:
+
+/* Line 1455 of yacc.c */
+#line 412 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) / (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 65:
+
+/* Line 1455 of yacc.c */
+#line 413 "dtc-parser.y"
+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) % (yyvsp[(3) - (3)].integer); ;}
+ break;
+
+ case 68:
+
+/* Line 1455 of yacc.c */
+#line 419 "dtc-parser.y"
+ { (yyval.integer) = -(yyvsp[(2) - (2)].integer); ;}
+ break;
+
+ case 69:
+
+/* Line 1455 of yacc.c */
+#line 420 "dtc-parser.y"
+ { (yyval.integer) = ~(yyvsp[(2) - (2)].integer); ;}
+ break;
+
+ case 70:
+
+/* Line 1455 of yacc.c */
+#line 421 "dtc-parser.y"
+ { (yyval.integer) = !(yyvsp[(2) - (2)].integer); ;}
+ break;
+
+ case 71:
+
+/* Line 1455 of yacc.c */
+#line 426 "dtc-parser.y"
+ {
+ (yyval.data) = empty_data;
+ ;}
+ break;
+
+ case 72:
+
+/* Line 1455 of yacc.c */
+#line 430 "dtc-parser.y"
+ {
+ (yyval.data) = data_append_byte((yyvsp[(1) - (2)].data), (yyvsp[(2) - (2)].byte));
+ ;}
+ break;
+
+ case 73:
+
+/* Line 1455 of yacc.c */
+#line 434 "dtc-parser.y"
+ {
+ (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref));
+ ;}
+ break;
+
+ case 74:
+
+/* Line 1455 of yacc.c */
+#line 441 "dtc-parser.y"
{
(yyval.nodelist) = NULL;
;}
break;
- case 36:
+ case 75:
+/* Line 1455 of yacc.c */
+#line 445 "dtc-parser.y"
{
(yyval.nodelist) = chain_node((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].nodelist));
;}
break;
- case 37:
+ case 76:
+/* Line 1455 of yacc.c */
+#line 449 "dtc-parser.y"
{
print_error("syntax error: properties must precede subnodes");
YYERROR;
;}
break;
- case 38:
+ case 77:
+/* Line 1455 of yacc.c */
+#line 457 "dtc-parser.y"
{
(yyval.node) = name_node((yyvsp[(2) - (2)].node), (yyvsp[(1) - (2)].propnodename));
;}
break;
- case 39:
+ case 78:
+/* Line 1455 of yacc.c */
+#line 461 "dtc-parser.y"
+ {
+ (yyval.node) = name_node(build_node_delete(), (yyvsp[(2) - (3)].propnodename));
+ ;}
+ break;
+
+ case 79:
+
+/* Line 1455 of yacc.c */
+#line 465 "dtc-parser.y"
{
add_label(&(yyvsp[(2) - (2)].node)->labels, (yyvsp[(1) - (2)].labelref));
(yyval.node) = (yyvsp[(2) - (2)].node);
@@ -1700,6 +2119,8 @@
+/* Line 1455 of yacc.c */
+#line 2124 "dtc-parser.tab.c"
default: break;
}
YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
@@ -1910,6 +2331,8 @@
+/* Line 1675 of yacc.c */
+#line 471 "dtc-parser.y"
void print_error(char const *fmt, ...)
@@ -1934,9 +2357,12 @@
errno = 0;
val = strtoull(s, &e, base);
- if (*e)
- print_error("bad characters in literal");
- else if ((errno == ERANGE)
+ if (*e) {
+ size_t uls = strspn(e, "UL");
+ if (e[uls])
+ print_error("bad characters in literal");
+ }
+ if ((errno == ERANGE)
|| ((bits < 64) && (val >= (1ULL << bits))))
print_error("literal out of range");
else if (errno != 0)
@@ -1944,3 +2370,29 @@
return val;
}
+static unsigned char eval_char_literal(const char *s)
+{
+ int i = 1;
+ char c = s[0];
+
+ if (c == '\0')
+ {
+ print_error("empty character literal");
+ return 0;
+ }
+
+ /*
+ * If the first character in the character literal is a \ then process
+ * the remaining characters as an escape encoding. If the first
+ * character is neither an escape or a terminator it should be the only
+ * character in the literal and will be returned.
+ */
+ if (c == '\\')
+ c = get_escape_char(s, &i);
+
+ if (s[i] != '\0')
+ print_error("malformed character literal");
+
+ return c;
+}
+
diff --git a/scripts/dtc/dtc-parser.tab.h_shipped b/scripts/dtc/dtc-parser.tab.h_shipped
index 4ee682b..9d2dce4 100644
--- a/scripts/dtc/dtc-parser.tab.h_shipped
+++ b/scripts/dtc/dtc-parser.tab.h_shipped
@@ -1,9 +1,10 @@
-/* A Bison parser, made by GNU Bison 2.4.3. */
+
+/* A Bison parser, made by GNU Bison 2.4.1. */
/* Skeleton interface for Bison's Yacc-like parsers in C
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
- 2009, 2010 Free Software Foundation, Inc.
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -40,14 +41,26 @@
enum yytokentype {
DT_V1 = 258,
DT_MEMRESERVE = 259,
- DT_PROPNODENAME = 260,
- DT_LITERAL = 261,
- DT_BASE = 262,
- DT_BYTE = 263,
- DT_STRING = 264,
- DT_LABEL = 265,
- DT_REF = 266,
- DT_INCBIN = 267
+ DT_LSHIFT = 260,
+ DT_RSHIFT = 261,
+ DT_LE = 262,
+ DT_GE = 263,
+ DT_EQ = 264,
+ DT_NE = 265,
+ DT_AND = 266,
+ DT_OR = 267,
+ DT_BITS = 268,
+ DT_DEL_PROP = 269,
+ DT_DEL_NODE = 270,
+ DT_PROPNODENAME = 271,
+ DT_LITERAL = 272,
+ DT_CHAR_LITERAL = 273,
+ DT_BASE = 274,
+ DT_BYTE = 275,
+ DT_STRING = 276,
+ DT_LABEL = 277,
+ DT_REF = 278,
+ DT_INCBIN = 279
};
#endif
@@ -57,6 +70,8 @@
typedef union YYSTYPE
{
+/* Line 1676 of yacc.c */
+#line 40 "dtc-parser.y"
char *propnodename;
char *literal;
@@ -65,16 +80,22 @@
uint8_t byte;
struct data data;
- uint64_t addr;
- cell_t cell;
+ struct {
+ struct data data;
+ int bits;
+ } array;
+
struct property *prop;
struct property *proplist;
struct node *node;
struct node *nodelist;
struct reserve_info *re;
+ uint64_t integer;
+/* Line 1676 of yacc.c */
+#line 99 "dtc-parser.tab.h"
} YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
diff --git a/scripts/dtc/dtc-parser.y b/scripts/dtc/dtc-parser.y
index 5e84a67..f412460 100644
--- a/scripts/dtc/dtc-parser.y
+++ b/scripts/dtc/dtc-parser.y
@@ -34,6 +34,7 @@
extern int treesource_error;
static unsigned long long eval_literal(const char *s, int base, int bits);
+static unsigned char eval_char_literal(const char *s);
%}
%union {
@@ -44,19 +45,28 @@
uint8_t byte;
struct data data;
- uint64_t addr;
- cell_t cell;
+ struct {
+ struct data data;
+ int bits;
+ } array;
+
struct property *prop;
struct property *proplist;
struct node *node;
struct node *nodelist;
struct reserve_info *re;
+ uint64_t integer;
}
%token DT_V1
%token DT_MEMRESERVE
+%token DT_LSHIFT DT_RSHIFT DT_LE DT_GE DT_EQ DT_NE DT_AND DT_OR
+%token DT_BITS
+%token DT_DEL_PROP
+%token DT_DEL_NODE
%token <propnodename> DT_PROPNODENAME
%token <literal> DT_LITERAL
+%token <literal> DT_CHAR_LITERAL
%token <cbase> DT_BASE
%token <byte> DT_BYTE
%token <data> DT_STRING
@@ -68,9 +78,7 @@
%type <data> propdataprefix
%type <re> memreserve
%type <re> memreserves
-%type <addr> addr
-%type <data> celllist
-%type <cell> cellval
+%type <array> arrayprefix
%type <data> bytestring
%type <prop> propdef
%type <proplist> proplist
@@ -80,6 +88,21 @@
%type <node> subnode
%type <nodelist> subnodes
+%type <integer> integer_prim
+%type <integer> integer_unary
+%type <integer> integer_mul
+%type <integer> integer_add
+%type <integer> integer_shift
+%type <integer> integer_rela
+%type <integer> integer_eq
+%type <integer> integer_bitand
+%type <integer> integer_bitxor
+%type <integer> integer_bitor
+%type <integer> integer_and
+%type <integer> integer_or
+%type <integer> integer_trinary
+%type <integer> integer_expr
+
%%
sourcefile:
@@ -102,7 +125,7 @@
;
memreserve:
- DT_MEMRESERVE addr addr ';'
+ DT_MEMRESERVE integer_prim integer_prim ';'
{
$$ = build_reserve_entry($2, $3);
}
@@ -113,13 +136,6 @@
}
;
-addr:
- DT_LITERAL
- {
- $$ = eval_literal($1, 0, 64);
- }
- ;
-
devicetree:
'/' nodedef
{
@@ -139,6 +155,17 @@
print_error("label or path, '%s', not found", $2);
$$ = $1;
}
+ | devicetree DT_DEL_NODE DT_REF ';'
+ {
+ struct node *target = get_node_by_ref($1, $3);
+
+ if (!target)
+ print_error("label or path, '%s', not found", $3);
+ else
+ delete_node(target);
+
+ $$ = $1;
+ }
;
nodedef:
@@ -168,6 +195,10 @@
{
$$ = build_property($1, empty_data);
}
+ | DT_DEL_PROP DT_PROPNODENAME ';'
+ {
+ $$ = build_property_delete($2);
+ }
| DT_LABEL propdef
{
add_label(&$2->labels, $1);
@@ -180,9 +211,9 @@
{
$$ = data_merge($1, $2);
}
- | propdataprefix '<' celllist '>'
+ | propdataprefix arrayprefix '>'
{
- $$ = data_merge($1, $3);
+ $$ = data_merge($1, $2.data);
}
| propdataprefix '[' bytestring ']'
{
@@ -192,7 +223,7 @@
{
$$ = data_add_marker($1, REF_PATH, $2);
}
- | propdataprefix DT_INCBIN '(' DT_STRING ',' addr ',' addr ')'
+ | propdataprefix DT_INCBIN '(' DT_STRING ',' integer_prim ',' integer_prim ')'
{
FILE *f = srcfile_relative_open($4.val, NULL);
struct data d;
@@ -240,31 +271,154 @@
}
;
-celllist:
- /* empty */
+arrayprefix:
+ DT_BITS DT_LITERAL '<'
{
- $$ = empty_data;
+ $$.data = empty_data;
+ $$.bits = eval_literal($2, 0, 7);
+
+ if (($$.bits != 8) &&
+ ($$.bits != 16) &&
+ ($$.bits != 32) &&
+ ($$.bits != 64))
+ {
+ print_error("Only 8, 16, 32 and 64-bit elements"
+ " are currently supported");
+ $$.bits = 32;
+ }
}
- | celllist cellval
+ | '<'
{
- $$ = data_append_cell($1, $2);
+ $$.data = empty_data;
+ $$.bits = 32;
}
- | celllist DT_REF
+ | arrayprefix integer_prim
{
- $$ = data_append_cell(data_add_marker($1, REF_PHANDLE,
- $2), -1);
+ if ($1.bits < 64) {
+ uint64_t mask = (1ULL << $1.bits) - 1;
+ /*
+ * Bits above mask must either be all zero
+ * (positive within range of mask) or all one
+ * (negative and sign-extended). The second
+ * condition is true if when we set all bits
+ * within the mask to one (i.e. | in the
+ * mask), all bits are one.
+ */
+ if (($2 > mask) && (($2 | mask) != -1ULL))
+ print_error(
+ "integer value out of range "
+ "%016lx (%d bits)", $1.bits);
+ }
+
+ $$.data = data_append_integer($1.data, $2, $1.bits);
}
- | celllist DT_LABEL
+ | arrayprefix DT_REF
{
- $$ = data_add_marker($1, LABEL, $2);
+ uint64_t val = ~0ULL >> (64 - $1.bits);
+
+ if ($1.bits == 32)
+ $1.data = data_add_marker($1.data,
+ REF_PHANDLE,
+ $2);
+ else
+ print_error("References are only allowed in "
+ "arrays with 32-bit elements.");
+
+ $$.data = data_append_integer($1.data, val, $1.bits);
+ }
+ | arrayprefix DT_LABEL
+ {
+ $$.data = data_add_marker($1.data, LABEL, $2);
}
;
-cellval:
+integer_prim:
DT_LITERAL
{
- $$ = eval_literal($1, 0, 32);
+ $$ = eval_literal($1, 0, 64);
}
+ | DT_CHAR_LITERAL
+ {
+ $$ = eval_char_literal($1);
+ }
+ | '(' integer_expr ')'
+ {
+ $$ = $2;
+ }
+ ;
+
+integer_expr:
+ integer_trinary
+ ;
+
+integer_trinary:
+ integer_or
+ | integer_or '?' integer_expr ':' integer_trinary { $$ = $1 ? $3 : $5; }
+ ;
+
+integer_or:
+ integer_and
+ | integer_or DT_OR integer_and { $$ = $1 || $3; }
+ ;
+
+integer_and:
+ integer_bitor
+ | integer_and DT_AND integer_bitor { $$ = $1 && $3; }
+ ;
+
+integer_bitor:
+ integer_bitxor
+ | integer_bitor '|' integer_bitxor { $$ = $1 | $3; }
+ ;
+
+integer_bitxor:
+ integer_bitand
+ | integer_bitxor '^' integer_bitand { $$ = $1 ^ $3; }
+ ;
+
+integer_bitand:
+ integer_eq
+ | integer_bitand '&' integer_eq { $$ = $1 & $3; }
+ ;
+
+integer_eq:
+ integer_rela
+ | integer_eq DT_EQ integer_rela { $$ = $1 == $3; }
+ | integer_eq DT_NE integer_rela { $$ = $1 != $3; }
+ ;
+
+integer_rela:
+ integer_shift
+ | integer_rela '<' integer_shift { $$ = $1 < $3; }
+ | integer_rela '>' integer_shift { $$ = $1 > $3; }
+ | integer_rela DT_LE integer_shift { $$ = $1 <= $3; }
+ | integer_rela DT_GE integer_shift { $$ = $1 >= $3; }
+ ;
+
+integer_shift:
+ integer_shift DT_LSHIFT integer_add { $$ = $1 << $3; }
+ | integer_shift DT_RSHIFT integer_add { $$ = $1 >> $3; }
+ | integer_add
+ ;
+
+integer_add:
+ integer_add '+' integer_mul { $$ = $1 + $3; }
+ | integer_add '-' integer_mul { $$ = $1 - $3; }
+ | integer_mul
+ ;
+
+integer_mul:
+ integer_mul '*' integer_unary { $$ = $1 * $3; }
+ | integer_mul '/' integer_unary { $$ = $1 / $3; }
+ | integer_mul '%' integer_unary { $$ = $1 % $3; }
+ | integer_unary
+ ;
+
+integer_unary:
+ integer_prim
+ | '-' integer_unary { $$ = -$2; }
+ | '~' integer_unary { $$ = ~$2; }
+ | '!' integer_unary { $$ = !$2; }
;
bytestring:
@@ -303,6 +457,10 @@
{
$$ = name_node($2, $1);
}
+ | DT_DEL_NODE DT_PROPNODENAME ';'
+ {
+ $$ = name_node(build_node_delete(), $2);
+ }
| DT_LABEL subnode
{
add_label(&$2->labels, $1);
@@ -334,12 +492,41 @@
errno = 0;
val = strtoull(s, &e, base);
- if (*e)
- print_error("bad characters in literal");
- else if ((errno == ERANGE)
+ if (*e) {
+ size_t uls = strspn(e, "UL");
+ if (e[uls])
+ print_error("bad characters in literal");
+ }
+ if ((errno == ERANGE)
|| ((bits < 64) && (val >= (1ULL << bits))))
print_error("literal out of range");
else if (errno != 0)
print_error("bad literal");
return val;
}
+
+static unsigned char eval_char_literal(const char *s)
+{
+ int i = 1;
+ char c = s[0];
+
+ if (c == '\0')
+ {
+ print_error("empty character literal");
+ return 0;
+ }
+
+ /*
+ * If the first character in the character literal is a \ then process
+ * the remaining characters as an escape encoding. If the first
+ * character is neither an escape or a terminator it should be the only
+ * character in the literal and will be returned.
+ */
+ if (c == '\\')
+ c = get_escape_char(s, &i);
+
+ if (s[i] != '\0')
+ print_error("malformed character literal");
+
+ return c;
+}
diff --git a/scripts/dtc/dtc.c b/scripts/dtc/dtc.c
index 2ef5e2e..a375683 100644
--- a/scripts/dtc/dtc.c
+++ b/scripts/dtc/dtc.c
@@ -82,6 +82,8 @@
fprintf(stderr, "\t\tSet the physical boot cpu\n");
fprintf(stderr, "\t-f\n");
fprintf(stderr, "\t\tForce - try to produce output even if the input tree has errors\n");
+ fprintf(stderr, "\t-i\n");
+ fprintf(stderr, "\t\tAdd a path to search for include files\n");
fprintf(stderr, "\t-s\n");
fprintf(stderr, "\t\tSort nodes and properties before outputting (only useful for\n\t\tcomparing trees)\n");
fprintf(stderr, "\t-v\n");
@@ -91,6 +93,9 @@
fprintf(stderr, "\t\t\tlegacy - \"linux,phandle\" properties only\n");
fprintf(stderr, "\t\t\tepapr - \"phandle\" properties only\n");
fprintf(stderr, "\t\t\tboth - Both \"linux,phandle\" and \"phandle\" properties\n");
+ fprintf(stderr, "\t-W [no-]<checkname>\n");
+ fprintf(stderr, "\t-E [no-]<checkname>\n");
+ fprintf(stderr, "\t\t\tenable or disable warnings and errors\n");
exit(3);
}
@@ -113,7 +118,7 @@
minsize = 0;
padsize = 0;
- while ((opt = getopt(argc, argv, "hI:O:o:V:d:R:S:p:fcqb:vH:s"))
+ while ((opt = getopt(argc, argv, "hI:O:o:V:d:R:S:p:fqb:i:vH:sW:E:"))
!= EOF) {
switch (opt) {
case 'I':
@@ -149,6 +154,9 @@
case 'b':
cmdline_boot_cpuid = strtoll(optarg, NULL, 0);
break;
+ case 'i':
+ srcfile_add_search_path(optarg);
+ break;
case 'v':
printf("Version: %s\n", DTC_VERSION);
exit(0);
@@ -168,6 +176,14 @@
sort = 1;
break;
+ case 'W':
+ parse_checks_option(true, false, optarg);
+ break;
+
+ case 'E':
+ parse_checks_option(false, true, optarg);
+ break;
+
case 'h':
default:
usage();
@@ -188,9 +204,6 @@
if (minsize)
fprintf(stderr, "DTC: Use of \"-S\" is deprecated; it will be removed soon, use \"-p\" instead\n");
- fprintf(stderr, "DTC: %s->%s on file \"%s\"\n",
- inform, outform, arg);
-
if (depname) {
depfile = fopen(depname, "w");
if (!depfile)
diff --git a/scripts/dtc/dtc.h b/scripts/dtc/dtc.h
index f37c97e..3e42a07 100644
--- a/scripts/dtc/dtc.h
+++ b/scripts/dtc/dtc.h
@@ -25,6 +25,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
+#include <stdbool.h>
#include <stdarg.h>
#include <assert.h>
#include <ctype.h>
@@ -109,6 +110,7 @@
const void *p, int len);
struct data data_merge(struct data d1, struct data d2);
struct data data_append_cell(struct data d, cell_t word);
+struct data data_append_integer(struct data d, uint64_t word, int bits);
struct data data_append_re(struct data d, const struct fdt_reserve_entry *re);
struct data data_append_addr(struct data d, uint64_t addr);
struct data data_append_byte(struct data d, uint8_t byte);
@@ -126,11 +128,13 @@
/* Live trees */
struct label {
+ int deleted;
char *label;
struct label *next;
};
struct property {
+ int deleted;
char *name;
struct data val;
@@ -140,6 +144,7 @@
};
struct node {
+ int deleted;
char *name;
struct property *proplist;
struct node *children;
@@ -156,28 +161,47 @@
struct label *labels;
};
-#define for_each_label(l0, l) \
+#define for_each_label_withdel(l0, l) \
for ((l) = (l0); (l); (l) = (l)->next)
-#define for_each_property(n, p) \
+#define for_each_label(l0, l) \
+ for_each_label_withdel(l0, l) \
+ if (!(l)->deleted)
+
+#define for_each_property_withdel(n, p) \
for ((p) = (n)->proplist; (p); (p) = (p)->next)
-#define for_each_child(n, c) \
+#define for_each_property(n, p) \
+ for_each_property_withdel(n, p) \
+ if (!(p)->deleted)
+
+#define for_each_child_withdel(n, c) \
for ((c) = (n)->children; (c); (c) = (c)->next_sibling)
+#define for_each_child(n, c) \
+ for_each_child_withdel(n, c) \
+ if (!(c)->deleted)
+
void add_label(struct label **labels, char *label);
+void delete_labels(struct label **labels);
struct property *build_property(char *name, struct data val);
+struct property *build_property_delete(char *name);
struct property *chain_property(struct property *first, struct property *list);
struct property *reverse_properties(struct property *first);
struct node *build_node(struct property *proplist, struct node *children);
+struct node *build_node_delete(void);
struct node *name_node(struct node *node, char *name);
struct node *chain_node(struct node *first, struct node *list);
struct node *merge_nodes(struct node *old_node, struct node *new_node);
void add_property(struct node *node, struct property *prop);
+void delete_property_by_name(struct node *node, char *name);
+void delete_property(struct property *prop);
void add_child(struct node *parent, struct node *child);
+void delete_node_by_name(struct node *parent, char *name);
+void delete_node(struct node *node);
const char *get_unitname(struct node *node);
struct property *get_property(struct node *node, const char *propname);
@@ -224,6 +248,7 @@
/* Checks */
+void parse_checks_option(bool warn, bool error, const char *optarg);
void process_checks(int force, struct boot_info *bi);
/* Flattened trees */
diff --git a/scripts/dtc/fdtdump.c b/scripts/dtc/fdtdump.c
new file mode 100644
index 0000000..207a46d
--- /dev/null
+++ b/scripts/dtc/fdtdump.c
@@ -0,0 +1,162 @@
+/*
+ * fdtdump.c - Contributed by Pantelis Antoniou <pantelis.antoniou AT gmail.com>
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <fdt.h>
+#include <libfdt_env.h>
+
+#include "util.h"
+
+#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
+#define PALIGN(p, a) ((void *)(ALIGN((unsigned long)(p), (a))))
+#define GET_CELL(p) (p += 4, *((const uint32_t *)(p-4)))
+
+static void print_data(const char *data, int len)
+{
+ int i;
+ const char *p = data;
+
+ /* no data, don't print */
+ if (len == 0)
+ return;
+
+ if (util_is_printable_string(data, len)) {
+ printf(" = \"%s\"", (const char *)data);
+ } else if ((len % 4) == 0) {
+ printf(" = <");
+ for (i = 0; i < len; i += 4)
+ printf("0x%08x%s", fdt32_to_cpu(GET_CELL(p)),
+ i < (len - 4) ? " " : "");
+ printf(">");
+ } else {
+ printf(" = [");
+ for (i = 0; i < len; i++)
+ printf("%02x%s", *p++, i < len - 1 ? " " : "");
+ printf("]");
+ }
+}
+
+static void dump_blob(void *blob)
+{
+ struct fdt_header *bph = blob;
+ uint32_t off_mem_rsvmap = fdt32_to_cpu(bph->off_mem_rsvmap);
+ uint32_t off_dt = fdt32_to_cpu(bph->off_dt_struct);
+ uint32_t off_str = fdt32_to_cpu(bph->off_dt_strings);
+ struct fdt_reserve_entry *p_rsvmap =
+ (struct fdt_reserve_entry *)((char *)blob + off_mem_rsvmap);
+ const char *p_struct = (const char *)blob + off_dt;
+ const char *p_strings = (const char *)blob + off_str;
+ uint32_t version = fdt32_to_cpu(bph->version);
+ uint32_t totalsize = fdt32_to_cpu(bph->totalsize);
+ uint32_t tag;
+ const char *p, *s, *t;
+ int depth, sz, shift;
+ int i;
+ uint64_t addr, size;
+
+ depth = 0;
+ shift = 4;
+
+ printf("/dts-v1/;\n");
+ printf("// magic:\t\t0x%x\n", fdt32_to_cpu(bph->magic));
+ printf("// totalsize:\t\t0x%x (%d)\n", totalsize, totalsize);
+ printf("// off_dt_struct:\t0x%x\n", off_dt);
+ printf("// off_dt_strings:\t0x%x\n", off_str);
+ printf("// off_mem_rsvmap:\t0x%x\n", off_mem_rsvmap);
+ printf("// version:\t\t%d\n", version);
+ printf("// last_comp_version:\t%d\n",
+ fdt32_to_cpu(bph->last_comp_version));
+ if (version >= 2)
+ printf("// boot_cpuid_phys:\t0x%x\n",
+ fdt32_to_cpu(bph->boot_cpuid_phys));
+
+ if (version >= 3)
+ printf("// size_dt_strings:\t0x%x\n",
+ fdt32_to_cpu(bph->size_dt_strings));
+ if (version >= 17)
+ printf("// size_dt_struct:\t0x%x\n",
+ fdt32_to_cpu(bph->size_dt_struct));
+ printf("\n");
+
+ for (i = 0; ; i++) {
+ addr = fdt64_to_cpu(p_rsvmap[i].address);
+ size = fdt64_to_cpu(p_rsvmap[i].size);
+ if (addr == 0 && size == 0)
+ break;
+
+ printf("/memreserve/ %llx %llx;\n",
+ (unsigned long long)addr, (unsigned long long)size);
+ }
+
+ p = p_struct;
+ while ((tag = fdt32_to_cpu(GET_CELL(p))) != FDT_END) {
+
+ /* printf("tag: 0x%08x (%d)\n", tag, p - p_struct); */
+
+ if (tag == FDT_BEGIN_NODE) {
+ s = p;
+ p = PALIGN(p + strlen(s) + 1, 4);
+
+ if (*s == '\0')
+ s = "/";
+
+ printf("%*s%s {\n", depth * shift, "", s);
+
+ depth++;
+ continue;
+ }
+
+ if (tag == FDT_END_NODE) {
+ depth--;
+
+ printf("%*s};\n", depth * shift, "");
+ continue;
+ }
+
+ if (tag == FDT_NOP) {
+ printf("%*s// [NOP]\n", depth * shift, "");
+ continue;
+ }
+
+ if (tag != FDT_PROP) {
+ fprintf(stderr, "%*s ** Unknown tag 0x%08x\n", depth * shift, "", tag);
+ break;
+ }
+ sz = fdt32_to_cpu(GET_CELL(p));
+ s = p_strings + fdt32_to_cpu(GET_CELL(p));
+ if (version < 16 && sz >= 8)
+ p = PALIGN(p, 8);
+ t = p;
+
+ p = PALIGN(p + sz, 4);
+
+ printf("%*s%s", depth * shift, "", s);
+ print_data(t, sz);
+ printf(";\n");
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ char *buf;
+
+ if (argc < 2) {
+ fprintf(stderr, "supply input filename\n");
+ return 5;
+ }
+
+ buf = utilfdt_read(argv[1]);
+ if (buf)
+ dump_blob(buf);
+ else
+ return 10;
+
+ return 0;
+}
diff --git a/scripts/dtc/fdtget.c b/scripts/dtc/fdtget.c
new file mode 100644
index 0000000..c2fbab2
--- /dev/null
+++ b/scripts/dtc/fdtget.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ *
+ * Portions from U-Boot cmd_fdt.c (C) Copyright 2007
+ * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
+ * Based on code written by:
+ * Pantelis Antoniou <pantelis.antoniou@gmail.com> and
+ * Matthew McClintock <msm@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libfdt.h>
+
+#include "util.h"
+
+enum display_mode {
+ MODE_SHOW_VALUE, /* show values for node properties */
+ MODE_LIST_PROPS, /* list the properties for a node */
+ MODE_LIST_SUBNODES, /* list the subnodes of a node */
+};
+
+/* Holds information which controls our output and options */
+struct display_info {
+ int type; /* data type (s/i/u/x or 0 for default) */
+ int size; /* data size (1/2/4) */
+ enum display_mode mode; /* display mode that we are using */
+ const char *default_val; /* default value if node/property not found */
+};
+
+static void report_error(const char *where, int err)
+{
+ fprintf(stderr, "Error at '%s': %s\n", where, fdt_strerror(err));
+}
+
+/**
+ * Displays data of a given length according to selected options
+ *
+ * If a specific data type is provided in disp, then this is used. Otherwise
+ * we try to guess the data type / size from the contents.
+ *
+ * @param disp Display information / options
+ * @param data Data to display
+ * @param len Maximum length of buffer
+ * @return 0 if ok, -1 if data does not match format
+ */
+static int show_data(struct display_info *disp, const char *data, int len)
+{
+ int i, size;
+ const uint8_t *p = (const uint8_t *)data;
+ const char *s;
+ int value;
+ int is_string;
+ char fmt[3];
+
+ /* no data, don't print */
+ if (len == 0)
+ return 0;
+
+ is_string = (disp->type) == 's' ||
+ (!disp->type && util_is_printable_string(data, len));
+ if (is_string) {
+ if (data[len - 1] != '\0') {
+ fprintf(stderr, "Unterminated string\n");
+ return -1;
+ }
+ for (s = data; s - data < len; s += strlen(s) + 1) {
+ if (s != data)
+ printf(" ");
+ printf("%s", (const char *)s);
+ }
+ return 0;
+ }
+ size = disp->size;
+ if (size == -1) {
+ size = (len % 4) == 0 ? 4 : 1;
+ } else if (len % size) {
+ fprintf(stderr, "Property length must be a multiple of "
+ "selected data size\n");
+ return -1;
+ }
+ fmt[0] = '%';
+ fmt[1] = disp->type ? disp->type : 'd';
+ fmt[2] = '\0';
+ for (i = 0; i < len; i += size, p += size) {
+ if (i)
+ printf(" ");
+ value = size == 4 ? fdt32_to_cpu(*(const uint32_t *)p) :
+ size == 2 ? (*p << 8) | p[1] : *p;
+ printf(fmt, value);
+ }
+ return 0;
+}
+
+/**
+ * List all properties in a node, one per line.
+ *
+ * @param blob FDT blob
+ * @param node Node to display
+ * @return 0 if ok, or FDT_ERR... if not.
+ */
+static int list_properties(const void *blob, int node)
+{
+ const struct fdt_property *data;
+ const char *name;
+ int prop;
+
+ prop = fdt_first_property_offset(blob, node);
+ do {
+ /* Stop silently when there are no more properties */
+ if (prop < 0)
+ return prop == -FDT_ERR_NOTFOUND ? 0 : prop;
+ data = fdt_get_property_by_offset(blob, prop, NULL);
+ name = fdt_string(blob, fdt32_to_cpu(data->nameoff));
+ if (name)
+ puts(name);
+ prop = fdt_next_property_offset(blob, prop);
+ } while (1);
+}
+
+#define MAX_LEVEL 32 /* how deeply nested we will go */
+
+/**
+ * List all subnodes in a node, one per line
+ *
+ * @param blob FDT blob
+ * @param node Node to display
+ * @return 0 if ok, or FDT_ERR... if not.
+ */
+static int list_subnodes(const void *blob, int node)
+{
+ int nextoffset; /* next node offset from libfdt */
+ uint32_t tag; /* current tag */
+ int level = 0; /* keep track of nesting level */
+ const char *pathp;
+ int depth = 1; /* the assumed depth of this node */
+
+ while (level >= 0) {
+ tag = fdt_next_tag(blob, node, &nextoffset);
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ pathp = fdt_get_name(blob, node, NULL);
+ if (level <= depth) {
+ if (pathp == NULL)
+ pathp = "/* NULL pointer error */";
+ if (*pathp == '\0')
+ pathp = "/"; /* root is nameless */
+ if (level == 1)
+ puts(pathp);
+ }
+ level++;
+ if (level >= MAX_LEVEL) {
+ printf("Nested too deep, aborting.\n");
+ return 1;
+ }
+ break;
+ case FDT_END_NODE:
+ level--;
+ if (level == 0)
+ level = -1; /* exit the loop */
+ break;
+ case FDT_END:
+ return 1;
+ case FDT_PROP:
+ break;
+ default:
+ if (level <= depth)
+ printf("Unknown tag 0x%08X\n", tag);
+ return 1;
+ }
+ node = nextoffset;
+ }
+ return 0;
+}
+
+/**
+ * Show the data for a given node (and perhaps property) according to the
+ * display option provided.
+ *
+ * @param blob FDT blob
+ * @param disp Display information / options
+ * @param node Node to display
+ * @param property Name of property to display, or NULL if none
+ * @return 0 if ok, -ve on error
+ */
+static int show_data_for_item(const void *blob, struct display_info *disp,
+ int node, const char *property)
+{
+ const void *value = NULL;
+ int len, err = 0;
+
+ switch (disp->mode) {
+ case MODE_LIST_PROPS:
+ err = list_properties(blob, node);
+ break;
+
+ case MODE_LIST_SUBNODES:
+ err = list_subnodes(blob, node);
+ break;
+
+ default:
+ assert(property);
+ value = fdt_getprop(blob, node, property, &len);
+ if (value) {
+ if (show_data(disp, value, len))
+ err = -1;
+ else
+ printf("\n");
+ } else if (disp->default_val) {
+ puts(disp->default_val);
+ } else {
+ report_error(property, len);
+ err = -1;
+ }
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * Run the main fdtget operation, given a filename and valid arguments
+ *
+ * @param disp Display information / options
+ * @param filename Filename of blob file
+ * @param arg List of arguments to process
+ * @param arg_count Number of arguments
+ * @param return 0 if ok, -ve on error
+ */
+static int do_fdtget(struct display_info *disp, const char *filename,
+ char **arg, int arg_count, int args_per_step)
+{
+ char *blob;
+ const char *prop;
+ int i, node;
+
+ blob = utilfdt_read(filename);
+ if (!blob)
+ return -1;
+
+ for (i = 0; i + args_per_step <= arg_count; i += args_per_step) {
+ node = fdt_path_offset(blob, arg[i]);
+ if (node < 0) {
+ if (disp->default_val) {
+ puts(disp->default_val);
+ continue;
+ } else {
+ report_error(arg[i], node);
+ return -1;
+ }
+ }
+ prop = args_per_step == 1 ? NULL : arg[i + 1];
+
+ if (show_data_for_item(blob, disp, node, prop))
+ return -1;
+ }
+ return 0;
+}
+
+static const char *usage_msg =
+ "fdtget - read values from device tree\n"
+ "\n"
+ "Each value is printed on a new line.\n\n"
+ "Usage:\n"
+ " fdtget <options> <dt file> [<node> <property>]...\n"
+ " fdtget -p <options> <dt file> [<node> ]...\n"
+ "Options:\n"
+ "\t-t <type>\tType of data\n"
+ "\t-p\t\tList properties for each node\n"
+ "\t-l\t\tList subnodes for each node\n"
+ "\t-d\t\tDefault value to display when the property is "
+ "missing\n"
+ "\t-h\t\tPrint this help\n\n"
+ USAGE_TYPE_MSG;
+
+static void usage(const char *msg)
+{
+ if (msg)
+ fprintf(stderr, "Error: %s\n\n", msg);
+
+ fprintf(stderr, "%s", usage_msg);
+ exit(2);
+}
+
+int main(int argc, char *argv[])
+{
+ char *filename = NULL;
+ struct display_info disp;
+ int args_per_step = 2;
+
+ /* set defaults */
+ memset(&disp, '\0', sizeof(disp));
+ disp.size = -1;
+ disp.mode = MODE_SHOW_VALUE;
+ for (;;) {
+ int c = getopt(argc, argv, "d:hlpt:");
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ case '?':
+ usage(NULL);
+
+ case 't':
+ if (utilfdt_decode_type(optarg, &disp.type,
+ &disp.size))
+ usage("Invalid type string");
+ break;
+
+ case 'p':
+ disp.mode = MODE_LIST_PROPS;
+ args_per_step = 1;
+ break;
+
+ case 'l':
+ disp.mode = MODE_LIST_SUBNODES;
+ args_per_step = 1;
+ break;
+
+ case 'd':
+ disp.default_val = optarg;
+ break;
+ }
+ }
+
+ if (optind < argc)
+ filename = argv[optind++];
+ if (!filename)
+ usage("Missing filename");
+
+ argv += optind;
+ argc -= optind;
+
+ /* Allow no arguments, and silently succeed */
+ if (!argc)
+ return 0;
+
+ /* Check for node, property arguments */
+ if (args_per_step == 2 && (argc % 2))
+ usage("Must have an even number of arguments");
+
+ if (do_fdtget(&disp, filename, argv, argc, args_per_step))
+ return 1;
+ return 0;
+}
diff --git a/scripts/dtc/fdtput.c b/scripts/dtc/fdtput.c
new file mode 100644
index 0000000..f2197f5
--- /dev/null
+++ b/scripts/dtc/fdtput.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors. 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 as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libfdt.h>
+
+#include "util.h"
+
+/* These are the operations we support */
+enum oper_type {
+ OPER_WRITE_PROP, /* Write a property in a node */
+ OPER_CREATE_NODE, /* Create a new node */
+};
+
+struct display_info {
+ enum oper_type oper; /* operation to perform */
+ int type; /* data type (s/i/u/x or 0 for default) */
+ int size; /* data size (1/2/4) */
+ int verbose; /* verbose output */
+ int auto_path; /* automatically create all path components */
+};
+
+
+/**
+ * Report an error with a particular node.
+ *
+ * @param name Node name to report error on
+ * @param namelen Length of node name, or -1 to use entire string
+ * @param err Error number to report (-FDT_ERR_...)
+ */
+static void report_error(const char *name, int namelen, int err)
+{
+ if (namelen == -1)
+ namelen = strlen(name);
+ fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name,
+ fdt_strerror(err));
+}
+
+/**
+ * Encode a series of arguments in a property value.
+ *
+ * @param disp Display information / options
+ * @param arg List of arguments from command line
+ * @param arg_count Number of arguments (may be 0)
+ * @param valuep Returns buffer containing value
+ * @param *value_len Returns length of value encoded
+ */
+static int encode_value(struct display_info *disp, char **arg, int arg_count,
+ char **valuep, int *value_len)
+{
+ char *value = NULL; /* holding area for value */
+ int value_size = 0; /* size of holding area */
+ char *ptr; /* pointer to current value position */
+ int len; /* length of this cell/string/byte */
+ int ival;
+ int upto; /* the number of bytes we have written to buf */
+ char fmt[3];
+
+ upto = 0;
+
+ if (disp->verbose)
+ fprintf(stderr, "Decoding value:\n");
+
+ fmt[0] = '%';
+ fmt[1] = disp->type ? disp->type : 'd';
+ fmt[2] = '\0';
+ for (; arg_count > 0; arg++, arg_count--, upto += len) {
+ /* assume integer unless told otherwise */
+ if (disp->type == 's')
+ len = strlen(*arg) + 1;
+ else
+ len = disp->size == -1 ? 4 : disp->size;
+
+ /* enlarge our value buffer by a suitable margin if needed */
+ if (upto + len > value_size) {
+ value_size = (upto + len) + 500;
+ value = realloc(value, value_size);
+ if (!value) {
+ fprintf(stderr, "Out of mmory: cannot alloc "
+ "%d bytes\n", value_size);
+ return -1;
+ }
+ }
+
+ ptr = value + upto;
+ if (disp->type == 's') {
+ memcpy(ptr, *arg, len);
+ if (disp->verbose)
+ fprintf(stderr, "\tstring: '%s'\n", ptr);
+ } else {
+ int *iptr = (int *)ptr;
+ sscanf(*arg, fmt, &ival);
+ if (len == 4)
+ *iptr = cpu_to_fdt32(ival);
+ else
+ *ptr = (uint8_t)ival;
+ if (disp->verbose) {
+ fprintf(stderr, "\t%s: %d\n",
+ disp->size == 1 ? "byte" :
+ disp->size == 2 ? "short" : "int",
+ ival);
+ }
+ }
+ }
+ *value_len = upto;
+ *valuep = value;
+ if (disp->verbose)
+ fprintf(stderr, "Value size %d\n", upto);
+ return 0;
+}
+
+static int store_key_value(void *blob, const char *node_name,
+ const char *property, const char *buf, int len)
+{
+ int node;
+ int err;
+
+ node = fdt_path_offset(blob, node_name);
+ if (node < 0) {
+ report_error(node_name, -1, node);
+ return -1;
+ }
+
+ err = fdt_setprop(blob, node, property, buf, len);
+ if (err) {
+ report_error(property, -1, err);
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Create paths as needed for all components of a path
+ *
+ * Any components of the path that do not exist are created. Errors are
+ * reported.
+ *
+ * @param blob FDT blob to write into
+ * @param in_path Path to process
+ * @return 0 if ok, -1 on error
+ */
+static int create_paths(void *blob, const char *in_path)
+{
+ const char *path = in_path;
+ const char *sep;
+ int node, offset = 0;
+
+ /* skip leading '/' */
+ while (*path == '/')
+ path++;
+
+ for (sep = path; *sep; path = sep + 1, offset = node) {
+ /* equivalent to strchrnul(), but it requires _GNU_SOURCE */
+ sep = strchr(path, '/');
+ if (!sep)
+ sep = path + strlen(path);
+
+ node = fdt_subnode_offset_namelen(blob, offset, path,
+ sep - path);
+ if (node == -FDT_ERR_NOTFOUND) {
+ node = fdt_add_subnode_namelen(blob, offset, path,
+ sep - path);
+ }
+ if (node < 0) {
+ report_error(path, sep - path, node);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Create a new node in the fdt.
+ *
+ * This will overwrite the node_name string. Any error is reported.
+ *
+ * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this.
+ *
+ * @param blob FDT blob to write into
+ * @param node_name Name of node to create
+ * @return new node offset if found, or -1 on failure
+ */
+static int create_node(void *blob, const char *node_name)
+{
+ int node = 0;
+ char *p;
+
+ p = strrchr(node_name, '/');
+ if (!p) {
+ report_error(node_name, -1, -FDT_ERR_BADPATH);
+ return -1;
+ }
+ *p = '\0';
+
+ if (p > node_name) {
+ node = fdt_path_offset(blob, node_name);
+ if (node < 0) {
+ report_error(node_name, -1, node);
+ return -1;
+ }
+ }
+
+ node = fdt_add_subnode(blob, node, p + 1);
+ if (node < 0) {
+ report_error(p + 1, -1, node);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int do_fdtput(struct display_info *disp, const char *filename,
+ char **arg, int arg_count)
+{
+ char *value;
+ char *blob;
+ int len, ret = 0;
+
+ blob = utilfdt_read(filename);
+ if (!blob)
+ return -1;
+
+ switch (disp->oper) {
+ case OPER_WRITE_PROP:
+ /*
+ * Convert the arguments into a single binary value, then
+ * store them into the property.
+ */
+ assert(arg_count >= 2);
+ if (disp->auto_path && create_paths(blob, *arg))
+ return -1;
+ if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
+ store_key_value(blob, *arg, arg[1], value, len))
+ ret = -1;
+ break;
+ case OPER_CREATE_NODE:
+ for (; ret >= 0 && arg_count--; arg++) {
+ if (disp->auto_path)
+ ret = create_paths(blob, *arg);
+ else
+ ret = create_node(blob, *arg);
+ }
+ break;
+ }
+ if (ret >= 0)
+ ret = utilfdt_write(filename, blob);
+
+ free(blob);
+ return ret;
+}
+
+static const char *usage_msg =
+ "fdtput - write a property value to a device tree\n"
+ "\n"
+ "The command line arguments are joined together into a single value.\n"
+ "\n"
+ "Usage:\n"
+ " fdtput <options> <dt file> <node> <property> [<value>...]\n"
+ " fdtput -c <options> <dt file> [<node>...]\n"
+ "Options:\n"
+ "\t-c\t\tCreate nodes if they don't already exist\n"
+ "\t-p\t\tAutomatically create nodes as needed for the node path\n"
+ "\t-t <type>\tType of data\n"
+ "\t-v\t\tVerbose: display each value decoded from command line\n"
+ "\t-h\t\tPrint this help\n\n"
+ USAGE_TYPE_MSG;
+
+static void usage(const char *msg)
+{
+ if (msg)
+ fprintf(stderr, "Error: %s\n\n", msg);
+
+ fprintf(stderr, "%s", usage_msg);
+ exit(2);
+}
+
+int main(int argc, char *argv[])
+{
+ struct display_info disp;
+ char *filename = NULL;
+
+ memset(&disp, '\0', sizeof(disp));
+ disp.size = -1;
+ disp.oper = OPER_WRITE_PROP;
+ for (;;) {
+ int c = getopt(argc, argv, "chpt:v");
+ if (c == -1)
+ break;
+
+ /*
+ * TODO: add options to:
+ * - delete property
+ * - delete node (optionally recursively)
+ * - rename node
+ * - pack fdt before writing
+ * - set amount of free space when writing
+ * - expand fdt if value doesn't fit
+ */
+ switch (c) {
+ case 'c':
+ disp.oper = OPER_CREATE_NODE;
+ break;
+ case 'h':
+ case '?':
+ usage(NULL);
+ case 'p':
+ disp.auto_path = 1;
+ break;
+ case 't':
+ if (utilfdt_decode_type(optarg, &disp.type,
+ &disp.size))
+ usage("Invalid type string");
+ break;
+
+ case 'v':
+ disp.verbose = 1;
+ break;
+ }
+ }
+
+ if (optind < argc)
+ filename = argv[optind++];
+ if (!filename)
+ usage("Missing filename");
+
+ argv += optind;
+ argc -= optind;
+
+ if (disp.oper == OPER_WRITE_PROP) {
+ if (argc < 1)
+ usage("Missing node");
+ if (argc < 2)
+ usage("Missing property");
+ }
+
+ if (do_fdtput(&disp, filename, argv, argc))
+ return 1;
+ return 0;
+}
diff --git a/scripts/dtc/flattree.c b/scripts/dtc/flattree.c
index 28d0b23..665dad7 100644
--- a/scripts/dtc/flattree.c
+++ b/scripts/dtc/flattree.c
@@ -263,6 +263,9 @@
struct node *child;
int seen_name_prop = 0;
+ if (tree->deleted)
+ return;
+
emit->beginnode(etarget, tree->labels);
if (vi->flags & FTF_FULLPATH)
diff --git a/scripts/dtc/libfdt/Makefile.libfdt b/scripts/dtc/libfdt/Makefile.libfdt
index 6c42acf..91126c0 100644
--- a/scripts/dtc/libfdt/Makefile.libfdt
+++ b/scripts/dtc/libfdt/Makefile.libfdt
@@ -3,6 +3,8 @@
# This is not a complete Makefile of itself. Instead, it is designed to
# be easily embeddable into other systems of Makefiles.
#
-LIBFDT_INCLUDES = fdt.h libfdt.h
-LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c
+LIBFDT_soname = libfdt.$(SHAREDLIB_EXT).1
+LIBFDT_INCLUDES = fdt.h libfdt.h libfdt_env.h
+LIBFDT_VERSION = version.lds
+LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c fdt_empty_tree.c
LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o)
diff --git a/scripts/dtc/libfdt/fdt.c b/scripts/dtc/libfdt/fdt.c
index 2acaec5..e56833a 100644
--- a/scripts/dtc/libfdt/fdt.c
+++ b/scripts/dtc/libfdt/fdt.c
@@ -74,7 +74,7 @@
return 0;
}
-const void *fdt_offset_ptr(const void *fdt, int offset, int len)
+const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
{
const char *p;
@@ -90,42 +90,53 @@
return p;
}
-uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset)
+uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
{
const uint32_t *tagp, *lenp;
uint32_t tag;
+ int offset = startoffset;
const char *p;
- if (offset % FDT_TAGSIZE)
- return -1;
-
+ *nextoffset = -FDT_ERR_TRUNCATED;
tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
- if (! tagp)
+ if (!tagp)
return FDT_END; /* premature end */
tag = fdt32_to_cpu(*tagp);
offset += FDT_TAGSIZE;
+ *nextoffset = -FDT_ERR_BADSTRUCTURE;
switch (tag) {
case FDT_BEGIN_NODE:
/* skip name */
do {
p = fdt_offset_ptr(fdt, offset++, 1);
} while (p && (*p != '\0'));
- if (! p)
- return FDT_END;
+ if (!p)
+ return FDT_END; /* premature end */
break;
+
case FDT_PROP:
lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
- if (! lenp)
- return FDT_END;
- /* skip name offset, length and value */
- offset += 2*FDT_TAGSIZE + fdt32_to_cpu(*lenp);
+ if (!lenp)
+ return FDT_END; /* premature end */
+ /* skip-name offset, length and value */
+ offset += sizeof(struct fdt_property) - FDT_TAGSIZE
+ + fdt32_to_cpu(*lenp);
break;
+
+ case FDT_END:
+ case FDT_END_NODE:
+ case FDT_NOP:
+ break;
+
+ default:
+ return FDT_END;
}
- if (nextoffset)
- *nextoffset = FDT_TAGALIGN(offset);
+ if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
+ return FDT_END; /* premature end */
+ *nextoffset = FDT_TAGALIGN(offset);
return tag;
}
@@ -138,6 +149,15 @@
return offset;
}
+int _fdt_check_prop_offset(const void *fdt, int offset)
+{
+ if ((offset < 0) || (offset % FDT_TAGSIZE)
+ || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
+ return -FDT_ERR_BADOFFSET;
+
+ return offset;
+}
+
int fdt_next_node(const void *fdt, int offset, int *depth)
{
int nextoffset = 0;
@@ -162,15 +182,16 @@
break;
case FDT_END_NODE:
- if (depth)
- (*depth)--;
+ if (depth && ((--(*depth)) < 0))
+ return nextoffset;
break;
case FDT_END:
- return -FDT_ERR_NOTFOUND;
-
- default:
- return -FDT_ERR_BADSTRUCTURE;
+ if ((nextoffset >= 0)
+ || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
+ return -FDT_ERR_NOTFOUND;
+ else
+ return nextoffset;
}
} while (tag != FDT_BEGIN_NODE);
diff --git a/scripts/dtc/libfdt/fdt_empty_tree.c b/scripts/dtc/libfdt/fdt_empty_tree.c
new file mode 100644
index 0000000..f72d13b
--- /dev/null
+++ b/scripts/dtc/libfdt/fdt_empty_tree.c
@@ -0,0 +1,84 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2012 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+int fdt_create_empty_tree(void *buf, int bufsize)
+{
+ int err;
+
+ err = fdt_create(buf, bufsize);
+ if (err)
+ return err;
+
+ err = fdt_finish_reservemap(buf);
+ if (err)
+ return err;
+
+ err = fdt_begin_node(buf, "");
+ if (err)
+ return err;
+
+ err = fdt_end_node(buf);
+ if (err)
+ return err;
+
+ err = fdt_finish(buf);
+ if (err)
+ return err;
+
+ return fdt_open_into(buf, buf, bufsize);
+}
+
diff --git a/scripts/dtc/libfdt/fdt_ro.c b/scripts/dtc/libfdt/fdt_ro.c
index 22e6929..02b6d68 100644
--- a/scripts/dtc/libfdt/fdt_ro.c
+++ b/scripts/dtc/libfdt/fdt_ro.c
@@ -80,6 +80,14 @@
return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
}
+static int _fdt_string_eq(const void *fdt, int stroffset,
+ const char *s, int len)
+{
+ const char *p = fdt_string(fdt, stroffset);
+
+ return (strlen(p) == len) && (memcmp(p, s, len) == 0);
+}
+
int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
{
FDT_CHECK_HEADER(fdt);
@@ -97,6 +105,30 @@
return i;
}
+static int _nextprop(const void *fdt, int offset)
+{
+ uint32_t tag;
+ int nextoffset;
+
+ do {
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_END:
+ if (nextoffset >= 0)
+ return -FDT_ERR_BADSTRUCTURE;
+ else
+ return nextoffset;
+
+ case FDT_PROP:
+ return offset;
+ }
+ offset = nextoffset;
+ } while (tag == FDT_NOP);
+
+ return -FDT_ERR_NOTFOUND;
+}
+
int fdt_subnode_offset_namelen(const void *fdt, int offset,
const char *name, int namelen)
{
@@ -104,20 +136,16 @@
FDT_CHECK_HEADER(fdt);
- for (depth = 0, offset = fdt_next_node(fdt, offset, &depth);
- (offset >= 0) && (depth > 0);
- offset = fdt_next_node(fdt, offset, &depth)) {
- if (depth < 0)
- return -FDT_ERR_NOTFOUND;
- else if ((depth == 1)
- && _fdt_nodename_eq(fdt, offset, name, namelen))
+ for (depth = 0;
+ (offset >= 0) && (depth >= 0);
+ offset = fdt_next_node(fdt, offset, &depth))
+ if ((depth == 1)
+ && _fdt_nodename_eq(fdt, offset, name, namelen))
return offset;
- }
- if (offset < 0)
- return offset; /* error */
- else
+ if (depth < 0)
return -FDT_ERR_NOTFOUND;
+ return offset; /* error */
}
int fdt_subnode_offset(const void *fdt, int parentoffset,
@@ -134,8 +162,20 @@
FDT_CHECK_HEADER(fdt);
- if (*path != '/')
- return -FDT_ERR_BADPATH;
+ /* see if we have an alias */
+ if (*path != '/') {
+ const char *q = strchr(path, '/');
+
+ if (!q)
+ q = end;
+
+ p = fdt_get_alias_namelen(fdt, p, q - p);
+ if (!p)
+ return -FDT_ERR_BADPATH;
+ offset = fdt_path_offset(fdt, p);
+
+ p = q;
+ }
while (*p) {
const char *q;
@@ -178,93 +218,142 @@
return NULL;
}
+int fdt_first_property_offset(const void *fdt, int nodeoffset)
+{
+ int offset;
+
+ if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
+ return offset;
+
+ return _nextprop(fdt, offset);
+}
+
+int fdt_next_property_offset(const void *fdt, int offset)
+{
+ if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0)
+ return offset;
+
+ return _nextprop(fdt, offset);
+}
+
+const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
+ int offset,
+ int *lenp)
+{
+ int err;
+ const struct fdt_property *prop;
+
+ if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
+ if (lenp)
+ *lenp = err;
+ return NULL;
+ }
+
+ prop = _fdt_offset_ptr(fdt, offset);
+
+ if (lenp)
+ *lenp = fdt32_to_cpu(prop->len);
+
+ return prop;
+}
+
+const struct fdt_property *fdt_get_property_namelen(const void *fdt,
+ int offset,
+ const char *name,
+ int namelen, int *lenp)
+{
+ for (offset = fdt_first_property_offset(fdt, offset);
+ (offset >= 0);
+ (offset = fdt_next_property_offset(fdt, offset))) {
+ const struct fdt_property *prop;
+
+ if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) {
+ offset = -FDT_ERR_INTERNAL;
+ break;
+ }
+ if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
+ name, namelen))
+ return prop;
+ }
+
+ if (lenp)
+ *lenp = offset;
+ return NULL;
+}
+
const struct fdt_property *fdt_get_property(const void *fdt,
int nodeoffset,
const char *name, int *lenp)
{
- uint32_t tag;
- const struct fdt_property *prop;
- int namestroff;
- int offset, nextoffset;
- int err;
-
- if (((err = fdt_check_header(fdt)) != 0)
- || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
- goto fail;
-
- nextoffset = err;
- do {
- offset = nextoffset;
-
- tag = fdt_next_tag(fdt, offset, &nextoffset);
- switch (tag) {
- case FDT_END:
- err = -FDT_ERR_TRUNCATED;
- goto fail;
-
- case FDT_BEGIN_NODE:
- case FDT_END_NODE:
- case FDT_NOP:
- break;
-
- case FDT_PROP:
- err = -FDT_ERR_BADSTRUCTURE;
- prop = fdt_offset_ptr(fdt, offset, sizeof(*prop));
- if (! prop)
- goto fail;
- namestroff = fdt32_to_cpu(prop->nameoff);
- if (strcmp(fdt_string(fdt, namestroff), name) == 0) {
- /* Found it! */
- int len = fdt32_to_cpu(prop->len);
- prop = fdt_offset_ptr(fdt, offset,
- sizeof(*prop)+len);
- if (! prop)
- goto fail;
-
- if (lenp)
- *lenp = len;
-
- return prop;
- }
- break;
-
- default:
- err = -FDT_ERR_BADSTRUCTURE;
- goto fail;
- }
- } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
-
- err = -FDT_ERR_NOTFOUND;
- fail:
- if (lenp)
- *lenp = err;
- return NULL;
+ return fdt_get_property_namelen(fdt, nodeoffset, name,
+ strlen(name), lenp);
}
-const void *fdt_getprop(const void *fdt, int nodeoffset,
- const char *name, int *lenp)
+const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
+ const char *name, int namelen, int *lenp)
{
const struct fdt_property *prop;
- prop = fdt_get_property(fdt, nodeoffset, name, lenp);
+ prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
if (! prop)
return NULL;
return prop->data;
}
+const void *fdt_getprop_by_offset(const void *fdt, int offset,
+ const char **namep, int *lenp)
+{
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property_by_offset(fdt, offset, lenp);
+ if (!prop)
+ return NULL;
+ if (namep)
+ *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
+ return prop->data;
+}
+
+const void *fdt_getprop(const void *fdt, int nodeoffset,
+ const char *name, int *lenp)
+{
+ return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
+}
+
uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
{
const uint32_t *php;
int len;
- php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
- if (!php || (len != sizeof(*php)))
- return 0;
+ /* FIXME: This is a bit sub-optimal, since we potentially scan
+ * over all the properties twice. */
+ php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
+ if (!php || (len != sizeof(*php))) {
+ php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
+ if (!php || (len != sizeof(*php)))
+ return 0;
+ }
return fdt32_to_cpu(*php);
}
+const char *fdt_get_alias_namelen(const void *fdt,
+ const char *name, int namelen)
+{
+ int aliasoffset;
+
+ aliasoffset = fdt_path_offset(fdt, "/aliases");
+ if (aliasoffset < 0)
+ return NULL;
+
+ return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
+}
+
+const char *fdt_get_alias(const void *fdt, const char *name)
+{
+ return fdt_get_alias_namelen(fdt, name, strlen(name));
+}
+
int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
{
int pdepth = 0, p = 0;
@@ -279,9 +368,6 @@
for (offset = 0, depth = 0;
(offset >= 0) && (offset <= nodeoffset);
offset = fdt_next_node(fdt, offset, &depth)) {
- if (pdepth < depth)
- continue; /* overflowed buffer */
-
while (pdepth > depth) {
do {
p--;
@@ -289,14 +375,16 @@
pdepth--;
}
- name = fdt_get_name(fdt, offset, &namelen);
- if (!name)
- return namelen;
- if ((p + namelen + 1) <= buflen) {
- memcpy(buf + p, name, namelen);
- p += namelen;
- buf[p++] = '/';
- pdepth++;
+ if (pdepth >= depth) {
+ name = fdt_get_name(fdt, offset, &namelen);
+ if (!name)
+ return namelen;
+ if ((p + namelen + 1) <= buflen) {
+ memcpy(buf + p, name, namelen);
+ p += namelen;
+ buf[p++] = '/';
+ pdepth++;
+ }
}
if (offset == nodeoffset) {
@@ -306,7 +394,7 @@
if (p > 1) /* special case so that root path is "/", not "" */
p--;
buf[p] = '\0';
- return p;
+ return 0;
}
}
@@ -404,14 +492,31 @@
int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
{
+ int offset;
+
if ((phandle == 0) || (phandle == -1))
return -FDT_ERR_BADPHANDLE;
- phandle = cpu_to_fdt32(phandle);
- return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle",
- &phandle, sizeof(phandle));
+
+ FDT_CHECK_HEADER(fdt);
+
+ /* FIXME: The algorithm here is pretty horrible: we
+ * potentially scan each property of a node in
+ * fdt_get_phandle(), then if that didn't find what
+ * we want, we scan over them again making our way to the next
+ * node. Still it's the easiest to implement approach;
+ * performance can come later. */
+ for (offset = fdt_next_node(fdt, -1, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ if (fdt_get_phandle(fdt, offset) == phandle)
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
}
-static int _stringlist_contains(const char *strlist, int listlen, const char *str)
+static int _fdt_stringlist_contains(const char *strlist, int listlen,
+ const char *str)
{
int len = strlen(str);
const char *p;
@@ -437,7 +542,7 @@
prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
if (!prop)
return len;
- if (_stringlist_contains(prop, len, compatible))
+ if (_fdt_stringlist_contains(prop, len, compatible))
return 0;
else
return 1;
diff --git a/scripts/dtc/libfdt/fdt_rw.c b/scripts/dtc/libfdt/fdt_rw.c
index 8e7ec4c..24437df 100644
--- a/scripts/dtc/libfdt/fdt_rw.c
+++ b/scripts/dtc/libfdt/fdt_rw.c
@@ -289,6 +289,33 @@
return 0;
}
+int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ struct fdt_property *prop;
+ int err, oldlen, newlen;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
+ if (prop) {
+ newlen = len + oldlen;
+ err = _fdt_splice_struct(fdt, prop->data,
+ FDT_TAGALIGN(oldlen),
+ FDT_TAGALIGN(newlen));
+ if (err)
+ return err;
+ prop->len = cpu_to_fdt32(newlen);
+ memcpy(prop->data + oldlen, val, len);
+ } else {
+ err = _fdt_add_property(fdt, nodeoffset, name, len, &prop);
+ if (err)
+ return err;
+ memcpy(prop->data, val, len);
+ }
+ return 0;
+}
+
int fdt_delprop(void *fdt, int nodeoffset, const char *name)
{
struct fdt_property *prop;
@@ -406,6 +433,8 @@
struct_size = 0;
while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
;
+ if (struct_size < 0)
+ return struct_size;
}
if (!_fdt_blocks_misordered(fdt, mem_rsv_size, struct_size)) {
diff --git a/scripts/dtc/libfdt/fdt_sw.c b/scripts/dtc/libfdt/fdt_sw.c
index 698329e..55ebebf 100644
--- a/scripts/dtc/libfdt/fdt_sw.c
+++ b/scripts/dtc/libfdt/fdt_sw.c
@@ -70,7 +70,7 @@
return err; \
}
-static void *_fdt_grab_space(void *fdt, int len)
+static void *_fdt_grab_space(void *fdt, size_t len)
{
int offset = fdt_size_dt_struct(fdt);
int spaceleft;
@@ -82,7 +82,7 @@
return NULL;
fdt_set_size_dt_struct(fdt, offset + len);
- return fdt_offset_ptr_w(fdt, offset, len);
+ return _fdt_offset_ptr_w(fdt, offset);
}
int fdt_create(void *buf, int bufsize)
@@ -237,18 +237,17 @@
while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
if (tag == FDT_PROP) {
struct fdt_property *prop =
- fdt_offset_ptr_w(fdt, offset, sizeof(*prop));
+ _fdt_offset_ptr_w(fdt, offset);
int nameoff;
- if (! prop)
- return -FDT_ERR_BADSTRUCTURE;
-
nameoff = fdt32_to_cpu(prop->nameoff);
nameoff += fdt_size_dt_strings(fdt);
prop->nameoff = cpu_to_fdt32(nameoff);
}
offset = nextoffset;
}
+ if (nextoffset < 0)
+ return nextoffset;
/* Finally, adjust the header */
fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
diff --git a/scripts/dtc/libfdt/fdt_wip.c b/scripts/dtc/libfdt/fdt_wip.c
index a4652c6..6025fa1 100644
--- a/scripts/dtc/libfdt/fdt_wip.c
+++ b/scripts/dtc/libfdt/fdt_wip.c
@@ -94,41 +94,14 @@
return 0;
}
-int _fdt_node_end_offset(void *fdt, int nodeoffset)
+int _fdt_node_end_offset(void *fdt, int offset)
{
- int level = 0;
- uint32_t tag;
- int offset, nextoffset;
+ int depth = 0;
- tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
- if (tag != FDT_BEGIN_NODE)
- return -FDT_ERR_BADOFFSET;
- do {
- offset = nextoffset;
- tag = fdt_next_tag(fdt, offset, &nextoffset);
+ while ((offset >= 0) && (depth >= 0))
+ offset = fdt_next_node(fdt, offset, &depth);
- switch (tag) {
- case FDT_END:
- return offset;
-
- case FDT_BEGIN_NODE:
- level++;
- break;
-
- case FDT_END_NODE:
- level--;
- break;
-
- case FDT_PROP:
- case FDT_NOP:
- break;
-
- default:
- return -FDT_ERR_BADSTRUCTURE;
- }
- } while (level >= 0);
-
- return nextoffset;
+ return offset;
}
int fdt_nop_node(void *fdt, int nodeoffset)
diff --git a/scripts/dtc/libfdt/libfdt.h b/scripts/dtc/libfdt/libfdt.h
index ff6246f..73f4975 100644
--- a/scripts/dtc/libfdt/libfdt.h
+++ b/scripts/dtc/libfdt/libfdt.h
@@ -61,7 +61,7 @@
#define FDT_ERR_NOTFOUND 1
/* FDT_ERR_NOTFOUND: The requested node or property does not exist */
#define FDT_ERR_EXISTS 2
- /* FDT_ERR_EXISTS: Attempted to create a node or property which
+ /* FDT_ERR_EXISTS: Attemped to create a node or property which
* already exists */
#define FDT_ERR_NOSPACE 3
/* FDT_ERR_NOSPACE: Operation needed to expand the device
@@ -122,7 +122,7 @@
/* Low-level functions (you probably don't need these) */
/**********************************************************************/
-const void *fdt_offset_ptr(const void *fdt, int offset, int checklen);
+const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen);
static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen)
{
return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen);
@@ -156,7 +156,7 @@
#define __fdt_set_hdr(name) \
static inline void fdt_set_##name(void *fdt, uint32_t val) \
{ \
- struct fdt_header *fdth = fdt; \
+ struct fdt_header *fdth = (struct fdt_header*)fdt; \
fdth->name = cpu_to_fdt32(val); \
}
__fdt_set_hdr(magic);
@@ -343,6 +343,91 @@
const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp);
/**
+ * fdt_first_property_offset - find the offset of a node's first property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of a node
+ *
+ * fdt_first_property_offset() finds the first property of the node at
+ * the given structure block offset.
+ *
+ * returns:
+ * structure block offset of the property (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the requested node has no properties
+ * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_first_property_offset(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_next_property_offset - step through a node's properties
+ * @fdt: pointer to the device tree blob
+ * @offset: structure block offset of a property
+ *
+ * fdt_next_property_offset() finds the property immediately after the
+ * one at the given structure block offset. This will be a property
+ * of the same node as the given property.
+ *
+ * returns:
+ * structure block offset of the next property (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the given property is the last in its node
+ * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_next_property_offset(const void *fdt, int offset);
+
+/**
+ * fdt_get_property_by_offset - retrieve the property at a given offset
+ * @fdt: pointer to the device tree blob
+ * @offset: offset of the property to retrieve
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_property_by_offset() retrieves a pointer to the
+ * fdt_property structure within the device tree blob at the given
+ * offset. If lenp is non-NULL, the length of the property value is
+ * also returned, in the integer pointed to by lenp.
+ *
+ * returns:
+ * pointer to the structure representing the property
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
+ int offset,
+ int *lenp);
+
+/**
+ * fdt_get_property_namelen - find a property based on substring
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @namelen: number of characters of name to consider
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * Identical to fdt_get_property_namelen(), but only examine the first
+ * namelen characters of name for matching the property name.
+ */
+const struct fdt_property *fdt_get_property_namelen(const void *fdt,
+ int nodeoffset,
+ const char *name,
+ int namelen, int *lenp);
+
+/**
* fdt_get_property - find a given property in a given node
* @fdt: pointer to the device tree blob
* @nodeoffset: offset of the node whose property to find
@@ -380,6 +465,54 @@
}
/**
+ * fdt_getprop_by_offset - retrieve the value of a property at a given offset
+ * @fdt: pointer to the device tree blob
+ * @ffset: offset of the property to read
+ * @namep: pointer to a string variable (will be overwritten) or NULL
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_getprop_by_offset() retrieves a pointer to the value of the
+ * property at structure block offset 'offset' (this will be a pointer
+ * to within the device blob itself, not a copy of the value). If
+ * lenp is non-NULL, the length of the property value is also
+ * returned, in the integer pointed to by lenp. If namep is non-NULL,
+ * the property's namne will also be returned in the char * pointed to
+ * by namep (this will be a pointer to within the device tree's string
+ * block, not a new copy of the name).
+ *
+ * returns:
+ * pointer to the property's value
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * if namep is non-NULL *namep contiains a pointer to the property
+ * name.
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+const void *fdt_getprop_by_offset(const void *fdt, int offset,
+ const char **namep, int *lenp);
+
+/**
+ * fdt_getprop_namelen - get property value based on substring
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @namelen: number of characters of name to consider
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * Identical to fdt_getprop(), but only examine the first namelen
+ * characters of name for matching the property name.
+ */
+const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
+ const char *name, int namelen, int *lenp);
+
+/**
* fdt_getprop - retrieve the value of a given property
* @fdt: pointer to the device tree blob
* @nodeoffset: offset of the node whose property to find
@@ -429,6 +562,32 @@
uint32_t fdt_get_phandle(const void *fdt, int nodeoffset);
/**
+ * fdt_get_alias_namelen - get alias based on substring
+ * @fdt: pointer to the device tree blob
+ * @name: name of the alias th look up
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_get_alias(), but only examine the first namelen
+ * characters of name for matching the alias name.
+ */
+const char *fdt_get_alias_namelen(const void *fdt,
+ const char *name, int namelen);
+
+/**
+ * fdt_get_alias - retreive the path referenced by a given alias
+ * @fdt: pointer to the device tree blob
+ * @name: name of the alias th look up
+ *
+ * fdt_get_alias() retrieves the value of a given alias. That is, the
+ * value of the property named 'name' in the node /aliases.
+ *
+ * returns:
+ * a pointer to the expansion of the alias named 'name', of it exists
+ * NULL, if the given alias or the /aliases node does not exist
+ */
+const char *fdt_get_alias(const void *fdt, const char *name);
+
+/**
* fdt_get_path - determine the full path of a node
* @fdt: pointer to the device tree blob
* @nodeoffset: offset of the node whose path to find
@@ -693,17 +852,17 @@
const void *val, int len);
/**
- * fdt_setprop_inplace_cell - change the value of a single-cell property
+ * fdt_setprop_inplace_u32 - change the value of a 32-bit integer property
* @fdt: pointer to the device tree blob
* @nodeoffset: offset of the node whose property to change
* @name: name of the property to change
- * @val: cell (32-bit integer) value to replace the property with
+ * @val: 32-bit integer value to replace the property with
*
- * fdt_setprop_inplace_cell() replaces the value of a given property
- * with the 32-bit integer cell value in val, converting val to
- * big-endian if necessary. This function cannot change the size of a
- * property, and so will only work if the property already exists and
- * has length 4.
+ * fdt_setprop_inplace_u32() replaces the value of a given property
+ * with the 32-bit integer value in val, converting val to big-endian
+ * if necessary. This function cannot change the size of a property,
+ * and so will only work if the property already exists and has length
+ * 4.
*
* This function will alter only the bytes in the blob which contain
* the given property value, and will not alter or move any other part
@@ -712,7 +871,7 @@
* returns:
* 0, on success
* -FDT_ERR_NOSPACE, if the property's length is not equal to 4
- * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_NOTFOUND, node does not have the named property
* -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
* -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION,
@@ -720,14 +879,60 @@
* -FDT_ERR_BADSTRUCTURE,
* -FDT_ERR_TRUNCATED, standard meanings
*/
-static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset,
- const char *name, uint32_t val)
+static inline int fdt_setprop_inplace_u32(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
{
val = cpu_to_fdt32(val);
return fdt_setprop_inplace(fdt, nodeoffset, name, &val, sizeof(val));
}
/**
+ * fdt_setprop_inplace_u64 - change the value of a 64-bit integer property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 64-bit integer value to replace the property with
+ *
+ * fdt_setprop_inplace_u64() replaces the value of a given property
+ * with the 64-bit integer value in val, converting val to big-endian
+ * if necessary. This function cannot change the size of a property,
+ * and so will only work if the property already exists and has length
+ * 8.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if the property's length is not equal to 8
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_inplace_u64(void *fdt, int nodeoffset,
+ const char *name, uint64_t val)
+{
+ val = cpu_to_fdt64(val);
+ return fdt_setprop_inplace(fdt, nodeoffset, name, &val, sizeof(val));
+}
+
+/**
+ * fdt_setprop_inplace_cell - change the value of a single-cell property
+ *
+ * This is an alternative name for fdt_setprop_inplace_u32()
+ */
+static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ return fdt_setprop_inplace_u32(fdt, nodeoffset, name, val);
+}
+
+/**
* fdt_nop_property - replace a property with nop tags
* @fdt: pointer to the device tree blob
* @nodeoffset: offset of the node whose property to nop
@@ -786,11 +991,20 @@
int fdt_finish_reservemap(void *fdt);
int fdt_begin_node(void *fdt, const char *name);
int fdt_property(void *fdt, const char *name, const void *val, int len);
-static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val)
+static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val)
{
val = cpu_to_fdt32(val);
return fdt_property(fdt, name, &val, sizeof(val));
}
+static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val)
+{
+ val = cpu_to_fdt64(val);
+ return fdt_property(fdt, name, &val, sizeof(val));
+}
+static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val)
+{
+ return fdt_property_u32(fdt, name, val);
+}
#define fdt_property_string(fdt, name, str) \
fdt_property(fdt, name, str, strlen(str)+1)
int fdt_end_node(void *fdt);
@@ -800,6 +1014,7 @@
/* Read-write functions */
/**********************************************************************/
+int fdt_create_empty_tree(void *buf, int bufsize);
int fdt_open_into(const void *fdt, void *buf, int bufsize);
int fdt_pack(void *fdt);
@@ -909,14 +1124,14 @@
const void *val, int len);
/**
- * fdt_setprop_cell - set a property to a single cell value
+ * fdt_setprop_u32 - set a property to a 32-bit integer
* @fdt: pointer to the device tree blob
* @nodeoffset: offset of the node whose property to change
* @name: name of the property to change
* @val: 32-bit integer value for the property (native endian)
*
- * fdt_setprop_cell() sets the value of the named property in the
- * given node to the given cell value (converting to big-endian if
+ * fdt_setprop_u32() sets the value of the named property in the given
+ * node to the given 32-bit integer value (converting to big-endian if
* necessary), or creates a new property with that value if it does
* not already exist.
*
@@ -936,14 +1151,60 @@
* -FDT_ERR_BADLAYOUT,
* -FDT_ERR_TRUNCATED, standard meanings
*/
-static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name,
- uint32_t val)
+static inline int fdt_setprop_u32(void *fdt, int nodeoffset, const char *name,
+ uint32_t val)
{
val = cpu_to_fdt32(val);
return fdt_setprop(fdt, nodeoffset, name, &val, sizeof(val));
}
/**
+ * fdt_setprop_u64 - set a property to a 64-bit integer
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 64-bit integer value for the property (native endian)
+ *
+ * fdt_setprop_u64() sets the value of the named property in the given
+ * node to the given 64-bit integer value (converting to big-endian if
+ * necessary), or creates a new property with that value if it does
+ * not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_u64(void *fdt, int nodeoffset, const char *name,
+ uint64_t val)
+{
+ val = cpu_to_fdt64(val);
+ return fdt_setprop(fdt, nodeoffset, name, &val, sizeof(val));
+}
+
+/**
+ * fdt_setprop_cell - set a property to a single cell value
+ *
+ * This is an alternative name for fdt_setprop_u32()
+ */
+static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name,
+ uint32_t val)
+{
+ return fdt_setprop_u32(fdt, nodeoffset, name, val);
+}
+
+/**
* fdt_setprop_string - set a property to a string value
* @fdt: pointer to the device tree blob
* @nodeoffset: offset of the node whose property to change
@@ -975,6 +1236,147 @@
fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
/**
+ * fdt_appendprop - append to or create a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to append to
+ * @val: pointer to data to append to the property value
+ * @len: length of the data to append to the property value
+ *
+ * fdt_appendprop() appends the value to the named property in the
+ * given node, creating the property if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len);
+
+/**
+ * fdt_appendprop_u32 - append a 32-bit integer value to a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 32-bit integer value to append to the property (native endian)
+ *
+ * fdt_appendprop_u32() appends the given 32-bit integer value
+ * (converting to big-endian if necessary) to the value of the named
+ * property in the given node, or creates a new property with that
+ * value if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_appendprop_u32(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ val = cpu_to_fdt32(val);
+ return fdt_appendprop(fdt, nodeoffset, name, &val, sizeof(val));
+}
+
+/**
+ * fdt_appendprop_u64 - append a 64-bit integer value to a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 64-bit integer value to append to the property (native endian)
+ *
+ * fdt_appendprop_u64() appends the given 64-bit integer value
+ * (converting to big-endian if necessary) to the value of the named
+ * property in the given node, or creates a new property with that
+ * value if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_appendprop_u64(void *fdt, int nodeoffset,
+ const char *name, uint64_t val)
+{
+ val = cpu_to_fdt64(val);
+ return fdt_appendprop(fdt, nodeoffset, name, &val, sizeof(val));
+}
+
+/**
+ * fdt_appendprop_cell - append a single cell value to a property
+ *
+ * This is an alternative name for fdt_appendprop_u32()
+ */
+static inline int fdt_appendprop_cell(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ return fdt_appendprop_u32(fdt, nodeoffset, name, val);
+}
+
+/**
+ * fdt_appendprop_string - append a string to a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @str: string value to append to the property
+ *
+ * fdt_appendprop_string() appends the given string to the value of
+ * the named property in the given node, or creates a new property
+ * with that value if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+#define fdt_appendprop_string(fdt, nodeoffset, name, str) \
+ fdt_appendprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
+
+/**
* fdt_delprop - delete a property
* @fdt: pointer to the device tree blob
* @nodeoffset: offset of the node whose property to nop
diff --git a/scripts/dtc/libfdt/libfdt_env.h b/scripts/dtc/libfdt/libfdt_env.h
index 449bf60..213d7fb 100644
--- a/scripts/dtc/libfdt/libfdt_env.h
+++ b/scripts/dtc/libfdt/libfdt_env.h
@@ -5,19 +5,25 @@
#include <stdint.h>
#include <string.h>
-#define _B(n) ((unsigned long long)((uint8_t *)&x)[n])
+#define EXTRACT_BYTE(n) ((unsigned long long)((uint8_t *)&x)[n])
+static inline uint16_t fdt16_to_cpu(uint16_t x)
+{
+ return (EXTRACT_BYTE(0) << 8) | EXTRACT_BYTE(1);
+}
+#define cpu_to_fdt16(x) fdt16_to_cpu(x)
+
static inline uint32_t fdt32_to_cpu(uint32_t x)
{
- return (_B(0) << 24) | (_B(1) << 16) | (_B(2) << 8) | _B(3);
+ return (EXTRACT_BYTE(0) << 24) | (EXTRACT_BYTE(1) << 16) | (EXTRACT_BYTE(2) << 8) | EXTRACT_BYTE(3);
}
#define cpu_to_fdt32(x) fdt32_to_cpu(x)
static inline uint64_t fdt64_to_cpu(uint64_t x)
{
- return (_B(0) << 56) | (_B(1) << 48) | (_B(2) << 40) | (_B(3) << 32)
- | (_B(4) << 24) | (_B(5) << 16) | (_B(6) << 8) | _B(7);
+ return (EXTRACT_BYTE(0) << 56) | (EXTRACT_BYTE(1) << 48) | (EXTRACT_BYTE(2) << 40) | (EXTRACT_BYTE(3) << 32)
+ | (EXTRACT_BYTE(4) << 24) | (EXTRACT_BYTE(5) << 16) | (EXTRACT_BYTE(6) << 8) | EXTRACT_BYTE(7);
}
#define cpu_to_fdt64(x) fdt64_to_cpu(x)
-#undef _B
+#undef EXTRACT_BYTE
#endif /* _LIBFDT_ENV_H */
diff --git a/scripts/dtc/libfdt/libfdt_internal.h b/scripts/dtc/libfdt/libfdt_internal.h
index 46eb93e..381133b 100644
--- a/scripts/dtc/libfdt/libfdt_internal.h
+++ b/scripts/dtc/libfdt/libfdt_internal.h
@@ -62,8 +62,8 @@
return err; \
}
-uint32_t _fdt_next_tag(const void *fdt, int startoffset, int *nextoffset);
int _fdt_check_node_offset(const void *fdt, int offset);
+int _fdt_check_prop_offset(const void *fdt, int offset);
const char *_fdt_find_string(const char *strtab, int tabsize, const char *s);
int _fdt_node_end_offset(void *fdt, int nodeoffset);
diff --git a/scripts/dtc/livetree.c b/scripts/dtc/livetree.c
index 26d0e1e..b61465f 100644
--- a/scripts/dtc/livetree.c
+++ b/scripts/dtc/livetree.c
@@ -29,16 +29,27 @@
struct label *new;
/* Make sure the label isn't already there */
- for_each_label(*labels, new)
- if (streq(new->label, label))
+ for_each_label_withdel(*labels, new)
+ if (streq(new->label, label)) {
+ new->deleted = 0;
return;
+ }
new = xmalloc(sizeof(*new));
+ memset(new, 0, sizeof(*new));
new->label = label;
new->next = *labels;
*labels = new;
}
+void delete_labels(struct label **labels)
+{
+ struct label *label;
+
+ for_each_label(*labels, label)
+ label->deleted = 1;
+}
+
struct property *build_property(char *name, struct data val)
{
struct property *new = xmalloc(sizeof(*new));
@@ -51,6 +62,18 @@
return new;
}
+struct property *build_property_delete(char *name)
+{
+ struct property *new = xmalloc(sizeof(*new));
+
+ memset(new, 0, sizeof(*new));
+
+ new->name = name;
+ new->deleted = 1;
+
+ return new;
+}
+
struct property *chain_property(struct property *first, struct property *list)
{
assert(first->next == NULL);
@@ -91,6 +114,17 @@
return new;
}
+struct node *build_node_delete(void)
+{
+ struct node *new = xmalloc(sizeof(*new));
+
+ memset(new, 0, sizeof(*new));
+
+ new->deleted = 1;
+
+ return new;
+}
+
struct node *name_node(struct node *node, char *name)
{
assert(node->name == NULL);
@@ -106,8 +140,10 @@
struct node *new_child, *old_child;
struct label *l;
+ old_node->deleted = 0;
+
/* Add new node labels to old node */
- for_each_label(new_node->labels, l)
+ for_each_label_withdel(new_node->labels, l)
add_label(&old_node->labels, l->label);
/* Move properties from the new node to the old node. If there
@@ -118,14 +154,21 @@
new_node->proplist = new_prop->next;
new_prop->next = NULL;
+ if (new_prop->deleted) {
+ delete_property_by_name(old_node, new_prop->name);
+ free(new_prop);
+ continue;
+ }
+
/* Look for a collision, set new value if there is */
- for_each_property(old_node, old_prop) {
+ for_each_property_withdel(old_node, old_prop) {
if (streq(old_prop->name, new_prop->name)) {
/* Add new labels to old property */
- for_each_label(new_prop->labels, l)
+ for_each_label_withdel(new_prop->labels, l)
add_label(&old_prop->labels, l->label);
old_prop->val = new_prop->val;
+ old_prop->deleted = 0;
free(new_prop);
new_prop = NULL;
break;
@@ -146,8 +189,14 @@
new_child->parent = NULL;
new_child->next_sibling = NULL;
+ if (new_child->deleted) {
+ delete_node_by_name(old_node, new_child->name);
+ free(new_child);
+ continue;
+ }
+
/* Search for a collision. Merge if there is */
- for_each_child(old_node, old_child) {
+ for_each_child_withdel(old_node, old_child) {
if (streq(old_child->name, new_child->name)) {
merge_nodes(old_child, new_child);
new_child = NULL;
@@ -155,7 +204,7 @@
}
}
- /* if no collision occurred, add child to the old node. */
+ /* if no collision occured, add child to the old node. */
if (new_child)
add_child(old_node, new_child);
}
@@ -188,6 +237,25 @@
*p = prop;
}
+void delete_property_by_name(struct node *node, char *name)
+{
+ struct property *prop = node->proplist;
+
+ while (prop) {
+ if (!strcmp(prop->name, name)) {
+ delete_property(prop);
+ return;
+ }
+ prop = prop->next;
+ }
+}
+
+void delete_property(struct property *prop)
+{
+ prop->deleted = 1;
+ delete_labels(&prop->labels);
+}
+
void add_child(struct node *parent, struct node *child)
{
struct node **p;
@@ -202,6 +270,32 @@
*p = child;
}
+void delete_node_by_name(struct node *parent, char *name)
+{
+ struct node *node = parent->children;
+
+ while (node) {
+ if (!strcmp(node->name, name)) {
+ delete_node(node);
+ return;
+ }
+ node = node->next_sibling;
+ }
+}
+
+void delete_node(struct node *node)
+{
+ struct property *prop;
+ struct node *child;
+
+ node->deleted = 1;
+ for_each_child(node, child)
+ delete_node(child);
+ for_each_property(node, prop)
+ delete_property(prop);
+ delete_labels(&node->labels);
+}
+
struct reserve_info *build_reserve_entry(uint64_t address, uint64_t size)
{
struct reserve_info *new = xmalloc(sizeof(*new));
@@ -353,8 +447,11 @@
const char *p;
struct node *child;
- if (!path || ! (*path))
+ if (!path || ! (*path)) {
+ if (tree->deleted)
+ return NULL;
return tree;
+ }
while (path[0] == '/')
path++;
@@ -397,8 +494,11 @@
assert((phandle != 0) && (phandle != -1));
- if (tree->phandle == phandle)
+ if (tree->phandle == phandle) {
+ if (tree->deleted)
+ return NULL;
return tree;
+ }
for_each_child(tree, child) {
node = get_node_by_phandle(child, phandle);
@@ -535,7 +635,7 @@
int n = 0, i = 0;
struct property *prop, **tbl;
- for_each_property(node, prop)
+ for_each_property_withdel(node, prop)
n++;
if (n == 0)
@@ -543,7 +643,7 @@
tbl = xmalloc(n * sizeof(*tbl));
- for_each_property(node, prop)
+ for_each_property_withdel(node, prop)
tbl[i++] = prop;
qsort(tbl, n, sizeof(*tbl), cmp_prop);
@@ -571,7 +671,7 @@
int n = 0, i = 0;
struct node *subnode, **tbl;
- for_each_child(node, subnode)
+ for_each_child_withdel(node, subnode)
n++;
if (n == 0)
@@ -579,7 +679,7 @@
tbl = xmalloc(n * sizeof(*tbl));
- for_each_child(node, subnode)
+ for_each_child_withdel(node, subnode)
tbl[i++] = subnode;
qsort(tbl, n, sizeof(*tbl), cmp_subnode);
@@ -598,7 +698,7 @@
sort_properties(node);
sort_subnodes(node);
- for_each_child(node, c)
+ for_each_child_withdel(node, c)
sort_node(c);
}
diff --git a/scripts/dtc/srcpos.c b/scripts/dtc/srcpos.c
index 36a38e9..246ab4b 100644
--- a/scripts/dtc/srcpos.c
+++ b/scripts/dtc/srcpos.c
@@ -24,6 +24,15 @@
#include "dtc.h"
#include "srcpos.h"
+/* A node in our list of directories to search for source/include files */
+struct search_path {
+ struct search_path *next; /* next node in list, NULL for end */
+ const char *dirname; /* name of directory to search */
+};
+
+/* This is the list of directories that we search for source files */
+static struct search_path *search_path_head, **search_path_tail;
+
static char *dirname(const char *path)
{
@@ -47,6 +56,64 @@
#define MAX_SRCFILE_DEPTH (100)
static int srcfile_depth; /* = 0 */
+
+/**
+ * Try to open a file in a given directory.
+ *
+ * If the filename is an absolute path, then dirname is ignored. If it is a
+ * relative path, then we look in that directory for the file.
+ *
+ * @param dirname Directory to look in, or NULL for none
+ * @param fname Filename to look for
+ * @param fp Set to NULL if file did not open
+ * @return allocated filename on success (caller must free), NULL on failure
+ */
+static char *try_open(const char *dirname, const char *fname, FILE **fp)
+{
+ char *fullname;
+
+ if (!dirname || fname[0] == '/')
+ fullname = xstrdup(fname);
+ else
+ fullname = join_path(dirname, fname);
+
+ *fp = fopen(fullname, "r");
+ if (!*fp) {
+ free(fullname);
+ fullname = NULL;
+ }
+
+ return fullname;
+}
+
+/**
+ * Open a file for read access
+ *
+ * If it is a relative filename, we search the full search path for it.
+ *
+ * @param fname Filename to open
+ * @param fp Returns pointer to opened FILE, or NULL on failure
+ * @return pointer to allocated filename, which caller must free
+ */
+static char *fopen_any_on_path(const char *fname, FILE **fp)
+{
+ const char *cur_dir = NULL;
+ struct search_path *node;
+ char *fullname;
+
+ /* Try current directory first */
+ assert(fp);
+ if (current_srcfile)
+ cur_dir = current_srcfile->dir;
+ fullname = try_open(cur_dir, fname, fp);
+
+ /* Failing that, try each search path in turn */
+ for (node = search_path_head; !*fp && node; node = node->next)
+ fullname = try_open(node->dirname, fname, fp);
+
+ return fullname;
+}
+
FILE *srcfile_relative_open(const char *fname, char **fullnamep)
{
FILE *f;
@@ -56,13 +123,7 @@
f = stdin;
fullname = xstrdup("<stdin>");
} else {
- if (!current_srcfile || !current_srcfile->dir
- || (fname[0] == '/'))
- fullname = xstrdup(fname);
- else
- fullname = join_path(current_srcfile->dir, fname);
-
- f = fopen(fullname, "r");
+ fullname = fopen_any_on_path(fname, &f);
if (!f)
die("Couldn't open \"%s\": %s\n", fname,
strerror(errno));
@@ -119,6 +180,23 @@
return current_srcfile ? 1 : 0;
}
+void srcfile_add_search_path(const char *dirname)
+{
+ struct search_path *node;
+
+ /* Create the node */
+ node = xmalloc(sizeof(*node));
+ node->next = NULL;
+ node->dirname = xstrdup(dirname);
+
+ /* Add to the end of our list */
+ if (search_path_tail)
+ *search_path_tail = node;
+ else
+ search_path_head = node;
+ search_path_tail = &node->next;
+}
+
/*
* The empty source position.
*/
@@ -250,3 +328,9 @@
va_end(va);
}
+
+void srcpos_set_line(char *f, int l)
+{
+ current_srcfile->name = f;
+ current_srcfile->lineno = l;
+}
diff --git a/scripts/dtc/srcpos.h b/scripts/dtc/srcpos.h
index ce980ca..93a2712 100644
--- a/scripts/dtc/srcpos.h
+++ b/scripts/dtc/srcpos.h
@@ -33,10 +33,39 @@
extern FILE *depfile; /* = NULL */
extern struct srcfile_state *current_srcfile; /* = NULL */
+/**
+ * Open a source file.
+ *
+ * If the source file is a relative pathname, then it is searched for in the
+ * current directory (the directory of the last source file read) and after
+ * that in the search path.
+ *
+ * We work through the search path in order from the first path specified to
+ * the last.
+ *
+ * If the file is not found, then this function does not return, but calls
+ * die().
+ *
+ * @param fname Filename to search
+ * @param fullnamep If non-NULL, it is set to the allocated filename of the
+ * file that was opened. The caller is then responsible
+ * for freeing the pointer.
+ * @return pointer to opened FILE
+ */
FILE *srcfile_relative_open(const char *fname, char **fullnamep);
+
void srcfile_push(const char *fname);
int srcfile_pop(void);
+/**
+ * Add a new directory to the search path for input files
+ *
+ * The new path is added at the end of the list.
+ *
+ * @param dirname Directory to add
+ */
+void srcfile_add_search_path(const char *dirname);
+
struct srcpos {
int first_line;
int first_column;
@@ -84,4 +113,6 @@
extern void srcpos_warn(struct srcpos *pos, char const *, ...)
__attribute__((format(printf, 2, 3)));
+extern void srcpos_set_line(char *f, int l);
+
#endif /* _SRCPOS_H_ */
diff --git a/scripts/dtc/treesource.c b/scripts/dtc/treesource.c
index c09aafa..33eeba5 100644
--- a/scripts/dtc/treesource.c
+++ b/scripts/dtc/treesource.c
@@ -23,6 +23,7 @@
extern FILE *yyin;
extern int yyparse(void);
+extern YYLTYPE yylloc;
struct boot_info *the_boot_info;
int treesource_error;
@@ -34,6 +35,7 @@
srcfile_push(fname);
yyin = current_srcfile->f;
+ yylloc.file = current_srcfile;
if (yyparse() != 0)
die("Unable to parse input tree\n");
diff --git a/scripts/dtc/util.c b/scripts/dtc/util.c
index d7ac27d..2422c34 100644
--- a/scripts/dtc/util.c
+++ b/scripts/dtc/util.c
@@ -1,6 +1,10 @@
/*
+ * Copyright 2011 The Chromium Authors, All Rights Reserved.
* Copyright 2008 Jon Loeliger, Freescale Semiconductor, Inc.
*
+ * util_is_printable_string contributed by
+ * Pantelis Antoniou <pantelis.antoniou AT gmail.com>
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
@@ -17,11 +21,18 @@
* USA
*/
+#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "libfdt.h"
#include "util.h"
char *xstrdup(const char *s)
@@ -57,3 +68,264 @@
memcpy(str+lenp, name, lenn+1);
return str;
}
+
+int util_is_printable_string(const void *data, int len)
+{
+ const char *s = data;
+ const char *ss;
+
+ /* zero length is not */
+ if (len == 0)
+ return 0;
+
+ /* must terminate with zero */
+ if (s[len - 1] != '\0')
+ return 0;
+
+ ss = s;
+ while (*s && isprint(*s))
+ s++;
+
+ /* not zero, or not done yet */
+ if (*s != '\0' || (s + 1 - ss) < len)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Parse a octal encoded character starting at index i in string s. The
+ * resulting character will be returned and the index i will be updated to
+ * point at the character directly after the end of the encoding, this may be
+ * the '\0' terminator of the string.
+ */
+static char get_oct_char(const char *s, int *i)
+{
+ char x[4];
+ char *endx;
+ long val;
+
+ x[3] = '\0';
+ strncpy(x, s + *i, 3);
+
+ val = strtol(x, &endx, 8);
+
+ assert(endx > x);
+
+ (*i) += endx - x;
+ return val;
+}
+
+/*
+ * Parse a hexadecimal encoded character starting at index i in string s. The
+ * resulting character will be returned and the index i will be updated to
+ * point at the character directly after the end of the encoding, this may be
+ * the '\0' terminator of the string.
+ */
+static char get_hex_char(const char *s, int *i)
+{
+ char x[3];
+ char *endx;
+ long val;
+
+ x[2] = '\0';
+ strncpy(x, s + *i, 2);
+
+ val = strtol(x, &endx, 16);
+ if (!(endx > x))
+ die("\\x used with no following hex digits\n");
+
+ (*i) += endx - x;
+ return val;
+}
+
+char get_escape_char(const char *s, int *i)
+{
+ char c = s[*i];
+ int j = *i + 1;
+ char val;
+
+ assert(c);
+ switch (c) {
+ case 'a':
+ val = '\a';
+ break;
+ case 'b':
+ val = '\b';
+ break;
+ case 't':
+ val = '\t';
+ break;
+ case 'n':
+ val = '\n';
+ break;
+ case 'v':
+ val = '\v';
+ break;
+ case 'f':
+ val = '\f';
+ break;
+ case 'r':
+ val = '\r';
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ j--; /* need to re-read the first digit as
+ * part of the octal value */
+ val = get_oct_char(s, &j);
+ break;
+ case 'x':
+ val = get_hex_char(s, &j);
+ break;
+ default:
+ val = c;
+ }
+
+ (*i) = j;
+ return val;
+}
+
+int utilfdt_read_err(const char *filename, char **buffp)
+{
+ int fd = 0; /* assume stdin */
+ char *buf = NULL;
+ off_t bufsize = 1024, offset = 0;
+ int ret = 0;
+
+ *buffp = NULL;
+ if (strcmp(filename, "-") != 0) {
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return errno;
+ }
+
+ /* Loop until we have read everything */
+ buf = malloc(bufsize);
+ do {
+ /* Expand the buffer to hold the next chunk */
+ if (offset == bufsize) {
+ bufsize *= 2;
+ buf = realloc(buf, bufsize);
+ if (!buf) {
+ ret = ENOMEM;
+ break;
+ }
+ }
+
+ ret = read(fd, &buf[offset], bufsize - offset);
+ if (ret < 0) {
+ ret = errno;
+ break;
+ }
+ offset += ret;
+ } while (ret != 0);
+
+ /* Clean up, including closing stdin; return errno on error */
+ close(fd);
+ if (ret)
+ free(buf);
+ else
+ *buffp = buf;
+ return ret;
+}
+
+char *utilfdt_read(const char *filename)
+{
+ char *buff;
+ int ret = utilfdt_read_err(filename, &buff);
+
+ if (ret) {
+ fprintf(stderr, "Couldn't open blob from '%s': %s\n", filename,
+ strerror(ret));
+ return NULL;
+ }
+ /* Successful read */
+ return buff;
+}
+
+int utilfdt_write_err(const char *filename, const void *blob)
+{
+ int fd = 1; /* assume stdout */
+ int totalsize;
+ int offset;
+ int ret = 0;
+ const char *ptr = blob;
+
+ if (strcmp(filename, "-") != 0) {
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0)
+ return errno;
+ }
+
+ totalsize = fdt_totalsize(blob);
+ offset = 0;
+
+ while (offset < totalsize) {
+ ret = write(fd, ptr + offset, totalsize - offset);
+ if (ret < 0) {
+ ret = -errno;
+ break;
+ }
+ offset += ret;
+ }
+ /* Close the file/stdin; return errno on error */
+ if (fd != 1)
+ close(fd);
+ return ret < 0 ? -ret : 0;
+}
+
+
+int utilfdt_write(const char *filename, const void *blob)
+{
+ int ret = utilfdt_write_err(filename, blob);
+
+ if (ret) {
+ fprintf(stderr, "Couldn't write blob to '%s': %s\n", filename,
+ strerror(ret));
+ }
+ return ret ? -1 : 0;
+}
+
+int utilfdt_decode_type(const char *fmt, int *type, int *size)
+{
+ int qualifier = 0;
+
+ if (!*fmt)
+ return -1;
+
+ /* get the conversion qualifier */
+ *size = -1;
+ if (strchr("hlLb", *fmt)) {
+ qualifier = *fmt++;
+ if (qualifier == *fmt) {
+ switch (*fmt++) {
+/* TODO: case 'l': qualifier = 'L'; break;*/
+ case 'h':
+ qualifier = 'b';
+ break;
+ }
+ }
+ }
+
+ /* we should now have a type */
+ if ((*fmt == '\0') || !strchr("iuxs", *fmt))
+ return -1;
+
+ /* convert qualifier (bhL) to byte size */
+ if (*fmt != 's')
+ *size = qualifier == 'b' ? 1 :
+ qualifier == 'h' ? 2 :
+ qualifier == 'l' ? 4 : -1;
+ *type = *fmt++;
+
+ /* that should be it! */
+ if (*fmt)
+ return -1;
+ return 0;
+}
diff --git a/scripts/dtc/util.h b/scripts/dtc/util.h
index 9cead84..c8eb45d 100644
--- a/scripts/dtc/util.h
+++ b/scripts/dtc/util.h
@@ -1,7 +1,10 @@
#ifndef _UTIL_H
#define _UTIL_H
+#include <stdarg.h>
+
/*
+ * Copyright 2011 The Chromium Authors, All Rights Reserved.
* Copyright 2008 Jon Loeliger, Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or
@@ -53,4 +56,98 @@
extern char *xstrdup(const char *s);
extern char *join_path(const char *path, const char *name);
+/**
+ * Check a string of a given length to see if it is all printable and
+ * has a valid terminator.
+ *
+ * @param data The string to check
+ * @param len The string length including terminator
+ * @return 1 if a valid printable string, 0 if not */
+int util_is_printable_string(const void *data, int len);
+
+/*
+ * Parse an escaped character starting at index i in string s. The resulting
+ * character will be returned and the index i will be updated to point at the
+ * character directly after the end of the encoding, this may be the '\0'
+ * terminator of the string.
+ */
+char get_escape_char(const char *s, int *i);
+
+/**
+ * Read a device tree file into a buffer. This will report any errors on
+ * stderr.
+ *
+ * @param filename The filename to read, or - for stdin
+ * @return Pointer to allocated buffer containing fdt, or NULL on error
+ */
+char *utilfdt_read(const char *filename);
+
+/**
+ * Read a device tree file into a buffer. Does not report errors, but only
+ * returns them. The value returned can be passed to strerror() to obtain
+ * an error message for the user.
+ *
+ * @param filename The filename to read, or - for stdin
+ * @param buffp Returns pointer to buffer containing fdt
+ * @return 0 if ok, else an errno value representing the error
+ */
+int utilfdt_read_err(const char *filename, char **buffp);
+
+
+/**
+ * Write a device tree buffer to a file. This will report any errors on
+ * stderr.
+ *
+ * @param filename The filename to write, or - for stdout
+ * @param blob Poiner to buffer containing fdt
+ * @return 0 if ok, -1 on error
+ */
+int utilfdt_write(const char *filename, const void *blob);
+
+/**
+ * Write a device tree buffer to a file. Does not report errors, but only
+ * returns them. The value returned can be passed to strerror() to obtain
+ * an error message for the user.
+ *
+ * @param filename The filename to write, or - for stdout
+ * @param blob Poiner to buffer containing fdt
+ * @return 0 if ok, else an errno value representing the error
+ */
+int utilfdt_write_err(const char *filename, const void *blob);
+
+/**
+ * Decode a data type string. The purpose of this string
+ *
+ * The string consists of an optional character followed by the type:
+ * Modifier characters:
+ * hh or b 1 byte
+ * h 2 byte
+ * l 4 byte, default
+ *
+ * Type character:
+ * s string
+ * i signed integer
+ * u unsigned integer
+ * x hex
+ *
+ * TODO: Implement ll modifier (8 bytes)
+ * TODO: Implement o type (octal)
+ *
+ * @param fmt Format string to process
+ * @param type Returns type found(s/d/u/x), or 0 if none
+ * @param size Returns size found(1,2,4,8) or 4 if none
+ * @return 0 if ok, -1 on error (no type given, or other invalid format)
+ */
+int utilfdt_decode_type(const char *fmt, int *type, int *size);
+
+/*
+ * This is a usage message fragment for the -t option. It is the format
+ * supported by utilfdt_decode_type.
+ */
+
+#define USAGE_TYPE_MSG \
+ "<type>\ts=string, i=int, u=unsigned, x=hex\n" \
+ "\tOptional modifier prefix:\n" \
+ "\t\thh or b=byte, h=2 byte, l=4 byte (default)\n";
+
#endif /* _UTIL_H */
diff --git a/scripts/gcc-wrapper.py b/scripts/gcc-wrapper.py
index 2010c57..548d655 100755
--- a/scripts/gcc-wrapper.py
+++ b/scripts/gcc-wrapper.py
@@ -40,10 +40,7 @@
# force LANG to be set to en_US.UTF-8 to get consistent warnings.
allowed_warnings = set([
- "alignment.c:327",
- "mmu.c:602",
"return_address.c:62",
- "extents.c:2091",
])
# Capture the name of the object file, can find it.
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index d98e160..2e8d6f4 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -1199,6 +1199,10 @@
snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl);
pcm->streams[cidx].chmap_kctl = NULL;
}
+ if (pcm->streams[cidx].vol_kctl) {
+ snd_ctl_remove(pcm->card, pcm->streams[cidx].vol_kctl);
+ pcm->streams[cidx].vol_kctl = NULL;
+ }
}
unlock:
mutex_unlock(®ister_mutex);
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index e0ab899..06d617b 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -31,6 +31,8 @@
#include <sound/pcm_params.h>
#include <sound/timer.h>
+#define STRING_LENGTH_OF_INT 12
+
/*
* fill ring buffer with silence
* runtime->silence_start: starting pointer to silence area
@@ -2444,6 +2446,23 @@
kfree(info);
}
+static int pcm_volume_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0x2000;
+ return 0;
+}
+
+static void pcm_volume_ctl_private_free(struct snd_kcontrol *kcontrol)
+{
+ struct snd_pcm_volume *info = snd_kcontrol_chip(kcontrol);
+ info->pcm->streams[info->stream].vol_kctl = NULL;
+ kfree(info);
+}
+
/**
* snd_pcm_add_chmap_ctls - create channel-mapping control elements
* @pcm: the assigned PCM instance
@@ -2503,3 +2522,74 @@
return 0;
}
EXPORT_SYMBOL_GPL(snd_pcm_add_chmap_ctls);
+
+/**
+ * snd_pcm_add_volume_ctls - create volume control elements
+ * @pcm: the assigned PCM instance
+ * @stream: stream direction
+ * @max_length: the max length of the volume parameter of stream
+ * @private_value: the value passed to each kcontrol's private_value field
+ * @info_ret: store struct snd_pcm_volume instance if non-NULL
+ *
+ * Create volume control elements assigned to the given PCM stream(s).
+ * Returns zero if succeed, or a negative error value.
+ */
+int snd_pcm_add_volume_ctls(struct snd_pcm *pcm, int stream,
+ const struct snd_pcm_volume_elem *volume,
+ int max_length,
+ unsigned long private_value,
+ struct snd_pcm_volume **info_ret)
+{
+ struct snd_pcm_volume *info;
+ struct snd_kcontrol_new knew = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = pcm_volume_ctl_info,
+ };
+ int err;
+ int size;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->pcm = pcm;
+ info->stream = stream;
+ info->volume = volume;
+ info->max_length = max_length;
+ size = sizeof("Playback ") + sizeof(" Volume") +
+ STRING_LENGTH_OF_INT*sizeof(char) + 1;
+ knew.name = kzalloc(size, GFP_KERNEL);
+ if (!knew.name) {
+ kfree(info);
+ return -ENOMEM;
+ }
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snprintf(knew.name, size, "%s %d %s",
+ "Playback", pcm->device, "Volume");
+ else
+ snprintf(knew.name, size, "%s %d %s",
+ "Capture", pcm->device, "Volume");
+ knew.device = pcm->device;
+ knew.count = pcm->streams[stream].substream_count;
+ knew.private_value = private_value;
+ info->kctl = snd_ctl_new1(&knew, info);
+ if (!info->kctl) {
+ kfree(info);
+ kfree(knew.name);
+ return -ENOMEM;
+ }
+ info->kctl->private_free = pcm_volume_ctl_private_free;
+ err = snd_ctl_add(pcm->card, info->kctl);
+ if (err < 0) {
+ kfree(info);
+ kfree(knew.name);
+ return -ENOMEM;
+ }
+ pcm->streams[stream].vol_kctl = info->kctl;
+ if (info_ret)
+ *info_ret = info;
+ kfree(knew.name);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_add_volume_ctls);
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 155585a..8d6dee3 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -456,4 +456,8 @@
tristate
config SND_SOC_MSM_HDMI_CODEC_RX
- tristate
+ bool "HDMI Audio Playback"
+ depends on FB_MSM_MDSS_HDMI_PANEL && SND_SOC_MSM8974
+ help
+ HDMI audio drivers should be built only if the platform
+ supports hdmi panel.
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index f0cd026..c0469e3 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -104,7 +104,7 @@
snd-soc-timpani-objs := timpani.o
snd-soc-msm-stub-objs := msm_stub.o
-snd-soc-msm-hdmi-rx-objs := msm_hdmi_codec_rx.o
+obj-$(CONFIG_SND_SOC_MSM_HDMI_CODEC_RX) := msm_hdmi_codec_rx.o
# Amp
snd-soc-max9877-objs := max9877.o
@@ -214,7 +214,6 @@
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
obj-$(CONFIG_SND_SOC_MSM_STUB) += snd-soc-msm-stub.o
-obj-$(CONFIG_SND_SOC_MSM_HDMI_CODEC_RX) += snd-soc-msm-hdmi-rx.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
diff --git a/sound/soc/codecs/msm8x10-wcd.c b/sound/soc/codecs/msm8x10-wcd.c
index c8647fb1..0ba1ec5 100644
--- a/sound/soc/codecs/msm8x10-wcd.c
+++ b/sound/soc/codecs/msm8x10-wcd.c
@@ -34,6 +34,7 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
+#include <mach/qdsp6v2/apr.h>
#include "msm8x10-wcd.h"
#include "wcd9xxx-resmgr.h"
#include "msm8x10_wcd_registers.h"
@@ -52,7 +53,7 @@
#define MAX_MSM8X10_WCD_DEVICE 4
#define CODEC_DT_MAX_PROP_SIZE 40
-#define MSM8X10_WCD_I2C_GSBI_SLAVE_ID "1-000d"
+#define MSM8X10_WCD_I2C_GSBI_SLAVE_ID "5-000d"
enum {
MSM8X10_WCD_I2C_TOP_LEVEL = 0,
@@ -82,6 +83,12 @@
static struct snd_soc_dai_driver msm8x10_wcd_i2s_dai[];
static const DECLARE_TLV_DB_SCALE(aux_pga_gain, 0, 2, 0);
+#define MSM8X10_WCD_ACQUIRE_LOCK(x) do { \
+ mutex_lock_nested(&x, SINGLE_DEPTH_NESTING); \
+} while (0)
+#define MSM8X10_WCD_RELEASE_LOCK(x) do { mutex_unlock(&x); } while (0)
+
+
/* Codec supports 2 IIR filters */
enum {
IIR1 = 0,
@@ -99,6 +106,12 @@
BAND_MAX,
};
+enum msm8x10_wcd_bandgap_type {
+ MSM8X10_WCD_BANDGAP_OFF = 0,
+ MSM8X10_WCD_BANDGAP_AUDIO_MODE,
+ MSM8X10_WCD_BANDGAP_MBHC_MODE,
+};
+
struct hpf_work {
struct msm8x10_wcd_priv *msm8x10_wcd;
u32 decimator;
@@ -113,11 +126,14 @@
u32 adc_count;
u32 rx_bias_count;
s32 dmic_1_2_clk_cnt;
-
+ enum msm8x10_wcd_bandgap_type bandgap_type;
+ bool mclk_enabled;
+ bool clock_active;
+ bool config_mode_active;
+ bool mbhc_polling_active;
+ struct mutex codec_resource_lock;
/* resmgr module */
struct wcd9xxx_resmgr resmgr;
- /* mbhc module */
- struct wcd9xxx_mbhc mbhc;
};
static unsigned short rx_digital_gain_reg[] = {
@@ -139,8 +155,8 @@
};
static char *msm8x10_wcd_supplies[] = {
- "cdc-vdd-mic-bias", "cdc-vdda-h", "cdc-vdd-1p2", "cdc-vdd-px",
- "cdc-vdda-cp",
+ "cdc-vdda-cp", "cdc-vdda-h", "cdc-vdd-px", "cdc-vdd-1p2v",
+ "cdc-vdd-mic-bias",
};
static int msm8x10_wcd_dt_parse_vreg_info(struct device *dev,
@@ -158,7 +174,6 @@
{
int rtn = 0;
int value = ((reg & 0x0f00) >> 8) & 0x000f;
- pr_debug("%s: reg(0x%x) value(%d)\n", __func__, reg, value);
switch (value) {
case 0:
case 1:
@@ -171,7 +186,7 @@
return rtn;
}
-static int msm8x10_wcd_abh_write_device(u16 reg, unsigned int *value, u32 bytes)
+static int msm8x10_wcd_abh_write_device(u16 reg, u8 *value, u32 bytes)
{
u32 temp = ((u32)(*value)) & 0x000000FF;
u32 offset = (((u32)(reg)) ^ 0x00000400) & 0x00000FFF;
@@ -179,11 +194,13 @@
return 0;
}
-static int msm8x10_wcd_abh_read_device(u16 reg, u32 bytes, unsigned int *value)
+static int msm8x10_wcd_abh_read_device(u16 reg, u32 bytes, u8 *value)
{
+ u32 temp;
u32 offset = (((u32)(reg)) ^ 0x00000400) & 0x00000FFF;
- *value = ioread32(ioremap(MSM8X10_DINO_CODEC_BASE_ADDR +
+ temp = ioread32(ioremap(MSM8X10_DINO_CODEC_BASE_ADDR +
offset, 4));
+ *value = (u8)temp;
return 0;
}
@@ -274,7 +291,7 @@
}
}
}
- pr_debug("%s: Reg 0x%x = 0x%x\n", __func__, reg, *dest);
+ pr_debug("%s: reg 0x%x = 0x%x\n", __func__, reg, *dest);
return 0;
}
@@ -292,20 +309,22 @@
u16 reg, unsigned int *val)
{
int ret = -EINVAL;
+ u8 temp;
/* check if use I2C interface for Helicon or AHB for Dino */
mutex_lock(&msm8x10_wcd->io_lock);
if (MSM8X10_WCD_IS_HELICON_REG(reg))
- ret = msm8x10_wcd_i2c_read(reg, 1, val);
+ ret = msm8x10_wcd_i2c_read(reg, 1, &temp);
else if (MSM8X10_WCD_IS_DINO_REG(reg))
- ret = msm8x10_wcd_abh_read_device(reg, 1, val);
+ ret = msm8x10_wcd_abh_read_device(reg, 1, &temp);
mutex_unlock(&msm8x10_wcd->io_lock);
+ *val = temp;
return ret;
}
static int msm8x10_wcd_reg_write(struct msm8x10_wcd *msm8x10_wcd, u16 reg,
- unsigned int val)
+ u8 val)
{
int ret = -EINVAL;
@@ -393,7 +412,7 @@
reg, ret);
}
- return msm8x10_wcd_reg_write(codec->control_data, reg, value);
+ return msm8x10_wcd_reg_write(codec->control_data, reg, (u8)value);
}
static unsigned int msm8x10_wcd_read(struct snd_soc_codec *codec,
@@ -438,10 +457,14 @@
regnode = of_parse_phandle(dev->of_node, prop_name, 0);
if (!regnode) {
- dev_err(dev, "Looking up %s property in node %s failed",
+ dev_err(dev, "Looking up %s property in node %s failed\n",
prop_name, dev->of_node->full_name);
return -ENODEV;
}
+
+ dev_dbg(dev, "Looking up %s property in node %s\n",
+ prop_name, dev->of_node->full_name);
+
vreg->name = vreg_name;
snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE,
@@ -481,17 +504,7 @@
u32 prop_val;
snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE,
- "qcom,cdc-micbias-ldoh-v");
- ret = of_property_read_u32(dev->of_node, prop_name, &prop_val);
- if (ret) {
- dev_err(dev, "Looking up %s property in node %s failed",
- prop_name, dev->of_node->full_name);
- return -ENODEV;
- }
- micbias->ldoh_v = (u8)prop_val;
-
- snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE,
- "qcom,cdc-micbias-cfilt1-mv");
+ "qcom,cdc-micbias-cfilt-mv");
ret = of_property_read_u32(dev->of_node, prop_name,
&micbias->cfilt1_mv);
if (ret) {
@@ -501,7 +514,7 @@
}
snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE,
- "qcom,cdc-micbias1-cfilt-sel");
+ "qcom,cdc-micbias-cfilt-sel");
ret = of_property_read_u32(dev->of_node, prop_name, &prop_val);
if (ret) {
dev_err(dev, "Looking up %s property in node %s failed",
@@ -527,7 +540,7 @@
struct device *dev)
{
struct msm8x10_wcd_pdata *pdata;
- int ret, i;
+ int ret = 0, i;
char **codec_supplies;
u32 num_of_supplies = 0;
@@ -559,23 +572,14 @@
if (ret)
goto err;
}
-
ret = msm8x10_wcd_dt_parse_micbias_info(dev, &pdata->micbias);
if (ret)
goto err;
-
- pdata->reset_gpio = of_get_named_gpio(dev->of_node,
- "qcom,cdc-reset-gpio", 0);
- if (pdata->reset_gpio < 0) {
- dev_err(dev, "Looking up %s property in node %s failed %d\n",
- "qcom, cdc-reset-gpio", dev->of_node->full_name,
- pdata->reset_gpio);
- goto err;
- }
- dev_dbg(dev, "%s: reset gpio %d", __func__, pdata->reset_gpio);
return pdata;
err:
devm_kfree(dev, pdata);
+ dev_err(dev, "%s: Failed to populate DT data ret = %d\n",
+ __func__, ret);
return NULL;
}
@@ -611,6 +615,9 @@
MSM8X10_WCD_A_CDC_CLK_OTHR_CTL, 0x01,
0x00);
snd_soc_update_bits(codec,
+ MSM8X10_WCD_A_CDC_CLK_OTHR_RESET_B1_CTL,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
MSM8X10_WCD_A_CP_STATIC, 0x08, 0x00);
break;
}
@@ -846,7 +853,7 @@
SOC_ENUM_EXT("EAR PA Gain", msm8x10_wcd_ear_pa_gain_enum[0],
msm8x10_wcd_pa_gain_get, msm8x10_wcd_pa_gain_put),
- SOC_SINGLE_TLV("LINEOUT1 Volume", MSM8X10_WCD_A_RX_LINE_1_GAIN,
+ SOC_SINGLE_TLV("LINEOUT Volume", MSM8X10_WCD_A_RX_LINE_1_GAIN,
0, 12, 1, line_gain),
SOC_SINGLE_TLV("HPHL Volume", MSM8X10_WCD_A_RX_HPH_L_GAIN,
@@ -1102,9 +1109,6 @@
switch (decimator) {
case 1:
case 2:
- if (dec_mux == 1)
- adc_dmic_sel = 0x1;
- else
adc_dmic_sel = 0x0;
break;
default:
@@ -1148,68 +1152,6 @@
SOC_DAPM_SINGLE("Switch", MSM8X10_WCD_A_RX_HPH_L_DAC_CTL, 6, 1, 0)
};
-/* virtual port entries */
-static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
-
- ucontrol->value.integer.value[0] = widget->value;
- return 0;
-}
-
-static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return 0;
-}
-
-static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
-
- ucontrol->value.enumerated.item[0] = widget->value;
- return 0;
-}
-
-static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return 0;
-}
-
-
-static const char *const slim_rx_mux_text[] = {
- "ZERO", "AIF1_PB"
-};
-
-static const struct soc_enum slim_rx_mux_enum =
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
-
-static const struct snd_kcontrol_new slim_rx_mux[MSM8X10_WCD_RX_MAX] = {
- SOC_DAPM_ENUM_EXT("I2S RX1 Mux", slim_rx_mux_enum,
- slim_rx_mux_get, slim_rx_mux_put),
- SOC_DAPM_ENUM_EXT("I2S RX2 Mux", slim_rx_mux_enum,
- slim_rx_mux_get, slim_rx_mux_put),
- SOC_DAPM_ENUM_EXT("I2S RX3 Mux", slim_rx_mux_enum,
- slim_rx_mux_get, slim_rx_mux_put),
-};
-
-static const struct snd_kcontrol_new aif_cap_mixer[] = {
- SOC_SINGLE_EXT("I2S TX1", SND_SOC_NOPM, MSM8X10_WCD_TX1, 1, 0,
- slim_tx_mixer_get, slim_tx_mixer_put),
- SOC_SINGLE_EXT("I2S TX2", SND_SOC_NOPM, MSM8X10_WCD_TX2, 1, 0,
- slim_tx_mixer_get, slim_tx_mixer_put),
- SOC_SINGLE_EXT("I2S TX3", SND_SOC_NOPM, MSM8X10_WCD_TX3, 1, 0,
- slim_tx_mixer_get, slim_tx_mixer_put),
- SOC_SINGLE_EXT("I2S TX4", SND_SOC_NOPM, MSM8X10_WCD_TX4, 1, 0,
- slim_tx_mixer_get, slim_tx_mixer_put),
-};
-
-
static void msm8x10_wcd_codec_enable_adc_block(struct snd_soc_codec *codec,
int enable)
{
@@ -1243,7 +1185,7 @@
if (w->reg == MSM8X10_WCD_A_TX_1_EN)
init_bit_shift = 7;
- else if (adc_reg == MSM8X10_WCD_A_TX_2_EN)
+ else if (w->reg == MSM8X10_WCD_A_TX_2_EN)
init_bit_shift = 6;
else {
dev_err(codec->dev, "%s: Error, invalid adc register\n",
@@ -1365,54 +1307,36 @@
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
- struct msm8x10_wcd_priv *msm8x10_wcd = snd_soc_codec_get_drvdata(codec);
u16 micb_int_reg;
- u8 cfilt_sel_val = 0;
char *internal1_text = "Internal1";
char *internal2_text = "Internal2";
char *internal3_text = "Internal3";
- enum wcd9xxx_notify_event e_post_off, e_pre_on, e_post_on;
dev_dbg(codec->dev, "%s %d\n", __func__, event);
switch (w->reg) {
case MSM8X10_WCD_A_MICB_1_CTL:
micb_int_reg = MSM8X10_WCD_A_MICB_1_INT_RBIAS;
- cfilt_sel_val =
- msm8x10_wcd->resmgr.pdata->micbias.bias1_cfilt_sel;
- e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_1_ON;
- e_post_on = WCD9XXX_EVENT_POST_MICBIAS_1_ON;
- e_post_off = WCD9XXX_EVENT_POST_MICBIAS_1_OFF;
break;
default:
dev_err(codec->dev,
- "%s: Error, invalid micbias register\n", __func__);
+ "%s: Error, invalid micbias register 0x%x\n",
+ __func__, w->reg);
return -EINVAL;
}
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- /* Let MBHC module know so micbias switch to be off */
- wcd9xxx_resmgr_notifier_call(&msm8x10_wcd->resmgr, e_pre_on);
-
- /* Get cfilt */
- wcd9xxx_resmgr_cfilt_get(&msm8x10_wcd->resmgr, cfilt_sel_val);
-
if (strnstr(w->name, internal1_text, 30))
- snd_soc_update_bits(codec, micb_int_reg, 0xE0, 0xE0);
+ snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x80);
else if (strnstr(w->name, internal2_text, 30))
- snd_soc_update_bits(codec, micb_int_reg, 0x1C, 0x1C);
+ snd_soc_update_bits(codec, micb_int_reg, 0x10, 0x10);
else if (strnstr(w->name, internal3_text, 30))
- snd_soc_update_bits(codec, micb_int_reg, 0x3, 0x3);
+ snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x2);
break;
case SND_SOC_DAPM_POST_PMU:
usleep_range(20000, 20100);
- /* Let MBHC module know so micbias is on */
- wcd9xxx_resmgr_notifier_call(&msm8x10_wcd->resmgr, e_post_on);
break;
case SND_SOC_DAPM_POST_PMD:
- /* Let MBHC module know so micbias switch to be off */
- wcd9xxx_resmgr_notifier_call(&msm8x10_wcd->resmgr, e_post_off);
-
if (strnstr(w->name, internal1_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x00);
else if (strnstr(w->name, internal2_text, 30))
@@ -1420,14 +1344,36 @@
else if (strnstr(w->name, internal3_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0);
- /* Put cfilt */
- wcd9xxx_resmgr_cfilt_put(&msm8x10_wcd->resmgr, cfilt_sel_val);
break;
}
-
return 0;
}
+static void tx_hpf_corner_freq_callback(struct work_struct *work)
+{
+ struct delayed_work *hpf_delayed_work;
+ struct hpf_work *hpf_work;
+ struct msm8x10_wcd_priv *msm8x10_wcd;
+ struct snd_soc_codec *codec;
+ u16 tx_mux_ctl_reg;
+ u8 hpf_cut_of_freq;
+
+ hpf_delayed_work = to_delayed_work(work);
+ hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork);
+ msm8x10_wcd = hpf_work->msm8x10_wcd;
+ codec = hpf_work->msm8x10_wcd->codec;
+ hpf_cut_of_freq = hpf_work->tx_hpf_cut_of_freq;
+
+ tx_mux_ctl_reg = MSM8X10_WCD_A_CDC_TX1_MUX_CTL +
+ (hpf_work->decimator - 1) * 32;
+
+ dev_info(codec->dev, "%s(): decimator %u hpf_cut_of_freq 0x%x\n",
+ __func__, hpf_work->decimator, (unsigned int)hpf_cut_of_freq);
+
+ snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, hpf_cut_of_freq << 4);
+}
+
+
#define TX_MUX_CTL_CUT_OFF_FREQ_MASK 0x30
#define CF_MIN_3DB_4HZ 0x0
#define CF_MIN_3DB_75HZ 0x1
@@ -1582,6 +1528,7 @@
{
struct snd_soc_codec *codec = w->codec;
struct msm8x10_wcd_priv *msm8x10_wcd = snd_soc_codec_get_drvdata(codec);
+ msm8x10_wcd->resmgr.codec = codec;
dev_dbg(codec->dev, "%s %d\n", __func__, event);
@@ -1697,21 +1644,12 @@
{"I2S TX1", NULL, "TX_I2S_CLK"},
{"I2S TX2", NULL, "TX_I2S_CLK"},
- {"I2S TX3", NULL, "TX_I2S_CLK"},
- {"I2S TX4", NULL, "TX_I2S_CLK"},
- {"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+ {"DEC1 MUX", NULL, "TX CLK"},
+ {"DEC2 MUX", NULL, "TX CLK"},
- {"AIF1_CAP Mixer", "I2S TX1", "I2S TX1 MUX"},
- {"AIF1_CAP Mixer", "I2S TX2", "I2S TX2 MUX"},
- {"AIF1_CAP Mixer", "I2S TX3", "I2S TX3 MUX"},
- {"AIF1_CAP Mixer", "I2S TX4", "I2S TX4 MUX"},
-
- {"I2S TX1 MUX", NULL, "DEC1 MUX"},
- {"I2S TX2 MUX", NULL, "DEC2 MUX"},
- {"I2S TX3 MUX", NULL, "RX1 MIX1"},
- {"I2S TX4 MUX", "RMIX2", "RX1 MIX2"},
- {"I2S TX4 MUX", "RMIX3", "RX1 MIX3"},
+ {"I2S TX1", NULL, "DEC1 MUX"},
+ {"I2S TX2", NULL, "DEC2 MUX"},
/* Earpiece (RX MIX1) */
{"EAR", NULL, "EAR PA"},
@@ -1725,33 +1663,32 @@
{"HPHL", NULL, "HPHL DAC"},
{"HPHR", NULL, "HPHR DAC"},
- {"HPHR_PA_MIXER", NULL, "HPHR DAC"},
{"HPHL DAC", NULL, "CP"},
{"HPHR DAC", NULL, "CP"},
+ {"SPK DAC", NULL, "CP"},
{"DAC1", "Switch", "RX1 CHAIN"},
{"HPHL DAC", "Switch", "RX1 CHAIN"},
{"HPHR DAC", NULL, "RX2 CHAIN"},
- {"LINEOUT1", NULL, "LINEOUT1 PA"},
+ {"LINEOUT", NULL, "LINEOUT PA"},
{"SPK_OUT", NULL, "SPK PA"},
- {"LINEOUT1 PA", NULL, "CP"},
- {"LINEOUT1 PA", NULL, "LINEOUT1 DAC"},
+ {"LINEOUT PA", NULL, "CP"},
+ {"LINEOUT PA", NULL, "LINEOUT DAC"},
- {"LINEOUT1 DAC", "RX2 INPUT", "RX2 MIX1"},
- {"LINEOUT1 DAC", "RX3 INPUT", "RX3 MIX1"},
-
+ {"CP", NULL, "RX_BIAS"},
{"SPK PA", NULL, "SPK DAC"},
- {"SPK DAC", NULL, "RX7 MIX2"},
+ {"SPK DAC", NULL, "RX3 CHAIN"},
+ {"RX1 CHAIN", NULL, "RX1 CLK"},
+ {"RX2 CHAIN", NULL, "RX2 CLK"},
+ {"RX3 CHAIN", NULL, "RX3 CLK"},
{"RX1 CHAIN", NULL, "RX1 MIX2"},
{"RX2 CHAIN", NULL, "RX2 MIX2"},
-
- {"LINEOUT1 DAC", NULL, "RX_BIAS"},
- {"SPK DAC", NULL, "RX_BIAS"},
+ {"RX3 CHAIN", NULL, "RX3 MIX1"},
{"RX1 MIX1", NULL, "RX1 MIX1 INP1"},
{"RX1 MIX1", NULL, "RX1 MIX1 INP2"},
@@ -1761,19 +1698,7 @@
{"RX3 MIX1", NULL, "RX3 MIX1 INP1"},
{"RX3 MIX1", NULL, "RX3 MIX1 INP2"},
{"RX1 MIX2", NULL, "RX1 MIX1"},
- {"RX1 MIX2", NULL, "RX1 MIX2 INP1"},
- {"RX1 MIX2", NULL, "RX1 MIX2 INP2"},
{"RX2 MIX2", NULL, "RX2 MIX1"},
- {"RX2 MIX2", NULL, "RX2 MIX2 INP1"},
- {"RX2 MIX2", NULL, "RX2 MIX2 INP2"},
-
- {"I2S RX1 MUX", "AIF1_PB", "AIF1 PB"},
- {"I2S RX2 MUX", "AIF1_PB", "AIF1 PB"},
- {"I2S RX3 MUX", "AIF1_PB", "AIF1 PB"},
-
- {"I2S RX1", NULL, "I2S RX1 MUX"},
- {"I2S RX2", NULL, "I2S RX2 MUX"},
- {"I2S RX3", NULL, "I2S RX3 MUX"},
{"RX1 MIX1 INP1", "RX1", "I2S RX1"},
{"RX1 MIX1 INP1", "RX2", "I2S RX2"},
@@ -1825,41 +1750,108 @@
{"IIR1", NULL, "IIR1 INP1 MUX"},
{"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"},
{"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"},
-
- /* There is no LDO_H in Helicon */
- {"MIC BIAS1 Internal1", NULL, "LDO_H"},
- {"MIC BIAS1 Internal2", NULL, "LDO_H"},
- {"MIC BIAS1 External", NULL, "LDO_H"},
+ {"MIC BIAS Internal1", NULL, "INT_LDO_H"},
+ {"MIC BIAS Internal2", NULL, "INT_LDO_H"},
+ {"MIC BIAS External", NULL, "INT_LDO_H"},
};
static int msm8x10_wcd_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct msm8x10_wcd *msm8x10_wcd_core =
- dev_get_drvdata(dai->codec->dev);
dev_dbg(dai->codec->dev, "%s(): substream = %s stream = %d\n",
__func__,
substream->name, substream->stream);
- if ((msm8x10_wcd_core != NULL) &&
- (msm8x10_wcd_core->dev != NULL))
- pm_runtime_get_sync(msm8x10_wcd_core->dev);
-
return 0;
}
static void msm8x10_wcd_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct msm8x10_wcd *msm8x10_wcd_core =
- dev_get_drvdata(dai->codec->dev);
dev_dbg(dai->codec->dev,
"%s(): substream = %s stream = %d\n" , __func__,
substream->name, substream->stream);
- if ((msm8x10_wcd_core != NULL) &&
- (msm8x10_wcd_core->dev != NULL)) {
- pm_runtime_mark_last_busy(msm8x10_wcd_core->dev);
- pm_runtime_put(msm8x10_wcd_core->dev);
+}
+
+static int msm8x10_wcd_codec_enable_clock_block(struct snd_soc_codec *codec,
+ int enable)
+{
+ if (enable) {
+ snd_soc_update_bits(codec, MSM8X10_WCD_A_CDC_CLK_MCLK_CTL,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec, MSM8X10_WCD_A_CDC_CLK_PDM_CTL,
+ 0x03, 0x03);
+ snd_soc_update_bits(codec, MSM8X10_WCD_A_CDC_TOP_CLK_CTL,
+ 0x0f, 0x0d);
+ } else {
+ snd_soc_update_bits(codec, MSM8X10_WCD_A_CDC_TOP_CLK_CTL,
+ 0x0f, 0x00);
+ snd_soc_update_bits(codec, MSM8X10_WCD_A_CDC_CLK_PDM_CTL,
+ 0x03, 0x00);
}
+ return 0;
+}
+
+static void msm8x10_wcd_codec_enable_audio_mode_bandgap(struct snd_soc_codec
+ *codec)
+{
+ snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL, 0x80,
+ 0x80);
+ snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL, 0x04,
+ 0x04);
+ snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL, 0x01,
+ 0x01);
+ usleep_range(1000, 1000);
+ snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL, 0x80,
+ 0x00);
+}
+
+static void msm8x10_wcd_codec_enable_bandgap(struct snd_soc_codec *codec,
+ enum msm8x10_wcd_bandgap_type choice)
+{
+ struct msm8x10_wcd_priv *msm8x10_wcd = snd_soc_codec_get_drvdata(codec);
+
+ /* TODO lock resources accessed by audio streams and threaded
+ * interrupt handlers
+ */
+
+ dev_dbg(codec->dev, "%s, choice is %d, current is %d\n",
+ __func__, choice,
+ msm8x10_wcd->bandgap_type);
+
+ if (msm8x10_wcd->bandgap_type == choice)
+ return;
+
+ if ((msm8x10_wcd->bandgap_type == MSM8X10_WCD_BANDGAP_OFF) &&
+ (choice == MSM8X10_WCD_BANDGAP_AUDIO_MODE)) {
+ msm8x10_wcd_codec_enable_audio_mode_bandgap(codec);
+ } else if (choice == MSM8X10_WCD_BANDGAP_MBHC_MODE) {
+ /* bandgap mode becomes fast,
+ * mclk should be off or clk buff source souldn't be VBG
+ * Let's turn off mclk always */
+ snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL,
+ 0x2, 0x2);
+ snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL,
+ 0x80, 0x80);
+ snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL,
+ 0x4, 0x4);
+ snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL,
+ 0x01, 0x01);
+ usleep_range(1000, 1000);
+ snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL,
+ 0x80, 0x00);
+ } else if ((msm8x10_wcd->bandgap_type ==
+ MSM8X10_WCD_BANDGAP_MBHC_MODE) &&
+ (choice == MSM8X10_WCD_BANDGAP_AUDIO_MODE)) {
+ snd_soc_write(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL, 0x50);
+ usleep_range(100, 100);
+ msm8x10_wcd_codec_enable_audio_mode_bandgap(codec);
+ } else if (choice == MSM8X10_WCD_BANDGAP_OFF) {
+ snd_soc_write(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL, 0x50);
+ } else {
+ dev_err(codec->dev,
+ "%s: Error, Invalid bandgap settings\n", __func__);
+ }
+ msm8x10_wcd->bandgap_type = choice;
}
int msm8x10_wcd_mclk_enable(struct snd_soc_codec *codec,
@@ -1867,24 +1859,30 @@
{
struct msm8x10_wcd_priv *msm8x10_wcd = snd_soc_codec_get_drvdata(codec);
- dev_dbg(codec->dev,
- "%s: mclk_enable = %u, dapm = %d\n", __func__,
- mclk_enable, dapm);
- WCD9XXX_BCL_LOCK(&msm8x10_wcd->resmgr);
+ dev_dbg(codec->dev, "%s: mclk_enable = %u, dapm = %d\n",
+ __func__, mclk_enable, dapm);
+ if (dapm)
+ MSM8X10_WCD_ACQUIRE_LOCK(msm8x10_wcd->codec_resource_lock);
if (mclk_enable) {
- wcd9xxx_resmgr_get_bandgap(&msm8x10_wcd->resmgr,
- WCD9XXX_BANDGAP_AUDIO_MODE);
- wcd9xxx_resmgr_get_clk_block(&msm8x10_wcd->resmgr,
- WCD9XXX_CLK_MCLK);
+ msm8x10_wcd->mclk_enabled = true;
+ msm8x10_wcd_codec_enable_bandgap(codec,
+ MSM8X10_WCD_BANDGAP_AUDIO_MODE);
+ msm8x10_wcd_codec_enable_clock_block(codec, 1);
} else {
- /* Put clock and BG */
- wcd9xxx_resmgr_put_clk_block(&msm8x10_wcd->resmgr,
- WCD9XXX_CLK_MCLK);
- wcd9xxx_resmgr_put_bandgap(&msm8x10_wcd->resmgr,
- WCD9XXX_BANDGAP_AUDIO_MODE);
+ if (!msm8x10_wcd->mclk_enabled) {
+ if (dapm)
+ MSM8X10_WCD_RELEASE_LOCK(
+ msm8x10_wcd->codec_resource_lock);
+ dev_err(codec->dev, "Error, MCLK already diabled\n");
+ return -EINVAL;
+ }
+ msm8x10_wcd->mclk_enabled = false;
+ msm8x10_wcd_codec_enable_clock_block(codec, 0);
+ msm8x10_wcd_codec_enable_bandgap(codec,
+ MSM8X10_WCD_BANDGAP_OFF);
}
- WCD9XXX_BCL_UNLOCK(&msm8x10_wcd->resmgr);
-
+ if (dapm)
+ MSM8X10_WCD_RELEASE_LOCK(msm8x10_wcd->codec_resource_lock);
return 0;
}
@@ -2027,7 +2025,7 @@
.rate_max = 192000,
.rate_min = 8000,
.channels_min = 1,
- .channels_max = 4,
+ .channels_max = 3,
},
.ops = &msm8x10_wcd_dai_ops,
},
@@ -2077,23 +2075,16 @@
SND_SOC_DAPM_MIXER("DAC1", MSM8X10_WCD_A_RX_EAR_EN, 6, 0, dac1_switch,
ARRAY_SIZE(dac1_switch)),
- SND_SOC_DAPM_AIF_IN("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
- AIF1_PB, 0),
+ SND_SOC_DAPM_AIF_IN("I2S RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_MUX("I2S RX1 MUX", SND_SOC_NOPM, MSM8X10_WCD_RX1, 0,
- &slim_rx_mux[MSM8X10_WCD_RX1]),
- SND_SOC_DAPM_MUX("I2S RX2 MUX", SND_SOC_NOPM, MSM8X10_WCD_RX2, 0,
- &slim_rx_mux[MSM8X10_WCD_RX2]),
- SND_SOC_DAPM_MUX("I2S RX3 MUX", SND_SOC_NOPM, MSM8X10_WCD_RX3, 0,
- &slim_rx_mux[MSM8X10_WCD_RX3]),
+ SND_SOC_DAPM_AIF_IN("I2S RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_MIXER("I2S RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("I2S RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("I2S RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("I2S RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("I2S RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_AIF_IN("I2S RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
- /* Headphone */
+ SND_SOC_DAPM_SUPPLY("TX CLK", MSM8X10_WCD_A_CDC_DIG_CLK_CTL,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0),
+
SND_SOC_DAPM_OUTPUT("HEADPHONE"),
SND_SOC_DAPM_PGA_E("HPHL", MSM8X10_WCD_A_RX_HPH_CNP_EN,
5, 0, NULL, 0,
@@ -2114,10 +2105,10 @@
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
/* Speaker */
- SND_SOC_DAPM_OUTPUT("LINEOUT1"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT"),
SND_SOC_DAPM_OUTPUT("SPK_OUT"),
- SND_SOC_DAPM_PGA_E("LINEOUT1 PA", MSM8X10_WCD_A_RX_LINE_CNP_EN,
+ SND_SOC_DAPM_PGA_E("LINEOUT PA", MSM8X10_WCD_A_RX_LINE_CNP_EN,
0, 0, NULL, 0, msm8x10_wcd_codec_enable_lineout,
SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
@@ -2127,7 +2118,7 @@
SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_DAC_E("LINEOUT1 DAC", NULL,
+ SND_SOC_DAPM_DAC_E("LINEOUT DAC", NULL,
MSM8X10_WCD_A_RX_LINE_1_DAC_CTL, 7, 0,
msm8x10_wcd_lineout_dac_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
@@ -2152,10 +2143,18 @@
0, msm8x10_wcd_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("RX1 CLK", MSM8X10_WCD_A_CDC_DIG_CLK_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RX2 CLK", MSM8X10_WCD_A_CDC_DIG_CLK_CTL,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RX3 CLK", MSM8X10_WCD_A_CDC_DIG_CLK_CTL,
+ 2, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX1 CHAIN", MSM8X10_WCD_A_CDC_RX1_B6_CTL,
5, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX2 CHAIN", MSM8X10_WCD_A_CDC_RX2_B6_CTL,
5, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX3 CHAIN", MSM8X10_WCD_A_CDC_RX3_B6_CTL,
+ 5, 0, NULL, 0),
SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
&rx_mix1_inp1_mux),
@@ -2191,26 +2190,28 @@
SND_SOC_DAPM_INPUT("AMIC1"),
- SND_SOC_DAPM_MICBIAS_E("MIC BIAS1 External",
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal1",
MSM8X10_WCD_A_MICB_1_CTL, 7, 0,
msm8x10_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MICBIAS_E("MIC BIAS1 Internal1",
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal2",
MSM8X10_WCD_A_MICB_1_CTL, 7, 0,
msm8x10_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MICBIAS_E("MIC BIAS1 Internal2",
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal3",
MSM8X10_WCD_A_MICB_1_CTL, 7, 0,
msm8x10_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_ADC_E("ADC1", NULL, MSM8X10_WCD_A_TX_1_EN, 7, 0,
msm8x10_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_ADC_E("ADC1", NULL, MSM8X10_WCD_A_TX_2_EN, 7, 0,
+ SND_SOC_DAPM_ADC_E("ADC2", NULL, MSM8X10_WCD_A_TX_2_EN, 7, 0,
msm8x10_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS("MIC BIAS External", MSM8X10_WCD_A_MICB_1_CTL,
+ 7, 0),
+
SND_SOC_DAPM_INPUT("AMIC3"),
SND_SOC_DAPM_MUX_E("DEC1 MUX",
@@ -2226,11 +2227,13 @@
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_INPUT("AMIC2"),
- SND_SOC_DAPM_AIF_OUT("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
- AIF1_CAP, 0),
- SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
- aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
+ SND_SOC_DAPM_AIF_OUT("I2S TX1", "AIF1 Capture", 0, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("I2S TX2", "AIF1 Capture", 0, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("I2S TX3", "AIF1 Capture", 0, SND_SOC_NOPM,
+ 0, 0),
/* Digital Mic Inputs */
SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
@@ -2254,7 +2257,7 @@
static const struct msm8x10_wcd_reg_mask_val msm8x10_wcd_reg_defaults[] = {
/* set MCLk to 9.6 */
- MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CHIP_CTL, 0x0A),
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CHIP_CTL, 0x00),
/* EAR PA deafults */
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_RX_EAR_CMBUFF, 0x05),
@@ -2278,9 +2281,20 @@
/* Reduce LINE DAC bias to 70% */
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_RX_LINE_BIAS_PA, 0x78),
-
/* Disable TX7 internal biasing path which can cause leakage */
- MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_TX_SUP_SWITCH_CTRL_1, 0xBF),
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_BIAS_CURR_CTL_2, 0x04),
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_MICB_CFILT_1_VAL, 0x60),
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_MICB_1_CTL, 0x82),
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_TX_COM_BIAS, 0xE0),
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_TX_1_EN, 0x32),
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_TX_2_EN, 0x32),
+
+ /* ClassG fine tuning setting for 16 ohm HPH */
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_CLSG_FREQ_THRESH_B1_CTL, 0x05),
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_CLSG_FREQ_THRESH_B2_CTL, 0x0C),
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_CLSG_FREQ_THRESH_B3_CTL, 0x1A),
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_CLSG_FREQ_THRESH_B4_CTL, 0x47),
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_CLSG_GAIN_THRESH_CTL, 0x23),
};
static void msm8x10_wcd_update_reg_defaults(struct snd_soc_codec *codec)
@@ -2338,12 +2352,36 @@
static int msm8x10_wcd_codec_probe(struct snd_soc_codec *codec)
{
+ struct msm8x10_wcd_priv *msm8x10_wcd;
+ int i;
dev_dbg(codec->dev, "%s()\n", __func__);
+ msm8x10_wcd = kzalloc(sizeof(struct msm8x10_wcd_priv), GFP_KERNEL);
+ if (!msm8x10_wcd) {
+ dev_err(codec->dev, "Failed to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0 ; i < NUM_DECIMATORS; i++) {
+ tx_hpf_work[i].msm8x10_wcd = msm8x10_wcd;
+ tx_hpf_work[i].decimator = i + 1;
+ INIT_DELAYED_WORK(&tx_hpf_work[i].dwork,
+ tx_hpf_corner_freq_callback);
+ }
+
codec->control_data = dev_get_drvdata(codec->dev);
+ snd_soc_codec_set_drvdata(codec, msm8x10_wcd);
+ msm8x10_wcd->codec = codec;
msm8x10_wcd_codec_init_reg(codec);
msm8x10_wcd_update_reg_defaults(codec);
+ msm8x10_wcd->mclk_enabled = false;
+ msm8x10_wcd->bandgap_type = MSM8X10_WCD_BANDGAP_OFF;
+ msm8x10_wcd->clock_active = false;
+ msm8x10_wcd->config_mode_active = false;
+ msm8x10_wcd->mbhc_polling_active = false;
+ mutex_init(&msm8x10_wcd->codec_resource_lock);
+ msm8x10_wcd->codec = codec;
return 0;
}
@@ -2353,18 +2391,6 @@
return 0;
}
-static int msm8x10_wcd_device_init(struct msm8x10_wcd *msm8x10)
-{
-
- mutex_init(&msm8x10->io_lock);
- mutex_init(&msm8x10->xfer_lock);
- mutex_init(&msm8x10->pm_lock);
- msm8x10->wlock_holders = 0;
-
- return 0;
-}
-
-
static struct snd_soc_codec_driver soc_codec_dev_msm8x10_wcd = {
.probe = msm8x10_wcd_codec_probe,
.remove = msm8x10_wcd_codec_remove,
@@ -2387,6 +2413,124 @@
.num_dapm_routes = ARRAY_SIZE(audio_map),
};
+static int msm8x10_wcd_enable_supplies(struct msm8x10_wcd *msm8x10,
+ struct msm8x10_wcd_pdata *pdata)
+{
+ int ret;
+ int i;
+ msm8x10->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
+ ARRAY_SIZE(pdata->regulator),
+ GFP_KERNEL);
+ if (!msm8x10->supplies) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ msm8x10->num_of_supplies = 0;
+
+ if (ARRAY_SIZE(pdata->regulator) > MAX_REGULATOR) {
+ dev_err(msm8x10->dev, "%s: Array Size out of bound\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+ if (pdata->regulator[i].name) {
+ msm8x10->supplies[i].supply = pdata->regulator[i].name;
+ msm8x10->num_of_supplies++;
+ }
+ }
+
+ ret = regulator_bulk_get(msm8x10->dev, msm8x10->num_of_supplies,
+ msm8x10->supplies);
+ if (ret != 0) {
+ dev_err(msm8x10->dev, "Failed to get supplies: err = %d\n",
+ ret);
+ goto err_supplies;
+ }
+
+ for (i = 0; i < msm8x10->num_of_supplies; i++) {
+ ret = regulator_set_voltage(msm8x10->supplies[i].consumer,
+ pdata->regulator[i].min_uV, pdata->regulator[i].max_uV);
+ if (ret) {
+ dev_err(msm8x10->dev, "%s: Setting regulator voltage failed for regulator %s err = %d\n",
+ __func__, msm8x10->supplies[i].supply, ret);
+ goto err_get;
+ }
+
+ ret = regulator_set_optimum_mode(msm8x10->supplies[i].consumer,
+ pdata->regulator[i].optimum_uA);
+ if (ret < 0) {
+ dev_err(msm8x10->dev, "%s: Setting regulator optimum mode failed for regulator %s err = %d\n",
+ __func__, msm8x10->supplies[i].supply, ret);
+ goto err_get;
+ }
+ }
+
+ ret = regulator_bulk_enable(msm8x10->num_of_supplies,
+ msm8x10->supplies);
+ if (ret != 0) {
+ dev_err(msm8x10->dev, "Failed to enable supplies: err = %d\n",
+ ret);
+ goto err_configure;
+ }
+ return ret;
+
+err_configure:
+ for (i = 0; i < msm8x10->num_of_supplies; i++) {
+ regulator_set_voltage(msm8x10->supplies[i].consumer, 0,
+ pdata->regulator[i].max_uV);
+ regulator_set_optimum_mode(msm8x10->supplies[i].consumer, 0);
+ }
+err_get:
+ regulator_bulk_free(msm8x10->num_of_supplies, msm8x10->supplies);
+err_supplies:
+ kfree(msm8x10->supplies);
+err:
+ return ret;
+}
+
+static void msm8x10_wcd_disable_supplies(struct msm8x10_wcd *msm8x10,
+ struct msm8x10_wcd_pdata *pdata)
+{
+ int i;
+
+ regulator_bulk_disable(msm8x10->num_of_supplies,
+ msm8x10->supplies);
+ for (i = 0; i < msm8x10->num_of_supplies; i++) {
+ regulator_set_voltage(msm8x10->supplies[i].consumer, 0,
+ pdata->regulator[i].max_uV);
+ regulator_set_optimum_mode(msm8x10->supplies[i].consumer, 0);
+ }
+ regulator_bulk_free(msm8x10->num_of_supplies, msm8x10->supplies);
+ kfree(msm8x10->supplies);
+}
+
+static int msm8x10_wcd_bringup(struct msm8x10_wcd *msm8x10)
+{
+ msm8x10->read_dev = msm8x10_wcd_reg_read;
+ msm8x10->write_dev(msm8x10, MSM8X10_WCD_A_CDC_RST_CTL, 0x02);
+ msm8x10->write_dev(msm8x10, MSM8X10_WCD_A_CHIP_CTL, 0x00);
+ usleep_range(5000, 5000);
+ msm8x10->write_dev(msm8x10, MSM8X10_WCD_A_CDC_RST_CTL, 0x03);
+ return 0;
+}
+
+static int msm8x10_wcd_device_init(struct msm8x10_wcd *msm8x10)
+{
+ mutex_init(&msm8x10->io_lock);
+ mutex_init(&msm8x10->xfer_lock);
+ mutex_init(&msm8x10->pm_lock);
+ msm8x10->wlock_holders = 0;
+
+ iowrite32(0x03C00000, ioremap(0xFD512050, 4));
+ usleep_range(5000, 5000);
+
+ msm8x10_wcd_bringup(msm8x10);
+ return 0;
+}
+
static int __devinit msm8x10_wcd_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -2401,7 +2545,7 @@
if (device_id > 0) {
msm8x10_wcd_modules[device_id++].client = client;
- return ret;
+ goto rtn;
}
dev = &client->dev;
@@ -2421,19 +2565,25 @@
dev_err(&client->dev,
"%s: error, allocation failed\n", __func__);
ret = -ENOMEM;
- goto fail;
+ goto rtn;
}
msm8x10->dev = &client->dev;
msm8x10_wcd_modules[device_id++].client = client;
msm8x10->read_dev = msm8x10_wcd_reg_read;
msm8x10->write_dev = msm8x10_wcd_reg_write;
+ ret = msm8x10_wcd_enable_supplies(msm8x10, pdata);
+ if (ret) {
+ dev_err(&client->dev, "%s: Fail to enable Codec supplies\n",
+ __func__);
+ goto err_codec;
+ }
ret = msm8x10_wcd_device_init(msm8x10);
if (ret) {
dev_err(&client->dev,
"%s:msm8x10_wcd_device_init failed with error %d\n",
__func__, ret);
- goto fail;
+ goto err_supplies;
}
dev_set_drvdata(&client->dev, msm8x10);
ret = snd_soc_register_codec(&client->dev, &soc_codec_dev_msm8x10_wcd,
@@ -2443,7 +2593,14 @@
dev_err(&client->dev,
"%s:snd_soc_register_codec failed with error %d\n",
__func__, ret);
-fail:
+ else
+ goto rtn;
+
+err_supplies:
+ msm8x10_wcd_disable_supplies(msm8x10, pdata);
+err_codec:
+ kfree(msm8x10);
+rtn:
return ret;
}
diff --git a/sound/soc/codecs/msm8x10-wcd.h b/sound/soc/codecs/msm8x10-wcd.h
index 44e8a6d..d250e0a 100644
--- a/sound/soc/codecs/msm8x10-wcd.h
+++ b/sound/soc/codecs/msm8x10-wcd.h
@@ -199,7 +199,7 @@
int (*read_dev)(struct msm8x10_wcd *msm8x10,
unsigned short reg, unsigned int *val);
int (*write_dev)(struct msm8x10_wcd *msm8x10,
- unsigned short reg, unsigned int val);
+ unsigned short reg, u8 val);
u32 num_of_supplies;
struct regulator_bulk_data *supplies;
diff --git a/sound/soc/codecs/wcd9304-tables.c b/sound/soc/codecs/wcd9304-tables.c
index 83c0c1d..7ec0152 100644
--- a/sound/soc/codecs/wcd9304-tables.c
+++ b/sound/soc/codecs/wcd9304-tables.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -235,8 +235,24 @@
[SITAR_A_CDC_RX1_B4_CTL] = SITAR_A_CDC_RX1_B4_CTL__POR,
[SITAR_A_CDC_RX1_B5_CTL] = SITAR_A_CDC_RX1_B5_CTL__POR,
[SITAR_A_CDC_RX1_B6_CTL] = SITAR_A_CDC_RX1_B6_CTL__POR,
+ [SITAR_A_CDC_RX2_B1_CTL] = SITAR_A_CDC_RX2_B1_CTL__POR,
+ [SITAR_A_CDC_RX2_B2_CTL] = SITAR_A_CDC_RX2_B2_CTL__POR,
+ [SITAR_A_CDC_RX2_B3_CTL] = SITAR_A_CDC_RX2_B3_CTL__POR,
+ [SITAR_A_CDC_RX2_B4_CTL] = SITAR_A_CDC_RX2_B4_CTL__POR,
+ [SITAR_A_CDC_RX2_B5_CTL] = SITAR_A_CDC_RX2_B5_CTL__POR,
+ [SITAR_A_CDC_RX2_B6_CTL] = SITAR_A_CDC_RX2_B6_CTL__POR,
+ [SITAR_A_CDC_RX3_B1_CTL] = SITAR_A_CDC_RX3_B1_CTL__POR,
+ [SITAR_A_CDC_RX3_B2_CTL] = SITAR_A_CDC_RX3_B2_CTL__POR,
+ [SITAR_A_CDC_RX3_B3_CTL] = SITAR_A_CDC_RX3_B3_CTL__POR,
+ [SITAR_A_CDC_RX3_B4_CTL] = SITAR_A_CDC_RX3_B4_CTL__POR,
+ [SITAR_A_CDC_RX3_B5_CTL] = SITAR_A_CDC_RX3_B5_CTL__POR,
+ [SITAR_A_CDC_RX3_B6_CTL] = SITAR_A_CDC_RX3_B6_CTL__POR,
[SITAR_A_CDC_RX1_VOL_CTL_B1_CTL] = SITAR_A_CDC_RX1_VOL_CTL_B1_CTL__POR,
[SITAR_A_CDC_RX1_VOL_CTL_B2_CTL] = SITAR_A_CDC_RX1_VOL_CTL_B2_CTL__POR,
+ [SITAR_A_CDC_RX2_VOL_CTL_B1_CTL] = SITAR_A_CDC_RX2_VOL_CTL_B1_CTL__POR,
+ [SITAR_A_CDC_RX2_VOL_CTL_B2_CTL] = SITAR_A_CDC_RX2_VOL_CTL_B2_CTL__POR,
+ [SITAR_A_CDC_RX3_VOL_CTL_B1_CTL] = SITAR_A_CDC_RX3_VOL_CTL_B1_CTL__POR,
+ [SITAR_A_CDC_RX3_VOL_CTL_B2_CTL] = SITAR_A_CDC_RX3_VOL_CTL_B2_CTL__POR,
[SITAR_A_CDC_CLK_ANC_RESET_CTL] = SITAR_A_CDC_CLK_ANC_RESET_CTL__POR,
[SITAR_A_CDC_CLK_RX_RESET_CTL] = SITAR_A_CDC_CLK_RX_RESET_CTL__POR,
[SITAR_A_CDC_CLK_TX_RESET_B1_CTL] =
@@ -322,6 +338,15 @@
[SITAR_A_CDC_COMP1_SHUT_DOWN_STATUS] =
SITAR_A_CDC_COMP1_SHUT_DOWN_STATUS__POR,
[SITAR_A_CDC_COMP1_FS_CFG] = SITAR_A_CDC_COMP1_FS_CFG__POR,
+ [SITAR_A_CDC_COMP2_B1_CTL] = SITAR_A_CDC_COMP2_B1_CTL__POR,
+ [SITAR_A_CDC_COMP2_B2_CTL] = SITAR_A_CDC_COMP2_B2_CTL__POR,
+ [SITAR_A_CDC_COMP2_B3_CTL] = SITAR_A_CDC_COMP2_B3_CTL__POR,
+ [SITAR_A_CDC_COMP2_B4_CTL] = SITAR_A_CDC_COMP2_B4_CTL__POR,
+ [SITAR_A_CDC_COMP2_B5_CTL] = SITAR_A_CDC_COMP2_B5_CTL__POR,
+ [SITAR_A_CDC_COMP2_B6_CTL] = SITAR_A_CDC_COMP2_B6_CTL__POR,
+ [SITAR_A_CDC_COMP2_SHUT_DOWN_STATUS] =
+ SITAR_A_CDC_COMP2_SHUT_DOWN_STATUS__POR,
+ [SITAR_A_CDC_COMP2_FS_CFG] = SITAR_A_CDC_COMP2_FS_CFG__POR,
[SITAR_A_CDC_CONN_RX1_B1_CTL] = SITAR_A_CDC_CONN_RX1_B1_CTL__POR,
[SITAR_A_CDC_CONN_RX1_B2_CTL] = SITAR_A_CDC_CONN_RX1_B2_CTL__POR,
[SITAR_A_CDC_CONN_RX1_B3_CTL] = SITAR_A_CDC_CONN_RX1_B3_CTL__POR,
diff --git a/sound/soc/codecs/wcd9304.c b/sound/soc/codecs/wcd9304.c
index f5f4e23..58ea22d 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -45,6 +45,7 @@
#define NUM_DECIMATORS 4
#define NUM_INTERPOLATORS 3
#define BITS_PER_REG 8
+#define SITAR_RX_PORT_START_NUMBER 10
enum {
AIF1_PB = 0,
@@ -53,11 +54,11 @@
};
struct wcd9xxx_ch sitar_rx_chs[SITAR_RX_MAX] = {
- WCD9XXX_CH(10, 0),
- WCD9XXX_CH(11, 1),
- WCD9XXX_CH(12, 2),
- WCD9XXX_CH(13, 3),
- WCD9XXX_CH(14, 4)
+ WCD9XXX_CH(SITAR_RX_PORT_START_NUMBER, 0),
+ WCD9XXX_CH(SITAR_RX_PORT_START_NUMBER + 1, 1),
+ WCD9XXX_CH(SITAR_RX_PORT_START_NUMBER + 2, 2),
+ WCD9XXX_CH(SITAR_RX_PORT_START_NUMBER + 3, 3),
+ WCD9XXX_CH(SITAR_RX_PORT_START_NUMBER + 4, 4)
};
struct wcd9xxx_ch sitar_tx_chs[SITAR_TX_MAX] = {
@@ -81,6 +82,11 @@
#define SITAR_OCP_ATTEMPT 1
+#define COMP_DIGITAL_DB_GAIN_APPLY(a, b) \
+ (((a) <= 0) ? ((a) - b) : (a))
+/* The wait time value comes from codec HW specification */
+#define COMP_BRINGUP_WAIT_TIME 3000
+
#define SITAR_MCLK_RATE_12288KHZ 12288000
#define SITAR_MCLK_RATE_9600KHZ 9600000
@@ -148,6 +154,22 @@
BAND_MAX,
};
+enum {
+ COMPANDER_1 = 0,
+ COMPANDER_2,
+ COMPANDER_MAX,
+};
+
+enum {
+ COMPANDER_FS_8KHZ = 0,
+ COMPANDER_FS_16KHZ,
+ COMPANDER_FS_32KHZ,
+ COMPANDER_FS_48KHZ,
+ COMPANDER_FS_96KHZ,
+ COMPANDER_FS_192KHZ,
+ COMPANDER_FS_MAX,
+};
+
/* Flags to track of PA and DAC state.
* PA and DAC should be tracked separately as AUXPGA loopback requires
* only PA to be turned on without DAC being on. */
@@ -158,6 +180,33 @@
SITAR_HPHR_DAC_OFF_ACK
};
+struct comp_sample_dependent_params {
+ u32 peak_det_timeout;
+ u32 rms_meter_div_fact;
+ u32 rms_meter_resamp_fact;
+};
+
+struct comp_dgtl_gain_offset {
+ u8 whole_db_gain;
+ u8 half_db_gain;
+};
+
+static const struct comp_dgtl_gain_offset comp_dgtl_gain[] = {
+ {0, 0},
+ {1, 1},
+ {3, 0},
+ {4, 1},
+ {6, 0},
+ {7, 1},
+ {9, 0},
+ {10, 1},
+ {12, 0},
+ {13, 1},
+ {15, 0},
+ {16, 1},
+ {18, 0},
+};
+
/* Data used by MBHC */
struct mbhc_internal_cal_data {
u16 dce_z;
@@ -273,6 +322,11 @@
/* num of slim ports required */
struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
+ /*compander*/
+ int comp_enabled[COMPANDER_MAX];
+ u32 comp_fs[COMPANDER_MAX];
+ u8 comp_gain_offset[NUM_INTERPOLATORS];
+
/* Currently, only used for mbhc purpose, to protect
* concurrent execution of mbhc threaded irq handlers and
* kill race between DAPM and MBHC.But can serve as a
@@ -295,6 +349,47 @@
struct sitar_priv *debug_sitar_priv;
#endif
+static const int comp_rx_path[] = {
+ COMPANDER_2,
+ COMPANDER_1,
+ COMPANDER_1,
+ COMPANDER_MAX,
+};
+
+static const struct comp_sample_dependent_params
+ comp_samp_params[COMPANDER_FS_MAX] = {
+ {
+ .peak_det_timeout = 0x6,
+ .rms_meter_div_fact = 0x9 << 4,
+ .rms_meter_resamp_fact = 0x06,
+ },
+ {
+ .peak_det_timeout = 0x7,
+ .rms_meter_div_fact = 0xA << 4,
+ .rms_meter_resamp_fact = 0x0C,
+ },
+ {
+ .peak_det_timeout = 0x8,
+ .rms_meter_div_fact = 0xB << 4,
+ .rms_meter_resamp_fact = 0x30,
+ },
+ {
+ .peak_det_timeout = 0x9,
+ .rms_meter_div_fact = 0xB << 4,
+ .rms_meter_resamp_fact = 0x28,
+ },
+ {
+ .peak_det_timeout = 0xA,
+ .rms_meter_div_fact = 0xC << 4,
+ .rms_meter_resamp_fact = 0x50,
+ },
+ {
+ .peak_det_timeout = 0xB,
+ .rms_meter_div_fact = 0xC << 4,
+ .rms_meter_resamp_fact = 0x50,
+ },
+};
+
static int sitar_get_anc_slot(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -539,6 +634,268 @@
return 0;
}
+static int sitar_compander_gain_offset(
+ struct snd_soc_codec *codec, u32 enable,
+ unsigned int pa_reg, unsigned int vol_reg,
+ int mask, int event,
+ struct comp_dgtl_gain_offset *gain_offset,
+ int index)
+{
+ unsigned int pa_gain = snd_soc_read(codec, pa_reg);
+ unsigned int digital_vol = snd_soc_read(codec, vol_reg);
+ int pa_mode = pa_gain & mask;
+ struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: pa_gain(0x%x=0x%x)digital_vol(0x%x=0x%x)event(0x%x) index(%d)\n",
+ __func__, pa_reg, pa_gain, vol_reg, digital_vol, event, index);
+ if (((pa_gain & 0xF) + 1) > ARRAY_SIZE(comp_dgtl_gain) ||
+ (index >= ARRAY_SIZE(sitar->comp_gain_offset))) {
+ pr_err("%s: Out of array boundary\n", __func__);
+ return -EINVAL;
+ }
+
+ if (SND_SOC_DAPM_EVENT_ON(event) && (enable != 0)) {
+ gain_offset->whole_db_gain = COMP_DIGITAL_DB_GAIN_APPLY(
+ (digital_vol - comp_dgtl_gain[pa_gain & 0xF].whole_db_gain),
+ comp_dgtl_gain[pa_gain & 0xF].half_db_gain);
+ pr_debug("%s: listed whole_db_gain:0x%x, adjusted whole_db_gain:0x%x\n",
+ __func__, comp_dgtl_gain[pa_gain & 0xF].whole_db_gain,
+ gain_offset->whole_db_gain);
+ gain_offset->half_db_gain =
+ comp_dgtl_gain[pa_gain & 0xF].half_db_gain;
+ sitar->comp_gain_offset[index] = digital_vol -
+ gain_offset->whole_db_gain ;
+ }
+ if (SND_SOC_DAPM_EVENT_OFF(event) && (pa_mode == 0)) {
+ gain_offset->whole_db_gain = digital_vol +
+ sitar->comp_gain_offset[index];
+ pr_debug("%s: listed whole_db_gain:0x%x, adjusted whole_db_gain:0x%x\n",
+ __func__, comp_dgtl_gain[pa_gain & 0xF].whole_db_gain,
+ gain_offset->whole_db_gain);
+ gain_offset->half_db_gain = 0;
+ }
+
+ pr_debug("%s: half_db_gain(%d)whole_db_gain(0x%x)comp_gain_offset[%d](%d)\n",
+ __func__, gain_offset->half_db_gain,
+ gain_offset->whole_db_gain, index,
+ sitar->comp_gain_offset[index]);
+ return 0;
+}
+
+static int sitar_config_gain_compander(
+ struct snd_soc_codec *codec,
+ u32 compander, u32 enable, int event)
+{
+ int value = 0;
+ int mask = 1 << 4;
+ struct comp_dgtl_gain_offset gain_offset = {0, 0};
+ if (compander >= COMPANDER_MAX) {
+ pr_err("%s: Error, invalid compander channel\n", __func__);
+ return -EINVAL;
+ }
+
+ if ((enable == 0) || SND_SOC_DAPM_EVENT_OFF(event))
+ value = 1 << 4;
+
+ if (compander == COMPANDER_1) {
+ sitar_compander_gain_offset(codec, enable,
+ SITAR_A_RX_HPH_L_GAIN,
+ SITAR_A_CDC_RX2_VOL_CTL_B2_CTL,
+ mask, event, &gain_offset, 1);
+ snd_soc_update_bits(codec, SITAR_A_RX_HPH_L_GAIN, mask, value);
+ snd_soc_update_bits(codec, SITAR_A_CDC_RX2_VOL_CTL_B2_CTL,
+ 0xFF, gain_offset.whole_db_gain);
+ snd_soc_update_bits(codec, SITAR_A_CDC_RX2_B6_CTL,
+ 0x02, gain_offset.half_db_gain);
+ sitar_compander_gain_offset(codec, enable,
+ SITAR_A_RX_HPH_R_GAIN,
+ SITAR_A_CDC_RX3_VOL_CTL_B2_CTL,
+ mask, event, &gain_offset, 2);
+ snd_soc_update_bits(codec, SITAR_A_RX_HPH_R_GAIN, mask, value);
+ snd_soc_update_bits(codec, SITAR_A_CDC_RX3_VOL_CTL_B2_CTL,
+ 0xFF, gain_offset.whole_db_gain);
+ snd_soc_update_bits(codec, SITAR_A_CDC_RX3_B6_CTL,
+ 0x02, gain_offset.half_db_gain);
+ } else if (compander == COMPANDER_2) {
+ sitar_compander_gain_offset(codec, enable,
+ SITAR_A_RX_LINE_1_GAIN,
+ SITAR_A_CDC_RX1_VOL_CTL_B2_CTL,
+ mask, event, &gain_offset, 0);
+ snd_soc_update_bits(codec, SITAR_A_RX_LINE_1_GAIN, mask, value);
+ snd_soc_update_bits(codec, SITAR_A_RX_LINE_2_GAIN, mask, value);
+ snd_soc_update_bits(codec, SITAR_A_CDC_RX1_VOL_CTL_B2_CTL,
+ 0xFF, gain_offset.whole_db_gain);
+ snd_soc_update_bits(codec, SITAR_A_CDC_RX1_B6_CTL,
+ 0x02, gain_offset.half_db_gain);
+ }
+ return 0;
+}
+
+static int sitar_get_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int comp = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = sitar->comp_enabled[comp];
+
+ return 0;
+}
+
+static int sitar_set_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+ int comp = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: compander #%d enable %d\n",
+ __func__, comp + 1, value);
+ if (value == sitar->comp_enabled[comp]) {
+ pr_debug("%s: compander #%d enable %d no change\n",
+ __func__, comp + 1, value);
+ return 0;
+ }
+ sitar->comp_enabled[comp] = value;
+ return 0;
+}
+
+static int sitar_config_compander(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+ u32 rate = sitar->comp_fs[w->shift];
+ u32 value;
+
+ pr_debug("%s: compander #%d enable %d event %d widget name %s\n",
+ __func__, w->shift + 1,
+ sitar->comp_enabled[w->shift], event , w->name);
+ if (sitar->comp_enabled[w->shift] == 0)
+ goto rtn;
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Update compander sample rate */
+ snd_soc_update_bits(codec, SITAR_A_CDC_COMP1_FS_CFG +
+ w->shift * 8, 0x07, rate);
+ /* Enable compander clock */
+ snd_soc_update_bits(codec,
+ SITAR_A_CDC_CLK_RX_B2_CTL,
+ 1 << w->shift,
+ 1 << w->shift);
+ /* Toggle compander reset bits */
+ snd_soc_update_bits(codec,
+ SITAR_A_CDC_CLK_OTHR_RESET_CTL,
+ 1 << w->shift,
+ 1 << w->shift);
+ snd_soc_update_bits(codec,
+ SITAR_A_CDC_CLK_OTHR_RESET_CTL,
+ 1 << w->shift, 0);
+ sitar_config_gain_compander(codec, w->shift, 1, event);
+ /* Compander enable -> 0x370/0x378 */
+ snd_soc_update_bits(codec, SITAR_A_CDC_COMP1_B1_CTL +
+ w->shift * 8, 0x03, 0x03);
+ /* Update the RMS meter resampling */
+ snd_soc_update_bits(codec,
+ SITAR_A_CDC_COMP1_B3_CTL +
+ w->shift * 8, 0xFF, 0x01);
+ snd_soc_update_bits(codec,
+ SITAR_A_CDC_COMP1_B2_CTL +
+ w->shift * 8, 0xF0, 0x50);
+ usleep_range(COMP_BRINGUP_WAIT_TIME, COMP_BRINGUP_WAIT_TIME);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec,
+ SITAR_A_CDC_CLSG_CTL,
+ 0x11, 0x00);
+ if (w->shift == COMPANDER_1)
+ value = 0x22;
+ else
+ value = 0x11;
+ snd_soc_write(codec,
+ SITAR_A_CDC_CONN_CLSG_CTL, value);
+
+ snd_soc_update_bits(codec, SITAR_A_CDC_COMP1_B2_CTL +
+ w->shift * 8, 0x0F,
+ comp_samp_params[rate].peak_det_timeout);
+ snd_soc_update_bits(codec, SITAR_A_CDC_COMP1_B2_CTL +
+ w->shift * 8, 0xF0,
+ comp_samp_params[rate].rms_meter_div_fact);
+ snd_soc_update_bits(codec, SITAR_A_CDC_COMP1_B3_CTL +
+ w->shift * 8, 0xFF,
+ comp_samp_params[rate].rms_meter_resamp_fact);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, SITAR_A_CDC_COMP1_B1_CTL +
+ w->shift * 8, 0x03, 0x00);
+ /* Toggle compander reset bits */
+ snd_soc_update_bits(codec,
+ SITAR_A_CDC_CLK_OTHR_RESET_CTL,
+ 1 << w->shift,
+ 1 << w->shift);
+ snd_soc_update_bits(codec,
+ SITAR_A_CDC_CLK_OTHR_RESET_CTL,
+ 1 << w->shift, 0);
+ /* Disable compander clock */
+ snd_soc_update_bits(codec,
+ SITAR_A_CDC_CLK_RX_B2_CTL,
+ 1 << w->shift,
+ 0);
+ /* Restore the gain */
+ sitar_config_gain_compander(codec, w->shift,
+ sitar->comp_enabled[w->shift],
+ event);
+ snd_soc_update_bits(codec,
+ SITAR_A_CDC_CLSG_CTL,
+ 0x11, 0x11);
+ snd_soc_write(codec,
+ SITAR_A_CDC_CONN_CLSG_CTL, 0x14);
+ break;
+ }
+rtn:
+ return 0;
+}
+
+static int sitar_codec_dem_input_selection(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+ pr_debug("%s: compander#1->enable(%d) compander#2->enable(%d) reg(0x%x = 0x%x) event(%d)\n",
+ __func__, sitar->comp_enabled[COMPANDER_1],
+ sitar->comp_enabled[COMPANDER_2],
+ SITAR_A_CDC_RX1_B6_CTL + w->shift * 8,
+ snd_soc_read(codec, SITAR_A_CDC_RX1_B6_CTL + w->shift * 8),
+ event);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (sitar->comp_enabled[COMPANDER_1] ||
+ sitar->comp_enabled[COMPANDER_2])
+ snd_soc_update_bits(codec,
+ SITAR_A_CDC_RX1_B6_CTL +
+ w->shift * 8,
+ 1 << 5, 0);
+ else
+ snd_soc_update_bits(codec,
+ SITAR_A_CDC_RX1_B6_CTL +
+ w->shift * 8,
+ 1 << 5, 0x20);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec,
+ SITAR_A_CDC_RX1_B6_CTL + w->shift * 8,
+ 1 << 5, 0);
+ break;
+ }
+ return 0;
+}
+
static const char * const sitar_ear_pa_gain_text[] = {"POS_6_DB",
"POS_2_DB", "NEG_2P5_DB", "NEG_12_DB"};
@@ -652,6 +1009,10 @@
sitar_get_iir_band_audio_mixer, sitar_put_iir_band_audio_mixer),
SOC_SINGLE_MULTI_EXT("IIR2 Band5", IIR2, BAND5, 255, 0, 5,
sitar_get_iir_band_audio_mixer, sitar_put_iir_band_audio_mixer),
+ SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0,
+ sitar_get_compander, sitar_set_compander),
+ SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0,
+ sitar_get_compander, sitar_set_compander),
};
static const char *rx_mix1_text[] = {
@@ -1008,10 +1369,10 @@
vport_check_table[dai_id],
port_id,
sitar_p->dai)) {
- pr_info("%s: TX%u is used by other virtual port\n",
+ dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n",
__func__, port_id + 1);
mutex_unlock(&codec->mutex);
- return -EINVAL;
+ return 0;
}
widget->value |= 1 << port_id;
list_add_tail(&core->tx_chs[port_id].list,
@@ -1021,10 +1382,10 @@
list_del_init(&core->tx_chs[port_id].list);
} else {
if (enable)
- pr_info("%s: TX%u port is used by this virtual port\n",
+ dev_dbg(codec->dev, "%s: TX%u port is used by this virtual port\n",
__func__, port_id + 1);
else
- pr_info("%s: TX%u port is not used by this virtual port\n",
+ dev_dbg(codec->dev, "%s: TX%u port is not used by this virtual port\n",
__func__, port_id + 1);
/* avoid update power function */
mutex_unlock(&codec->mutex);
@@ -1086,9 +1447,13 @@
list_del_init(&core->rx_chs[port_id].list);
break;
case 1:
- if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
- &sitar_p->dai[AIF1_PB].wcd9xxx_ch_list))
- goto pr_err;
+ if (wcd9xxx_rx_vport_validation(port_id +
+ SITAR_RX_PORT_START_NUMBER,
+ &sitar_p->dai[AIF1_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+ goto rtn;
+ }
list_add_tail(&core->rx_chs[port_id].list,
&sitar_p->dai[AIF1_PB].wcd9xxx_ch_list);
break;
@@ -1098,14 +1463,10 @@
goto err;
}
-
+rtn:
snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
-
mutex_unlock(&codec->mutex);
return 0;
-pr_err:
- pr_err("%s: RX%u is used by current requesting AIF_PB itself\n",
- __func__, port_id + 1);
err:
mutex_unlock(&codec->mutex);
return -EINVAL;
@@ -1267,9 +1628,14 @@
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
+ struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
u16 lineout_gain_reg;
- pr_debug("%s %d %s\n", __func__, event, w->name);
+ pr_debug("%s %d %s comp2 enable %d\n", __func__, event, w->name,
+ sitar->comp_enabled[COMPANDER_2]);
+
+ if (sitar->comp_enabled[COMPANDER_2])
+ goto rtn;
switch (w->shift) {
case 0:
@@ -1311,6 +1677,7 @@
snd_soc_update_bits(codec, lineout_gain_reg, 0x10, 0x00);
break;
}
+rtn:
return 0;
}
@@ -2010,16 +2377,22 @@
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
+ struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
- pr_debug("%s %s %d\n", __func__, w->name, event);
+ pr_debug("%s %s %d comp#1 enable %d\n", __func__,
+ w->name, event, sitar->comp_enabled[COMPANDER_1]);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (w->reg == SITAR_A_RX_HPH_L_DAC_CTL) {
- snd_soc_update_bits(codec, SITAR_A_CDC_CONN_CLSG_CTL,
- 0x30, 0x20);
- snd_soc_update_bits(codec, SITAR_A_CDC_CONN_CLSG_CTL,
- 0x0C, 0x08);
+ if (!sitar->comp_enabled[COMPANDER_1]) {
+ snd_soc_update_bits(codec,
+ SITAR_A_CDC_CONN_CLSG_CTL,
+ 0x30, 0x20);
+ snd_soc_update_bits(codec,
+ SITAR_A_CDC_CONN_CLSG_CTL,
+ 0x0C, 0x08);
+ }
}
snd_soc_update_bits(codec, w->reg, 0x40, 0x40);
break;
@@ -2338,9 +2711,15 @@
SND_SOC_DAPM_MUX("DAC4 MUX", SND_SOC_NOPM, 0, 0,
&rx_dac4_mux),
- SND_SOC_DAPM_MIXER("RX1 CHAIN", SITAR_A_CDC_RX1_B6_CTL, 5, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("RX2 CHAIN", SITAR_A_CDC_RX2_B6_CTL, 5, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("RX3 CHAIN", SITAR_A_CDC_RX3_B6_CTL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("RX1 CHAIN", SND_SOC_NOPM, 0, 0, NULL,
+ 0, sitar_codec_dem_input_selection,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX2 CHAIN", SND_SOC_NOPM, 1, 0, NULL,
+ 0, sitar_codec_dem_input_selection,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX3 CHAIN", SND_SOC_NOPM, 2, 0, NULL,
+ 0, sitar_codec_dem_input_selection,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
&rx_mix1_inp1_mux),
@@ -2463,6 +2842,13 @@
sitar_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("COMP1_CLK", SND_SOC_NOPM, COMPANDER_1, 0,
+ sitar_config_compander, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("COMP2_CLK", SND_SOC_NOPM, COMPANDER_2, 0,
+ sitar_config_compander, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
/* Sidetone */
SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
SND_SOC_DAPM_PGA("IIR1", SITAR_A_CDC_CLK_SD_CTL, 0, 0, NULL, 0),
@@ -2592,6 +2978,10 @@
{"SLIM RX3", NULL, "SLIM RX3 MUX"},
{"SLIM RX4", NULL, "SLIM RX4 MUX"},
+ {"RX1 MIX1", NULL, "COMP2_CLK"},
+ {"RX2 MIX1", NULL, "COMP1_CLK"},
+ {"RX3 MIX1", NULL, "COMP1_CLK"},
+
/* Slimbus port 5 is non functional in Sitar 1.0 */
{"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
{"RX1 MIX1 INP1", "RX2", "SLIM RX2"},
@@ -2733,6 +3123,10 @@
if (reg == SITAR_A_CDC_RX1_VOL_CTL_B2_CTL + (8 * i))
return 1;
}
+
+ if ((reg == SITAR_A_CDC_COMP1_SHUT_DOWN_STATUS) ||
+ (reg == SITAR_A_CDC_COMP2_SHUT_DOWN_STATUS))
+ return 1;
return 0;
}
@@ -3191,6 +3585,7 @@
struct snd_soc_codec *codec = dai->codec;
struct sitar_priv *sitar = snd_soc_codec_get_drvdata(dai->codec);
u8 path, shift;
+ u32 compander_fs;
u16 tx_fs_reg, rx_fs_reg;
u8 tx_fs_rate, rx_fs_rate, rx_state, tx_state;
@@ -3200,18 +3595,32 @@
case 8000:
tx_fs_rate = 0x00;
rx_fs_rate = 0x00;
+ compander_fs = COMPANDER_FS_8KHZ;
break;
case 16000:
tx_fs_rate = 0x01;
rx_fs_rate = 0x20;
+ compander_fs = COMPANDER_FS_16KHZ;
break;
case 32000:
tx_fs_rate = 0x02;
rx_fs_rate = 0x40;
+ compander_fs = COMPANDER_FS_32KHZ;
break;
case 48000:
tx_fs_rate = 0x03;
rx_fs_rate = 0x60;
+ compander_fs = COMPANDER_FS_48KHZ;
+ break;
+ case 96000:
+ tx_fs_rate = 0x04;
+ rx_fs_rate = 0x80;
+ compander_fs = COMPANDER_FS_96KHZ;
+ break;
+ case 192000:
+ tx_fs_rate = 0x05;
+ rx_fs_rate = 0xa0;
+ compander_fs = COMPANDER_FS_192KHZ;
break;
default:
pr_err("%s: Invalid sampling rate %d\n", __func__,
@@ -3285,6 +3694,9 @@
+ (BITS_PER_REG*(path-1));
snd_soc_update_bits(codec, rx_fs_reg,
0xE0, rx_fs_rate);
+ if (comp_rx_path[shift] < COMPANDER_MAX)
+ sitar->comp_fs[comp_rx_path[shift]]
+ = compander_fs;
}
}
if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
@@ -5484,6 +5896,11 @@
if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_I2C)
sitar_i2c_codec_init_reg(codec);
+ for (i = 0; i < COMPANDER_MAX; i++) {
+ sitar->comp_enabled[i] = 0;
+ sitar->comp_fs[i] = COMPANDER_FS_48KHZ;
+ }
+
ret = sitar_handle_pdata(sitar);
if (IS_ERR_VALUE(ret)) {
pr_err("%s: bad pdata\n", __func__);
diff --git a/sound/soc/codecs/wcd9306.c b/sound/soc/codecs/wcd9306.c
index a7069a6..8ae6050 100644
--- a/sound/soc/codecs/wcd9306.c
+++ b/sound/soc/codecs/wcd9306.c
@@ -60,20 +60,19 @@
#define BITS_PER_REG 8
/* This actual number of TX ports supported in slimbus slave */
#define TAPAN_TX_PORT_NUMBER 16
+#define TAPAN_RX_PORT_START_NUMBER 16
/* Nummer of TX ports actually connected from Slimbus slave to codec Digital */
#define TAPAN_SLIM_CODEC_TX_PORTS 5
#define TAPAN_I2S_MASTER_MODE_MASK 0x08
#define TAPAN_MCLK_CLK_12P288MHZ 12288000
-#define TAPAN_MCLK_CLK_9P6HZ 9600000
+#define TAPAN_MCLK_CLK_9P6MHZ 9600000
#define TAPAN_SLIM_CLOSE_TIMEOUT 1000
#define TAPAN_SLIM_IRQ_OVERFLOW (1 << 0)
#define TAPAN_SLIM_IRQ_UNDERFLOW (1 << 1)
#define TAPAN_SLIM_IRQ_PORT_CLOSED (1 << 2)
-#define TAPAN_MCLK_CLK_12P288MHZ 12288000
-#define TAPAN_MCLK_CLK_9P6HZ 9600000
enum {
AIF1_PB = 0,
AIF1_CAP,
@@ -155,11 +154,11 @@
static struct hpf_work tx_hpf_work[NUM_DECIMATORS];
static const struct wcd9xxx_ch tapan_rx_chs[TAPAN_RX_MAX] = {
- WCD9XXX_CH(16, 0),
- WCD9XXX_CH(17, 1),
- WCD9XXX_CH(18, 2),
- WCD9XXX_CH(19, 3),
- WCD9XXX_CH(20, 4),
+ WCD9XXX_CH(TAPAN_RX_PORT_START_NUMBER, 0),
+ WCD9XXX_CH(TAPAN_RX_PORT_START_NUMBER + 1, 1),
+ WCD9XXX_CH(TAPAN_RX_PORT_START_NUMBER + 2, 2),
+ WCD9XXX_CH(TAPAN_RX_PORT_START_NUMBER + 3, 3),
+ WCD9XXX_CH(TAPAN_RX_PORT_START_NUMBER + 4, 4),
};
static const struct wcd9xxx_ch tapan_tx_chs[TAPAN_TX_MAX] = {
@@ -461,8 +460,8 @@
kcontrol->private_value)->shift;
ucontrol->value.integer.value[0] =
- snd_soc_read(codec, (TAPAN_A_CDC_IIR1_CTL + 16 * iir_idx)) &
- (1 << band_idx);
+ (snd_soc_read(codec, (TAPAN_A_CDC_IIR1_CTL + 16 * iir_idx)) &
+ (1 << band_idx)) != 0;
dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__,
iir_idx, band_idx,
@@ -485,23 +484,54 @@
snd_soc_update_bits(codec, (TAPAN_A_CDC_IIR1_CTL + 16 * iir_idx),
(1 << band_idx), (value << band_idx));
- dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__,
- iir_idx, band_idx, value);
+ pr_debug("%s: IIR #%d band #%d enable %d\n", __func__,
+ iir_idx, band_idx,
+ ((snd_soc_read(codec, (TAPAN_A_CDC_IIR1_CTL + 16 * iir_idx)) &
+ (1 << band_idx)) != 0));
return 0;
}
static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec,
int iir_idx, int band_idx,
int coeff_idx)
{
+ uint32_t value = 0;
+
/* Address does not automatically update if reading */
snd_soc_write(codec,
(TAPAN_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
- (band_idx * BAND_MAX + coeff_idx) & 0x1F);
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t)) & 0x7F);
+
+ value |= snd_soc_read(codec,
+ (TAPAN_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx));
+
+ snd_soc_write(codec,
+ (TAPAN_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 1) & 0x7F);
+
+ value |= (snd_soc_read(codec,
+ (TAPAN_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx)) << 8);
+
+ snd_soc_write(codec,
+ (TAPAN_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 2) & 0x7F);
+
+ value |= (snd_soc_read(codec,
+ (TAPAN_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx)) << 16);
+
+ snd_soc_write(codec,
+ (TAPAN_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 3) & 0x7F);
/* Mask bits top 2 bits since they are reserved */
- return ((snd_soc_read(codec,
- (TAPAN_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx)) << 24)) &
- 0x3FFFFFFF;
+ value |= ((snd_soc_read(codec,
+ (TAPAN_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx)) & 0x3F) << 24);
+
+ return value;
+
}
static int tapan_get_iir_band_audio_mixer(
@@ -545,13 +575,19 @@
static void set_iir_band_coeff(struct snd_soc_codec *codec,
int iir_idx, int band_idx,
- int coeff_idx, uint32_t value)
+ uint32_t value)
{
- /* Mask top 3 bits, 6-8 are reserved */
- /* Update address manually each time */
snd_soc_write(codec,
- (TAPAN_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
- (band_idx * BAND_MAX + coeff_idx) & 0x1F);
+ (TAPAN_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx),
+ (value & 0xFF));
+
+ snd_soc_write(codec,
+ (TAPAN_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx),
+ (value >> 8) & 0xFF);
+
+ snd_soc_write(codec,
+ (TAPAN_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx),
+ (value >> 16) & 0xFF);
/* Mask top 2 bits, 7-8 are reserved */
snd_soc_write(codec,
@@ -570,15 +606,21 @@
int band_idx = ((struct soc_multi_mixer_control *)
kcontrol->private_value)->shift;
- set_iir_band_coeff(codec, iir_idx, band_idx, 0,
+ /* Mask top bit it is reserved */
+ /* Updates addr automatically for each B2 write */
+ snd_soc_write(codec,
+ (TAPAN_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+ (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F);
+
+ set_iir_band_coeff(codec, iir_idx, band_idx,
ucontrol->value.integer.value[0]);
- set_iir_band_coeff(codec, iir_idx, band_idx, 1,
+ set_iir_band_coeff(codec, iir_idx, band_idx,
ucontrol->value.integer.value[1]);
- set_iir_band_coeff(codec, iir_idx, band_idx, 2,
+ set_iir_band_coeff(codec, iir_idx, band_idx,
ucontrol->value.integer.value[2]);
- set_iir_band_coeff(codec, iir_idx, band_idx, 3,
+ set_iir_band_coeff(codec, iir_idx, band_idx,
ucontrol->value.integer.value[3]);
- set_iir_band_coeff(codec, iir_idx, band_idx, 4,
+ set_iir_band_coeff(codec, iir_idx, band_idx,
ucontrol->value.integer.value[4]);
dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n"
@@ -1367,7 +1409,7 @@
dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n",
__func__, port_id + 1);
mutex_unlock(&codec->mutex);
- return -EINVAL;
+ return 0;
}
widget->value |= 1 << port_id;
list_add_tail(&core->tx_chs[port_id].list,
@@ -1452,23 +1494,35 @@
list_del_init(&core->rx_chs[port_id].list);
break;
case 1:
- if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
- &tapan_p->dai[AIF1_PB].wcd9xxx_ch_list))
- goto pr_err;
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TAPAN_RX_PORT_START_NUMBER,
+ &tapan_p->dai[AIF1_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+ goto rtn;
+ }
list_add_tail(&core->rx_chs[port_id].list,
&tapan_p->dai[AIF1_PB].wcd9xxx_ch_list);
break;
case 2:
- if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
- &tapan_p->dai[AIF2_PB].wcd9xxx_ch_list))
- goto pr_err;
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TAPAN_RX_PORT_START_NUMBER,
+ &tapan_p->dai[AIF2_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+ goto rtn;
+ }
list_add_tail(&core->rx_chs[port_id].list,
&tapan_p->dai[AIF2_PB].wcd9xxx_ch_list);
break;
case 3:
- if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
- &tapan_p->dai[AIF3_PB].wcd9xxx_ch_list))
- goto pr_err;
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TAPAN_RX_PORT_START_NUMBER,
+ &tapan_p->dai[AIF3_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+ goto rtn;
+ }
list_add_tail(&core->rx_chs[port_id].list,
&tapan_p->dai[AIF3_PB].wcd9xxx_ch_list);
break;
@@ -1477,13 +1531,10 @@
goto err;
}
+rtn:
snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
-
mutex_unlock(&codec->mutex);
return 0;
-pr_err:
- pr_err("%s: RX%u is used by current requesting AIF_PB itself\n",
- __func__, port_id + 1);
err:
mutex_unlock(&codec->mutex);
return -EINVAL;
@@ -1814,7 +1865,7 @@
release_firmware(fw);
break;
- case SND_SOC_DAPM_POST_PMD:
+ case SND_SOC_DAPM_PRE_PMD:
msleep(40);
snd_soc_update_bits(codec, TAPAN_A_CDC_ANC1_B1_CTL, 0x01, 0x00);
snd_soc_update_bits(codec, TAPAN_A_CDC_ANC2_B1_CTL, 0x02, 0x00);
@@ -2286,12 +2337,11 @@
snd_soc_update_bits(codec,
TAPAN_A_RX_HPH_CNP_EN, 0x30, 0x00);
msleep(40);
- }
- if (w->shift == 5) {
snd_soc_update_bits(codec,
TAPAN_A_TX_7_MBHC_EN, 0x80, 00);
ret |= tapan_codec_enable_anc(w, kcontrol, event);
}
+ break;
case SND_SOC_DAPM_POST_PMD:
ret = tapan_hph_pa_event(w, kcontrol, event);
break;
@@ -4218,9 +4268,6 @@
*/
TAPAN_REG_VAL(TAPAN_A_MICB_2_MBHC, 0x41),
- /* not needed if MBHC is not needed */
- /* Disable TX7 internal biasing path which can cause leakage */
- TAPAN_REG_VAL(TAPAN_A_TX_SUP_SWITCH_CTRL_1, 0xBF),
};
static const struct tapan_reg_mask_val tapan_2_x_reg_reset_values[] = {
@@ -4278,6 +4325,23 @@
snd_soc_write(codec, TAPAN_A_SPKR_DRV_EN, 0xEF);
}
+static void tapan_update_reg_mclk_rate(struct wcd9xxx *wcd9xxx)
+{
+ struct snd_soc_codec *codec;
+
+ codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+ dev_dbg(codec->dev, "%s: MCLK Rate = %x\n",
+ __func__, wcd9xxx->mclk_rate);
+
+ if (wcd9xxx->mclk_rate == TAPAN_MCLK_CLK_12P288MHZ) {
+ snd_soc_update_bits(codec, TAPAN_A_CHIP_CTL, 0x06, 0x0);
+ snd_soc_update_bits(codec, TAPAN_A_RX_COM_TIMER_DIV, 0x01,
+ 0x01);
+ } else if (wcd9xxx->mclk_rate == TAPAN_MCLK_CLK_9P6MHZ) {
+ snd_soc_update_bits(codec, TAPAN_A_CHIP_CTL, 0x06, 0x2);
+ }
+}
+
static const struct tapan_reg_mask_val tapan_codec_reg_init_val[] = {
/* Initialize current threshold to 350MA
* number of wait and run cycles to 4096
@@ -4348,29 +4412,38 @@
tapan_codec_reg_init_val[i].mask,
tapan_codec_reg_init_val[i].val);
}
-
-static int tapan_setup_irqs(struct tapan_priv *tapan)
+static void tapan_slim_interface_init_reg(struct snd_soc_codec *codec)
{
int i;
- int ret = 0;
- struct snd_soc_codec *codec = tapan->codec;
-
- ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
- tapan_slimbus_irq, "SLIMBUS Slave", tapan);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- WCD9XXX_IRQ_SLIMBUS);
- goto exit;
- }
for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++)
wcd9xxx_interface_reg_write(codec->control_data,
TAPAN_SLIM_PGD_PORT_INT_EN0 + i,
0xFF);
-exit:
+}
+
+static int tapan_setup_irqs(struct tapan_priv *tapan)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = tapan->codec;
+
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
+ tapan_slimbus_irq, "SLIMBUS Slave", tapan);
+ if (ret)
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_SLIMBUS);
+ else
+ tapan_slim_interface_init_reg(codec);
+
return ret;
}
+static void tapan_cleanup_irqs(struct tapan_priv *tapan)
+{
+ struct snd_soc_codec *codec = tapan->codec;
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, tapan);
+}
+
int tapan_hs_detect(struct snd_soc_codec *codec,
struct wcd9xxx_mbhc_config *mbhc_cfg)
{
@@ -4379,9 +4452,74 @@
}
EXPORT_SYMBOL_GPL(tapan_hs_detect);
+static int tapan_post_reset_cb(struct wcd9xxx *wcd9xxx)
+{
+ int ret = 0;
+ int rco_clk_rate;
+ struct snd_soc_codec *codec;
+ struct tapan_priv *tapan;
+
+ codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+ tapan = snd_soc_codec_get_drvdata(codec);
+ mutex_lock(&codec->mutex);
+ WCD9XXX_BCL_LOCK(&tapan->resmgr);
+
+ if (codec->reg_def_copy) {
+ pr_debug("%s: Update ASOC cache", __func__);
+ kfree(codec->reg_cache);
+ codec->reg_cache = kmemdup(codec->reg_def_copy,
+ codec->reg_size, GFP_KERNEL);
+ if (!codec->reg_cache) {
+ pr_err("%s: Cache update failed!\n", __func__);
+ WCD9XXX_BCL_UNLOCK(&tapan->resmgr);
+ mutex_unlock(&codec->mutex);
+ return -ENOMEM;
+ }
+ }
+
+ wcd9xxx_resmgr_post_ssr(&tapan->resmgr);
+ if (spkr_drv_wrnd == 1)
+ snd_soc_update_bits(codec, TAPAN_A_SPKR_DRV_EN, 0x80, 0x80);
+ WCD9XXX_BCL_UNLOCK(&tapan->resmgr);
+
+ tapan_update_reg_defaults(codec);
+ tapan_update_reg_mclk_rate(wcd9xxx);
+ tapan_codec_init_reg(codec);
+ ret = tapan_handle_pdata(tapan);
+ if (IS_ERR_VALUE(ret))
+ pr_err("%s: bad pdata\n", __func__);
+
+ tapan_slim_interface_init_reg(codec);
+
+ wcd9xxx_mbhc_deinit(&tapan->mbhc);
+
+ if (TAPAN_IS_1_0(wcd9xxx->version))
+ rco_clk_rate = TAPAN_MCLK_CLK_12P288MHZ;
+ else
+ rco_clk_rate = TAPAN_MCLK_CLK_9P6MHZ;
+
+ ret = wcd9xxx_mbhc_init(&tapan->mbhc, &tapan->resmgr, codec, NULL,
+ WCD9XXX_MBHC_VERSION_TAPAN,
+ rco_clk_rate);
+ if (ret)
+ pr_err("%s: mbhc init failed %d\n", __func__, ret);
+ else
+ wcd9xxx_mbhc_start(&tapan->mbhc, tapan->mbhc.mbhc_cfg);
+ mutex_unlock(&codec->mutex);
+ return ret;
+}
+
static struct wcd9xxx_reg_address tapan_reg_address = {
};
+static int wcd9xxx_ssr_register(struct wcd9xxx *control,
+ int (*post_reset_cb)(struct wcd9xxx *wcd9xxx), void *priv)
+{
+ control->post_reset = post_reset_cb;
+ control->ssr_priv = priv;
+ return 0;
+}
+
static int tapan_codec_probe(struct snd_soc_codec *codec)
{
struct wcd9xxx *control;
@@ -4390,12 +4528,14 @@
struct wcd9xxx *wcd9xxx;
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret = 0;
- int i;
+ int i, rco_clk_rate;
void *ptr = NULL;
codec->control_data = dev_get_drvdata(codec->dev->parent);
control = codec->control_data;
+ wcd9xxx_ssr_register(control, tapan_post_reset_cb, (void *)codec);
+
dev_info(codec->dev, "%s()\n", __func__);
tapan = kzalloc(sizeof(struct tapan_priv), GFP_KERNEL);
@@ -4426,8 +4566,14 @@
return ret;
}
- ret = wcd9xxx_mbhc_init(&tapan->mbhc, &tapan->resmgr, codec,
- WCD9XXX_MBHC_VERSION_TAPAN);
+ if (TAPAN_IS_1_0(control->version))
+ rco_clk_rate = TAPAN_MCLK_CLK_12P288MHZ;
+ else
+ rco_clk_rate = TAPAN_MCLK_CLK_9P6MHZ;
+
+ ret = wcd9xxx_mbhc_init(&tapan->mbhc, &tapan->resmgr, codec, NULL,
+ WCD9XXX_MBHC_VERSION_TAPAN,
+ rco_clk_rate);
if (ret) {
pr_err("%s: mbhc init failed %d\n", __func__, ret);
return ret;
@@ -4443,17 +4589,7 @@
tapan->aux_l_gain = 0x1F;
tapan->aux_r_gain = 0x1F;
tapan_update_reg_defaults(codec);
-
- dev_dbg(codec->dev, "%s: MCLK Rate = %x\n",
- __func__, wcd9xxx->mclk_rate);
-
- if (wcd9xxx->mclk_rate == TAPAN_MCLK_CLK_12P288MHZ) {
- snd_soc_update_bits(codec, TAPAN_A_CHIP_CTL, 0x06, 0x0);
- snd_soc_update_bits(codec, TAPAN_A_RX_COM_TIMER_DIV, 0x01,
- 0x01);
- } else if (wcd9xxx->mclk_rate == TAPAN_MCLK_CLK_9P6HZ) {
- snd_soc_update_bits(codec, TAPAN_A_CHIP_CTL, 0x06, 0x2);
- }
+ tapan_update_reg_mclk_rate(wcd9xxx);
tapan_codec_init_reg(codec);
ret = tapan_handle_pdata(tapan);
if (IS_ERR_VALUE(ret)) {
@@ -4512,6 +4648,10 @@
mutex_unlock(&dapm->codec->mutex);
codec->ignore_pmdown_time = 1;
+
+ if (ret)
+ tapan_cleanup_irqs(tapan);
+
return ret;
err_pdata:
@@ -4532,6 +4672,9 @@
wcd9xxx_resmgr_put_bandgap(&tapan->resmgr,
WCD9XXX_BANDGAP_AUDIO_MODE);
WCD9XXX_BCL_UNLOCK(&tapan->resmgr);
+
+ tapan_cleanup_irqs(tapan);
+
/* cleanup MBHC */
wcd9xxx_mbhc_deinit(&tapan->mbhc);
/* cleanup resmgr */
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index b8a4a86..69e4cca 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -55,9 +55,12 @@
#define MBHC_FW_READ_ATTEMPTS 15
#define MBHC_FW_READ_TIMEOUT 2000000
#define MBHC_VDDIO_SWITCH_WAIT_MS 10
+#define COMP_DIGITAL_DB_GAIN_APPLY(a, b) \
+ (((a) <= 0) ? ((a) - b) : (a))
#define SLIM_CLOSE_TIMEOUT 1000
-
+/* The wait time value comes from codec HW specification */
+#define COMP_BRINGUP_WAIT_TIME 2000
enum {
MBHC_USE_HPHL_TRIGGER = 1,
MBHC_USE_MB_TRIGGER = 2
@@ -99,9 +102,7 @@
RX_MIX1_INP_SEL_RX6,
RX_MIX1_INP_SEL_RX7,
};
-
-#define TABLA_COMP_DIGITAL_GAIN_HP_OFFSET 3
-#define TABLA_COMP_DIGITAL_GAIN_LINEOUT_OFFSET 6
+#define MAX_PA_GAIN_OPTIONS 13
#define TABLA_MCLK_RATE_12288KHZ 12288000
#define TABLA_MCLK_RATE_9600KHZ 9600000
@@ -128,6 +129,8 @@
#define TABLA_GPIO_IRQ_DEBOUNCE_TIME_US 5000
#define TABLA_MBHC_GND_MIC_SWAP_THRESHOLD 2
+#define TABLA_RX_PORT_START_NUMBER 10
+
#define TABLA_ACQUIRE_LOCK(x) do { \
mutex_lock_nested(&x, SINGLE_DEPTH_NESTING); \
@@ -220,6 +223,28 @@
u32 shutdown_timeout;
};
+struct comp_dgtl_gain_offset {
+ u8 whole_db_gain;
+ u8 half_db_gain;
+};
+
+static const struct comp_dgtl_gain_offset
+ comp_dgtl_gain[MAX_PA_GAIN_OPTIONS] = {
+ {0, 0},
+ {1, 1},
+ {3, 0},
+ {4, 1},
+ {6, 0},
+ {7, 1},
+ {9, 0},
+ {10, 1},
+ {12, 0},
+ {13, 1},
+ {15, 0},
+ {16, 1},
+ {18, 0},
+};
+
/* Data used by MBHC */
struct mbhc_internal_cal_data {
u16 dce_z;
@@ -279,13 +304,13 @@
static struct hpf_work tx_hpf_work[NUM_DECIMATORS];
static const struct wcd9xxx_ch tabla_rx_chs[TABLA_RX_MAX] = {
- WCD9XXX_CH(10, 0),
- WCD9XXX_CH(11, 1),
- WCD9XXX_CH(12, 2),
- WCD9XXX_CH(13, 3),
- WCD9XXX_CH(14, 4),
- WCD9XXX_CH(15, 5),
- WCD9XXX_CH(16, 6)
+ WCD9XXX_CH(TABLA_RX_PORT_START_NUMBER, 0),
+ WCD9XXX_CH(TABLA_RX_PORT_START_NUMBER + 1, 1),
+ WCD9XXX_CH(TABLA_RX_PORT_START_NUMBER + 2, 2),
+ WCD9XXX_CH(TABLA_RX_PORT_START_NUMBER + 3, 3),
+ WCD9XXX_CH(TABLA_RX_PORT_START_NUMBER + 4, 4),
+ WCD9XXX_CH(TABLA_RX_PORT_START_NUMBER + 5, 5),
+ WCD9XXX_CH(TABLA_RX_PORT_START_NUMBER + 6, 6)
};
static const struct wcd9xxx_ch tabla_tx_chs[TABLA_TX_MAX] = {
@@ -377,6 +402,7 @@
/*compander*/
int comp_enabled[COMPANDER_MAX];
u32 comp_fs[COMPANDER_MAX];
+ u8 comp_gain_offset[TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS - 1];
/* Maintain the status of AUX PGA */
int aux_pga_cnt;
@@ -547,7 +573,10 @@
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+ mutex_lock(&codec->dapm.codec->mutex);
ucontrol->value.integer.value[0] = (tabla->anc_func == true ? 1 : 0);
+ mutex_unlock(&codec->dapm.codec->mutex);
return 0;
}
@@ -802,34 +831,51 @@
static int tabla_compander_gain_offset(
struct snd_soc_codec *codec, u32 enable,
- unsigned int reg, int mask, int event, u32 comp)
+ unsigned int pa_reg, unsigned int vol_reg,
+ int mask, int event,
+ struct comp_dgtl_gain_offset *gain_offset,
+ int index)
{
- int pa_mode = snd_soc_read(codec, reg) & mask;
- int gain_offset = 0;
- /* if PMU && enable is 1-> offset is 3
- * if PMU && enable is 0-> offset is 0
- * if PMD && pa_mode is PA -> offset is 0: PMU compander is off
- * if PMD && pa_mode is comp -> offset is -3: PMU compander is on.
- */
+ unsigned int pa_gain = snd_soc_read(codec, pa_reg);
+ unsigned int digital_vol = snd_soc_read(codec, vol_reg);
+ int pa_mode = pa_gain & mask;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: pa_gain(0x%x=0x%x)digital_vol(0x%x=0x%x)event(0x%x) index(%d)\n",
+ __func__, pa_reg, pa_gain, vol_reg, digital_vol, event, index);
+ if (((pa_gain & 0xF) + 1) > ARRAY_SIZE(comp_dgtl_gain) ||
+ (index >= ARRAY_SIZE(tabla->comp_gain_offset))) {
+ pr_err("%s: Out of array boundary\n", __func__);
+ return -EINVAL;
+ }
if (SND_SOC_DAPM_EVENT_ON(event) && (enable != 0)) {
- if (comp == COMPANDER_1)
- gain_offset = TABLA_COMP_DIGITAL_GAIN_HP_OFFSET;
- if (comp == COMPANDER_2)
- gain_offset = TABLA_COMP_DIGITAL_GAIN_LINEOUT_OFFSET;
+ gain_offset->whole_db_gain = COMP_DIGITAL_DB_GAIN_APPLY(
+ (digital_vol - comp_dgtl_gain[pa_gain & 0xF].whole_db_gain),
+ comp_dgtl_gain[pa_gain & 0xF].half_db_gain);
+ pr_debug("%s: listed whole_db_gain:0x%x, adjusted whole_db_gain:0x%x\n",
+ __func__, comp_dgtl_gain[pa_gain & 0xF].whole_db_gain,
+ gain_offset->whole_db_gain);
+ gain_offset->half_db_gain =
+ comp_dgtl_gain[pa_gain & 0xF].half_db_gain;
+ tabla->comp_gain_offset[index] = digital_vol -
+ gain_offset->whole_db_gain ;
}
if (SND_SOC_DAPM_EVENT_OFF(event) && (pa_mode == 0)) {
- if (comp == COMPANDER_1)
- gain_offset = -TABLA_COMP_DIGITAL_GAIN_HP_OFFSET;
- if (comp == COMPANDER_2)
- gain_offset = -TABLA_COMP_DIGITAL_GAIN_LINEOUT_OFFSET;
-
+ gain_offset->whole_db_gain = digital_vol +
+ tabla->comp_gain_offset[index];
+ pr_debug("%s: listed whole_db_gain:0x%x, adjusted whole_db_gain:0x%x\n",
+ __func__, comp_dgtl_gain[pa_gain & 0xF].whole_db_gain,
+ gain_offset->whole_db_gain);
+ gain_offset->half_db_gain = 0;
}
- pr_debug("%s: compander #%d gain_offset %d\n",
- __func__, comp + 1, gain_offset);
- return gain_offset;
-}
+ pr_debug("%s: half_db_gain(%d)whole_db_gain(%d)comp_gain_offset[%d](%d)\n",
+ __func__, gain_offset->half_db_gain,
+ gain_offset->whole_db_gain, index,
+ tabla->comp_gain_offset[index]);
+ return 0;
+}
static int tabla_config_gain_compander(
struct snd_soc_codec *codec,
@@ -837,8 +883,7 @@
{
int value = 0;
int mask = 1 << 4;
- int gain = 0;
- int gain_offset;
+ struct comp_dgtl_gain_offset gain_offset = {0, 0};
if (compander >= COMPANDER_MAX) {
pr_err("%s: Error, invalid compander channel\n", __func__);
return -EINVAL;
@@ -848,43 +893,61 @@
value = 1 << 4;
if (compander == COMPANDER_1) {
- gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_HPH_L_GAIN, mask, event, compander);
+ tabla_compander_gain_offset(codec, enable,
+ TABLA_A_RX_HPH_L_GAIN,
+ TABLA_A_CDC_RX1_VOL_CTL_B2_CTL,
+ mask, event, &gain_offset, 0);
snd_soc_update_bits(codec, TABLA_A_RX_HPH_L_GAIN, mask, value);
- gain = snd_soc_read(codec, TABLA_A_CDC_RX1_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX1_VOL_CTL_B2_CTL,
- 0xFF, gain - gain_offset);
- gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_HPH_R_GAIN, mask, event, compander);
+ 0xFF, gain_offset.whole_db_gain);
+ snd_soc_update_bits(codec, TABLA_A_CDC_RX1_B6_CTL,
+ 0x02, gain_offset.half_db_gain);
+ tabla_compander_gain_offset(codec, enable,
+ TABLA_A_RX_HPH_R_GAIN,
+ TABLA_A_CDC_RX2_VOL_CTL_B2_CTL,
+ mask, event, &gain_offset, 1);
snd_soc_update_bits(codec, TABLA_A_RX_HPH_R_GAIN, mask, value);
- gain = snd_soc_read(codec, TABLA_A_CDC_RX2_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX2_VOL_CTL_B2_CTL,
- 0xFF, gain - gain_offset);
+ 0xFF, gain_offset.whole_db_gain);
+ snd_soc_update_bits(codec, TABLA_A_CDC_RX2_B6_CTL,
+ 0x02, gain_offset.half_db_gain);
} else if (compander == COMPANDER_2) {
- gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_LINE_1_GAIN, mask, event, compander);
+ tabla_compander_gain_offset(codec, enable,
+ TABLA_A_RX_LINE_1_GAIN,
+ TABLA_A_CDC_RX3_VOL_CTL_B2_CTL,
+ mask, event, &gain_offset, 2);
snd_soc_update_bits(codec, TABLA_A_RX_LINE_1_GAIN, mask, value);
- gain = snd_soc_read(codec, TABLA_A_CDC_RX3_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX3_VOL_CTL_B2_CTL,
- 0xFF, gain - gain_offset);
- gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_LINE_3_GAIN, mask, event, compander);
+ 0xFF, gain_offset.whole_db_gain);
+ snd_soc_update_bits(codec, TABLA_A_CDC_RX3_B6_CTL,
+ 0x02, gain_offset.half_db_gain);
+ tabla_compander_gain_offset(codec, enable,
+ TABLA_A_RX_LINE_3_GAIN,
+ TABLA_A_CDC_RX4_VOL_CTL_B2_CTL,
+ mask, event, &gain_offset, 3);
snd_soc_update_bits(codec, TABLA_A_RX_LINE_3_GAIN, mask, value);
- gain = snd_soc_read(codec, TABLA_A_CDC_RX4_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX4_VOL_CTL_B2_CTL,
- 0xFF, gain - gain_offset);
- gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_LINE_2_GAIN, mask, event, compander);
+ 0xFF, gain_offset.whole_db_gain);
+ snd_soc_update_bits(codec, TABLA_A_CDC_RX4_B6_CTL,
+ 0x02, gain_offset.half_db_gain);
+ tabla_compander_gain_offset(codec, enable,
+ TABLA_A_RX_LINE_2_GAIN,
+ TABLA_A_CDC_RX5_VOL_CTL_B2_CTL,
+ mask, event, &gain_offset, 4);
snd_soc_update_bits(codec, TABLA_A_RX_LINE_2_GAIN, mask, value);
- gain = snd_soc_read(codec, TABLA_A_CDC_RX5_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX5_VOL_CTL_B2_CTL,
- 0xFF, gain - gain_offset);
- gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_LINE_4_GAIN, mask, event, compander);
+ 0xFF, gain_offset.whole_db_gain);
+ snd_soc_update_bits(codec, TABLA_A_CDC_RX5_B6_CTL,
+ 0x02, gain_offset.half_db_gain);
+ tabla_compander_gain_offset(codec, enable,
+ TABLA_A_RX_LINE_4_GAIN,
+ TABLA_A_CDC_RX6_VOL_CTL_B2_CTL,
+ mask, event, &gain_offset, 5);
snd_soc_update_bits(codec, TABLA_A_RX_LINE_4_GAIN, mask, value);
- gain = snd_soc_read(codec, TABLA_A_CDC_RX6_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX6_VOL_CTL_B2_CTL,
- 0xFF, gain - gain_offset);
+ 0xFF, gain_offset.whole_db_gain);
+ snd_soc_update_bits(codec, TABLA_A_CDC_RX6_B6_CTL,
+ 0x02, gain_offset.half_db_gain);
}
return 0;
}
@@ -921,7 +984,6 @@
return 0;
}
-
static int tabla_config_compander(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
@@ -929,106 +991,161 @@
struct snd_soc_codec *codec = w->codec;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
u32 rate = tabla->comp_fs[w->shift];
- u32 status;
- unsigned long timeout;
- pr_debug("%s: compander #%d enable %d event %d\n",
+
+ pr_debug("%s: compander #%d enable %d event %d widget name %s\n",
__func__, w->shift + 1,
- tabla->comp_enabled[w->shift], event);
+ tabla->comp_enabled[w->shift], event , w->name);
+ if (tabla->comp_enabled[w->shift] == 0)
+ goto rtn;
+ if ((w->shift == COMPANDER_1) && (tabla->anc_func)) {
+ pr_debug("%s: ANC is enabled so compander #%d cannot be enabled\n",
+ __func__, w->shift + 1);
+ goto rtn;
+ }
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- if (tabla->comp_enabled[w->shift] != 0) {
- /* Enable both L/R compander clocks */
- snd_soc_update_bits(codec,
- TABLA_A_CDC_CLK_RX_B2_CTL,
- 1 << comp_shift[w->shift],
- 1 << comp_shift[w->shift]);
- /* Clear the HALT for the compander*/
- snd_soc_update_bits(codec,
- TABLA_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 1 << 2, 0);
- /* Toggle compander reset bits*/
- snd_soc_update_bits(codec,
- TABLA_A_CDC_CLK_OTHR_RESET_CTL,
- 1 << comp_shift[w->shift],
- 1 << comp_shift[w->shift]);
- snd_soc_update_bits(codec,
- TABLA_A_CDC_CLK_OTHR_RESET_CTL,
- 1 << comp_shift[w->shift], 0);
- tabla_config_gain_compander(codec, w->shift, 1, event);
- /* Compander enable -> 0x370/0x378*/
- snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 0x03, 0x03);
- /* Update the RMS meter resampling*/
- snd_soc_update_bits(codec,
- TABLA_A_CDC_COMP1_B3_CTL +
- w->shift * 8, 0xFF, 0x01);
- snd_soc_update_bits(codec,
- TABLA_A_CDC_COMP1_B2_CTL +
- w->shift * 8, 0xF0, 0x50);
- /* Wait for 1ms*/
- usleep_range(5000, 5000);
- }
+ /* Update compander sample rate */
+ snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_FS_CFG +
+ w->shift * 8, 0x07, rate);
+ /* Enable both L/R compander clocks */
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_CLK_RX_B2_CTL,
+ 1 << comp_shift[w->shift],
+ 1 << comp_shift[w->shift]);
+ /* Toggle compander reset bits */
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_CLK_OTHR_RESET_CTL,
+ 1 << comp_shift[w->shift],
+ 1 << comp_shift[w->shift]);
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_CLK_OTHR_RESET_CTL,
+ 1 << comp_shift[w->shift], 0);
+ tabla_config_gain_compander(codec, w->shift, 1, event);
+ /* Compander enable -> 0x370/0x378 */
+ snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
+ w->shift * 8, 0x03, 0x03);
+ /* Update the RMS meter resampling */
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_COMP1_B3_CTL +
+ w->shift * 8, 0xFF, 0x01);
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_COMP1_B2_CTL +
+ w->shift * 8, 0xF0, 0x50);
+ usleep_range(COMP_BRINGUP_WAIT_TIME, COMP_BRINGUP_WAIT_TIME);
break;
case SND_SOC_DAPM_POST_PMU:
- /* Set sample rate dependent paramater*/
- if (tabla->comp_enabled[w->shift] != 0) {
- snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_FS_CFG +
- w->shift * 8, 0x07, rate);
- snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B2_CTL +
- w->shift * 8, 0x0F,
- comp_samp_params[rate].peak_det_timeout);
- snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B2_CTL +
- w->shift * 8, 0xF0,
- comp_samp_params[rate].rms_meter_div_fact);
- snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B3_CTL +
- w->shift * 8, 0xFF,
- comp_samp_params[rate].rms_meter_resamp_fact);
- snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 0x38,
- comp_samp_params[rate].shutdown_timeout);
+ /* Set sample rate dependent paramater */
+ if (w->shift == COMPANDER_1) {
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_CLSG_CTL,
+ 0x11, 0x00);
+ snd_soc_write(codec,
+ TABLA_A_CDC_CONN_CLSG_CTL, 0x11);
}
+ snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B2_CTL +
+ w->shift * 8, 0x0F,
+ comp_samp_params[rate].peak_det_timeout);
+ snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B2_CTL +
+ w->shift * 8, 0xF0,
+ comp_samp_params[rate].rms_meter_div_fact);
+ snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B3_CTL +
+ w->shift * 8, 0xFF,
+ comp_samp_params[rate].rms_meter_resamp_fact);
+ snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
+ w->shift * 8, 0x38,
+ comp_samp_params[rate].shutdown_timeout);
break;
case SND_SOC_DAPM_PRE_PMD:
- if (tabla->comp_enabled[w->shift] != 0) {
- status = snd_soc_read(codec,
- TABLA_A_CDC_COMP1_SHUT_DOWN_STATUS +
- w->shift * 8);
- pr_debug("%s: compander #%d shutdown status %d in event %d\n",
- __func__, w->shift + 1, status, event);
- /* Halt the compander*/
- snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 1 << 2, 1 << 2);
- }
break;
case SND_SOC_DAPM_POST_PMD:
- if (tabla->comp_enabled[w->shift] != 0) {
- /* Wait up to a second for shutdown complete */
- timeout = jiffies + HZ;
- do {
- status = snd_soc_read(codec,
- TABLA_A_CDC_COMP1_SHUT_DOWN_STATUS +
- w->shift * 8);
- if (status == 0x3)
- break;
- usleep_range(5000, 5000);
- } while (!(time_after(jiffies, timeout)));
- /* Restore the gain */
- tabla_config_gain_compander(codec, w->shift,
- tabla->comp_enabled[w->shift],
- event);
- /* Disable the compander*/
- snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 0x03, 0x00);
- /* Turn off the clock for compander in pair*/
- snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_B2_CTL,
- 0x03 << comp_shift[w->shift], 0);
- /* Clear the HALT for the compander*/
+ /* Disable the compander */
+ snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
+ w->shift * 8, 0x03, 0x00);
+ /* Toggle compander reset bits */
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_CLK_OTHR_RESET_CTL,
+ 1 << comp_shift[w->shift],
+ 1 << comp_shift[w->shift]);
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_CLK_OTHR_RESET_CTL,
+ 1 << comp_shift[w->shift], 0);
+ /* Turn off the clock for compander in pair */
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_B2_CTL,
+ 0x03 << comp_shift[w->shift], 0);
+ /* Restore the gain */
+ tabla_config_gain_compander(codec, w->shift,
+ tabla->comp_enabled[w->shift],
+ event);
+ if (w->shift == COMPANDER_1) {
snd_soc_update_bits(codec,
- TABLA_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 1 << 2, 0);
+ TABLA_A_CDC_CLSG_CTL,
+ 0x11, 0x11);
+ snd_soc_write(codec,
+ TABLA_A_CDC_CONN_CLSG_CTL, 0x14);
}
break;
}
+rtn:
+ return 0;
+}
+
+static int tabla_codec_hphr_dem_input_selection(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: compander#1->enable(%d) reg(0x%x = 0x%x) event(%d)\n",
+ __func__, tabla->comp_enabled[COMPANDER_1],
+ TABLA_A_CDC_RX1_B6_CTL,
+ snd_soc_read(codec, TABLA_A_CDC_RX1_B6_CTL), event);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (tabla->comp_enabled[COMPANDER_1] && !tabla->anc_func)
+ snd_soc_update_bits(codec, TABLA_A_CDC_RX1_B6_CTL,
+ 1 << w->shift, 0);
+ else
+ snd_soc_update_bits(codec, TABLA_A_CDC_RX1_B6_CTL,
+ 1 << w->shift, 1 << w->shift);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, TABLA_A_CDC_RX1_B6_CTL,
+ 1 << w->shift, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int tabla_codec_hphl_dem_input_selection(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: compander#1->enable(%d) reg(0x%x = 0x%x) event(%d)\n",
+ __func__, tabla->comp_enabled[COMPANDER_1],
+ TABLA_A_CDC_RX2_B6_CTL,
+ snd_soc_read(codec, TABLA_A_CDC_RX2_B6_CTL), event);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (tabla->comp_enabled[COMPANDER_1] && !tabla->anc_func)
+ snd_soc_update_bits(codec, TABLA_A_CDC_RX2_B6_CTL,
+ 1 << w->shift, 0);
+ else
+ snd_soc_update_bits(codec, TABLA_A_CDC_RX2_B6_CTL,
+ 1 << w->shift, 1 << w->shift);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, TABLA_A_CDC_RX2_B6_CTL,
+ 1 << w->shift, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
return 0;
}
@@ -1916,10 +2033,10 @@
vtable,
port_id,
tabla_p->dai)) {
- pr_info("%s: TX%u is used by other virtual port\n",
+ dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n",
__func__, port_id + 1);
mutex_unlock(&codec->mutex);
- return -EINVAL;
+ return 0;
}
widget->value |= 1 << port_id;
list_add_tail(&core->tx_chs[port_id].list,
@@ -1930,10 +2047,10 @@
list_del_init(&core->tx_chs[port_id].list);
} else {
if (enable)
- pr_info("%s: TX%u port is used by this virtual port\n",
+ dev_dbg(codec->dev, "%s: TX%u port is used by this virtual port\n",
__func__, port_id + 1);
else
- pr_info("%s: TX%u port is not used by this virtual port\n",
+ dev_dbg(codec->dev, "%s: TX%u port is not used by this virtual port\n",
__func__, port_id + 1);
/* avoid update power function */
mutex_unlock(&codec->mutex);
@@ -2001,23 +2118,35 @@
list_del_init(&core->rx_chs[port_id].list);
break;
case 1:
- if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
- &tabla_p->dai[AIF1_PB].wcd9xxx_ch_list))
- goto pr_err;
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TABLA_RX_PORT_START_NUMBER,
+ &tabla_p->dai[AIF1_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+ goto rtn;
+ }
list_add_tail(&core->rx_chs[port_id].list,
&tabla_p->dai[AIF1_PB].wcd9xxx_ch_list);
break;
case 2:
- if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
- &tabla_p->dai[AIF1_PB].wcd9xxx_ch_list))
- goto pr_err;
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TABLA_RX_PORT_START_NUMBER,
+ &tabla_p->dai[AIF1_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+ goto rtn;
+ }
list_add_tail(&core->rx_chs[port_id].list,
&tabla_p->dai[AIF2_PB].wcd9xxx_ch_list);
break;
case 3:
- if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
- &tabla_p->dai[AIF1_PB].wcd9xxx_ch_list))
- goto pr_err;
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TABLA_RX_PORT_START_NUMBER,
+ &tabla_p->dai[AIF1_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+ goto rtn;
+ }
list_add_tail(&core->rx_chs[port_id].list,
&tabla_p->dai[AIF3_PB].wcd9xxx_ch_list);
break;
@@ -2025,15 +2154,8 @@
pr_err("Unknown AIF %d\n", widget->value);
goto err;
}
-
+rtn:
snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
-
- mutex_unlock(&codec->mutex);
- return 0;
-
-pr_err:
- pr_err("%s: RX%u is used by current requesting AIF_PB itself\n",
- __func__, port_id + 1);
mutex_unlock(&codec->mutex);
return 0;
err:
@@ -2588,7 +2710,7 @@
}
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
- msleep(250);
+ msleep(20);
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
pr_debug("%s: leave\n", __func__);
@@ -5361,8 +5483,12 @@
&rx6_dsm_mux, tabla_codec_reset_interpolator,
SND_SOC_DAPM_PRE_PMU),
- SND_SOC_DAPM_MIXER("RX1 CHAIN", TABLA_A_CDC_RX1_B6_CTL, 5, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("RX2 CHAIN", TABLA_A_CDC_RX2_B6_CTL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("RX1 CHAIN", SND_SOC_NOPM, 5, 0, NULL,
+ 0, tabla_codec_hphr_dem_input_selection,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX2 CHAIN", SND_SOC_NOPM, 5, 0, NULL,
+ 0, tabla_codec_hphl_dem_input_selection,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
&rx_mix1_inp1_mux),
diff --git a/sound/soc/codecs/wcd9320-tables.c b/sound/soc/codecs/wcd9320-tables.c
index 0885c09..e834b80 100644
--- a/sound/soc/codecs/wcd9320-tables.c
+++ b/sound/soc/codecs/wcd9320-tables.c
@@ -671,6 +671,16 @@
[TAIKO_A_CDC_MAD_BEACON_CTL_8] = 1,
[TAIKO_A_CDC_MAD_BEACON_IIR_CTL_PTR] = 1,
[TAIKO_A_CDC_MAD_BEACON_IIR_CTL_VAL] = 1,
+ [TAIKO_A_CDC_SPKR_CLIPDET_B1_CTL] = 1,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL0] = 1,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL1] = 1,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL2] = 1,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL3] = 1,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL4] = 1,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL5] = 1,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL6] = 1,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL7] = 1,
+
};
const u8 taiko_reset_reg_defaults[TAIKO_CACHE_SIZE] = {
@@ -1356,4 +1366,14 @@
TAIKO_A_CDC_MAD_BEACON_IIR_CTL_PTR__POR,
[TAIKO_A_CDC_MAD_BEACON_IIR_CTL_VAL] =
TAIKO_A_CDC_MAD_BEACON_IIR_CTL_VAL__POR,
+ [TAIKO_A_CDC_SPKR_CLIPDET_B1_CTL] =
+ TAIKO_A_CDC_SPKR_CLIPDET_B1_CTL__POR,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL0] = TAIKO_A_CDC_SPKR_CLIPDET_VAL0__POR,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL1] = TAIKO_A_CDC_SPKR_CLIPDET_VAL1__POR,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL2] = TAIKO_A_CDC_SPKR_CLIPDET_VAL2__POR,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL3] = TAIKO_A_CDC_SPKR_CLIPDET_VAL3__POR,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL4] = TAIKO_A_CDC_SPKR_CLIPDET_VAL4__POR,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL5] = TAIKO_A_CDC_SPKR_CLIPDET_VAL5__POR,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL6] = TAIKO_A_CDC_SPKR_CLIPDET_VAL6__POR,
+ [TAIKO_A_CDC_SPKR_CLIPDET_VAL7] = TAIKO_A_CDC_SPKR_CLIPDET_VAL7__POR,
};
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 7ba43c0..a4f3c21 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -41,10 +41,14 @@
#define TAIKO_MAD_SLIMBUS_TX_PORT 12
#define TAIKO_MAD_AUDIO_FIRMWARE_PATH "wcd9320/wcd9320_mad_audio.bin"
+#define TAIKO_VALIDATE_RX_SBPORT_RANGE(port) ((port >= 16) && (port <= 22))
+#define TAIKO_CONVERT_RX_SBPORT_ID(port) (port - 16) /* RX1 port ID = 0 */
#define TAIKO_HPH_PA_SETTLE_COMP_ON 3000
#define TAIKO_HPH_PA_SETTLE_COMP_OFF 13000
+#define DAPM_MICBIAS2_EXTERNAL_STANDALONE "MIC BIAS2 External Standalone"
+
static atomic_t kp_taiko_priv;
static int spkr_drv_wrnd_param_set(const char *val,
const struct kernel_param *kp);
@@ -110,6 +114,15 @@
AANC_FF_GAIN_ADAPTIVE,
AANC_FFGAIN_ADAPTIVE_EN,
AANC_GAIN_CONTROL,
+ SPKR_CLIP_PIPE_BANK_SEL,
+ SPKR_CLIPDET_VAL0,
+ SPKR_CLIPDET_VAL1,
+ SPKR_CLIPDET_VAL2,
+ SPKR_CLIPDET_VAL3,
+ SPKR_CLIPDET_VAL4,
+ SPKR_CLIPDET_VAL5,
+ SPKR_CLIPDET_VAL6,
+ SPKR_CLIPDET_VAL7,
MAX_CFG_REGISTERS,
};
@@ -182,6 +195,74 @@
(TAIKO_REGISTER_START_OFFSET + TAIKO_A_CDC_ANC1_GAIN_CTL),
AANC_GAIN_CONTROL, 0xFF, 8, 0
},
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_INTR_DESTN3),
+ MAD_CLIP_INT_DEST_SELECT_REG, 0x8, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_INTR_MASK3),
+ MAD_CLIP_INT_MASK_REG, 0x8, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_INTR_STATUS3),
+ MAD_CLIP_INT_STATUS_REG, 0x8, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_INTR_CLEAR3),
+ MAD_CLIP_INT_CLEAR_REG, 0x8, 8, 0
+ },
+};
+
+static struct afe_param_cdc_reg_cfg clip_reg_cfg[] = {
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_CDC_SPKR_CLIPDET_B1_CTL),
+ SPKR_CLIP_PIPE_BANK_SEL, 0x3, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_CDC_SPKR_CLIPDET_VAL0),
+ SPKR_CLIPDET_VAL0, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_CDC_SPKR_CLIPDET_VAL1),
+ SPKR_CLIPDET_VAL1, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_CDC_SPKR_CLIPDET_VAL2),
+ SPKR_CLIPDET_VAL2, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_CDC_SPKR_CLIPDET_VAL3),
+ SPKR_CLIPDET_VAL3, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_CDC_SPKR_CLIPDET_VAL4),
+ SPKR_CLIPDET_VAL4, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_CDC_SPKR_CLIPDET_VAL5),
+ SPKR_CLIPDET_VAL5, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_CDC_SPKR_CLIPDET_VAL6),
+ SPKR_CLIPDET_VAL6, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_CDC_SPKR_CLIPDET_VAL7),
+ SPKR_CLIPDET_VAL7, 0xff, 8, 0
+ },
};
static struct afe_param_cdc_reg_cfg_data taiko_audio_reg_cfg = {
@@ -189,11 +270,22 @@
.reg_data = audio_reg_cfg,
};
+static struct afe_param_cdc_reg_cfg_data taiko_clip_reg_cfg = {
+ .num_registers = ARRAY_SIZE(clip_reg_cfg),
+ .reg_data = clip_reg_cfg,
+};
+
static struct afe_param_id_cdc_aanc_version taiko_cdc_aanc_version = {
.cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION,
.aanc_hw_version = AANC_HW_BLOCK_VERSION_2,
};
+static struct afe_param_id_clip_bank_sel clip_bank_sel = {
+ .minor_version = AFE_API_VERSION_CLIP_BANK_SEL_CFG,
+ .num_banks = AFE_CLIP_MAX_BANKS,
+ .bank_map = {0, 1, 2, 3},
+};
+
module_param_cb(spkr_drv_wrnd, &spkr_drv_wrnd_param_ops, &spkr_drv_wrnd, 0644);
MODULE_PARM_DESC(spkr_drv_wrnd,
"Run software workaround to avoid leakage on the speaker drive");
@@ -206,6 +298,7 @@
#define NUM_INTERPOLATORS 7
#define BITS_PER_REG 8
#define TAIKO_TX_PORT_NUMBER 16
+#define TAIKO_RX_PORT_START_NUMBER 16
#define TAIKO_I2S_MASTER_MODE_MASK 0x08
@@ -229,7 +322,7 @@
#define TAIKO_SLIM_IRQ_UNDERFLOW (1 << 1)
#define TAIKO_SLIM_IRQ_PORT_CLOSED (1 << 2)
#define TAIKO_MCLK_CLK_12P288MHZ 12288000
-#define TAIKO_MCLK_CLK_9P6HZ 9600000
+#define TAIKO_MCLK_CLK_9P6MHZ 9600000
#define TAIKO_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FORMAT_S24_LE)
@@ -321,19 +414,19 @@
static struct hpf_work tx_hpf_work[NUM_DECIMATORS];
static const struct wcd9xxx_ch taiko_rx_chs[TAIKO_RX_MAX] = {
- WCD9XXX_CH(16, 0),
- WCD9XXX_CH(17, 1),
- WCD9XXX_CH(18, 2),
- WCD9XXX_CH(19, 3),
- WCD9XXX_CH(20, 4),
- WCD9XXX_CH(21, 5),
- WCD9XXX_CH(22, 6),
- WCD9XXX_CH(23, 7),
- WCD9XXX_CH(24, 8),
- WCD9XXX_CH(25, 9),
- WCD9XXX_CH(26, 10),
- WCD9XXX_CH(27, 11),
- WCD9XXX_CH(28, 12),
+ WCD9XXX_CH(TAIKO_RX_PORT_START_NUMBER, 0),
+ WCD9XXX_CH(TAIKO_RX_PORT_START_NUMBER + 1, 1),
+ WCD9XXX_CH(TAIKO_RX_PORT_START_NUMBER + 2, 2),
+ WCD9XXX_CH(TAIKO_RX_PORT_START_NUMBER + 3, 3),
+ WCD9XXX_CH(TAIKO_RX_PORT_START_NUMBER + 4, 4),
+ WCD9XXX_CH(TAIKO_RX_PORT_START_NUMBER + 5, 5),
+ WCD9XXX_CH(TAIKO_RX_PORT_START_NUMBER + 6, 6),
+ WCD9XXX_CH(TAIKO_RX_PORT_START_NUMBER + 7, 7),
+ WCD9XXX_CH(TAIKO_RX_PORT_START_NUMBER + 8, 8),
+ WCD9XXX_CH(TAIKO_RX_PORT_START_NUMBER + 9, 9),
+ WCD9XXX_CH(TAIKO_RX_PORT_START_NUMBER + 10, 10),
+ WCD9XXX_CH(TAIKO_RX_PORT_START_NUMBER + 11, 11),
+ WCD9XXX_CH(TAIKO_RX_PORT_START_NUMBER + 12, 12),
};
static const struct wcd9xxx_ch taiko_tx_chs[TAIKO_TX_MAX] = {
@@ -378,6 +471,8 @@
s32 dmic_1_2_clk_cnt;
s32 dmic_3_4_clk_cnt;
s32 dmic_5_6_clk_cnt;
+ s32 ldo_h_users;
+ s32 micb_2_users;
u32 anc_slot;
bool anc_func;
@@ -400,6 +495,8 @@
bool spkr_pa_widget_on;
struct regulator *spkdrv_reg;
+ bool mbhc_started;
+
struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg;
/* resmgr module */
@@ -409,6 +506,9 @@
/* class h specific data */
struct wcd9xxx_clsh_cdc_data clsh_d;
+
+ int (*machine_codec_event_cb)(struct snd_soc_codec *codec,
+ enum wcd9xxx_codec_event);
};
static const u32 comp_shift[] = {
@@ -1952,11 +2052,10 @@
vtable,
port_id,
taiko_p->dai)) {
- pr_debug("%s: TX%u is used by other\n"
- "virtual port\n",
+ dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n",
__func__, port_id + 1);
mutex_unlock(&codec->mutex);
- return -EINVAL;
+ return 0;
}
widget->value |= 1 << port_id;
list_add_tail(&core->tx_chs[port_id].list,
@@ -1967,11 +2066,11 @@
list_del_init(&core->tx_chs[port_id].list);
} else {
if (enable)
- pr_debug("%s: TX%u port is used by\n"
+ dev_dbg(codec->dev, "%s: TX%u port is used by\n"
"this virtual port\n",
__func__, port_id + 1);
else
- pr_debug("%s: TX%u port is not used by\n"
+ dev_dbg(codec->dev, "%s: TX%u port is not used by\n"
"this virtual port\n",
__func__, port_id + 1);
/* avoid update power function */
@@ -2040,23 +2139,35 @@
list_del_init(&core->rx_chs[port_id].list);
break;
case 1:
- if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
- &taiko_p->dai[AIF1_PB].wcd9xxx_ch_list))
- goto pr_err;
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TAIKO_RX_PORT_START_NUMBER,
+ &taiko_p->dai[AIF1_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+ goto rtn;
+ }
list_add_tail(&core->rx_chs[port_id].list,
&taiko_p->dai[AIF1_PB].wcd9xxx_ch_list);
break;
case 2:
- if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
- &taiko_p->dai[AIF2_PB].wcd9xxx_ch_list))
- goto pr_err;
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TAIKO_RX_PORT_START_NUMBER,
+ &taiko_p->dai[AIF2_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+ goto rtn;
+ }
list_add_tail(&core->rx_chs[port_id].list,
&taiko_p->dai[AIF2_PB].wcd9xxx_ch_list);
break;
case 3:
- if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
- &taiko_p->dai[AIF3_PB].wcd9xxx_ch_list))
- goto pr_err;
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TAIKO_RX_PORT_START_NUMBER,
+ &taiko_p->dai[AIF3_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+ goto rtn;
+ }
list_add_tail(&core->rx_chs[port_id].list,
&taiko_p->dai[AIF3_PB].wcd9xxx_ch_list);
break;
@@ -2064,14 +2175,11 @@
pr_err("Unknown AIF %d\n", widget->value);
goto err;
}
-
+rtn:
snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
mutex_unlock(&codec->mutex);
return 0;
-pr_err:
- pr_err("%s: RX%u is used by current requesting AIF_PB itself\n",
- __func__, port_id + 1);
err:
mutex_unlock(&codec->mutex);
return -EINVAL;
@@ -2560,14 +2668,26 @@
else if (strnstr(w->name, internal3_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x3, 0x3);
- if (micb_ctl_reg == TAIKO_A_MICB_2_CTL)
- wcd9xxx_resmgr_add_cond_update_bits(&taiko->resmgr,
- WCD9XXX_COND_HPH_MIC,
- micb_ctl_reg, w->shift,
- false);
- else
+ if (taiko->mbhc_started && micb_ctl_reg == TAIKO_A_MICB_2_CTL) {
+ if (++taiko->micb_2_users == 1) {
+ if (taiko->resmgr.pdata->
+ micbias.bias2_is_headset_only)
+ wcd9xxx_resmgr_add_cond_update_bits(
+ &taiko->resmgr,
+ WCD9XXX_COND_HPH_MIC,
+ micb_ctl_reg, w->shift,
+ false);
+ else
+ snd_soc_update_bits(codec, micb_ctl_reg,
+ 1 << w->shift,
+ 1 << w->shift);
+ }
+ pr_debug("%s: micb_2_users %d\n", __func__,
+ taiko->micb_2_users);
+ } else {
snd_soc_update_bits(codec, micb_ctl_reg, 1 << w->shift,
1 << w->shift);
+ }
break;
case SND_SOC_DAPM_POST_PMU:
usleep_range(20000, 20000);
@@ -2575,13 +2695,27 @@
wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_on);
break;
case SND_SOC_DAPM_POST_PMD:
- if (micb_ctl_reg == TAIKO_A_MICB_2_CTL)
- wcd9xxx_resmgr_rm_cond_update_bits(&taiko->resmgr,
- WCD9XXX_COND_HPH_MIC,
- micb_ctl_reg, 7, false);
- else
+ if (taiko->mbhc_started && micb_ctl_reg == TAIKO_A_MICB_2_CTL) {
+ if (--taiko->micb_2_users == 0) {
+ if (taiko->resmgr.pdata->
+ micbias.bias2_is_headset_only)
+ wcd9xxx_resmgr_rm_cond_update_bits(
+ &taiko->resmgr,
+ WCD9XXX_COND_HPH_MIC,
+ micb_ctl_reg, 7, false);
+ else
+ snd_soc_update_bits(codec, micb_ctl_reg,
+ 1 << w->shift, 0);
+ }
+ pr_debug("%s: micb_2_users %d\n", __func__,
+ taiko->micb_2_users);
+ WARN(taiko->micb_2_users < 0,
+ "Unexpected micbias users %d\n",
+ taiko->micb_2_users);
+ } else {
snd_soc_update_bits(codec, micb_ctl_reg, 1 << w->shift,
0);
+ }
/* Let MBHC module know so micbias switch to be off */
wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_off);
@@ -2601,6 +2735,22 @@
return 0;
}
+/* called under codec_resource_lock acquisition */
+static int taiko_enable_mbhc_micbias(struct snd_soc_codec *codec, bool enable)
+{
+ int rc;
+
+ if (enable)
+ rc = snd_soc_dapm_force_enable_pin(&codec->dapm,
+ DAPM_MICBIAS2_EXTERNAL_STANDALONE);
+ else
+ rc = snd_soc_dapm_disable_pin(&codec->dapm,
+ DAPM_MICBIAS2_EXTERNAL_STANDALONE);
+ if (!rc)
+ snd_soc_dapm_sync(&codec->dapm);
+ pr_debug("%s: leave ret %d\n", __func__, rc);
+ return rc;
+}
static void tx_hpf_corner_freq_callback(struct work_struct *work)
{
@@ -2832,18 +2982,65 @@
return 0;
}
-static int taiko_codec_enable_ldo_h(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+/* called under codec_resource_lock acquisition */
+static int __taiko_codec_enable_ldo_h(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
{
+ struct snd_soc_codec *codec = w->codec;
+ struct taiko_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: enter\n", __func__);
switch (event) {
- case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_PRE_PMU:
+ if (++priv->ldo_h_users == 1) {
+ wcd9xxx_resmgr_get_bandgap(&priv->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ wcd9xxx_resmgr_get_clk_block(&priv->resmgr,
+ WCD9XXX_CLK_RCO);
+ snd_soc_update_bits(codec, TAIKO_A_LDO_H_MODE_1, 1 << 7,
+ 1 << 7);
+ wcd9xxx_resmgr_put_clk_block(&priv->resmgr,
+ WCD9XXX_CLK_RCO);
+ pr_debug("%s: ldo_h_users %d\n", __func__,
+ priv->ldo_h_users);
+ /* LDO enable requires 1ms to settle down */
+ usleep_range(1000, 1000);
+ }
+ break;
case SND_SOC_DAPM_POST_PMD:
- usleep_range(1000, 1000);
+ if (--priv->ldo_h_users == 0) {
+ wcd9xxx_resmgr_get_clk_block(&priv->resmgr,
+ WCD9XXX_CLK_RCO);
+ snd_soc_update_bits(codec, TAIKO_A_LDO_H_MODE_1, 1 << 7,
+ 0);
+ wcd9xxx_resmgr_put_clk_block(&priv->resmgr,
+ WCD9XXX_CLK_RCO);
+ wcd9xxx_resmgr_put_bandgap(&priv->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ pr_debug("%s: ldo_h_users %d\n", __func__,
+ priv->ldo_h_users);
+ }
+ WARN(priv->ldo_h_users < 0, "Unexpected ldo_h users %d\n",
+ priv->ldo_h_users);
break;
}
+ pr_debug("%s: leave\n", __func__);
return 0;
}
+static int taiko_codec_enable_ldo_h(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int rc;
+ struct snd_soc_codec *codec = w->codec;
+ struct taiko_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ WCD9XXX_BCL_LOCK(&priv->resmgr);
+ rc = __taiko_codec_enable_ldo_h(w, kcontrol, event);
+ WCD9XXX_BCL_UNLOCK(&priv->resmgr);
+ return rc;
+}
+
static int taiko_codec_enable_rx_bias(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -3003,7 +3200,7 @@
}
release_firmware(fw);
break;
- case SND_SOC_DAPM_POST_PMD:
+ case SND_SOC_DAPM_PRE_PMD:
msleep(40);
snd_soc_update_bits(codec, TAIKO_A_CDC_ANC1_B1_CTL, 0x01, 0x00);
snd_soc_update_bits(codec, TAIKO_A_CDC_ANC2_B1_CTL, 0x02, 0x00);
@@ -3104,8 +3301,6 @@
snd_soc_update_bits(codec,
TAIKO_A_RX_HPH_CNP_EN, 0x30, 0x00);
msleep(40);
- }
- if (w->shift == 5) {
snd_soc_update_bits(codec,
TAIKO_A_TX_7_MBHC_EN, 0x80, 00);
ret |= taiko_codec_enable_anc(w, kcontrol, event);
@@ -3593,13 +3788,15 @@
{"RX7 MIX1 INP2", "RX6", "SLIM RX6"},
{"RX7 MIX1 INP2", "RX7", "SLIM RX7"},
{"RX7 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX7 MIX1 INP2", "IIR2", "IIR2"},
+
+ /* IIR1, IIR2 inputs to Second RX Mixer on RX1, RX2 and RX7 chains. */
{"RX1 MIX2 INP1", "IIR1", "IIR1"},
{"RX1 MIX2 INP2", "IIR1", "IIR1"},
{"RX2 MIX2 INP1", "IIR1", "IIR1"},
{"RX2 MIX2 INP2", "IIR1", "IIR1"},
{"RX7 MIX2 INP1", "IIR1", "IIR1"},
{"RX7 MIX2 INP2", "IIR1", "IIR1"},
- {"RX7 MIX1 INP2", "IIR2", "IIR2"},
{"RX1 MIX2 INP1", "IIR2", "IIR2"},
{"RX1 MIX2 INP2", "IIR2", "IIR2"},
{"RX2 MIX2 INP1", "IIR2", "IIR2"},
@@ -3677,6 +3874,13 @@
{"IIR1 INP1 MUX", "DEC8", "DEC8 MUX"},
{"IIR1 INP1 MUX", "DEC9", "DEC9 MUX"},
{"IIR1 INP1 MUX", "DEC10", "DEC10 MUX"},
+ {"IIR1 INP1 MUX", "RX1", "SLIM RX1"},
+ {"IIR1 INP1 MUX", "RX2", "SLIM RX2"},
+ {"IIR1 INP1 MUX", "RX3", "SLIM RX3"},
+ {"IIR1 INP1 MUX", "RX4", "SLIM RX4"},
+ {"IIR1 INP1 MUX", "RX5", "SLIM RX5"},
+ {"IIR1 INP1 MUX", "RX6", "SLIM RX6"},
+ {"IIR1 INP1 MUX", "RX7", "SLIM RX7"},
{"IIR2", NULL, "IIR2 INP1 MUX"},
{"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"},
@@ -3689,6 +3893,13 @@
{"IIR2 INP1 MUX", "DEC8", "DEC8 MUX"},
{"IIR2 INP1 MUX", "DEC9", "DEC9 MUX"},
{"IIR2 INP1 MUX", "DEC10", "DEC10 MUX"},
+ {"IIR2 INP1 MUX", "RX1", "SLIM RX1"},
+ {"IIR2 INP1 MUX", "RX2", "SLIM RX2"},
+ {"IIR2 INP1 MUX", "RX3", "SLIM RX3"},
+ {"IIR2 INP1 MUX", "RX4", "SLIM RX4"},
+ {"IIR2 INP1 MUX", "RX5", "SLIM RX5"},
+ {"IIR2 INP1 MUX", "RX6", "SLIM RX6"},
+ {"IIR2 INP1 MUX", "RX7", "SLIM RX7"},
{"MIC BIAS1 Internal1", NULL, "LDO_H"},
{"MIC BIAS1 Internal2", NULL, "LDO_H"},
@@ -3701,7 +3912,7 @@
{"MIC BIAS3 Internal2", NULL, "LDO_H"},
{"MIC BIAS3 External", NULL, "LDO_H"},
{"MIC BIAS4 External", NULL, "LDO_H"},
-
+ {DAPM_MICBIAS2_EXTERNAL_STANDALONE, NULL, "LDO_H Standalone"},
};
static int taiko_readable(struct snd_soc_codec *ssc, unsigned int reg)
@@ -4172,6 +4383,65 @@
return 0;
}
+static void taiko_set_rxsb_port_format(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_codec_dai_data *cdc_dai;
+ struct wcd9xxx_ch *ch;
+ int port;
+ u8 bit_sel;
+ u16 sb_ctl_reg, field_shift;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bit_sel = 0x2;
+ taiko_p->dai[dai->id].bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bit_sel = 0x0;
+ taiko_p->dai[dai->id].bit_width = 24;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid format\n");
+ return;
+ }
+
+ cdc_dai = &taiko_p->dai[dai->id];
+
+ list_for_each_entry(ch, &cdc_dai->wcd9xxx_ch_list, list) {
+ port = wcd9xxx_get_slave_port(ch->ch_num);
+
+ if (IS_ERR_VALUE(port) ||
+ !TAIKO_VALIDATE_RX_SBPORT_RANGE(port)) {
+ dev_warn(codec->dev,
+ "%s: invalid port ID %d returned for RX DAI\n",
+ __func__, port);
+ return;
+ }
+
+ port = TAIKO_CONVERT_RX_SBPORT_ID(port);
+
+ if (port <= 3) {
+ sb_ctl_reg = TAIKO_A_CDC_CONN_RX_SB_B1_CTL;
+ field_shift = port << 1;
+ } else if (port <= 6) {
+ sb_ctl_reg = TAIKO_A_CDC_CONN_RX_SB_B2_CTL;
+ field_shift = (port - 4) << 1;
+ } else { /* should not happen */
+ dev_warn(codec->dev,
+ "%s: bad port ID %d\n", __func__, port);
+ return;
+ }
+
+ dev_dbg(codec->dev, "%s: sb_ctl_reg %x field_shift %x\n",
+ __func__, sb_ctl_reg, field_shift);
+ snd_soc_update_bits(codec, sb_ctl_reg, 0x3 << field_shift,
+ bit_sel << field_shift);
+ }
+}
+
static int taiko_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -4286,29 +4556,7 @@
snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RX_I2S_CTL,
0x03, (rx_fs_rate >> 0x05));
} else {
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- snd_soc_update_bits(codec,
- TAIKO_A_CDC_CONN_RX_SB_B1_CTL,
- 0xFF, 0xAA);
- snd_soc_update_bits(codec,
- TAIKO_A_CDC_CONN_RX_SB_B2_CTL,
- 0xFF, 0x2A);
- taiko->dai[dai->id].bit_width = 16;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- snd_soc_update_bits(codec,
- TAIKO_A_CDC_CONN_RX_SB_B1_CTL,
- 0xFF, 0x00);
- snd_soc_update_bits(codec,
- TAIKO_A_CDC_CONN_RX_SB_B2_CTL,
- 0xFF, 0x00);
- taiko->dai[dai->id].bit_width = 24;
- break;
- default:
- dev_err(codec->dev, "Invalid format\n");
- break;
- }
+ taiko_set_rxsb_port_format(params, dai);
taiko->dai[dai->id].rate = params_rate(params);
}
break;
@@ -4620,9 +4868,6 @@
dai = &taiko_p->dai[w->shift];
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- /*Enable Clip Detection*/
- snd_soc_update_bits(codec, TAIKO_A_SPKR_DRV_CLIP_DET,
- 0x8, 0x8);
/*Enable V&I sensing*/
snd_soc_update_bits(codec, TAIKO_A_SPKR_PROT_EN,
0x88, 0x88);
@@ -4635,6 +4880,7 @@
/*Enable Current Decimator*/
snd_soc_update_bits(codec,
TAIKO_A_CDC_CONN_TX_SB_B10_CTL, 0x1F, 0x13);
+ (void) taiko_codec_enable_slim_chmask(dai, true);
ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
dai->rate, dai->bit_width,
&dai->grph);
@@ -4657,9 +4903,6 @@
/*Disable V&I sensing*/
snd_soc_update_bits(codec, TAIKO_A_SPKR_PROT_EN,
0x88, 0x00);
- /*Disable clip detection*/
- snd_soc_update_bits(codec, TAIKO_A_SPKR_DRV_CLIP_DET,
- 0x8, 0x0);
break;
}
out_vi:
@@ -5024,8 +5267,17 @@
SND_SOC_DAPM_SUPPLY("CDC_CONN", WCD9XXX_A_CDC_CLK_OTHR_CTL, 2, 0, NULL,
0),
- SND_SOC_DAPM_SUPPLY("LDO_H", TAIKO_A_LDO_H_MODE_1, 7, 0,
- taiko_codec_enable_ldo_h, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("LDO_H", SND_SOC_NOPM, 7, 0,
+ taiko_codec_enable_ldo_h,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ /*
+ * DAPM 'LDO_H Standalone' is to be powered by mbhc driver after
+ * acquring codec_resource lock.
+ * So call __taiko_codec_enable_ldo_h instead and avoid deadlock.
+ */
+ SND_SOC_DAPM_SUPPLY("LDO_H Standalone", SND_SOC_NOPM, 7, 0,
+ __taiko_codec_enable_ldo_h,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("COMP0_CLK", SND_SOC_NOPM, 0, 0,
taiko_config_compander, SND_SOC_DAPM_PRE_PMU |
@@ -5130,6 +5382,10 @@
SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux),
SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS2_EXTERNAL_STANDALONE, SND_SOC_NOPM,
+ 7, 0, taiko_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MICBIAS_E("MIC BIAS2 External", SND_SOC_NOPM, 7, 0,
taiko_codec_enable_micbias,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
@@ -5241,10 +5497,10 @@
/* Sidetone */
SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
- SND_SOC_DAPM_PGA("IIR1", TAIKO_A_CDC_CLK_SD_CTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("IIR1", TAIKO_A_CDC_CLK_SD_CTL, 0, 0, NULL, 0),
SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp1_mux),
- SND_SOC_DAPM_PGA("IIR2", TAIKO_A_CDC_CLK_SD_CTL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("IIR2", TAIKO_A_CDC_CLK_SD_CTL, 1, 0, NULL, 0),
/* AUX PGA */
SND_SOC_DAPM_ADC_E("AUX_PGA_Left", NULL, TAIKO_A_RX_AUX_SW_CTL, 7, 0,
@@ -5488,7 +5744,7 @@
snd_soc_update_bits(codec, TAIKO_A_MICB_4_CTL, 0x1E, value);
/* Set the DMIC sample rate */
- if (pdata->mclk_rate == TAIKO_MCLK_CLK_9P6HZ) {
+ if (pdata->mclk_rate == TAIKO_MCLK_CLK_9P6MHZ) {
switch (pdata->dmic_sample_rate) {
case TAIKO_DMIC_SAMPLE_RATE_2P4MHZ:
dmic_sample_rate_value = TAIKO_DMIC_SAMPLE_RATE_DIV_4;
@@ -5629,6 +5885,9 @@
TAIKO_REG_VAL(TAIKO_A_CDC_CLK_OTHR_RESET_B1_CTL, 0x00),
TAIKO_REG_VAL(TAIKO_A_CDC_CLK_OTHR_CTL, 0x00),
TAIKO_REG_VAL(TAIKO_A_CDC_CONN_MAD, 0x01),
+
+ /* Set HPH Path to low power mode */
+ TAIKO_REG_VAL(TAIKO_A_RX_HPH_BIAS_PA, 0x55),
};
static const struct wcd9xxx_reg_mask_val taiko_1_0_reg_defaults[] = {
@@ -5656,8 +5915,6 @@
* Rx PA bring up.
*/
TAIKO_REG_VAL(WCD9XXX_A_BUCK_MODE_3, 0xCE),
- /* Reduce HPH DAC bias to 70% */
- TAIKO_REG_VAL(TAIKO_A_RX_HPH_BIAS_PA, 0x7A),
/*Reduce EAR DAC bias to 70% */
TAIKO_REG_VAL(TAIKO_A_RX_EAR_BIAS_PA, 0x76),
/* Reduce LINE DAC bias to 70% */
@@ -5699,7 +5956,6 @@
TAIKO_REG_VAL(WCD9XXX_A_BUCK_CTRL_CCL_4, 0x51),
TAIKO_REG_VAL(TAIKO_A_NCP_DTEST, 0x10),
TAIKO_REG_VAL(TAIKO_A_RX_HPH_CHOP_CTL, 0xA4),
- TAIKO_REG_VAL(TAIKO_A_RX_HPH_BIAS_PA, 0x7A),
TAIKO_REG_VAL(TAIKO_A_RX_HPH_OCP_CTL, 0x69),
TAIKO_REG_VAL(TAIKO_A_RX_HPH_CNP_WG_CTL, 0xDA),
TAIKO_REG_VAL(TAIKO_A_RX_HPH_CNP_WG_TIME, 0x15),
@@ -5841,36 +6097,61 @@
taiko_codec_reg_init_val[i].val);
}
-static int taiko_setup_irqs(struct taiko_priv *taiko)
+static void taiko_slim_interface_init_reg(struct snd_soc_codec *codec)
{
int i;
- int ret = 0;
- struct snd_soc_codec *codec = taiko->codec;
-
- ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
- taiko_slimbus_irq, "SLIMBUS Slave", taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- WCD9XXX_IRQ_SLIMBUS);
- goto exit;
- }
for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++)
wcd9xxx_interface_reg_write(codec->control_data,
TAIKO_SLIM_PGD_PORT_INT_EN0 + i,
0xFF);
-exit:
+}
+
+static int taiko_setup_irqs(struct taiko_priv *taiko)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = taiko->codec;
+
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
+ taiko_slimbus_irq, "SLIMBUS Slave", taiko);
+ if (ret)
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_SLIMBUS);
+ else
+ taiko_slim_interface_init_reg(codec);
+
return ret;
}
+static void taiko_cleanup_irqs(struct taiko_priv *taiko)
+{
+ struct snd_soc_codec *codec = taiko->codec;
+
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, taiko);
+}
+
int taiko_hs_detect(struct snd_soc_codec *codec,
struct wcd9xxx_mbhc_config *mbhc_cfg)
{
+ int rc;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- return wcd9xxx_mbhc_start(&taiko->mbhc, mbhc_cfg);
+ rc = wcd9xxx_mbhc_start(&taiko->mbhc, mbhc_cfg);
+ if (!rc)
+ taiko->mbhc_started = true;
+ return rc;
}
EXPORT_SYMBOL_GPL(taiko_hs_detect);
+void taiko_event_register(
+ int (*machine_event_cb)(struct snd_soc_codec *codec,
+ enum wcd9xxx_codec_event),
+ struct snd_soc_codec *codec)
+{
+ struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+ taiko->machine_codec_event_cb = machine_event_cb;
+}
+EXPORT_SYMBOL_GPL(taiko_event_register);
+
static void taiko_init_slim_slave_cfg(struct snd_soc_codec *codec)
{
struct taiko_priv *priv = snd_soc_codec_get_drvdata(codec);
@@ -5896,6 +6177,7 @@
int ret = 0;
struct snd_soc_codec *codec;
struct taiko_priv *taiko;
+ int rco_clk_rate;
codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
taiko = snd_soc_codec_get_drvdata(codec);
@@ -5921,14 +6203,32 @@
pr_err("%s: bad pdata\n", __func__);
taiko_init_slim_slave_cfg(codec);
+ taiko_slim_interface_init_reg(codec);
- wcd9xxx_mbhc_deinit(&taiko->mbhc);
- ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
- WCD9XXX_MBHC_VERSION_TAIKO);
- if (ret)
- pr_err("%s: mbhc init failed %d\n", __func__, ret);
- else
- wcd9xxx_mbhc_start(&taiko->mbhc, taiko->mbhc.mbhc_cfg);
+ if (taiko->mbhc_started) {
+ wcd9xxx_mbhc_deinit(&taiko->mbhc);
+ taiko->mbhc_started = false;
+
+ if (TAIKO_IS_1_0(wcd9xxx->version))
+ rco_clk_rate = TAIKO_MCLK_CLK_12P288MHZ;
+ else
+ rco_clk_rate = TAIKO_MCLK_CLK_9P6MHZ;
+
+ ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
+ taiko_enable_mbhc_micbias,
+ WCD9XXX_MBHC_VERSION_TAIKO,
+ rco_clk_rate);
+ if (ret) {
+ pr_err("%s: mbhc init failed %d\n", __func__, ret);
+ } else {
+ ret = wcd9xxx_mbhc_start(&taiko->mbhc,
+ taiko->mbhc.mbhc_cfg);
+ if (!ret)
+ taiko->mbhc_started = true;
+ }
+ }
+ taiko->machine_codec_event_cb(codec, WCD9XXX_CODEC_EVENT_CODEC_UP);
+
mutex_unlock(&codec->mutex);
return ret;
}
@@ -5937,6 +6237,7 @@
enum afe_config_type config_type)
{
struct taiko_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *taiko_core = dev_get_drvdata(codec->dev->parent);
switch (config_type) {
case AFE_SLIMBUS_SLAVE_CONFIG:
@@ -5947,6 +6248,16 @@
return &taiko_slimbus_slave_port_cfg;
case AFE_AANC_VERSION:
return &taiko_cdc_aanc_version;
+ case AFE_CLIP_BANK_SEL:
+ if (!TAIKO_IS_1_0(taiko_core->version))
+ return &clip_bank_sel;
+ else
+ return NULL;
+ case AFE_CDC_CLIP_REGISTERS_CONFIG:
+ if (!TAIKO_IS_1_0(taiko_core->version))
+ return &taiko_clip_reg_cfg;
+ else
+ return NULL;
default:
pr_err("%s: Unknown config_type 0x%x\n", __func__, config_type);
return NULL;
@@ -6040,7 +6351,7 @@
struct wcd9xxx *wcd9xxx;
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret = 0;
- int i;
+ int i, rco_clk_rate;
void *ptr = NULL;
struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
@@ -6063,10 +6374,8 @@
tx_hpf_corner_freq_callback);
}
-
snd_soc_codec_set_drvdata(codec, taiko);
-
/* codec resmgr module init */
wcd9xxx = codec->control_data;
pdata = dev_get_platdata(codec->dev->parent);
@@ -6074,18 +6383,25 @@
&taiko_reg_address);
if (ret) {
pr_err("%s: wcd9xxx init failed %d\n", __func__, ret);
- return ret;
+ goto err_init;
}
taiko->clsh_d.buck_mv = taiko_codec_get_buck_mv(codec);
wcd9xxx_clsh_init(&taiko->clsh_d, &taiko->resmgr);
+ if (TAIKO_IS_1_0(core->version))
+ rco_clk_rate = TAIKO_MCLK_CLK_12P288MHZ;
+ else
+ rco_clk_rate = TAIKO_MCLK_CLK_9P6MHZ;
+
/* init and start mbhc */
ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
- WCD9XXX_MBHC_VERSION_TAIKO);
+ taiko_enable_mbhc_micbias,
+ WCD9XXX_MBHC_VERSION_TAIKO,
+ rco_clk_rate);
if (ret) {
pr_err("%s: mbhc init failed %d\n", __func__, ret);
- return ret;
+ goto err_init;
}
taiko->codec = codec;
@@ -6097,11 +6413,13 @@
taiko->aux_pga_cnt = 0;
taiko->aux_l_gain = 0x1F;
taiko->aux_r_gain = 0x1F;
+ taiko->ldo_h_users = 0;
+ taiko->micb_2_users = 0;
taiko_update_reg_defaults(codec);
pr_debug("%s: MCLK Rate = %x\n", __func__, wcd9xxx->mclk_rate);
if (wcd9xxx->mclk_rate == TAIKO_MCLK_CLK_12P288MHZ)
snd_soc_update_bits(codec, TAIKO_A_CHIP_CTL, 0x06, 0x0);
- else if (wcd9xxx->mclk_rate == TAIKO_MCLK_CLK_9P6HZ)
+ else if (wcd9xxx->mclk_rate == TAIKO_MCLK_CLK_9P6MHZ)
snd_soc_update_bits(codec, TAIKO_A_CHIP_CTL, 0x06, 0x2);
taiko_codec_init_reg(codec);
ret = taiko_handle_pdata(taiko);
@@ -6179,7 +6497,11 @@
snd_soc_dapm_sync(dapm);
- (void) taiko_setup_irqs(taiko);
+ ret = taiko_setup_irqs(taiko);
+ if (ret) {
+ pr_err("%s: taiko irq setup failed %d\n", __func__, ret);
+ goto err_irq;
+ }
atomic_set(&kp_taiko_priv, (unsigned long)taiko);
mutex_lock(&dapm->codec->mutex);
@@ -6194,10 +6516,13 @@
codec->ignore_pmdown_time = 1;
return ret;
+err_irq:
+ taiko_cleanup_irqs(taiko);
err_pdata:
kfree(ptr);
err_nomem_slimch:
kfree(taiko);
+err_init:
return ret;
}
static int taiko_codec_remove(struct snd_soc_codec *codec)
@@ -6212,6 +6537,8 @@
WCD9XXX_BANDGAP_AUDIO_MODE);
WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
+ taiko_cleanup_irqs(taiko);
+
/* cleanup MBHC */
wcd9xxx_mbhc_deinit(&taiko->mbhc);
/* cleanup resmgr */
diff --git a/sound/soc/codecs/wcd9320.h b/sound/soc/codecs/wcd9320.h
index a4dbd7a..8f222ad 100644
--- a/sound/soc/codecs/wcd9320.h
+++ b/sound/soc/codecs/wcd9320.h
@@ -18,6 +18,7 @@
#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
#include "wcd9xxx-mbhc.h"
#include "wcd9xxx-resmgr.h"
+#include "wcd9xxx-common.h"
#define TAIKO_NUM_REGISTERS 0x400
#define TAIKO_MAX_REGISTER (TAIKO_NUM_REGISTERS-1)
@@ -148,4 +149,9 @@
extern void *taiko_get_afe_config(struct snd_soc_codec *codec,
enum afe_config_type config_type);
+extern void taiko_event_register(
+ int (*machine_event_cb)(struct snd_soc_codec *codec,
+ enum wcd9xxx_codec_event),
+ struct snd_soc_codec *codec);
+
#endif
diff --git a/sound/soc/codecs/wcd9xxx-common.h b/sound/soc/codecs/wcd9xxx-common.h
index 6bc581c..316742d 100644
--- a/sound/soc/codecs/wcd9xxx-common.h
+++ b/sound/soc/codecs/wcd9xxx-common.h
@@ -73,4 +73,8 @@
extern void wcd9xxx_clsh_init(struct wcd9xxx_clsh_cdc_data *clsh,
struct wcd9xxx_resmgr *resmgr);
+enum wcd9xxx_codec_event {
+ WCD9XXX_CODEC_EVENT_CODEC_UP = 0,
+};
+
#endif
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index 1d01d2e..3c226f7 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -9,7 +9,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-
#include <linux/module.h>
#include <linux/init.h>
#include <linux/firmware.h>
@@ -47,7 +46,7 @@
SND_JACK_BTN_6 | SND_JACK_BTN_7)
#define NUM_DCE_PLUG_DETECT 3
-#define NUM_DCE_PLUG_INS_DETECT 4
+#define NUM_DCE_PLUG_INS_DETECT 5
#define NUM_ATTEMPTS_INSERT_DETECT 25
#define NUM_ATTEMPTS_TO_REPORT 5
@@ -75,7 +74,6 @@
#define MCLK_RATE_12288KHZ 12288000
#define MCLK_RATE_9600KHZ 9600000
-#define WCD9XXX_RCO_CLK_RATE MCLK_RATE_12288KHZ
#define DEFAULT_DCE_STA_WAIT 55
#define DEFAULT_DCE_WAIT 60000
@@ -83,20 +81,20 @@
#define VDDIO_MICBIAS_MV 1800
+#define WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US 5000
+
#define WCD9XXX_HPHL_STATUS_READY_WAIT_US 1000
-#define WCD9XXX_MUX_SWITCH_READY_WAIT_US 100
+#define WCD9XXX_MUX_SWITCH_READY_WAIT_MS 50
#define WCD9XXX_MEAS_DELTA_MAX_MV 50
#define WCD9XXX_MEAS_INVALD_RANGE_LOW_MV 20
#define WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV 80
#define WCD9XXX_GM_SWAP_THRES_MIN_MV 150
#define WCD9XXX_GM_SWAP_THRES_MAX_MV 650
+#define WCD9XXX_THRESHOLD_MIC_THRESHOLD 200
#define WCD9XXX_USLEEP_RANGE_MARGIN_US 1000
-#define WCD9XXX_IRQ_MBHC_JACK_SWITCH_TAIKO 28
-#define WCD9XXX_IRQ_MBHC_JACK_SWITCH_TAPAN 21
-
-static bool detect_use_vddio_switch;
+static bool detect_use_vddio_switch = true;
struct wcd9xxx_mbhc_detect {
u16 dce;
@@ -134,6 +132,14 @@
WCD9XXX_HPHR_DAC_OFF_ACK
};
+enum wcd9xxx_current_v_idx {
+ WCD9XXX_CURRENT_V_INS_H,
+ WCD9XXX_CURRENT_V_INS_HU,
+ WCD9XXX_CURRENT_V_B1_H,
+ WCD9XXX_CURRENT_V_B1_HU,
+ WCD9XXX_CURRENT_V_BR_H,
+};
+
static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
{
return mbhc->polling_active;
@@ -266,9 +272,15 @@
int cfilt_k_val;
bool override;
struct snd_soc_codec *codec;
+ struct mbhc_internal_cal_data *d = &mbhc->mbhc_data;
codec = mbhc->codec;
+ if (mbhc->micbias_enable) {
+ pr_debug("%s: micbias is already on\n", __func__);
+ return;
+ }
+
if (vddio_switch && !mbhc->mbhc_micbias_switched &&
(!checkpolling || mbhc->polling_active)) {
if (restartpolling)
@@ -278,7 +290,7 @@
if (!override)
wcd9xxx_turn_onoff_override(codec, true);
/* Adjust threshold if Mic Bias voltage changes */
- if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+ if (d->micb_mv != VDDIO_MICBIAS_MV) {
cfilt_k_val = wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
VDDIO_MICBIAS_MV);
usleep_range(10000, 10000);
@@ -286,11 +298,28 @@
mbhc->mbhc_bias_regs.cfilt_val,
0xFC, (cfilt_k_val << 2));
usleep_range(10000, 10000);
+ /* Threshods for insertion/removal */
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
- mbhc->mbhc_data.adj_v_ins_hu & 0xFF);
+ d->v_ins_hu[MBHC_V_IDX_VDDIO] & 0xFF);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
- (mbhc->mbhc_data.adj_v_ins_hu >> 8) &
+ (d->v_ins_hu[MBHC_V_IDX_VDDIO] >> 8) &
0xFF);
+ /* Threshods for button press */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
+ d->v_b1_hu[MBHC_V_IDX_VDDIO] & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
+ (d->v_b1_hu[MBHC_V_IDX_VDDIO] >> 8) &
+ 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
+ d->v_b1_h[MBHC_V_IDX_VDDIO] & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
+ (d->v_b1_h[MBHC_V_IDX_VDDIO] >> 8) &
+ 0xFF);
+ /* Threshods for button release */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
+ d->v_brh[MBHC_V_IDX_VDDIO] & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
+ (d->v_brh[MBHC_V_IDX_VDDIO] >> 8) & 0xFF);
pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
__func__);
}
@@ -312,18 +341,36 @@
restartpolling)
wcd9xxx_pause_hs_polling(mbhc);
/* Reprogram thresholds */
- if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+ if (d->micb_mv != VDDIO_MICBIAS_MV) {
cfilt_k_val =
wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
- mbhc->mbhc_data.micb_mv);
+ d->micb_mv);
snd_soc_update_bits(codec,
mbhc->mbhc_bias_regs.cfilt_val,
0xFC, (cfilt_k_val << 2));
usleep_range(10000, 10000);
+ /* Revert threshods for insertion/removal */
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
- mbhc->mbhc_data.v_ins_hu & 0xFF);
+ d->v_ins_hu[MBHC_V_IDX_CFILT] & 0xFF);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
- (mbhc->mbhc_data.v_ins_hu >> 8) & 0xFF);
+ (d->v_ins_hu[MBHC_V_IDX_CFILT] >> 8) &
+ 0xFF);
+ /* Revert threshods for button press */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
+ d->v_b1_hu[MBHC_V_IDX_CFILT] & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
+ (d->v_b1_hu[MBHC_V_IDX_CFILT] >> 8) &
+ 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
+ d->v_b1_h[MBHC_V_IDX_CFILT] & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
+ (d->v_b1_h[MBHC_V_IDX_CFILT] >> 8) &
+ 0xFF);
+ /* Revert threshods for button release */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
+ d->v_brh[MBHC_V_IDX_CFILT] & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
+ (d->v_brh[MBHC_V_IDX_CFILT] >> 8) & 0xFF);
pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
__func__);
}
@@ -347,17 +394,37 @@
return __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
}
-static s16 wcd9xxx_get_current_v_ins(struct wcd9xxx_mbhc *mbhc, bool hu)
+static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
+ const enum wcd9xxx_current_v_idx idx)
{
- s16 v_ins;
+ enum mbhc_v_index vidx;
+ s16 ret = -EINVAL;
+
if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
mbhc->mbhc_micbias_switched)
- v_ins = hu ? (s16)mbhc->mbhc_data.adj_v_ins_hu :
- (s16)mbhc->mbhc_data.adj_v_ins_h;
+ vidx = MBHC_V_IDX_VDDIO;
else
- v_ins = hu ? (s16)mbhc->mbhc_data.v_ins_hu :
- (s16)mbhc->mbhc_data.v_ins_h;
- return v_ins;
+ vidx = MBHC_V_IDX_CFILT;
+
+ switch (idx) {
+ case WCD9XXX_CURRENT_V_INS_H:
+ ret = (s16)mbhc->mbhc_data.v_ins_h[vidx];
+ break;
+ case WCD9XXX_CURRENT_V_INS_HU:
+ ret = (s16)mbhc->mbhc_data.v_ins_hu[vidx];
+ break;
+ case WCD9XXX_CURRENT_V_B1_H:
+ ret = (s16)mbhc->mbhc_data.v_b1_h[vidx];
+ break;
+ case WCD9XXX_CURRENT_V_B1_HU:
+ ret = (s16)mbhc->mbhc_data.v_b1_hu[vidx];
+ break;
+ case WCD9XXX_CURRENT_V_BR_H:
+ ret = (s16)mbhc->mbhc_data.v_brh[vidx];
+ break;
+ }
+
+ return ret;
}
void *wcd9xxx_mbhc_cal_btn_det_mp(
@@ -389,27 +456,25 @@
static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
{
struct snd_soc_codec *codec = mbhc->codec;
- const s16 v_ins_hu = wcd9xxx_get_current_v_ins(mbhc, true);
+ const s16 v_ins_hu = wcd9xxx_get_current_v(mbhc,
+ WCD9XXX_CURRENT_V_INS_HU);
+ const s16 v_b1_hu = wcd9xxx_get_current_v(mbhc,
+ WCD9XXX_CURRENT_V_B1_HU);
+ const s16 v_b1_h = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
+ const s16 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
(v_ins_hu >> 8) & 0xFF);
-
- snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
- mbhc->mbhc_data.v_b1_hu & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu & 0xFF);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
- (mbhc->mbhc_data.v_b1_hu >> 8) & 0xFF);
-
- snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
- mbhc->mbhc_data.v_b1_h & 0xFF);
+ (v_b1_hu >> 8) & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, v_b1_h & 0xFF);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
- (mbhc->mbhc_data.v_b1_h >> 8) & 0xFF);
-
- snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
- mbhc->mbhc_data.v_brh & 0xFF);
+ (v_b1_h >> 8) & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh & 0xFF);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
- (mbhc->mbhc_data.v_brh >> 8) & 0xFF);
-
+ (v_brh >> 8) & 0xFF);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL,
mbhc->mbhc_data.v_brl & 0xFF);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL,
@@ -612,7 +677,7 @@
&mbhc->hph_pa_dac_state)) {
pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL,
- 0xC0, 0xC0);
+ 0x80, 0x80);
}
if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK,
@@ -740,6 +805,12 @@
mbhc->buttons_pressed &=
~WCD9XXX_JACK_BUTTON_MASK;
}
+
+ if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
+ pr_debug("%s: Disabling micbias\n", __func__);
+ mbhc->micbias_enable_cb(mbhc->codec, false);
+ mbhc->micbias_enable = false;
+ }
pr_debug("%s: Reporting removal %d(%x)\n", __func__,
jack_type, mbhc->hph_status);
wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status,
@@ -753,8 +824,17 @@
if (mbhc->mbhc_cfg->detect_extn_cable) {
/* Report removal of current jack type */
if (mbhc->hph_status && mbhc->hph_status != jack_type) {
+ if (mbhc->micbias_enable &&
+ mbhc->micbias_enable_cb &&
+ mbhc->hph_status == SND_JACK_HEADSET) {
+ pr_debug("%s: Disabling micbias\n",
+ __func__);
+ mbhc->micbias_enable_cb(mbhc->codec,
+ false);
+ mbhc->micbias_enable = false;
+ }
pr_debug("%s: Reporting removal (%x)\n",
- __func__, mbhc->hph_status);
+ __func__, mbhc->hph_status);
wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
0, WCD9XXX_JACK_MASK);
mbhc->hph_status = 0;
@@ -773,6 +853,11 @@
} else if (jack_type == SND_JACK_LINEOUT) {
mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
}
+
+ if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
+ pr_debug("%s: Enabling micbias\n", __func__);
+ mbhc->micbias_enable_cb(mbhc->codec, true);
+ }
pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
jack_type, mbhc->hph_status);
wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
@@ -813,6 +898,19 @@
WCD9XXX_BCL_LOCK(mbhc->resmgr);
}
+static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
+{
+ int r;
+ int vddio_k, mb_k;
+ vddio_k = wcd9xxx_resmgr_get_k_val(mbhc->resmgr, VDDIO_MICBIAS_MV);
+ mb_k = wcd9xxx_resmgr_get_k_val(mbhc->resmgr, mbhc->mbhc_data.micb_mv);
+ if (tovddio)
+ r = v * (vddio_k + 4) / (mb_k + 4);
+ else
+ r = v * (mb_k + 4) / (vddio_k + 4);
+ return r;
+}
+
static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
{
s16 v_hs_max;
@@ -821,7 +919,7 @@
plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
mbhc->mbhc_micbias_switched)
- v_hs_max = mbhc->mbhc_data.adj_v_hs_max;
+ v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
else
v_hs_max = plug_type->v_hs_max;
return v_hs_max;
@@ -913,19 +1011,17 @@
return __wcd9xxx_codec_sta_dce(mbhc, dce, false, norel);
}
-static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
- u16 bias_value)
+static s32 __wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
+ u16 bias_value, s16 z)
{
- s16 value, z, mb;
+ s16 value, mb;
s32 mv;
value = bias_value;
if (dce) {
- z = (mbhc->mbhc_data.dce_z);
mb = (mbhc->mbhc_data.dce_mb);
mv = (value - z) * (s32)mbhc->mbhc_data.micb_mv / (mb - z);
} else {
- z = (mbhc->mbhc_data.sta_z);
mb = (mbhc->mbhc_data.sta_mb);
mv = (value - z) * (s32)mbhc->mbhc_data.micb_mv / (mb - z);
}
@@ -933,6 +1029,14 @@
return mv;
}
+static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
+ u16 bias_value)
+{
+ s16 z;
+ z = dce ? (s16)mbhc->mbhc_data.dce_z : (s16)mbhc->mbhc_data.sta_z;
+ return __wcd9xxx_codec_sta_dce_v(mbhc, dce, bias_value, z);
+}
+
/* called only from interrupt which is under codec_resource_lock acquisition */
static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc)
{
@@ -1005,7 +1109,7 @@
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
+ __wcd9xxx_switch_micbias(mbhc, 0, false, false);
usleep_range(generic->t_shutdown_plug_rem,
generic->t_shutdown_plug_rem);
@@ -1032,19 +1136,6 @@
mbhc->mbhc_state = MBHC_STATE_NONE;
}
-static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
-{
- int r;
- int vddio_k, mb_k;
- vddio_k = wcd9xxx_resmgr_get_k_val(mbhc->resmgr, VDDIO_MICBIAS_MV);
- mb_k = wcd9xxx_resmgr_get_k_val(mbhc->resmgr, mbhc->mbhc_data.micb_mv);
- if (tovddio)
- r = v * vddio_k / mb_k;
- else
- r = v * mb_k / vddio_k;
- return r;
-}
-
/* called under codec_resource_lock acquisition */
static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
{
@@ -1055,6 +1146,7 @@
static void wcd9xxx_onoff_vddio_switch(struct wcd9xxx_mbhc *mbhc, bool on)
{
+ pr_debug("%s: vddio %d\n", __func__, on);
if (on) {
snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1 << 7, 1 << 7);
@@ -1098,7 +1190,7 @@
int ch;
enum wcd9xxx_mbhc_plug_type type;
int vdce;
- struct wcd9xxx_mbhc_detect *d, *dprev, *dgnd = NULL;
+ struct wcd9xxx_mbhc_detect *d, *dprev, *dgnd = NULL, *dvddio = NULL;
int maxv = 0, minv = 0;
const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
@@ -1120,7 +1212,7 @@
d->_type = PLUG_TYPE_HIGH_HPH;
ch += d->hphl_status & 0x01;
- if (!d->swap_gnd && !d->hwvalue) {
+ if (!d->swap_gnd && !d->hwvalue && !d->vddio) {
if (maxv < d->_vdces)
maxv = d->_vdces;
if (!minv || minv > d->_vdces)
@@ -1153,6 +1245,11 @@
}
for (i = 0, d = dt; i < size; i++, d++) {
+ if (d->vddio) {
+ dvddio = d;
+ continue;
+ }
+
if ((i > 0) && (d->_type != dprev->_type)) {
pr_debug("%s: Invalid, inconsistent types\n", __func__);
type = PLUG_TYPE_INVALID;
@@ -1187,11 +1284,69 @@
__func__, type);
type = PLUG_TYPE_INVALID;
}
+ if (type == PLUG_TYPE_HEADSET && dvddio) {
+ if ((dvddio->_vdces > hs_max) ||
+ (dvddio->_vdces > minv + WCD9XXX_THRESHOLD_MIC_THRESHOLD)) {
+ pr_debug("%s: Headset with threshold on MIC detected\n",
+ __func__);
+ if (mbhc->mbhc_cfg->micbias_enable_flags &
+ (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
+ mbhc->micbias_enable = true;
+ } else {
+ pr_debug("%s: Headset with regular MIC detected\n",
+ __func__);
+ if (mbhc->mbhc_cfg->micbias_enable_flags &
+ (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET))
+ mbhc->micbias_enable = true;
+ }
+ }
exit:
- pr_debug("%s: Plug type %d detected\n", __func__, type);
+ pr_debug("%s: Plug type %d detected, micbias_enable %d\n", __func__,
+ type, mbhc->micbias_enable);
return type;
}
+/*
+ * Pull down MBHC micbias for provided duration in microsecond.
+ */
+static int wcd9xxx_pull_down_micbias(struct wcd9xxx_mbhc *mbhc, int us)
+{
+ bool micbiasconn = false;
+ struct snd_soc_codec *codec = mbhc->codec;
+ const u16 ctlreg = mbhc->mbhc_bias_regs.ctl_reg;
+
+ /*
+ * Disable MBHC to micbias connection to pull down
+ * micbias and pull down micbias for a moment.
+ */
+ if ((snd_soc_read(mbhc->codec, ctlreg) & 0x01)) {
+ WARN_ONCE(1, "MBHC micbias is already pulled down unexpectedly\n");
+ return -EFAULT;
+ }
+
+ if ((snd_soc_read(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL) & 1 << 4)) {
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
+ 1 << 4, 0);
+ micbiasconn = true;
+ }
+
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+
+ /*
+ * Pull down for 1ms to discharge bias. Give small margin (10us) to be
+ * able to get consistent result across DCEs.
+ */
+ usleep_range(1000, 1000 + 10);
+
+ if (micbiasconn)
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
+ 1 << 4, 1 << 4);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+ usleep_range(us, us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+
+ return 0;
+}
+
static enum wcd9xxx_mbhc_plug_type
wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
{
@@ -1213,6 +1368,12 @@
plug_type_ptr =
WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+ /*
+ * cfilter in fast mode requires 1ms to charge up and down micbias
+ * fully.
+ */
+ (void) wcd9xxx_pull_down_micbias(mbhc,
+ WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc);
rt[0].swap_gnd = false;
@@ -1221,7 +1382,7 @@
for (i = 1; i < NUM_DCE_PLUG_INS_DETECT; i++) {
rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 2);
if (detect_use_vddio_switch)
- rt[i].vddio = (i == NUM_DCE_PLUG_INS_DETECT - 1);
+ rt[i].vddio = (i == 1);
else
rt[i].vddio = false;
rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
@@ -1230,6 +1391,15 @@
wcd9xxx_codec_hphr_gnd_switch(codec, true);
if (rt[i].vddio)
wcd9xxx_onoff_vddio_switch(mbhc, true);
+ /*
+ * Pull down micbias to detect headset with mic which has
+ * threshold and to have more consistent voltage measurements.
+ *
+ * cfilter in fast mode requires 1ms to charge up and down
+ * micbias fully.
+ */
+ (void) wcd9xxx_pull_down_micbias(mbhc,
+ WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
if (rt[i].vddio)
wcd9xxx_onoff_vddio_switch(mbhc, false);
@@ -1428,6 +1598,11 @@
*/
wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
msleep(100);
+
+ /* if PA is already on, switch micbias source to VDDIO */
+ if (mbhc->event_state &
+ (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
+ __wcd9xxx_switch_micbias(mbhc, 1, false, false);
wcd9xxx_start_hs_polling(mbhc);
} else if (plug_type == PLUG_TYPE_HIGH_HPH) {
if (mbhc->mbhc_cfg->detect_extn_cable) {
@@ -1489,6 +1664,9 @@
wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
wcd9xxx_schedule_hs_detect_plug(mbhc,
&mbhc->correct_plug_swch);
+ } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
+ wcd9xxx_schedule_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
} else {
pr_debug("%s: Valid plug found, determine plug type %d\n",
__func__, plug_type);
@@ -1657,12 +1835,12 @@
/* called only from interrupt which is under codec_resource_lock acquisition */
static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc)
{
- short bias_value;
+ s16 dce;
+ unsigned long timeout;
bool removed = true;
struct snd_soc_codec *codec = mbhc->codec;
const struct wcd9xxx_mbhc_general_cfg *generic =
WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
- int min_us = FAKE_REMOVAL_MIN_PERIOD_MS * 1000;
pr_debug("%s: enter\n", __func__);
if (mbhc->current_plug != PLUG_TYPE_HEADSET) {
@@ -1674,22 +1852,21 @@
}
usleep_range(generic->t_shutdown_plug_rem,
- generic->t_shutdown_plug_rem);
+ generic->t_shutdown_plug_rem);
+ timeout = jiffies + msecs_to_jiffies(FAKE_REMOVAL_MIN_PERIOD_MS);
do {
- bias_value = wcd9xxx_codec_sta_dce(mbhc, 1, true);
- pr_debug("%s: DCE %d,%d, %d us left\n", __func__, bias_value,
- wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value), min_us);
- if (bias_value < wcd9xxx_get_current_v_ins(mbhc, false)) {
- pr_debug("%s: checking false removal\n", __func__);
- msleep(500);
- removed = !wcd9xxx_hs_remove_settle(mbhc);
- pr_debug("%s: headset %sactually removed\n", __func__,
- removed ? "" : "not ");
+ dce = wcd9xxx_codec_sta_dce(mbhc, 1, true);
+ pr_debug("%s: DCE 0x%x,%d\n", __func__, dce,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, dce));
+ if (dce <
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H)) {
+ removed = false;
break;
}
- min_us -= mbhc->mbhc_data.t_dce;
- } while (min_us > 0);
+ } while (!time_after(jiffies, timeout));
+ pr_debug("%s: headset %sactually removed\n", __func__,
+ removed ? "" : "not ");
if (removed) {
if (mbhc->mbhc_cfg->detect_extn_cable) {
@@ -1746,7 +1923,6 @@
static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
{
- bool vddio;
struct wcd9xxx_mbhc *mbhc = data;
pr_debug("%s: enter, removal interrupt\n", __func__);
@@ -1765,25 +1941,12 @@
WCD9XXX_COND_HPH, false);
}
- vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
- mbhc->mbhc_micbias_switched);
- if (vddio)
- __wcd9xxx_switch_micbias(mbhc, 0, false, true);
-
if (mbhc->mbhc_cfg->detect_extn_cable &&
!wcd9xxx_swch_level_remove(mbhc))
wcd9xxx_hs_remove_irq_noswch(mbhc);
else
wcd9xxx_hs_remove_irq_swch(mbhc);
- /*
- * if driver turned off vddio switch and headset is not removed,
- * turn on the vddio switch back, if headset is removed then vddio
- * switch is off by time now and shouldn't be turn on again from here
- */
- if (vddio && (mbhc->current_plug == PLUG_TYPE_HEADSET))
- __wcd9xxx_switch_micbias(mbhc, 1, true, true);
-
if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
WCD9XXX_COND_HPH, true);
@@ -1942,7 +2105,8 @@
static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
{
struct snd_soc_codec *codec;
- s16 btn_mv = 0, btn_delta_mv;
+ s16 adj_v_hs_max;
+ s16 btn_mv = 0, btn_mv_sta[MBHC_V_IDX_NUM], btn_mv_dce[MBHC_V_IDX_NUM];
struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
u16 *btn_high;
@@ -1953,23 +2117,21 @@
btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
- mbhc->mbhc_data.v_ins_hu =
+ mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_CFILT] =
wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max);
- mbhc->mbhc_data.v_ins_h =
+ mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_CFILT] =
wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max);
mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
- mbhc->mbhc_data.adj_v_hs_max =
- scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
- mbhc->mbhc_data.adj_v_ins_hu =
- wcd9xxx_codec_v_sta_dce(mbhc, STA,
- mbhc->mbhc_data.adj_v_hs_max);
- mbhc->mbhc_data.adj_v_ins_h =
- wcd9xxx_codec_v_sta_dce(mbhc, DCE,
- mbhc->mbhc_data.adj_v_hs_max);
+ adj_v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max,
+ true);
+ mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_VDDIO] =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, adj_v_hs_max);
+ mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_VDDIO] =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE, adj_v_hs_max);
mbhc->mbhc_data.v_inval_ins_low =
scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
false);
@@ -1983,17 +2145,27 @@
for (i = 0; i < btn_det->num_btn; i++)
btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
- mbhc->mbhc_data.v_b1_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv);
- btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_sta;
- mbhc->mbhc_data.v_b1_hu =
- wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_delta_mv);
+ btn_mv_sta[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_sta;
+ btn_mv_dce[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_cic;
+ btn_mv_sta[MBHC_V_IDX_VDDIO] =
+ scale_v_micb_vddio(mbhc, btn_mv_sta[MBHC_V_IDX_CFILT], true);
+ btn_mv_dce[MBHC_V_IDX_VDDIO] =
+ scale_v_micb_vddio(mbhc, btn_mv_dce[MBHC_V_IDX_CFILT], true);
- btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_cic;
+ mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_CFILT] =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_CFILT]);
+ mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT] =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_CFILT]);
+ mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_VDDIO] =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_VDDIO]);
+ mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO] =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_VDDIO]);
- mbhc->mbhc_data.v_b1_huc =
- wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_delta_mv);
+ mbhc->mbhc_data.v_brh[MBHC_V_IDX_CFILT] =
+ mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT];
+ mbhc->mbhc_data.v_brh[MBHC_V_IDX_VDDIO] =
+ mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO];
- mbhc->mbhc_data.v_brh = mbhc->mbhc_data.v_b1_h;
mbhc->mbhc_data.v_brl = BUTTON_MIN;
mbhc->mbhc_data.v_no_mic =
@@ -2079,6 +2251,9 @@
wcd9xxx_report_plug(mbhc, 1,
SND_JACK_HEADPHONE);
}
+ } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
+ pr_debug("%s: High HPH detected, continue polling\n",
+ __func__);
} else {
if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
pt_gnd_mic_swap_cnt++;
@@ -2117,6 +2292,11 @@
}
}
+ if (plug_type == PLUG_TYPE_HIGH_HPH) {
+ pr_debug("%s: polling is done, still HPH, so enabling MIC trigger\n",
+ __func__);
+ wcd9xxx_find_plug_and_report(mbhc, plug_type);
+ }
/* Turn off override */
if (!correction)
wcd9xxx_turn_onoff_override(codec, false);
@@ -2278,12 +2458,17 @@
static int wcd9xxx_is_fake_press(struct wcd9xxx_mbhc *mbhc)
{
int i;
+ s16 mb_v;
int r = 0;
const int dces = NUM_DCE_PLUG_DETECT;
- s16 mb_v, v_ins_hu, v_ins_h;
-
- v_ins_hu = wcd9xxx_get_current_v_ins(mbhc, true);
- v_ins_h = wcd9xxx_get_current_v_ins(mbhc, false);
+ const s16 v_ins_hu =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
+ const s16 v_ins_h =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
+ const s16 v_b1_hu =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
+ const s16 v_b1_h =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
for (i = 0; i < dces; i++) {
usleep_range(10000, 10000);
@@ -2291,8 +2476,7 @@
mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
- if (mb_v < (s16)mbhc->mbhc_data.v_b1_hu ||
- mb_v > v_ins_hu) {
+ if (mb_v < v_b1_hu || mb_v > v_ins_hu) {
r = 1;
break;
}
@@ -2300,8 +2484,7 @@
mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
- if (mb_v < (s16)mbhc->mbhc_data.v_b1_h ||
- mb_v > v_ins_h) {
+ if (mb_v < v_b1_h || mb_v > v_ins_h) {
r = 1;
break;
}
@@ -2371,18 +2554,45 @@
return mask;
}
+void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z)
+{
+ s16 reg0, reg1;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+ /* Pull down micbias to ground and disconnect vddio switch */
+ reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x81, 0x1);
+ reg1 = snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 1 << 7, 0);
+
+ /* Disconnect override from micbias */
+ snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
+ usleep_range(1000, 1000 + 1000);
+ *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, false);
+ *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, false);
+
+ /* Connect override from micbias */
+ snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 4);
+ /* Disable pull down micbias to ground */
+ snd_soc_write(codec, mbhc->mbhc_bias_regs.mbhc_reg, reg1);
+ snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
+}
+
irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
{
int i, mask;
- short dce, sta;
- s32 mv, mv_s, stamv_s;
bool vddio;
u8 mbhc_status;
+ s16 dce_z, sta_z;
int btn = -1, meas = 0;
struct wcd9xxx_mbhc *mbhc = data;
const struct wcd9xxx_mbhc_btn_detect_cfg *d =
WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
short btnmeas[d->n_btn_meas + 1];
+ short dce[d->n_btn_meas + 1], sta;
+ s32 mv[d->n_btn_meas + 1], mv_s[d->n_btn_meas + 1];
+ s32 stamv, stamv_s;
struct snd_soc_codec *codec = mbhc->codec;
struct wcd9xxx *core = mbhc->resmgr->core;
int n_btn_meas = d->n_btn_meas;
@@ -2406,9 +2616,6 @@
goto done;
}
- dce = wcd9xxx_read_dce_result(codec);
- mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, dce);
-
/* If switch nterrupt already kicked in, ignore button press */
if (mbhc->in_swch_irq_handler) {
pr_debug("%s: Swtich level changed, ignore button press\n",
@@ -2420,13 +2627,10 @@
/* Measure scaled HW DCE */
vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
mbhc->mbhc_micbias_switched);
- mv_s = vddio ? scale_v_micb_vddio(mbhc, mv, false) : mv;
/* Measure scaled HW STA */
+ dce[0] = wcd9xxx_read_dce_result(codec);
sta = wcd9xxx_read_sta_result(codec);
- stamv_s = wcd9xxx_codec_sta_dce_v(mbhc, 0, sta);
- if (vddio)
- stamv_s = scale_v_micb_vddio(mbhc, stamv_s, false);
if (mbhc_status != STATUS_REL_DETECTION) {
if (mbhc->mbhc_last_resume &&
!time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
@@ -2436,30 +2640,55 @@
} else {
pr_debug("%s: Button is released without resume",
__func__);
- btn = wcd9xxx_determine_button(mbhc, mv_s);
+ wcd9xxx_get_z(mbhc, &dce_z, &sta_z);
+ stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z);
+ if (vddio)
+ stamv_s = scale_v_micb_vddio(mbhc, stamv,
+ false);
+ else
+ stamv_s = stamv;
+ mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0],
+ dce_z);
+ mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0],
+ false) : mv[0];
+ btn = wcd9xxx_determine_button(mbhc, mv_s[0]);
if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
btn = -1;
goto done;
}
}
+ for (meas = 1; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1)));
+ meas++)
+ dce[meas] = wcd9xxx_codec_sta_dce(mbhc, 1, false);
+
+ wcd9xxx_get_z(mbhc, &dce_z, &sta_z);
+
+ stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z);
+ if (vddio)
+ stamv_s = scale_v_micb_vddio(mbhc, stamv, false);
+ else
+ stamv_s = stamv;
pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
- sta & 0xFFFF, wcd9xxx_codec_sta_dce_v(mbhc, 0, sta), stamv_s);
+ sta & 0xFFFF, stamv, stamv_s);
/* determine pressed button */
- btnmeas[meas++] = wcd9xxx_determine_button(mbhc, mv_s);
+ mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0], dce_z);
+ mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0], false) : mv[0];
+ btnmeas[0] = wcd9xxx_determine_button(mbhc, mv_s[0]);
pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
- dce & 0xFFFF, mv, mv_s, btnmeas[meas - 1]);
+ dce[0] & 0xFFFF, mv[0], mv_s[0], btnmeas[0]);
if (n_btn_meas == 0)
btn = btnmeas[0];
- for (; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1))); meas++) {
- dce = wcd9xxx_codec_sta_dce(mbhc, 1, false);
- mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, dce);
- mv_s = vddio ? scale_v_micb_vddio(mbhc, mv, false) : mv;
-
- btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s);
+ for (meas = 1; (n_btn_meas && d->n_btn_meas &&
+ (meas < (d->n_btn_meas + 1))); meas++) {
+ mv[meas] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[meas], dce_z);
+ mv_s[meas] = vddio ? scale_v_micb_vddio(mbhc, mv[meas], false) :
+ mv[meas];
+ btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s[meas]);
pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
- __func__, meas, dce & 0xFFFF, mv, mv_s, btnmeas[meas]);
+ __func__, meas, dce[meas] & 0xFFFF, mv[meas],
+ mv_s[meas], btnmeas[meas]);
/*
* if large enough measurements are collected,
* start to check if last all n_btn_con measurements were
@@ -2733,9 +2962,11 @@
ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
if (ret)
goto gen_err;
- usleep_range(WCD9XXX_MUX_SWITCH_READY_WAIT_US,
- WCD9XXX_MUX_SWITCH_READY_WAIT_US +
- WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ /*
+ * Hardware that has external cap can delay mic bias ramping down up
+ * to 50ms.
+ */
+ msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
/* DCE measurement for 0 voltage */
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
@@ -2755,7 +2986,11 @@
ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
if (ret)
goto gen_err;
- usleep_range(100, 100);
+ /*
+ * Hardware that has external cap can delay mic bias ramping down up
+ * to 50ms.
+ */
+ msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
@@ -2768,7 +3003,11 @@
ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
if (ret)
goto gen_err;
- usleep_range(100, 100);
+ /*
+ * Hardware that has external cap can delay mic bias ramping down up
+ * to 50ms.
+ */
+ msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
@@ -2786,6 +3025,7 @@
wcd9xxx_turn_onoff_rel_detection(codec, true);
pr_debug("%s: leave\n", __func__);
+ return;
gen_err:
pr_err("%s: Error returned, ret: %d\n", __func__, ret);
@@ -2872,10 +3112,10 @@
switch (mbhc->mbhc_version) {
case WCD9XXX_MBHC_VERSION_TAIKO:
- jack_irq = WCD9XXX_IRQ_MBHC_JACK_SWITCH_TAIKO;
+ jack_irq = WCD9320_IRQ_MBHC_JACK_SWITCH;
break;
case WCD9XXX_MBHC_VERSION_TAPAN:
- jack_irq = WCD9XXX_IRQ_MBHC_JACK_SWITCH_TAPAN;
+ jack_irq = WCD9306_IRQ_MBHC_JACK_SWITCH;
break;
default:
return -EINVAL;
@@ -2994,46 +3234,38 @@
int n = 0;
struct wcd9xxx_mbhc *mbhc = file->private_data;
const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
- const s16 v_ins_hu_cur = wcd9xxx_get_current_v_ins(mbhc, true);
- const s16 v_ins_h_cur = wcd9xxx_get_current_v_ins(mbhc, false);
+ const s16 v_ins_hu =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
+ const s16 v_ins_h =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
+ const s16 v_b1_hu =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
+ const s16 v_b1_h =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
+ const s16 v_br_h =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
- n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n", p->dce_z,
- wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
+ n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",
+ p->dce_z, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
- n += scnprintf(buffer + n, size - n, "t_dce = %x\n", p->t_dce);
- n += scnprintf(buffer + n, size - n, "t_sta = %x\n", p->t_sta);
- n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n",
- p->micb_mv);
- n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)%s\n",
- p->v_ins_hu,
- wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_ins_hu),
- p->v_ins_hu == v_ins_hu_cur ? "*" : "");
- n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)%s\n",
- p->v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_ins_h),
- p->v_ins_h == v_ins_h_cur ? "*" : "");
- n += scnprintf(buffer + n, size - n, "adj_v_ins_hu = %x(%dmv)%s\n",
- p->adj_v_ins_hu,
- wcd9xxx_codec_sta_dce_v(mbhc, 0, p->adj_v_ins_hu),
- p->adj_v_ins_hu == v_ins_hu_cur ? "*" : "");
- n += scnprintf(buffer + n, size - n, "adj_v_ins_h = %x(%dmv)%s\n",
- p->adj_v_ins_h,
- wcd9xxx_codec_sta_dce_v(mbhc, 1, p->adj_v_ins_h),
- p->adj_v_ins_h == v_ins_h_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "t_dce = %d\n", p->t_dce);
+ n += scnprintf(buffer + n, size - n, "t_sta = %d\n", p->t_sta);
+ n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n", p->micb_mv);
+ n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)\n",
+ v_ins_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_ins_hu));
+ n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)\n",
+ v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_ins_h));
n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
- p->v_b1_hu,
- wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_b1_hu));
+ v_b1_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_b1_hu));
n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
- p->v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_b1_h));
- n += scnprintf(buffer + n, size - n, "v_b1_huc = %x(%dmv)\n",
- p->v_b1_huc,
- wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_b1_huc));
+ v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_b1_h));
n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
- p->v_brh, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_brh));
+ v_br_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_br_h));
n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
@@ -3161,18 +3393,31 @@
enum wcd9xxx_micbias_num ret;
switch (event) {
case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_1_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
ret = MBHC_MICBIAS1;
break;
case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_2_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
ret = MBHC_MICBIAS2;
break;
case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_3_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
ret = MBHC_MICBIAS3;
break;
case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_4_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
ret = MBHC_MICBIAS4;
break;
default:
+ WARN_ONCE(1, "Cannot convert event %d to micbias\n", event);
ret = MBHC_MICBIAS_INVALID;
break;
}
@@ -3270,33 +3515,41 @@
case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
if (mbhc->mbhc_cfg->micbias ==
wcd9xxx_event_to_micbias(event) &&
- wcd9xxx_is_hph_pa_on(codec))
+ (mbhc->event_state &
+ (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR)))
wcd9xxx_switch_micbias(mbhc, 1);
break;
/* PA usage change */
case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
+ set_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg) & 0x80))
- /* if micbias is enabled, switch to vddio */
+ /* if micbias is not enabled, switch to vddio */
wcd9xxx_switch_micbias(mbhc, 1);
break;
case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
- /* Not used now */
+ set_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
break;
case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
+ clear_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
/* if HPH PAs are off, report OCP and switch back to CFILT */
clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
if (mbhc->hph_status & SND_JACK_OC_HPHL)
hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
- wcd9xxx_switch_micbias(mbhc, 0);
+ if (!(mbhc->event_state &
+ (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR)))
+ wcd9xxx_switch_micbias(mbhc, 0);
break;
case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
+ clear_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
/* if HPH PAs are off, report OCP and switch back to CFILT */
clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
if (mbhc->hph_status & SND_JACK_OC_HPHR)
hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
- wcd9xxx_switch_micbias(mbhc, 0);
+ if (!(mbhc->event_state &
+ (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR)))
+ wcd9xxx_switch_micbias(mbhc, 0);
break;
/* Clock usage change */
case WCD9XXX_EVENT_PRE_MCLK_ON:
@@ -3327,7 +3580,7 @@
snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
0 << 4);
/* Re-calibrate clock rate dependent values */
- wcd9xxx_update_mbhc_clk_rate(mbhc, WCD9XXX_RCO_CLK_RATE);
+ wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->rco_clk_rate);
/* If clock source changes, stop and restart polling */
if (wcd9xxx_mbhc_polling(mbhc)) {
wcd9xxx_calibrate_hs_polling(mbhc);
@@ -3393,7 +3646,10 @@
* NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
*/
int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
- struct snd_soc_codec *codec, int version)
+ struct snd_soc_codec *codec,
+ int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
+ int version,
+ int rco_clk_rate)
{
int ret;
void *core;
@@ -3416,7 +3672,9 @@
mbhc->codec = codec;
mbhc->resmgr = resmgr;
mbhc->resmgr->mbhc = mbhc;
+ mbhc->micbias_enable_cb = micbias_enable_cb;
mbhc->mbhc_version = version;
+ mbhc->rco_clk_rate = rco_clk_rate;
if (mbhc->headset_jack.jack == NULL) {
ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
@@ -3534,16 +3792,25 @@
{
void *cdata = mbhc->codec->control_data;
- wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_SLIMBUS, mbhc);
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_POTENTIAL, mbhc);
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
- wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_JACK_SWITCH, mbhc);
+ switch (mbhc->mbhc_version) {
+ case WCD9XXX_MBHC_VERSION_TAIKO:
+ wcd9xxx_free_irq(cdata, WCD9320_IRQ_MBHC_JACK_SWITCH, mbhc);
+ break;
+ case WCD9XXX_MBHC_VERSION_TAPAN:
+ wcd9xxx_free_irq(cdata, WCD9306_IRQ_MBHC_JACK_SWITCH, mbhc);
+ break;
+ default:
+ pr_err("%s: irq free failed! Invalid MBHC version %d\n",
+ __func__, mbhc->mbhc_version);
+ }
+
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, mbhc);
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, mbhc);
- wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
if (mbhc->mbhc_fw)
release_firmware(mbhc->mbhc_fw);
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
index 300e34e..71a62b2 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.h
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -28,6 +28,18 @@
u8 cfilt_sel;
};
+enum mbhc_v_index {
+ MBHC_V_IDX_CFILT,
+ MBHC_V_IDX_VDDIO,
+ MBHC_V_IDX_NUM,
+};
+
+enum mbhc_cal_type {
+ MBHC_CAL_MCLK,
+ MBHC_CAL_RCO,
+ MBHC_CAL_NUM,
+};
+
/* Data used by MBHC */
struct mbhc_internal_cal_data {
u16 dce_z;
@@ -38,17 +50,13 @@
u32 t_dce;
u32 t_sta;
u32 micb_mv;
- u16 v_ins_hu;
- u16 v_ins_h;
- u16 v_b1_hu;
- u16 v_b1_h;
- u16 v_b1_huc;
- u16 v_brh;
+ u16 v_ins_hu[MBHC_V_IDX_NUM];
+ u16 v_ins_h[MBHC_V_IDX_NUM];
+ u16 v_b1_hu[MBHC_V_IDX_NUM];
+ u16 v_b1_h[MBHC_V_IDX_NUM];
+ u16 v_brh[MBHC_V_IDX_NUM];
u16 v_brl;
u16 v_no_mic;
- s16 adj_v_hs_max;
- u16 adj_v_ins_hu;
- u16 adj_v_ins_h;
s16 v_inval_ins_low;
s16 v_inval_ins_high;
};
@@ -76,6 +84,11 @@
MBHC_MICBIAS4,
};
+enum wcd9xx_mbhc_micbias_enable_bits {
+ MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET,
+ MBHC_MICBIAS_ENABLE_REGULAR_HEADSET,
+};
+
enum wcd9xxx_mbhc_state {
MBHC_STATE_NONE = -1,
MBHC_STATE_POTENTIAL,
@@ -97,6 +110,11 @@
TAIKO_NUM_CLK_FREQS,
};
+enum wcd9xxx_mbhc_event_state {
+ MBHC_EVENT_PA_HPHL,
+ MBHC_EVENT_PA_HPHR,
+};
+
struct wcd9xxx_mbhc_general_cfg {
u8 t_ldoh;
u8 t_bg_fast_settle;
@@ -194,6 +212,8 @@
int gpio_level_insert;
bool insert_detect; /* codec has own MBHC_INSERT_DETECT */
bool detect_extn_cable;
+ /* bit mask of enum wcd9xx_mbhc_micbias_enable_bits */
+ unsigned long micbias_enable_flags;
/* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */
bool (*swap_gnd_mic) (struct snd_soc_codec *);
};
@@ -239,8 +259,13 @@
bool no_mic_headset_override;
- /* track PA/DAC state */
+ /* track PA/DAC state to sync with userspace */
unsigned long hph_pa_dac_state;
+ /*
+ * save codec's state with resmgr event notification
+ * bit flags of enum wcd9xxx_mbhc_event_state
+ */
+ unsigned long event_state;
unsigned long mbhc_last_resume; /* in jiffies */
@@ -251,8 +276,13 @@
struct notifier_block nblock;
+ bool micbias_enable;
+ int (*micbias_enable_cb) (struct snd_soc_codec*, bool);
+
enum wcd9xxx_mbhc_version mbhc_version;
+ u32 rco_clk_rate;
+
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_poke;
struct dentry *debugfs_mbhc;
@@ -317,7 +347,10 @@
int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
struct wcd9xxx_mbhc_config *mbhc_cfg);
int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
- struct snd_soc_codec *codec, int version);
+ struct snd_soc_codec *codec,
+ int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
+ int version,
+ int rco_clk_rate);
void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc);
void *wcd9xxx_mbhc_cal_btn_det_mp(
const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
index 60a76a2..77dbd36 100644
--- a/sound/soc/codecs/wcd9xxx-resmgr.c
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -659,7 +659,6 @@
bool set;
pr_debug("%s: enter\n", __func__);
- WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
set = !!test_bit(cond, &resmgr->cond_flags);
list_for_each(l, &resmgr->update_bit_cond_h) {
e = list_entry(l, struct wcd9xxx_resmgr_cond_entry, list);
@@ -675,13 +674,14 @@
void wcd9xxx_resmgr_cond_update_cond(struct wcd9xxx_resmgr *resmgr,
enum wcd9xxx_resmgr_cond cond, bool set)
{
- WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ mutex_lock(&resmgr->update_bit_cond_lock);
if ((set && !test_and_set_bit(cond, &resmgr->cond_flags)) ||
(!set && test_and_clear_bit(cond, &resmgr->cond_flags))) {
pr_debug("%s: Resource %d condition changed to %s\n", __func__,
cond, set ? "set" : "clear");
wcd9xxx_resmgr_cond_trigger_cond(resmgr, cond);
}
+ mutex_unlock(&resmgr->update_bit_cond_lock);
}
int wcd9xxx_resmgr_add_cond_update_bits(struct wcd9xxx_resmgr *resmgr,
@@ -700,11 +700,11 @@
entry->shift = shift;
entry->invert = invert;
- WCD9XXX_BCL_LOCK(resmgr);
+ mutex_lock(&resmgr->update_bit_cond_lock);
list_add_tail(&entry->list, &resmgr->update_bit_cond_h);
wcd9xxx_resmgr_cond_trigger_cond(resmgr, cond);
- WCD9XXX_BCL_UNLOCK(resmgr);
+ mutex_unlock(&resmgr->update_bit_cond_lock);
return 0;
}
@@ -722,7 +722,7 @@
struct wcd9xxx_resmgr_cond_entry *e = NULL;
pr_debug("%s: enter\n", __func__);
- WCD9XXX_BCL_LOCK(resmgr);
+ mutex_lock(&resmgr->update_bit_cond_lock);
list_for_each_safe(l, next, &resmgr->update_bit_cond_h) {
e = list_entry(l, struct wcd9xxx_resmgr_cond_entry, list);
if (e->reg == reg && e->shift == shift && e->invert == invert) {
@@ -730,12 +730,12 @@
1 << e->shift,
e->invert << e->shift);
list_del(&e->list);
- WCD9XXX_BCL_UNLOCK(resmgr);
+ mutex_unlock(&resmgr->update_bit_cond_lock);
kfree(e);
return 0;
}
}
- WCD9XXX_BCL_UNLOCK(resmgr);
+ mutex_unlock(&resmgr->update_bit_cond_lock);
pr_err("%s: Cannot find update bit entry reg 0x%x, shift %d\n",
__func__, e ? e->reg : 0, e ? e->shift : 0);
@@ -776,12 +776,14 @@
BLOCKING_INIT_NOTIFIER_HEAD(&resmgr->notifier);
mutex_init(&resmgr->codec_resource_lock);
+ mutex_init(&resmgr->update_bit_cond_lock);
return 0;
}
void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr)
{
+ mutex_destroy(&resmgr->update_bit_cond_lock);
mutex_destroy(&resmgr->codec_resource_lock);
}
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.h b/sound/soc/codecs/wcd9xxx-resmgr.h
index 8acc816..b5f950c 100644
--- a/sound/soc/codecs/wcd9xxx-resmgr.h
+++ b/sound/soc/codecs/wcd9xxx-resmgr.h
@@ -127,6 +127,7 @@
unsigned long cond_flags;
struct list_head update_bit_cond_h;
+ struct mutex update_bit_cond_lock;
/*
* Currently, only used for mbhc purpose, to protect
diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig
index 66c475f..40661ff 100644
--- a/sound/soc/msm/Kconfig
+++ b/sound/soc/msm/Kconfig
@@ -180,7 +180,6 @@
select SND_SOC_MSM_STUB
select SND_SOC_MSM_HOSTLESS_PCM
select SND_SOC_WCD9320
- select SND_SOC_MSM_HDMI_CODEC_RX
select SND_DYNAMIC_MINORS
select AUDIO_OCMEM
select DOLBY_DAP
@@ -191,6 +190,23 @@
the machine drivers and the corresponding
DAI-links.
+config SND_SOC_APQ8074
+ tristate "SoC Machine driver for APQ8O74 boards"
+ depends on ARCH_MSM8974
+ select SND_SOC_QDSP6V2
+ select SND_SOC_MSM_STUB
+ select SND_SOC_MSM_HOSTLESS_PCM
+ select SND_SOC_WCD9320
+ select SND_SOC_MSM_HDMI_CODEC_RX
+ select SND_DYNAMIC_MINORS
+ select AUDIO_OCMEM
+ help
+ To add support for SoC audio on APQ8074.
+ This will enable sound soc drivers which
+ interfaces with DSP, also it will enable
+ the machine drivers and the corresponding
+ DAI-links.
+
config SND_SOC_MSM8226
tristate "SoC Machine driver for MSM8226 boards"
depends on ARCH_MSM8226
diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile
index ebde90b..e206812 100644
--- a/sound/soc/msm/Makefile
+++ b/sound/soc/msm/Makefile
@@ -55,8 +55,7 @@
# for MSM 8960 sound card driver
obj-$(CONFIG_SND_SOC_MSM_QDSP6_INTF) += qdsp6/
-
-snd-soc-qdsp6-objs := msm-dai-q6.o msm-pcm-q6.o msm-multi-ch-pcm-q6.o msm-lowlatency-pcm-q6.o msm-pcm-routing.o msm-dai-fe.o msm-compr-q6.o msm-dai-stub.o
+snd-soc-qdsp6-objs := msm-dai-q6.o msm-pcm-q6.o msm-multi-ch-pcm-q6.o msm-lowlatency-pcm-q6.o msm-pcm-loopback.o msm-pcm-routing.o msm-dai-fe.o msm-compr-q6.o msm-dai-stub.o
obj-$(CONFIG_SND_SOC_MSM_QDSP6_HDMI_AUDIO) += msm-dai-q6-hdmi.o
obj-$(CONFIG_SND_SOC_VOICE) += msm-pcm-voice.o msm-pcm-voip.o msm-pcm-dtmf.o msm-pcm-host-voice.o
snd-soc-qdsp6-objs += msm-pcm-lpa.o msm-pcm-afe.o
@@ -85,6 +84,10 @@
snd-soc-qdsp6v2-objs := msm-dai-fe.o
obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
+# for APQ 8074 sound card driver
+snd-soc-apq8074-objs := apq8074.o
+obj-$(CONFIG_SND_SOC_APQ8074) += snd-soc-apq8074.o
+
#for MDM9625 sound card driver
snd-soc-mdm9625-objs := mdm9625.o
obj-$(CONFIG_SND_SOC_MDM9625) += snd-soc-mdm9625.o
diff --git a/sound/soc/msm/apq8074.c b/sound/soc/msm/apq8074.c
new file mode 100644
index 0000000..9a2f83b
--- /dev/null
+++ b/sound/soc/msm/apq8074.c
@@ -0,0 +1,2486 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/qpnp/clkdiv.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <sound/q6afe-v2.h>
+#include <asm/mach-types.h>
+#include <mach/socinfo.h>
+#include <sound/pcm_params.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+#include "../codecs/wcd9320.h"
+#include <linux/io.h>
+
+#define DRV_NAME "apq8074-asoc-taiko"
+
+#define APQ8074_SPK_ON 1
+#define APQ8074_SPK_OFF 0
+
+#define MSM_SLIM_0_RX_MAX_CHANNELS 2
+#define MSM_SLIM_0_TX_MAX_CHANNELS 4
+
+#define BTSCO_RATE_8KHZ 8000
+#define BTSCO_RATE_16KHZ 16000
+
+static int slim0_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+#define SAMPLING_RATE_48KHZ 48000
+#define SAMPLING_RATE_96KHZ 96000
+#define SAMPLING_RATE_192KHZ 192000
+
+static int apq8074_auxpcm_rate = 8000;
+#define LO_1_SPK_AMP 0x1
+#define LO_3_SPK_AMP 0x2
+#define LO_2_SPK_AMP 0x4
+#define LO_4_SPK_AMP 0x8
+
+#define LPAIF_OFFSET 0xFE000000
+#define LPAIF_PRI_MODE_MUXSEL (LPAIF_OFFSET + 0x2B000)
+#define LPAIF_SEC_MODE_MUXSEL (LPAIF_OFFSET + 0x2C000)
+#define LPAIF_TER_MODE_MUXSEL (LPAIF_OFFSET + 0x2D000)
+#define LPAIF_QUAD_MODE_MUXSEL (LPAIF_OFFSET + 0x2E000)
+
+#define I2S_PCM_SEL 1
+#define I2S_PCM_SEL_OFFSET 1
+
+
+#define WCD9XXX_MBHC_DEF_BUTTONS 8
+#define WCD9XXX_MBHC_DEF_RLOADS 5
+#define TAIKO_EXT_CLK_RATE 9600000
+
+/* It takes about 13ms for Class-D PAs to ramp-up */
+#define EXT_CLASS_D_EN_DELAY 13000
+#define EXT_CLASS_D_DIS_DELAY 3000
+#define EXT_CLASS_D_DELAY_DELTA 2000
+
+/* It takes about 13ms for Class-AB PAs to ramp-up */
+#define EXT_CLASS_AB_EN_DELAY 10000
+#define EXT_CLASS_AB_DIS_DELAY 1000
+#define EXT_CLASS_AB_DELAY_DELTA 1000
+
+#define NUM_OF_AUXPCM_GPIOS 4
+
+static inline int param_is_mask(int p)
+{
+ return ((p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+ (p <= SNDRV_PCM_HW_PARAM_LAST_MASK));
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
+{
+ return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit)
+{
+ if (bit >= SNDRV_MASK_MAX)
+ return;
+ if (param_is_mask(n)) {
+ struct snd_mask *m = param_to_mask(p, n);
+ m->bits[0] = 0;
+ m->bits[1] = 0;
+ m->bits[bit >> 5] |= (1 << (bit & 31));
+ }
+}
+
+static const char *const auxpcm_rate_text[] = {"rate_8000", "rate_16000"};
+static const struct soc_enum apq8074_auxpcm_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, auxpcm_rate_text),
+};
+
+static void *def_taiko_mbhc_cal(void);
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm);
+
+static struct wcd9xxx_mbhc_config mbhc_cfg = {
+ .read_fw_bin = false,
+ .calibration = NULL,
+ .micbias = MBHC_MICBIAS2,
+ .mclk_cb_fn = msm_snd_enable_codec_ext_clk,
+ .mclk_rate = TAIKO_EXT_CLK_RATE,
+ .gpio = 0,
+ .gpio_irq = 0,
+ .gpio_level_insert = 1,
+ .detect_extn_cable = true,
+ .insert_detect = true,
+ .swap_gnd_mic = NULL,
+};
+
+struct msm_auxpcm_gpio {
+ unsigned gpio_no;
+ const char *gpio_name;
+};
+
+struct msm_auxpcm_ctrl {
+ struct msm_auxpcm_gpio *pin_data;
+ u32 cnt;
+};
+
+struct apq8074_asoc_mach_data {
+ int mclk_gpio;
+ u32 mclk_freq;
+ int us_euro_gpio;
+ struct msm_auxpcm_ctrl *pri_auxpcm_ctrl;
+};
+
+#define GPIO_NAME_INDEX 0
+#define DT_PARSE_INDEX 1
+
+static char *msm_prim_auxpcm_gpio_name[][2] = {
+ {"PRIM_AUXPCM_CLK", "qcom,prim-auxpcm-gpio-clk"},
+ {"PRIM_AUXPCM_SYNC", "qcom,prim-auxpcm-gpio-sync"},
+ {"PRIM_AUXPCM_DIN", "qcom,prim-auxpcm-gpio-din"},
+ {"PRIM_AUXPCM_DOUT", "qcom,prim-auxpcm-gpio-dout"},
+};
+
+static void *lpaif_pri_muxsel_virt_addr;
+
+struct apq8074_liquid_dock_dev {
+ int dock_plug_gpio;
+ int dock_plug_irq;
+ struct snd_soc_dapm_context *dapm;
+ struct work_struct irq_work;
+};
+
+static struct apq8074_liquid_dock_dev *apq8074_liquid_dock_dev;
+static int dock_plug_det = -1;
+
+/* Shared channel numbers for Slimbus ports that connect APQ to MDM. */
+enum {
+ SLIM_1_RX_1 = 145, /* BT-SCO and USB TX */
+ SLIM_1_TX_1 = 146, /* BT-SCO and USB RX */
+ SLIM_2_RX_1 = 147, /* HDMI RX */
+ SLIM_3_RX_1 = 148, /* In-call recording RX */
+ SLIM_3_RX_2 = 149, /* In-call recording RX */
+ SLIM_4_TX_1 = 150, /* In-call musid delivery TX */
+};
+
+static struct platform_device *spdev;
+static struct regulator *ext_spk_amp_regulator;
+static int ext_spk_amp_gpio = -1;
+static int ext_ult_spk_amp_gpio = -1;
+static int apq8074_spk_control = 1;
+static int apq8074_ext_spk_pamp;
+static int msm_slim_0_rx_ch = 1;
+static int msm_slim_0_tx_ch = 1;
+
+static int msm_btsco_rate = BTSCO_RATE_8KHZ;
+static int msm_btsco_ch = 1;
+static int msm_hdmi_rx_ch = 2;
+static int slim0_rx_sample_rate = SAMPLING_RATE_48KHZ;
+static int msm_proxy_rx_ch = 2;
+
+static struct mutex cdc_mclk_mutex;
+static struct clk *codec_clk;
+static int clk_users;
+static atomic_t prim_auxpcm_rsc_ref;
+
+static int apq8074_liquid_ext_spk_power_amp_init(void)
+{
+ int ret = 0;
+
+ ext_spk_amp_gpio = of_get_named_gpio(spdev->dev.of_node,
+ "qcom,ext-spk-amp-gpio", 0);
+ if (ext_spk_amp_gpio >= 0) {
+ ret = gpio_request(ext_spk_amp_gpio, "ext_spk_amp_gpio");
+ if (ret) {
+ pr_err("%s: gpio_request failed for ext_spk_amp_gpio.\n",
+ __func__);
+ return -EINVAL;
+ }
+ gpio_direction_output(ext_spk_amp_gpio, 0);
+
+ if (ext_spk_amp_regulator == NULL) {
+ ext_spk_amp_regulator = regulator_get(&spdev->dev,
+ "qcom,ext-spk-amp");
+
+ if (IS_ERR(ext_spk_amp_regulator)) {
+ pr_err("%s: Cannot get regulator %s.\n",
+ __func__, "qcom,ext-spk-amp");
+
+ gpio_free(ext_spk_amp_gpio);
+ return PTR_ERR(ext_spk_amp_regulator);
+ }
+ }
+ }
+
+ ext_ult_spk_amp_gpio = of_get_named_gpio(spdev->dev.of_node,
+ "qcom,ext-ult-spk-amp-gpio", 0);
+
+ if (ext_ult_spk_amp_gpio >= 0) {
+ ret = gpio_request(ext_ult_spk_amp_gpio,
+ "ext_ult_spk_amp_gpio");
+ if (ret) {
+ pr_err("%s: gpio_request failed for ext-ult_spk-amp-gpio.\n",
+ __func__);
+ return -EINVAL;
+ }
+ gpio_direction_output(ext_ult_spk_amp_gpio, 0);
+ }
+
+ return 0;
+}
+
+static void apq8074_liquid_ext_ult_spk_power_amp_enable(u32 on)
+{
+ if (on) {
+ regulator_enable(ext_spk_amp_regulator);
+ gpio_direction_output(ext_ult_spk_amp_gpio, 1);
+ /* time takes enable the external power class AB amplifier */
+ usleep_range(EXT_CLASS_AB_EN_DELAY,
+ EXT_CLASS_AB_EN_DELAY + EXT_CLASS_AB_DELAY_DELTA);
+ } else {
+ gpio_direction_output(ext_ult_spk_amp_gpio, 0);
+ regulator_disable(ext_spk_amp_regulator);
+ /* time takes disable the external power class AB amplifier */
+ usleep_range(EXT_CLASS_AB_DIS_DELAY,
+ EXT_CLASS_AB_DIS_DELAY + EXT_CLASS_AB_DELAY_DELTA);
+ }
+
+ pr_debug("%s: %s external ultrasound SPKR_DRV PAs.\n", __func__,
+ on ? "Enable" : "Disable");
+}
+
+static void apq8074_liquid_ext_spk_power_amp_enable(u32 on)
+{
+ if (on) {
+ regulator_enable(ext_spk_amp_regulator);
+ gpio_direction_output(ext_spk_amp_gpio, on);
+ /*time takes enable the external power amplifier*/
+ usleep_range(EXT_CLASS_D_EN_DELAY,
+ EXT_CLASS_D_EN_DELAY + EXT_CLASS_D_DELAY_DELTA);
+ } else {
+ gpio_direction_output(ext_spk_amp_gpio, on);
+ regulator_disable(ext_spk_amp_regulator);
+ /*time takes disable the external power amplifier*/
+ usleep_range(EXT_CLASS_D_DIS_DELAY,
+ EXT_CLASS_D_DIS_DELAY + EXT_CLASS_D_DELAY_DELTA);
+ }
+
+ pr_debug("%s: %s external speaker PAs.\n", __func__,
+ on ? "Enable" : "Disable");
+}
+
+static void apq8074_liquid_docking_irq_work(struct work_struct *work)
+{
+ struct apq8074_liquid_dock_dev *dock_dev =
+ container_of(work,
+ struct apq8074_liquid_dock_dev,
+ irq_work);
+
+ struct snd_soc_dapm_context *dapm = dock_dev->dapm;
+
+
+ mutex_lock(&dapm->codec->mutex);
+ dock_plug_det =
+ gpio_get_value(dock_dev->dock_plug_gpio);
+
+
+ if (0 == dock_plug_det) {
+ if ((apq8074_ext_spk_pamp & LO_1_SPK_AMP) &&
+ (apq8074_ext_spk_pamp & LO_3_SPK_AMP) &&
+ (apq8074_ext_spk_pamp & LO_2_SPK_AMP) &&
+ (apq8074_ext_spk_pamp & LO_4_SPK_AMP))
+ apq8074_liquid_ext_spk_power_amp_enable(1);
+ } else {
+ if ((apq8074_ext_spk_pamp & LO_1_SPK_AMP) &&
+ (apq8074_ext_spk_pamp & LO_3_SPK_AMP) &&
+ (apq8074_ext_spk_pamp & LO_2_SPK_AMP) &&
+ (apq8074_ext_spk_pamp & LO_4_SPK_AMP))
+ apq8074_liquid_ext_spk_power_amp_enable(0);
+ }
+
+ mutex_unlock(&dapm->codec->mutex);
+
+}
+
+static irqreturn_t apq8074_liquid_docking_irq_handler(int irq, void *dev)
+{
+ struct apq8074_liquid_dock_dev *dock_dev = dev;
+
+ /* switch speakers should not run in interrupt context */
+ schedule_work(&dock_dev->irq_work);
+
+ return IRQ_HANDLED;
+}
+
+static int apq8074_liquid_init_docking(struct snd_soc_dapm_context *dapm)
+{
+ int ret = 0;
+ int dock_plug_gpio = 0;
+
+ /* plug in docking speaker+plug in device OR unplug one of them */
+ u32 dock_plug_irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+
+ dock_plug_det = 0;
+ dock_plug_gpio = of_get_named_gpio(spdev->dev.of_node,
+ "qcom,dock-plug-det-irq", 0);
+
+ if (dock_plug_gpio >= 0) {
+
+ apq8074_liquid_dock_dev =
+ kzalloc(sizeof(*apq8074_liquid_dock_dev), GFP_KERNEL);
+
+ if (!apq8074_liquid_dock_dev) {
+ pr_err("apq8074_liquid_dock_dev alloc fail.\n");
+ return -ENOMEM;
+ }
+
+ apq8074_liquid_dock_dev->dock_plug_gpio = dock_plug_gpio;
+
+ ret = gpio_request(apq8074_liquid_dock_dev->dock_plug_gpio,
+ "dock-plug-det-irq");
+ if (ret) {
+ pr_err("%s:failed request apq8074_liquid_dock_plug_gpio.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ dock_plug_det =
+ gpio_get_value(apq8074_liquid_dock_dev->dock_plug_gpio);
+
+ apq8074_liquid_dock_dev->dock_plug_irq =
+ gpio_to_irq(apq8074_liquid_dock_dev->dock_plug_gpio);
+
+ apq8074_liquid_dock_dev->dapm = dapm;
+
+ ret = request_irq(apq8074_liquid_dock_dev->dock_plug_irq,
+ apq8074_liquid_docking_irq_handler,
+ dock_plug_irq_flags,
+ "liquid_dock_plug_irq",
+ apq8074_liquid_dock_dev);
+
+ INIT_WORK(
+ &apq8074_liquid_dock_dev->irq_work,
+ apq8074_liquid_docking_irq_work);
+ }
+
+ return 0;
+}
+
+static int apq8074_liquid_ext_spk_power_amp_on(u32 spk)
+{
+ int rc;
+
+ if (spk & (LO_1_SPK_AMP | LO_3_SPK_AMP | LO_2_SPK_AMP | LO_4_SPK_AMP)) {
+ pr_debug("%s: External speakers are already on. spk = 0x%x\n",
+ __func__, spk);
+
+ apq8074_ext_spk_pamp |= spk;
+ if ((apq8074_ext_spk_pamp & LO_1_SPK_AMP) &&
+ (apq8074_ext_spk_pamp & LO_3_SPK_AMP) &&
+ (apq8074_ext_spk_pamp & LO_2_SPK_AMP) &&
+ (apq8074_ext_spk_pamp & LO_4_SPK_AMP))
+ if (ext_spk_amp_gpio >= 0 &&
+ dock_plug_det == 0)
+ apq8074_liquid_ext_spk_power_amp_enable(1);
+ rc = 0;
+ } else {
+ pr_err("%s: Invalid external speaker ampl. spk = 0x%x\n",
+ __func__, spk);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+
+static void apq8074_ext_spk_power_amp_on(u32 spk)
+{
+ if (gpio_is_valid(ext_spk_amp_gpio))
+ apq8074_liquid_ext_spk_power_amp_on(spk);
+}
+
+static void apq8074_liquid_ext_spk_power_amp_off(u32 spk)
+{
+
+ if (spk & (LO_1_SPK_AMP |
+ LO_3_SPK_AMP |
+ LO_2_SPK_AMP |
+ LO_4_SPK_AMP)) {
+
+ pr_debug("%s Left and right speakers case spk = 0x%08x",
+ __func__, spk);
+
+ if (!apq8074_ext_spk_pamp) {
+ if (ext_spk_amp_gpio >= 0 &&
+ dock_plug_det == 0)
+ apq8074_liquid_ext_spk_power_amp_enable(0);
+ apq8074_ext_spk_pamp = 0;
+ }
+
+ } else {
+
+ pr_err("%s: ERROR : Invalid Ext Spk Ampl. spk = 0x%08x\n",
+ __func__, spk);
+ return;
+ }
+}
+
+static void apq8074_ext_spk_power_amp_off(u32 spk)
+{
+ if (gpio_is_valid(ext_spk_amp_gpio))
+ apq8074_liquid_ext_spk_power_amp_off(spk);
+}
+
+static void apq8074_ext_control(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ mutex_lock(&dapm->codec->mutex);
+
+ pr_debug("%s: apq8074_spk_control = %d", __func__, apq8074_spk_control);
+ if (apq8074_spk_control == APQ8074_SPK_ON) {
+ snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_4 amp");
+ } else {
+ snd_soc_dapm_disable_pin(dapm, "Lineout_1 amp");
+ snd_soc_dapm_disable_pin(dapm, "Lineout_3 amp");
+ snd_soc_dapm_disable_pin(dapm, "Lineout_2 amp");
+ snd_soc_dapm_disable_pin(dapm, "Lineout_4 amp");
+ }
+
+ snd_soc_dapm_sync(dapm);
+ mutex_unlock(&dapm->codec->mutex);
+}
+
+static int apq8074_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: apq8074_spk_control = %d", __func__, apq8074_spk_control);
+ ucontrol->value.integer.value[0] = apq8074_spk_control;
+ return 0;
+}
+
+static int apq8074_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ pr_debug("%s()\n", __func__);
+ if (apq8074_spk_control == ucontrol->value.integer.value[0])
+ return 0;
+
+ apq8074_spk_control = ucontrol->value.integer.value[0];
+ apq8074_ext_control(codec);
+ return 1;
+}
+
+
+static int msm_ext_spkramp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ pr_debug("%s()\n", __func__);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ if (!strncmp(w->name, "Lineout_1 amp", 14))
+ apq8074_ext_spk_power_amp_on(LO_1_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_3 amp", 14))
+ apq8074_ext_spk_power_amp_on(LO_3_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_2 amp", 14))
+ apq8074_ext_spk_power_amp_on(LO_2_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_4 amp", 14))
+ apq8074_ext_spk_power_amp_on(LO_4_SPK_AMP);
+ else {
+ pr_err("%s() Invalid Speaker Widget = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+ } else {
+ if (!strncmp(w->name, "Lineout_1 amp", 14))
+ apq8074_ext_spk_power_amp_off(LO_1_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_3 amp", 14))
+ apq8074_ext_spk_power_amp_off(LO_3_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_2 amp", 14))
+ apq8074_ext_spk_power_amp_off(LO_2_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_4 amp", 14))
+ apq8074_ext_spk_power_amp_off(LO_4_SPK_AMP);
+ else {
+ pr_err("%s() Invalid Speaker Widget = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+
+}
+
+static int msm_ext_spkramp_ultrasound_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+
+ pr_debug("%s()\n", __func__);
+
+ if (!strncmp(w->name, "SPK_ultrasound amp", 19)) {
+ if (!gpio_is_valid(ext_ult_spk_amp_gpio)) {
+ pr_err("%s: ext_ult_spk_amp_gpio isn't configured\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ apq8074_liquid_ext_ult_spk_power_amp_enable(1);
+ else
+ apq8074_liquid_ext_ult_spk_power_amp_enable(0);
+
+ } else {
+ pr_err("%s() Invalid Speaker Widget = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm)
+{
+ int ret = 0;
+ pr_debug("%s: enable = %d clk_users = %d\n",
+ __func__, enable, clk_users);
+
+ mutex_lock(&cdc_mclk_mutex);
+ if (enable) {
+ if (!codec_clk) {
+ dev_err(codec->dev, "%s: did not get Taiko MCLK\n",
+ __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ clk_users++;
+ if (clk_users != 1)
+ goto exit;
+
+ if (codec_clk) {
+ clk_set_rate(codec_clk, TAIKO_EXT_CLK_RATE);
+ clk_prepare_enable(codec_clk);
+ taiko_mclk_enable(codec, 1, dapm);
+ } else {
+ pr_err("%s: Error setting Taiko MCLK\n", __func__);
+ clk_users--;
+ goto exit;
+ }
+ } else {
+ if (clk_users > 0) {
+ clk_users--;
+ if (clk_users == 0) {
+ taiko_mclk_enable(codec, 0, dapm);
+ clk_disable_unprepare(codec_clk);
+ }
+ } else {
+ pr_err("%s: Error releasing Taiko MCLK\n", __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+ }
+exit:
+ mutex_unlock(&cdc_mclk_mutex);
+ return ret;
+}
+
+static int apq8074_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ pr_debug("%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return msm_snd_enable_codec_ext_clk(w->codec, 1, true);
+ case SND_SOC_DAPM_POST_PMD:
+ return msm_snd_enable_codec_ext_clk(w->codec, 0, true);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget apq8074_dapm_widgets[] = {
+
+ SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
+ apq8074_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SPK("Lineout_1 amp", msm_ext_spkramp_event),
+ SND_SOC_DAPM_SPK("Lineout_3 amp", msm_ext_spkramp_event),
+
+ SND_SOC_DAPM_SPK("Lineout_2 amp", msm_ext_spkramp_event),
+ SND_SOC_DAPM_SPK("Lineout_4 amp", msm_ext_spkramp_event),
+ SND_SOC_DAPM_SPK("SPK_ultrasound amp",
+ msm_ext_spkramp_ultrasound_event),
+
+ SND_SOC_DAPM_MIC("Handset Mic", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic4", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic6", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic7", NULL),
+
+ SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic2", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic3", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic4", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic5", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic6", NULL),
+};
+
+static const char *const spk_function[] = {"Off", "On"};
+static const char *const slim0_rx_ch_text[] = {"One", "Two"};
+static const char *const slim0_tx_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five"};
+static char const *hdmi_rx_ch_text[] = {"Two", "Three", "Four", "Five",
+ "Six", "Seven", "Eight"};
+static char const *rx_bit_format_text[] = {"S16_LE", "S24_LE"};
+static char const *slim0_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+ "KHZ_192"};
+static const char *const proxy_rx_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven", "Eight"};
+
+static const char *const btsco_rate_text[] = {"8000", "16000"};
+static const struct soc_enum msm_btsco_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, btsco_rate_text),
+};
+
+static int slim0_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val = 0;
+
+ switch (slim0_rx_sample_rate) {
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 2;
+ break;
+
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 1;
+ break;
+
+ case SAMPLING_RATE_48KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: slim0_rx_sample_rate = %d\n", __func__,
+ slim0_rx_sample_rate);
+
+ return 0;
+}
+
+static int slim0_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: ucontrol value = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ slim0_rx_sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 1:
+ slim0_rx_sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 0:
+ default:
+ slim0_rx_sample_rate = SAMPLING_RATE_48KHZ;
+ }
+
+ pr_debug("%s: slim0_rx_sample_rate = %d\n", __func__,
+ slim0_rx_sample_rate);
+
+ return 0;
+}
+
+static int slim0_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ switch (slim0_rx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: slim0_rx_bit_format = %d, ucontrol value = %ld\n",
+ __func__, slim0_rx_bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int slim0_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ slim0_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ slim0_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ return 0;
+}
+
+static int msm_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__,
+ msm_slim_0_rx_ch);
+ ucontrol->value.integer.value[0] = msm_slim_0_rx_ch - 1;
+ return 0;
+}
+
+static int msm_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__,
+ msm_slim_0_rx_ch);
+ return 1;
+}
+
+static int msm_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__,
+ msm_slim_0_tx_ch);
+ ucontrol->value.integer.value[0] = msm_slim_0_tx_ch - 1;
+ return 0;
+}
+
+static int msm_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__, msm_slim_0_tx_ch);
+ return 1;
+}
+
+static int msm_btsco_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_btsco_rate = %d", __func__, msm_btsco_rate);
+ ucontrol->value.integer.value[0] = msm_btsco_rate;
+ return 0;
+}
+
+static int msm_btsco_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_btsco_rate = BTSCO_RATE_8KHZ;
+ break;
+ case 1:
+ msm_btsco_rate = BTSCO_RATE_16KHZ;
+ break;
+ default:
+ msm_btsco_rate = BTSCO_RATE_8KHZ;
+ break;
+ }
+ pr_debug("%s: msm_btsco_rate = %d\n", __func__, msm_btsco_rate);
+ return 0;
+}
+
+static int hdmi_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ switch (hdmi_rx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: hdmi_rx_bit_format = %d, ucontrol value = %ld\n",
+ __func__, hdmi_rx_bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int hdmi_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: hdmi_rx_bit_format = %d, ucontrol value = %ld\n",
+ __func__, hdmi_rx_bit_format,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_hdmi_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__,
+ msm_hdmi_rx_ch);
+ ucontrol->value.integer.value[0] = msm_hdmi_rx_ch - 2;
+
+ return 0;
+}
+
+static int msm_hdmi_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_hdmi_rx_ch = ucontrol->value.integer.value[0] + 2;
+ if (msm_hdmi_rx_ch > 8) {
+ pr_err("%s: channels exceeded 8.Limiting to max channels-8\n",
+ __func__);
+ msm_hdmi_rx_ch = 8;
+ }
+ pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__, msm_hdmi_rx_ch);
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new int_btsco_rate_mixer_controls[] = {
+ SOC_ENUM_EXT("Internal BTSCO SampleRate", msm_btsco_enum[0],
+ msm_btsco_rate_get, msm_btsco_rate_put),
+};
+
+static int msm_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = msm_btsco_rate;
+ channels->min = channels->max = msm_btsco_ch;
+
+ return 0;
+}
+
+static int apq8074_auxpcm_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = apq8074_auxpcm_rate;
+ return 0;
+}
+
+static int apq8074_auxpcm_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ apq8074_auxpcm_rate = 8000;
+ break;
+ case 1:
+ apq8074_auxpcm_rate = 16000;
+ break;
+ default:
+ apq8074_auxpcm_rate = 8000;
+ break;
+ }
+ return 0;
+}
+static int msm_proxy_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__,
+ msm_proxy_rx_ch);
+ ucontrol->value.integer.value[0] = msm_proxy_rx_ch - 1;
+ return 0;
+}
+
+static int msm_proxy_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_proxy_rx_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__,
+ msm_proxy_rx_ch);
+ return 1;
+}
+
+static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = apq8074_auxpcm_rate;
+ channels->min = channels->max = 1;
+
+ return 0;
+}
+
+static int msm_proxy_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s: msm_proxy_rx_ch =%d\n", __func__, msm_proxy_rx_ch);
+
+ if (channels->max < 2)
+ channels->min = channels->max = 2;
+ channels->min = channels->max = msm_proxy_rx_ch;
+ rate->min = rate->max = 48000;
+ return 0;
+}
+
+static int msm_proxy_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ rate->min = rate->max = 48000;
+ return 0;
+}
+
+static int apq8074_hdmi_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
+ channels->min, channels->max);
+
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ hdmi_rx_bit_format);
+ if (channels->max < 2)
+ channels->min = channels->max = 2;
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = msm_hdmi_rx_ch;
+
+ return 0;
+}
+
+static int msm_aux_pcm_get_gpios(struct msm_auxpcm_ctrl *auxpcm_ctrl)
+{
+ struct msm_auxpcm_gpio *pin_data = NULL;
+ int ret = 0;
+ int i;
+ int j;
+
+ pin_data = auxpcm_ctrl->pin_data;
+ for (i = 0; i < auxpcm_ctrl->cnt; i++, pin_data++) {
+ ret = gpio_request(pin_data->gpio_no,
+ pin_data->gpio_name);
+ pr_debug("%s: gpio = %d, gpio name = %s\n"
+ "ret = %d\n", __func__,
+ pin_data->gpio_no,
+ pin_data->gpio_name,
+ ret);
+ if (ret) {
+ pr_err("%s: Failed to request gpio %d\n",
+ __func__, pin_data->gpio_no);
+ /* Release all GPIOs on failure */
+ for (j = i; j >= 0; j--)
+ gpio_free(pin_data->gpio_no);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int msm_aux_pcm_free_gpios(struct msm_auxpcm_ctrl *auxpcm_ctrl)
+{
+ struct msm_auxpcm_gpio *pin_data = NULL;
+ int i;
+ int ret = 0;
+
+ if (auxpcm_ctrl == NULL || auxpcm_ctrl->pin_data == NULL) {
+ pr_err("%s: Ctrl pointers are NULL\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ pin_data = auxpcm_ctrl->pin_data;
+ for (i = 0; i < auxpcm_ctrl->cnt; i++, pin_data++) {
+ gpio_free(pin_data->gpio_no);
+ pr_debug("%s: gpio = %d, gpio_name = %s\n",
+ __func__, pin_data->gpio_no,
+ pin_data->gpio_name);
+ }
+err:
+ return ret;
+}
+
+static int msm_prim_auxpcm_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct apq8074_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct msm_auxpcm_ctrl *auxpcm_ctrl = NULL;
+ int ret = 0;
+
+ pr_debug("%s(): substream = %s, prim_auxpcm_rsc_ref counter = %d\n",
+ __func__, substream->name, atomic_read(&prim_auxpcm_rsc_ref));
+
+ auxpcm_ctrl = pdata->pri_auxpcm_ctrl;
+
+ if (auxpcm_ctrl == NULL || auxpcm_ctrl->pin_data == NULL) {
+ pr_err("%s: Ctrl pointers are NULL\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (atomic_inc_return(&prim_auxpcm_rsc_ref) == 1) {
+ if (lpaif_pri_muxsel_virt_addr != NULL)
+ iowrite32(I2S_PCM_SEL << I2S_PCM_SEL_OFFSET,
+ lpaif_pri_muxsel_virt_addr);
+ else
+ pr_err("%s lpaif_pri_muxsel_virt_addr is NULL\n",
+ __func__);
+ ret = msm_aux_pcm_get_gpios(auxpcm_ctrl);
+ }
+ if (ret < 0) {
+ pr_err("%s: Aux PCM GPIO request failed\n", __func__);
+ return -EINVAL;
+ }
+err:
+ return ret;
+}
+
+static void msm_prim_auxpcm_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct apq8074_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct msm_auxpcm_ctrl *auxpcm_ctrl = NULL;
+
+ pr_debug("%s(): substream = %s, prim_auxpcm_rsc_ref counter = %d\n",
+ __func__, substream->name, atomic_read(&prim_auxpcm_rsc_ref));
+
+ auxpcm_ctrl = pdata->pri_auxpcm_ctrl;
+
+ if (atomic_dec_return(&prim_auxpcm_rsc_ref) == 0)
+ msm_aux_pcm_free_gpios(auxpcm_ctrl);
+}
+static struct snd_soc_ops msm_auxpcm_be_ops = {
+ .startup = msm_prim_auxpcm_startup,
+ .shutdown = msm_prim_auxpcm_shutdown,
+};
+
+static int msm_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s()\n", __func__);
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim0_rx_bit_format);
+ rate->min = rate->max = slim0_rx_sample_rate;
+ channels->min = channels->max = msm_slim_0_rx_ch;
+
+ pr_debug("%s: format = %d, rate = %d, channels = %d\n",
+ __func__, params_format(params), params_rate(params),
+ msm_slim_0_rx_ch);
+
+ return 0;
+}
+
+static int msm_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s()\n", __func__);
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = msm_slim_0_tx_ch;
+
+ return 0;
+}
+
+static int msm_slim_5_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ int rc;
+ void *config;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_interval *rate =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s enter\n", __func__);
+ rate->min = rate->max = 16000;
+ channels->min = channels->max = 1;
+
+ config = taiko_get_afe_config(codec, AFE_SLIMBUS_SLAVE_PORT_CONFIG);
+ rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG, config,
+ SLIMBUS_5_TX);
+ if (rc) {
+ pr_err("%s: Failed to set slimbus slave port config %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ pr_debug("%s()\n", __func__);
+ rate->min = rate->max = 48000;
+
+ return 0;
+}
+
+static const struct soc_enum msm_snd_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+ SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(5, slim0_tx_ch_text),
+ SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(2, rx_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(3, slim0_rx_sample_rate_text),
+ SOC_ENUM_SINGLE_EXT(8, proxy_rx_ch_text),
+};
+
+static const struct snd_kcontrol_new msm_snd_controls[] = {
+ SOC_ENUM_EXT("Speaker Function", msm_snd_enum[0], apq8074_get_spk,
+ apq8074_set_spk),
+ SOC_ENUM_EXT("SLIM_0_RX Channels", msm_snd_enum[1],
+ msm_slim_0_rx_ch_get, msm_slim_0_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_0_TX Channels", msm_snd_enum[2],
+ msm_slim_0_tx_ch_get, msm_slim_0_tx_ch_put),
+ SOC_ENUM_EXT("AUX PCM SampleRate", apq8074_auxpcm_enum[0],
+ apq8074_auxpcm_rate_get, apq8074_auxpcm_rate_put),
+ SOC_ENUM_EXT("HDMI_RX Channels", msm_snd_enum[3],
+ msm_hdmi_rx_ch_get, msm_hdmi_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_0_RX Format", msm_snd_enum[4],
+ slim0_rx_bit_format_get, slim0_rx_bit_format_put),
+ SOC_ENUM_EXT("SLIM_0_RX SampleRate", msm_snd_enum[5],
+ slim0_rx_sample_rate_get, slim0_rx_sample_rate_put),
+ SOC_ENUM_EXT("HDMI_RX Bit Format", msm_snd_enum[4],
+ hdmi_rx_bit_format_get, hdmi_rx_bit_format_put),
+ SOC_ENUM_EXT("PROXY_RX Channels", msm_snd_enum[6],
+ msm_proxy_rx_ch_get, msm_proxy_rx_ch_put),
+};
+
+static bool apq8074_swap_gnd_mic(struct snd_soc_codec *codec)
+{
+ struct snd_soc_card *card = codec->card;
+ struct apq8074_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ int value = gpio_get_value_cansleep(pdata->us_euro_gpio);
+ pr_debug("%s: swap select switch %d to %d\n", __func__, value, !value);
+ gpio_set_value_cansleep(pdata->us_euro_gpio, !value);
+ return true;
+}
+
+static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int err;
+ void *config_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ /* Taiko SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+ * TX14, TX15, TX16
+ */
+ unsigned int rx_ch[TAIKO_RX_MAX] = {144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156};
+ unsigned int tx_ch[TAIKO_TX_MAX] = {128, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143};
+
+
+ pr_info("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
+
+ rtd->pmdown_time = 0;
+
+ err = snd_soc_add_codec_controls(codec, msm_snd_controls,
+ ARRAY_SIZE(msm_snd_controls));
+ if (err < 0)
+ return err;
+
+ err = apq8074_liquid_ext_spk_power_amp_init();
+ if (err) {
+ pr_err("%s: LiQUID 8974 CLASS_D PAs init failed (%d)\n",
+ __func__, err);
+ return err;
+ }
+
+ err = apq8074_liquid_init_docking(dapm);
+ if (err) {
+ pr_err("%s: LiQUID 8974 init Docking stat IRQ failed (%d)\n",
+ __func__, err);
+ return err;
+ }
+
+ snd_soc_dapm_new_controls(dapm, apq8074_dapm_widgets,
+ ARRAY_SIZE(apq8074_dapm_widgets));
+
+ snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_4 amp");
+
+
+ snd_soc_dapm_sync(dapm);
+
+ codec_clk = clk_get(cpu_dai->dev, "osr_clk");
+
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
+
+ config_data = taiko_get_afe_config(codec, AFE_CDC_REGISTERS_CONFIG);
+ err = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0);
+ if (err) {
+ pr_err("%s: Failed to set codec registers config %d\n",
+ __func__, err);
+ goto out;
+ }
+
+ config_data = taiko_get_afe_config(codec, AFE_SLIMBUS_SLAVE_CONFIG);
+ err = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0);
+ if (err) {
+ pr_err("%s: Failed to set slimbus slave config %d\n", __func__,
+ err);
+ goto out;
+ }
+
+ config_data = taiko_get_afe_config(codec, AFE_AANC_VERSION);
+ err = afe_set_config(AFE_AANC_VERSION, config_data, 0);
+ if (err) {
+ pr_err("%s: Failed to set aanc version %d\n",
+ __func__, err);
+ goto out;
+ }
+ config_data = taiko_get_afe_config(codec,
+ AFE_CDC_CLIP_REGISTERS_CONFIG);
+ if (config_data) {
+ err = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG,
+ config_data, 0);
+ if (err) {
+ pr_err("%s: Failed to set clip registers %d\n",
+ __func__, err);
+ return err;
+ }
+ }
+ config_data = taiko_get_afe_config(codec, AFE_CLIP_BANK_SEL);
+ if (config_data) {
+ err = afe_set_config(AFE_CLIP_BANK_SEL, config_data, 0);
+ if (err) {
+ pr_err("%s: Failed to set AFE bank selection %d\n",
+ __func__, err);
+ return err;
+ }
+ }
+ /* start mbhc */
+ mbhc_cfg.calibration = def_taiko_mbhc_cal();
+ if (mbhc_cfg.calibration) {
+ err = taiko_hs_detect(codec, &mbhc_cfg);
+ if (err)
+ goto out;
+ else
+ return err;
+ } else {
+ err = -ENOMEM;
+ goto out;
+ }
+out:
+ clk_put(codec_clk);
+ return err;
+}
+
+static int apq8074_snd_startup(struct snd_pcm_substream *substream)
+{
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+ return 0;
+}
+
+static void *def_taiko_mbhc_cal(void)
+{
+ void *taiko_cal;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+ u16 *btn_low, *btn_high;
+ u8 *n_ready, *n_cic, *gain;
+
+ taiko_cal = kzalloc(WCD9XXX_MBHC_CAL_SIZE(WCD9XXX_MBHC_DEF_BUTTONS,
+ WCD9XXX_MBHC_DEF_RLOADS),
+ GFP_KERNEL);
+ if (!taiko_cal) {
+ pr_err("%s: out of memory\n", __func__);
+ return NULL;
+ }
+
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_GENERAL_PTR(taiko_cal)->X) = (Y))
+ S(t_ldoh, 100);
+ S(t_bg_fast_settle, 100);
+ S(t_shutdown_plug_rem, 255);
+ S(mbhc_nsa, 4);
+ S(mbhc_navg, 4);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_DET_PTR(taiko_cal)->X) = (Y))
+ S(mic_current, TAIKO_PID_MIC_5_UA);
+ S(hph_current, TAIKO_PID_MIC_5_UA);
+ S(t_mic_pid, 100);
+ S(t_ins_complete, 250);
+ S(t_ins_retry, 200);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(taiko_cal)->X) = (Y))
+ S(v_no_mic, 30);
+ S(v_hs_max, 2400);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_BTN_DET_PTR(taiko_cal)->X) = (Y))
+ S(c[0], 62);
+ S(c[1], 124);
+ S(nc, 1);
+ S(n_meas, 3);
+ S(mbhc_nsc, 11);
+ S(n_btn_meas, 1);
+ S(n_btn_con, 2);
+ S(num_btn, WCD9XXX_MBHC_DEF_BUTTONS);
+ S(v_btn_press_delta_sta, 100);
+ S(v_btn_press_delta_cic, 50);
+#undef S
+ btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(taiko_cal);
+ btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_V_BTN_LOW);
+ btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg,
+ MBHC_BTN_DET_V_BTN_HIGH);
+ btn_low[0] = -50;
+ btn_high[0] = 20;
+ btn_low[1] = 21;
+ btn_high[1] = 61;
+ btn_low[2] = 62;
+ btn_high[2] = 104;
+ btn_low[3] = 105;
+ btn_high[3] = 148;
+ btn_low[4] = 149;
+ btn_high[4] = 189;
+ btn_low[5] = 190;
+ btn_high[5] = 228;
+ btn_low[6] = 229;
+ btn_high[6] = 269;
+ btn_low[7] = 270;
+ btn_high[7] = 500;
+ n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_READY);
+ n_ready[0] = 80;
+ n_ready[1] = 68;
+ n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_CIC);
+ n_cic[0] = 60;
+ n_cic[1] = 47;
+ gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_GAIN);
+ gain[0] = 11;
+ gain[1] = 9;
+
+ return taiko_cal;
+}
+
+static int msm_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+ unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+ unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0;
+ unsigned int user_set_tx_ch = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ pr_debug("%s: rx_0_ch=%d\n", __func__, msm_slim_0_rx_ch);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map\n", __func__);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+ msm_slim_0_rx_ch, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map\n", __func__);
+ goto end;
+ }
+ } else {
+
+ pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
+ codec_dai->name, codec_dai->id, user_set_tx_ch);
+
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map\n", __func__);
+ goto end;
+ }
+ /* For tabla_tx1 case */
+ if (codec_dai->id == 1)
+ user_set_tx_ch = msm_slim_0_tx_ch;
+ /* For tabla_tx2 case */
+ else if (codec_dai->id == 3)
+ user_set_tx_ch = params_channels(params);
+ else
+ user_set_tx_ch = tx_ch_cnt;
+
+ pr_debug("%s: msm_slim_0_tx_ch(%d)user_set_tx_ch(%d)tx_ch_cnt(%d)\n",
+ __func__, msm_slim_0_tx_ch, user_set_tx_ch, tx_ch_cnt);
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ user_set_tx_ch, tx_ch, 0 , 0);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map\n", __func__);
+ goto end;
+ }
+ }
+end:
+ return ret;
+}
+
+static void apq8074_snd_shudown(struct snd_pcm_substream *substream)
+{
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+}
+
+static struct snd_soc_ops apq8074_be_ops = {
+ .startup = apq8074_snd_startup,
+ .hw_params = msm_snd_hw_params,
+ .shutdown = apq8074_snd_shudown,
+};
+
+
+
+static int apq8074_slimbus_2_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+ unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+ unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0;
+ unsigned int num_tx_ch = 0;
+ unsigned int num_rx_ch = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+
+ num_rx_ch = params_channels(params);
+
+ pr_debug("%s: %s rx_dai_id = %d num_ch = %d\n", __func__,
+ codec_dai->name, codec_dai->id, num_rx_ch);
+
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map\n", __func__);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+ num_rx_ch, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map\n", __func__);
+ goto end;
+ }
+ } else {
+
+ num_tx_ch = params_channels(params);
+
+ pr_debug("%s: %s tx_dai_id = %d num_ch = %d\n", __func__,
+ codec_dai->name, codec_dai->id, num_tx_ch);
+
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map\n", __func__);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ num_tx_ch, tx_ch, 0 , 0);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map\n", __func__);
+ goto end;
+ }
+ }
+end:
+ return ret;
+}
+
+
+static struct snd_soc_ops apq8074_slimbus_2_be_ops = {
+ .startup = apq8074_snd_startup,
+ .hw_params = apq8074_slimbus_2_hw_params,
+ .shutdown = apq8074_snd_shudown,
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link apq8074_common_dai_links[] = {
+ /* FrontEnd DAI Links */
+ {
+ .name = "MSM8974 Media1",
+ .stream_name = "MultiMedia1",
+ .cpu_dai_name = "MultiMedia1",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
+ },
+ {
+ .name = "MSM8974 Media2",
+ .stream_name = "MultiMedia2",
+ .cpu_dai_name = "MultiMedia2",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+ },
+ {
+ .name = "Circuit-Switch Voice",
+ .stream_name = "CS-Voice",
+ .cpu_dai_name = "CS-VOICE",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_CS_VOICE,
+ },
+ {
+ .name = "MSM VoIP",
+ .stream_name = "VoIP",
+ .cpu_dai_name = "VoIP",
+ .platform_name = "msm-voip-dsp",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_VOIP,
+ },
+ {
+ .name = "MSM8974 LPA",
+ .stream_name = "LPA",
+ .cpu_dai_name = "MultiMedia3",
+ .platform_name = "msm-pcm-lpa",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+ },
+ /* Hostless PCM purpose */
+ {
+ .name = "SLIMBUS_0 Hostless",
+ .stream_name = "SLIMBUS_0 Hostless",
+ .cpu_dai_name = "SLIMBUS0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1, /* dai link has playback support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "INT_FM Hostless",
+ .stream_name = "INT_FM Hostless",
+ .cpu_dai_name = "INT_FM_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "MSM AFE-PCM RX",
+ .stream_name = "AFE-PROXY RX",
+ .cpu_dai_name = "msm-dai-q6-dev.241",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = "MSM AFE-PCM TX",
+ .stream_name = "AFE-PROXY TX",
+ .cpu_dai_name = "msm-dai-q6-dev.240",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ },
+ {
+ .name = "MSM8974 Compr",
+ .stream_name = "COMPR",
+ .cpu_dai_name = "MultiMedia4",
+ .platform_name = "msm-compr-dsp",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4,
+ },
+ {
+ .name = "AUXPCM Hostless",
+ .stream_name = "AUXPCM Hostless",
+ .cpu_dai_name = "AUXPCM_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "SLIMBUS_1 Hostless",
+ .stream_name = "SLIMBUS_1 Hostless",
+ .cpu_dai_name = "SLIMBUS1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1, /* dai link has playback support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "SLIMBUS_3 Hostless",
+ .stream_name = "SLIMBUS_3 Hostless",
+ .cpu_dai_name = "SLIMBUS3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1, /* dai link has playback support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "SLIMBUS_4 Hostless",
+ .stream_name = "SLIMBUS_4 Hostless",
+ .cpu_dai_name = "SLIMBUS4_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1, /* dai link has playback support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "VoLTE",
+ .stream_name = "VoLTE",
+ .cpu_dai_name = "VoLTE",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOLTE,
+ },
+ {
+ .name = "MSM8974 LowLatency",
+ .stream_name = "MultiMedia5",
+ .cpu_dai_name = "MultiMedia5",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,
+ },
+ /* LSM FE */
+ {
+ .name = "Listen Audio Service",
+ .stream_name = "Listen Audio Service",
+ .cpu_dai_name = "LSM",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM1,
+ },
+ /* Backend BT/FM DAI Links */
+ {
+ .name = LPASS_BE_INT_BT_SCO_RX,
+ .stream_name = "Internal BT-SCO Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.12288",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ .be_hw_params_fixup = msm_btsco_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_INT_BT_SCO_TX,
+ .stream_name = "Internal BT-SCO Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.12289",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ .be_hw_params_fixup = msm_btsco_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_INT_FM_RX,
+ .stream_name = "Internal FM Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.12292",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_FM_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_INT_FM_TX,
+ .stream_name = "Internal FM Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.12293",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_FM_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Backend AFE DAI Links */
+ {
+ .name = LPASS_BE_AFE_PCM_RX,
+ .stream_name = "AFE Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.224",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
+ .be_hw_params_fixup = msm_proxy_rx_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_AFE_PCM_TX,
+ .stream_name = "AFE Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.225",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
+ .be_hw_params_fixup = msm_proxy_tx_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* HDMI Hostless */
+ {
+ .name = "HDMI_RX_HOSTLESS",
+ .stream_name = "HDMI_RX_HOSTLESS",
+ .cpu_dai_name = "HDMI_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ /* AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_AUXPCM_RX,
+ .stream_name = "AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6.4106",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_RX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ .ops = &msm_auxpcm_be_ops,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ },
+ {
+ .name = LPASS_BE_AUXPCM_TX,
+ .stream_name = "AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6.4107",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_TX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ .ops = &msm_auxpcm_be_ops,
+ .ignore_suspend = 1,
+ },
+ /* Backend DAI Links */
+ {
+ .name = LPASS_BE_SLIMBUS_0_RX,
+ .stream_name = "Slimbus Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16384",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "taiko_codec",
+ .codec_dai_name = "taiko_rx1",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ .init = &msm_audrx_init,
+ .be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+ .ops = &apq8074_be_ops,
+ .ignore_pmdown_time = 1, /* dai link has playback support */
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_0_TX,
+ .stream_name = "Slimbus Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16385",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "taiko_codec",
+ .codec_dai_name = "taiko_tx1",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ .be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+ .ops = &apq8074_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_1_RX,
+ .stream_name = "Slimbus1 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16386",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "taiko_codec",
+ .codec_dai_name = "taiko_rx1",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ .be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+ .ops = &apq8074_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_1_TX,
+ .stream_name = "Slimbus1 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16387",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "taiko_codec",
+ .codec_dai_name = "taiko_tx1",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ .be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+ .ops = &apq8074_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_3_RX,
+ .stream_name = "Slimbus3 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16390",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "taiko_codec",
+ .codec_dai_name = "taiko_rx1",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ .be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+ .ops = &apq8074_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_3_TX,
+ .stream_name = "Slimbus3 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16391",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "taiko_codec",
+ .codec_dai_name = "taiko_tx1",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ .be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+ .ops = &apq8074_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_4_RX,
+ .stream_name = "Slimbus4 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16392",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "taiko_codec",
+ .codec_dai_name = "taiko_rx1",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX,
+ .be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+ .ops = &apq8074_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_4_TX,
+ .stream_name = "Slimbus4 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16393",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "taiko_codec",
+ .codec_dai_name = "taiko_vifeedback",
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ .be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+ .ops = &apq8074_be_ops,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Uplink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_TX,
+ .stream_name = "Voice Uplink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32772",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Downlink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_RX,
+ .stream_name = "Voice Downlink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32771",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* MAD BE */
+ {
+ .name = LPASS_BE_SLIMBUS_5_TX,
+ .stream_name = "Slimbus5 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16395",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "taiko_codec",
+ .codec_dai_name = "taiko_mad1",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ .be_hw_params_fixup = msm_slim_5_tx_be_hw_params_fixup,
+ .ops = &apq8074_be_ops,
+ },
+ /* Incall Music BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE_PLAYBACK_TX,
+ .stream_name = "Voice Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32773",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Ultrasound RX Back End DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Playback",
+ .stream_name = "SLIMBUS_2 Hostless Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16388",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "taiko_codec",
+ .codec_dai_name = "taiko_rx2",
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &apq8074_slimbus_2_be_ops,
+ },
+ /* Ultrasound TX Back End DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Capture",
+ .stream_name = "SLIMBUS_2 Hostless Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16389",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "taiko_codec",
+ .codec_dai_name = "taiko_tx2",
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &apq8074_slimbus_2_be_ops,
+ },
+};
+
+static struct snd_soc_dai_link apq8074_hdmi_dai_link[] = {
+/* HDMI BACK END DAI Link */
+ {
+ .name = LPASS_BE_HDMI,
+ .stream_name = "HDMI Playback",
+ .cpu_dai_name = "msm-dai-q6-hdmi.8",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-hdmi-audio-codec-rx",
+ .codec_dai_name = "msm_hdmi_audio_codec_rx_dai",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_HDMI_RX,
+ .be_hw_params_fixup = apq8074_hdmi_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link apq8074_dai_links[
+ ARRAY_SIZE(apq8074_common_dai_links) +
+ ARRAY_SIZE(apq8074_hdmi_dai_link)];
+
+struct snd_soc_card snd_soc_card_apq8074 = {
+ .name = "apq8074-taiko-snd-card",
+};
+
+static int apq8074_dtparse_auxpcm(struct platform_device *pdev,
+ struct msm_auxpcm_ctrl **auxpcm_ctrl,
+ char *msm_auxpcm_gpio_name[][2])
+{
+ int ret = 0;
+ int i = 0;
+ struct msm_auxpcm_gpio *pin_data = NULL;
+ struct msm_auxpcm_ctrl *ctrl;
+ unsigned int gpio_no[NUM_OF_AUXPCM_GPIOS];
+ enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;
+ int auxpcm_cnt = 0;
+
+ pin_data = devm_kzalloc(&pdev->dev, (ARRAY_SIZE(gpio_no) *
+ sizeof(struct msm_auxpcm_gpio)),
+ GFP_KERNEL);
+ if (!pin_data) {
+ dev_err(&pdev->dev, "No memory for gpio\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(gpio_no); i++) {
+ gpio_no[i] = of_get_named_gpio_flags(pdev->dev.of_node,
+ msm_auxpcm_gpio_name[i][DT_PARSE_INDEX],
+ 0, &flags);
+
+ if (gpio_no[i] > 0) {
+ pin_data[i].gpio_name =
+ msm_auxpcm_gpio_name[auxpcm_cnt][GPIO_NAME_INDEX];
+ pin_data[i].gpio_no = gpio_no[i];
+ dev_dbg(&pdev->dev, "%s:GPIO gpio[%s] =\n"
+ "0x%x\n", __func__,
+ pin_data[i].gpio_name,
+ pin_data[i].gpio_no);
+ auxpcm_cnt++;
+ } else {
+ dev_err(&pdev->dev, "%s:Invalid AUXPCM GPIO[%s]= %x\n",
+ __func__,
+ msm_auxpcm_gpio_name[i][GPIO_NAME_INDEX],
+ gpio_no[i]);
+ ret = -ENODEV;
+ goto err;
+ }
+ }
+
+ ctrl = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_auxpcm_ctrl), GFP_KERNEL);
+ if (!ctrl) {
+ dev_err(&pdev->dev, "No memory for gpio\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ctrl->pin_data = pin_data;
+ ctrl->cnt = auxpcm_cnt;
+ *auxpcm_ctrl = ctrl;
+ return ret;
+
+err:
+ if (pin_data)
+ devm_kfree(&pdev->dev, pin_data);
+ return ret;
+}
+
+static int apq8074_prepare_codec_mclk(struct snd_soc_card *card)
+{
+ struct apq8074_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ int ret;
+ if (pdata->mclk_gpio) {
+ ret = gpio_request(pdata->mclk_gpio, "TAIKO_CODEC_PMIC_MCLK");
+ if (ret) {
+ dev_err(card->dev,
+ "%s: Failed to request taiko mclk gpio %d\n",
+ __func__, pdata->mclk_gpio);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int apq8074_prepare_us_euro(struct snd_soc_card *card)
+{
+ struct apq8074_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ int ret;
+ if (pdata->us_euro_gpio) {
+ dev_dbg(card->dev, "%s : us_euro gpio request %d", __func__,
+ pdata->us_euro_gpio);
+ ret = gpio_request(pdata->us_euro_gpio, "TAIKO_CODEC_US_EURO");
+ if (ret) {
+ dev_err(card->dev,
+ "%s: Failed to request taiko US/EURO gpio %d error %d\n",
+ __func__, pdata->us_euro_gpio, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static __devinit int apq8074_asoc_machine_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_card_apq8074;
+ struct apq8074_asoc_mach_data *pdata;
+ int ret;
+ const char *auxpcm_pri_gpio_set = NULL;
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev, "No platform supplied from device tree\n");
+ return -EINVAL;
+ }
+
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct apq8074_asoc_mach_data), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&pdev->dev, "Can't allocate apq8074_asoc_mach_data\n");
+ return -ENOMEM;
+ }
+
+ /* Parse AUXPCM info from DT */
+ ret = apq8074_dtparse_auxpcm(pdev, &pdata->pri_auxpcm_ctrl,
+ msm_prim_auxpcm_gpio_name);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: Auxpcm pin data parse failed\n", __func__);
+ goto err;
+ }
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, pdata);
+
+ ret = snd_soc_of_parse_card_name(card, "qcom,model");
+ if (ret)
+ goto err;
+
+ ret = snd_soc_of_parse_audio_routing(card,
+ "qcom,audio-routing");
+ if (ret)
+ goto err;
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,taiko-mclk-clk-freq", &pdata->mclk_freq);
+ if (ret) {
+ dev_err(&pdev->dev, "Looking up %s property in node %s failed",
+ "qcom,taiko-mclk-clk-freq",
+ pdev->dev.of_node->full_name);
+ goto err;
+ }
+
+ if (pdata->mclk_freq != 9600000) {
+ dev_err(&pdev->dev, "unsupported taiko mclk freq %u\n",
+ pdata->mclk_freq);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ pdata->mclk_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,cdc-mclk-gpios", 0);
+ if (pdata->mclk_gpio < 0) {
+ dev_err(&pdev->dev,
+ "Looking up %s property in node %s failed %d\n",
+ "qcom, cdc-mclk-gpios", pdev->dev.of_node->full_name,
+ pdata->mclk_gpio);
+ ret = -ENODEV;
+ goto err;
+ }
+
+
+ ret = apq8074_prepare_codec_mclk(card);
+ if (ret)
+ goto err;
+
+ if (of_property_read_bool(pdev->dev.of_node, "qcom,hdmi-audio-rx")) {
+ dev_info(&pdev->dev, "%s(): hdmi audio support present\n",
+ __func__);
+
+ memcpy(apq8074_dai_links, apq8074_common_dai_links,
+ sizeof(apq8074_common_dai_links));
+
+ memcpy(apq8074_dai_links + ARRAY_SIZE(apq8074_common_dai_links),
+ apq8074_hdmi_dai_link, sizeof(apq8074_hdmi_dai_link));
+
+ card->dai_link = apq8074_dai_links;
+ card->num_links = ARRAY_SIZE(apq8074_dai_links);
+ } else {
+ dev_info(&pdev->dev, "%s(): No hdmi audio support\n", __func__);
+
+ card->dai_link = apq8074_common_dai_links;
+ card->num_links = ARRAY_SIZE(apq8074_common_dai_links);
+ }
+
+ pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,us-euro-gpios", 0);
+ if (pdata->us_euro_gpio < 0) {
+ dev_err(&pdev->dev, "Looking up %s property in node %s failed",
+ "qcom,us-euro-gpios",
+ pdev->dev.of_node->full_name);
+ } else {
+ dev_dbg(&pdev->dev, "%s detected %d",
+ "qcom,us-euro-gpios", pdata->us_euro_gpio);
+ mbhc_cfg.swap_gnd_mic = apq8074_swap_gnd_mic;
+ }
+
+ ret = apq8074_prepare_us_euro(card);
+ if (ret)
+ dev_err(&pdev->dev, "apq8074_prepare_us_euro failed (%d)\n",
+ ret);
+
+ mutex_init(&cdc_mclk_mutex);
+ atomic_set(&prim_auxpcm_rsc_ref, 0);
+ spdev = pdev;
+ ext_spk_amp_regulator = NULL;
+ apq8074_liquid_dock_dev = NULL;
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err;
+ }
+
+ ret = of_property_read_string(pdev->dev.of_node,
+ "qcom,prim-auxpcm-gpio-set", &auxpcm_pri_gpio_set);
+ if (ret) {
+ dev_err(&pdev->dev, "Looking up %s property in node %s failed",
+ "qcom,prim-auxpcm-gpio-set",
+ pdev->dev.of_node->full_name);
+ goto err;
+ }
+ if (!strcmp(auxpcm_pri_gpio_set, "prim-gpio-prim")) {
+ lpaif_pri_muxsel_virt_addr =
+ ioremap(LPAIF_PRI_MODE_MUXSEL, 4);
+ } else if (!strcmp(auxpcm_pri_gpio_set, "prim-gpio-tert")) {
+ lpaif_pri_muxsel_virt_addr =
+ ioremap(LPAIF_TER_MODE_MUXSEL, 4);
+ } else {
+ dev_err(&pdev->dev, "Invalid value %s for AUXPCM GPIO set\n",
+ auxpcm_pri_gpio_set);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (lpaif_pri_muxsel_virt_addr == NULL) {
+ pr_err("%s Pri muxsel virt addr is null\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ return 0;
+
+err:
+ if (pdata->mclk_gpio > 0) {
+ dev_dbg(&pdev->dev, "%s free gpio %d\n",
+ __func__, pdata->mclk_gpio);
+ gpio_free(pdata->mclk_gpio);
+ pdata->mclk_gpio = 0;
+ }
+ if (pdata->us_euro_gpio > 0) {
+ dev_dbg(&pdev->dev, "%s free us_euro gpio %d\n",
+ __func__, pdata->us_euro_gpio);
+ gpio_free(pdata->us_euro_gpio);
+ pdata->us_euro_gpio = 0;
+ }
+ devm_kfree(&pdev->dev, pdata);
+ return ret;
+}
+
+static int __devexit apq8074_asoc_machine_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct apq8074_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+
+ if (ext_spk_amp_regulator)
+ regulator_put(ext_spk_amp_regulator);
+
+ if (gpio_is_valid(ext_ult_spk_amp_gpio))
+ gpio_free(ext_ult_spk_amp_gpio);
+
+ gpio_free(pdata->mclk_gpio);
+ gpio_free(pdata->us_euro_gpio);
+ if (gpio_is_valid(ext_spk_amp_gpio))
+ gpio_free(ext_spk_amp_gpio);
+
+ if (apq8074_liquid_dock_dev != NULL) {
+ if (apq8074_liquid_dock_dev->dock_plug_gpio)
+ gpio_free(apq8074_liquid_dock_dev->dock_plug_gpio);
+
+ if (apq8074_liquid_dock_dev->dock_plug_irq)
+ free_irq(apq8074_liquid_dock_dev->dock_plug_irq,
+ apq8074_liquid_dock_dev);
+
+ kfree(apq8074_liquid_dock_dev);
+ apq8074_liquid_dock_dev = NULL;
+ }
+
+ iounmap(lpaif_pri_muxsel_virt_addr);
+ snd_soc_unregister_card(card);
+
+ return 0;
+}
+
+static const struct of_device_id apq8074_asoc_machine_of_match[] = {
+ { .compatible = "qcom,apq8074-audio-taiko", },
+ {},
+};
+
+static struct platform_driver apq8074_asoc_machine_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = apq8074_asoc_machine_of_match,
+ },
+ .probe = apq8074_asoc_machine_probe,
+ .remove = __devexit_p(apq8074_asoc_machine_remove),
+};
+module_platform_driver(apq8074_asoc_machine_driver);
+
+MODULE_DESCRIPTION("ALSA SoC msm");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, apq8074_asoc_machine_of_match);
diff --git a/sound/soc/msm/mdm9615.c b/sound/soc/msm/mdm9615.c
index b140b5b..c3967dc 100644
--- a/sound/soc/msm/mdm9615.c
+++ b/sound/soc/msm/mdm9615.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -2336,6 +2336,9 @@
{
int ret;
+ /* Set GPIO headset detection by default */
+ hs_detect_use_gpio = true;
+
if (!cpu_is_msm9615()) {
pr_err("%s: Not the right machine type\n", __func__);
return -ENODEV ;
@@ -2413,8 +2416,6 @@
sif_virt_addr = ioremap(LPASS_SIF_MUX_ADDR, 4);
secpcm_portslc_virt_addr = ioremap(SEC_PCM_PORT_SLC_ADDR, 4);
- hs_detect_use_gpio = true;
-
return ret;
}
module_init(mdm9615_audio_init);
diff --git a/sound/soc/msm/mdm9625.c b/sound/soc/msm/mdm9625.c
index f3ccb33..fd21c38 100644
--- a/sound/soc/msm/mdm9625.c
+++ b/sound/soc/msm/mdm9625.c
@@ -309,18 +309,18 @@
pr_err("set format for codec dai failed\n");
return ret;
}
- }
- /* This sets the CONFIG PARAMETER WS_SRC.
- * 1 means internal clock master mode.
- * 0 means external clock slave mode.
- */
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
- if (ret < 0)
- pr_err("set fmt cpu dai failed\n");
+ /* This sets the CONFIG PARAMETER WS_SRC.
+ * 1 means internal clock master mode.
+ * 0 means external clock slave mode.
+ */
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ pr_err("set fmt cpu dai failed\n");
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_CBS_CFS);
- if (ret < 0)
- pr_err("set fmt for codec dai failed\n");
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ pr_err("set fmt for codec dai failed\n");
+ }
return ret;
}
diff --git a/sound/soc/msm/msm-compr-q6.c b/sound/soc/msm/msm-compr-q6.c
index e54f8b7..373090e 100644
--- a/sound/soc/msm/msm-compr-q6.c
+++ b/sound/soc/msm/msm-compr-q6.c
@@ -1302,7 +1302,7 @@
}
rc = wait_event_timeout(the_locks.flush_wait,
prtd->cmd_ack, 5 * HZ);
- if (rc < 0)
+ if (!rc)
pr_err("Flush cmd timeout\n");
prtd->pcm_irq_pos = 0;
}
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 8db13f6..1b51595 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -236,6 +236,17 @@
.rate_min = 8000,
.rate_max = 192000,
},
+ .capture = {
+ .stream_name = "MultiMedia6 Capture",
+ .aif_name = "MM_UL6",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
.ops = &msm_fe_Multimedia_dai_ops,
.name = "MultiMedia6",
},
@@ -549,6 +560,34 @@
.name = "SEC_I2S_RX_HOSTLESS",
},
{
+ .capture = {
+ .stream_name = "Primary MI2S_TX Hostless Capture",
+ .aif_name = "PRI_MI2S_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_MI2S_TX_HOSTLESS",
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary MI2S_RX Hostless Playback",
+ .aif_name = "SEC_MI2S_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_MI2S_RX_HOSTLESS",
+ },
+ {
.playback = {
.stream_name = "Voice2 Playback",
.aif_name = "VOICE2_DL",
diff --git a/sound/soc/msm/msm-lowlatency-pcm-q6.c b/sound/soc/msm/msm-lowlatency-pcm-q6.c
index d5281e4..28e112c 100644
--- a/sound/soc/msm/msm-lowlatency-pcm-q6.c
+++ b/sound/soc/msm/msm-lowlatency-pcm-q6.c
@@ -434,7 +434,7 @@
__func__, atomic_read(&prtd->out_count));
ret = wait_event_timeout(the_locks.write_wait,
(atomic_read(&prtd->out_count)), 5 * HZ);
- if (ret < 0) {
+ if (!ret) {
pr_err("%s: wait_event_timeout failed\n", __func__);
goto fail;
}
@@ -487,7 +487,7 @@
dir = IN;
ret = wait_event_timeout(the_locks.eos_wait,
prtd->cmd_ack, 5 * HZ);
- if (ret < 0)
+ if (!ret)
pr_err("%s: CMD_EOS failed\n", __func__);
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
q6asm_audio_client_buf_free_contiguous(dir,
@@ -525,7 +525,7 @@
ret = wait_event_timeout(the_locks.read_wait,
(atomic_read(&prtd->in_count)), 5 * HZ);
- if (ret < 0) {
+ if (!ret) {
pr_debug("%s: wait_event_timeout failed\n", __func__);
goto fail;
}
diff --git a/sound/soc/msm/msm-multi-ch-pcm-q6.c b/sound/soc/msm/msm-multi-ch-pcm-q6.c
index 26bf3d9..d620099 100644
--- a/sound/soc/msm/msm-multi-ch-pcm-q6.c
+++ b/sound/soc/msm/msm-multi-ch-pcm-q6.c
@@ -537,7 +537,7 @@
__func__, atomic_read(&prtd->out_count));
ret = wait_event_timeout(the_locks.write_wait,
(atomic_read(&prtd->out_count)), 5 * HZ);
- if (ret < 0) {
+ if (!ret) {
pr_err("%s: wait_event_timeout failed\n", __func__);
goto fail;
}
@@ -590,7 +590,7 @@
dir = IN;
ret = wait_event_timeout(the_locks.eos_wait,
prtd->cmd_ack, 5 * HZ);
- if (ret < 0)
+ if (!ret)
pr_err("%s: CMD_EOS failed\n", __func__);
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
q6asm_audio_client_buf_free_contiguous(dir,
@@ -629,7 +629,7 @@
ret = wait_event_timeout(the_locks.read_wait,
(atomic_read(&prtd->in_count)), 5 * HZ);
- if (ret < 0) {
+ if (!ret) {
pr_debug("%s: wait_event_timeout failed\n", __func__);
goto fail;
}
diff --git a/sound/soc/msm/msm-pcm-loopback.c b/sound/soc/msm/msm-pcm-loopback.c
new file mode 100644
index 0000000..ecf8394
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-loopback.c
@@ -0,0 +1,431 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/apr_audio.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/q6asm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+
+#include "msm-pcm-routing.h"
+
+#define LOOPBACK_VOL_MAX_STEPS 0x2000
+
+static const DECLARE_TLV_DB_LINEAR(loopback_rx_vol_gain, 0,
+ LOOPBACK_VOL_MAX_STEPS);
+
+struct msm_pcm_loopback {
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+
+ int instance;
+
+ struct mutex lock;
+
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+
+ int playback_start;
+ int capture_start;
+ int session_id;
+ struct audio_client *audio_client;
+ int volume;
+};
+
+static void stop_pcm(struct msm_pcm_loopback *pcm);
+
+static const struct snd_pcm_hardware dummy_pcm_hardware = {
+ .formats = 0xffffffff,
+ .channels_min = 1,
+ .channels_max = UINT_MAX,
+
+ /* Random values to keep userspace happy when checking constraints */
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .buffer_bytes_max = 128*1024,
+ .period_bytes_min = 1024,
+ .period_bytes_max = 1024*2,
+ .periods_min = 2,
+ .periods_max = 128,
+};
+
+static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event,
+ void *priv_data)
+{
+ struct msm_pcm_loopback *pcm = priv_data;
+
+ BUG_ON(!pcm);
+
+ pr_debug("%s: event %x\n", __func__, event);
+
+ switch (event) {
+ case MSM_PCM_RT_EVT_DEVSWITCH:
+ q6asm_cmd(pcm->audio_client, CMD_PAUSE);
+ q6asm_cmd(pcm->audio_client, CMD_FLUSH);
+ q6asm_run(pcm->audio_client, 0, 0, 0);
+ default:
+ break;
+ }
+}
+
+static void msm_pcm_loopback_event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ pr_debug("%s\n", __func__);
+ switch (opcode) {
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ pr_err("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+}
+
+static int pcm_loopback_set_volume(struct msm_pcm_loopback *prtd, int volume)
+{
+ int rc = 0;
+
+ pr_debug("%s Setting volume 0x%x\n", __func__, volume);
+
+ if (prtd) {
+ rc = q6asm_set_volume(prtd->audio_client, volume);
+ if (rc < 0) {
+ pr_err("%s: Send Volume command failed rc = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ prtd->volume = volume;
+ }
+ return rc;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct msm_pcm_loopback *pcm;
+ int ret = 0;
+ struct msm_pcm_routing_evt event;
+
+ pcm = dev_get_drvdata(rtd->platform->dev);
+ mutex_lock(&pcm->lock);
+
+ snd_soc_set_runtime_hwparams(substream, &dummy_pcm_hardware);
+ pcm->volume = 0x2000;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pcm->playback_substream = substream;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ pcm->capture_substream = substream;
+
+ pcm->instance++;
+ dev_dbg(rtd->platform->dev, "%s: pcm out open: %d,%d\n", __func__,
+ pcm->instance, substream->stream);
+ if (pcm->instance == 2) {
+ struct snd_soc_pcm_runtime *soc_pcm_rx =
+ pcm->playback_substream->private_data;
+ struct snd_soc_pcm_runtime *soc_pcm_tx =
+ pcm->capture_substream->private_data;
+ if (pcm->audio_client != NULL)
+ stop_pcm(pcm);
+
+ pcm->audio_client = q6asm_audio_client_alloc(
+ (app_cb)msm_pcm_loopback_event_handler, pcm);
+ if (!pcm->audio_client) {
+ dev_err(rtd->platform->dev,
+ "%s: Could not allocate memory\n", __func__);
+ mutex_unlock(&pcm->lock);
+ return -ENOMEM;
+ }
+ pcm->session_id = pcm->audio_client->session;
+ pcm->audio_client->perf_mode = false;
+ ret = q6asm_open_loopack(pcm->audio_client);
+ if (ret < 0) {
+ dev_err(rtd->platform->dev,
+ "%s: pcm out open failed\n", __func__);
+ q6asm_audio_client_free(pcm->audio_client);
+ mutex_unlock(&pcm->lock);
+ return -ENOMEM;
+ }
+ event.event_func = msm_pcm_route_event_handler;
+ event.priv_data = (void *) pcm;
+ msm_pcm_routing_reg_phy_stream(soc_pcm_tx->dai_link->be_id,
+ pcm->audio_client->perf_mode,
+ pcm->session_id, pcm->capture_substream->stream);
+ msm_pcm_routing_reg_phy_stream_v2(soc_pcm_rx->dai_link->be_id,
+ pcm->audio_client->perf_mode,
+ pcm->session_id, pcm->playback_substream->stream,
+ event);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ pcm->playback_substream = substream;
+ ret = pcm_loopback_set_volume(pcm, pcm->volume);
+ if (ret < 0)
+ dev_err(rtd->platform->dev,
+ "Error %d setting volume", ret);
+ }
+ }
+ dev_info(rtd->platform->dev, "%s: Instance = %d, Stream ID = %s\n",
+ __func__ , pcm->instance, substream->pcm->id);
+ runtime->private_data = pcm;
+
+ mutex_unlock(&pcm->lock);
+
+ return 0;
+}
+
+static void stop_pcm(struct msm_pcm_loopback *pcm)
+{
+ struct snd_soc_pcm_runtime *soc_pcm_rx =
+ pcm->playback_substream->private_data;
+ struct snd_soc_pcm_runtime *soc_pcm_tx =
+ pcm->capture_substream->private_data;
+
+ if (pcm->audio_client == NULL)
+ return;
+ q6asm_cmd(pcm->audio_client, CMD_CLOSE);
+
+ msm_pcm_routing_dereg_phy_stream(soc_pcm_rx->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ msm_pcm_routing_dereg_phy_stream(soc_pcm_tx->dai_link->be_id,
+ SNDRV_PCM_STREAM_CAPTURE);
+ q6asm_audio_client_free(pcm->audio_client);
+ pcm->audio_client = NULL;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_pcm_loopback *pcm = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ int ret = 0;
+
+ mutex_lock(&pcm->lock);
+
+ dev_dbg(rtd->platform->dev, "%s: end pcm call:%d\n",
+ __func__, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pcm->playback_start = 0;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ pcm->capture_start = 0;
+
+ pcm->instance--;
+ if (!pcm->playback_start || !pcm->capture_start) {
+ dev_dbg(rtd->platform->dev, "%s: end pcm call\n", __func__);
+ stop_pcm(pcm);
+ }
+
+ mutex_unlock(&pcm->lock);
+ return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_pcm_loopback *pcm = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+
+ mutex_lock(&pcm->lock);
+
+ dev_dbg(rtd->platform->dev, "%s: ASM loopback stream:%d\n",
+ __func__, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (!pcm->playback_start)
+ pcm->playback_start = 1;
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (!pcm->capture_start)
+ pcm->capture_start = 1;
+ }
+ mutex_unlock(&pcm->lock);
+
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_pcm_loopback *pcm = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ dev_dbg(rtd->platform->dev,
+ "%s: playback_start:%d,capture_start:%d\n", __func__,
+ pcm->playback_start, pcm->capture_start);
+ if (pcm->playback_start && pcm->capture_start)
+ q6asm_run_nowait(pcm->audio_client, 0, 0, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ dev_dbg(rtd->platform->dev,
+ "%s:Pause/Stop - playback_start:%d,capture_start:%d\n",
+ __func__, pcm->playback_start, pcm->capture_start);
+ if (pcm->playback_start && pcm->capture_start)
+ q6asm_cmd_nowait(pcm->audio_client, CMD_PAUSE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+
+ dev_dbg(rtd->platform->dev, "%s: ASM loopback\n", __func__);
+
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(params));
+}
+
+static int msm_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .hw_params = msm_pcm_hw_params,
+ .hw_free = msm_pcm_hw_free,
+ .close = msm_pcm_close,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+};
+
+static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ struct snd_pcm_volume *vol = kcontrol->private_data;
+ struct snd_pcm_substream *substream = vol->pcm->streams[0].substream;
+ struct msm_pcm_loopback *prtd = substream->runtime->private_data;
+ int volume = ucontrol->value.integer.value[0];
+
+ rc = pcm_loopback_set_volume(prtd, volume);
+ return rc;
+}
+
+static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
+ struct snd_pcm_volume *volume_info;
+ struct snd_kcontrol *kctl;
+ int ret = 0;
+
+ dev_dbg(rtd->dev, "%s, Volume cntrl add\n", __func__);
+ ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, 1,
+ rtd->dai_link->be_id,
+ &volume_info);
+ if (ret < 0)
+ return ret;
+ kctl = volume_info->kctl;
+ kctl->put = msm_pcm_volume_ctl_put;
+ kctl->tlv.p = loopback_rx_vol_gain;
+ return 0;
+}
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ ret = msm_pcm_add_controls(rtd);
+ if (ret)
+ dev_err(rtd->dev, "%s, kctl add failed\n", __func__);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+};
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ struct msm_pcm_loopback *pcm;
+
+ dev_dbg(&pdev->dev, "%s: dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+
+ pcm = kzalloc(sizeof(struct msm_pcm_loopback), GFP_KERNEL);
+ if (!pcm) {
+ dev_err(&pdev->dev, "%s Failed to allocate memory for pcm\n",
+ __func__);
+ return -ENOMEM;
+ } else {
+ mutex_init(&pcm->lock);
+ dev_set_drvdata(&pdev->dev, pcm);
+ }
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ struct msm_pcm_loopback *pcm;
+
+ pcm = dev_get_drvdata(&pdev->dev);
+ mutex_destroy(&pcm->lock);
+
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-loopback",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM loopback platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-pcm-lpa.c b/sound/soc/msm/msm-pcm-lpa.c
index 95c5cd7..bfab124 100644
--- a/sound/soc/msm/msm-pcm-lpa.c
+++ b/sound/soc/msm/msm-pcm-lpa.c
@@ -382,7 +382,7 @@
pr_debug("%s\n", __func__);
rc = wait_event_timeout(the_locks.eos_wait,
prtd->cmd_ack, 5 * HZ);
- if (rc < 0)
+ if (!rc)
pr_err("EOS cmd timeout\n");
prtd->pcm_irq_pos = 0;
}
@@ -537,7 +537,7 @@
pr_err("%s: flush cmd failed rc=%d\n", __func__, rc);
rc = wait_event_timeout(the_locks.eos_wait,
prtd->cmd_ack, 5 * HZ);
- if (rc < 0)
+ if (!rc)
pr_err("Flush cmd timeout\n");
prtd->pcm_irq_pos = 0;
break;
diff --git a/sound/soc/msm/msm-pcm-q6.c b/sound/soc/msm/msm-pcm-q6.c
index 1d15c11..a05a216 100644
--- a/sound/soc/msm/msm-pcm-q6.c
+++ b/sound/soc/msm/msm-pcm-q6.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, 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
@@ -165,7 +165,7 @@
/* assume data size = 0 during flushing */
if (in_frame_info[token][0]) {
- prtd->pcm_irq_pos += in_frame_info[token][0];
+ prtd->pcm_irq_pos += prtd->pcm_count;
pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
if (atomic_read(&prtd->start))
snd_pcm_period_elapsed(substream);
@@ -419,7 +419,7 @@
__func__, atomic_read(&prtd->out_count));
ret = wait_event_timeout(the_locks.write_wait,
(atomic_read(&prtd->out_count)), 5 * HZ);
- if (ret < 0) {
+ if (!ret) {
pr_err("%s: wait_event_timeout failed\n", __func__);
goto fail;
}
@@ -472,7 +472,7 @@
dir = IN;
ret = wait_event_timeout(the_locks.eos_wait,
prtd->cmd_ack, 5 * HZ);
- if (ret < 0)
+ if (!ret)
pr_err("%s: CMD_EOS failed\n", __func__);
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
q6asm_audio_client_buf_free_contiguous(dir,
@@ -510,7 +510,7 @@
ret = wait_event_timeout(the_locks.read_wait,
(atomic_read(&prtd->in_count)), 5 * HZ);
- if (ret < 0) {
+ if (!ret) {
pr_debug("%s: wait_event_timeout failed\n", __func__);
goto fail;
}
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index e74a0dd..95122b2 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -658,6 +658,11 @@
msm_bedais[reg].sample_rate, channels,
DEFAULT_COPP_TOPOLOGY);
+ if (session_type == SESSION_TYPE_RX &&
+ fdai->event_info.event_func)
+ fdai->event_info.event_func(
+ MSM_PCM_RT_EVT_DEVSWITCH,
+ fdai->event_info.priv_data);
msm_pcm_routing_build_matrix(val,
fdai->strm_id, path_type);
@@ -976,10 +981,8 @@
static int msm_routing_set_fm_vol_mixer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- afe_loopback_gain(INT_FM_TX , ucontrol->value.integer.value[0]);
-
+ afe_loopback_gain(INT_FM_TX, ucontrol->value.integer.value[0]);
msm_route_fm_vol_control = ucontrol->value.integer.value[0];
-
return 0;
}
@@ -1461,8 +1464,8 @@
ret = voc_set_ext_ec_ref(msm_route_ext_ec_ref, false);
break;
}
- snd_soc_dapm_mux_update_power(widget, kcontrol, 1, mux, e);
mutex_unlock(&routing_lock);
+ snd_soc_dapm_mux_update_power(widget, kcontrol, 1, mux, e);
return ret;
}
@@ -1660,6 +1663,9 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_INT_BT_SCO_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
@@ -1696,6 +1702,9 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_AFE_PCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = {
@@ -1714,6 +1723,9 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_AUXPCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = {
@@ -1810,6 +1822,12 @@
msm_routing_put_audio_mixer),
};
+static const struct snd_kcontrol_new mmul6_mixer_controls[] = {
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_I2S_RX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
@@ -2715,6 +2733,7 @@
SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL4", "MultiMedia4 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("VoLTE_DL", "VoLTE Playback", 0, 0, 0, 0),
@@ -2828,6 +2847,8 @@
mmul4_mixer_controls, ARRAY_SIZE(mmul4_mixer_controls)),
SND_SOC_DAPM_MIXER("MultiMedia5 Mixer", SND_SOC_NOPM, 0, 0,
mmul5_mixer_controls, ARRAY_SIZE(mmul5_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia6 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul6_mixer_controls, ARRAY_SIZE(mmul6_mixer_controls)),
SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)),
SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
@@ -3008,6 +3029,7 @@
{"MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"MI2S_RX", NULL, "MI2S_RX Audio Mixer"},
{"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
@@ -3026,6 +3048,7 @@
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"},
{"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -3040,12 +3063,14 @@
{"AFE_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"AFE_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"AFE_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"PCM_RX", NULL, "AFE_PCM_RX Audio Mixer"},
{"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
{"MultiMedia5 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
{"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"MultiMedia5 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MultiMedia6 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"MultiMedia1 Mixer", "AFE_PCM_TX", "PCM_TX"},
{"MultiMedia5 Mixer", "AFE_PCM_TX", "PCM_TX"},
@@ -3054,12 +3079,14 @@
{"MM_UL2", NULL, "MultiMedia2 Mixer"},
{"MM_UL4", NULL, "MultiMedia4 Mixer"},
{"MM_UL5", NULL, "MultiMedia5 Mixer"},
+ {"MM_UL6", NULL, "MultiMedia6 Mixer"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"},
{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index c42e155..e25a44a 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -117,6 +117,7 @@
enum msm_pcm_routing_event {
MSM_PCM_RT_EVT_BUF_RECFG,
+ MSM_PCM_RT_EVT_DEVSWITCH,
MSM_PCM_RT_EVT_MAX,
};
/* dai_id: front-end ID,
diff --git a/sound/soc/msm/msm-pcm-voice.c b/sound/soc/msm/msm-pcm-voice.c
index 26e6ae6..6d0fcea 100644
--- a/sound/soc/msm/msm-pcm-voice.c
+++ b/sound/soc/msm/msm-pcm-voice.c
@@ -505,6 +505,9 @@
voc_set_tty_mode(voc_get_session_id(VOICE_SESSION_NAME), tty_mode);
voc_set_tty_mode(voc_get_session_id(VOICE2_SESSION_NAME), tty_mode);
+
+ voc_set_tty_mode(voc_get_session_id(VOLTE_SESSION_NAME), tty_mode);
+
return 0;
}
static int msm_voice_widevoice_put(struct snd_kcontrol *kcontrol,
diff --git a/sound/soc/msm/msm8226.c b/sound/soc/msm/msm8226.c
index 08731f6..22cd61a 100644
--- a/sound/soc/msm/msm8226.c
+++ b/sound/soc/msm/msm8226.c
@@ -26,6 +26,7 @@
#include <mach/socinfo.h>
#include <qdsp6v2/msm-pcm-routing-v2.h>
#include "../codecs/wcd9306.h"
+#include <linux/io.h>
#define DRV_NAME "msm8226-asoc-tapan"
@@ -35,15 +36,28 @@
#define BTSCO_RATE_8KHZ 8000
#define BTSCO_RATE_16KHZ 16000
-#define GPIO_AUX_PCM_DOUT 43
-#define GPIO_AUX_PCM_DIN 44
-#define GPIO_AUX_PCM_SYNC 45
-#define GPIO_AUX_PCM_CLK 46
-
#define WCD9XXX_MBHC_DEF_BUTTONS 8
#define WCD9XXX_MBHC_DEF_RLOADS 5
#define TAPAN_EXT_CLK_RATE 9600000
+#define NUM_OF_AUXPCM_GPIOS 4
+
+static int msm8226_auxpcm_rate = 8000;
+static atomic_t auxpcm_rsc_ref;
+static const char *const auxpcm_rate_text[] = {"rate_8000", "rate_16000"};
+static const struct soc_enum msm8226_auxpcm_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, auxpcm_rate_text),
+};
+
+#define LPAIF_OFFSET 0xFE000000
+#define LPAIF_PRI_MODE_MUXSEL (LPAIF_OFFSET + 0x2B000)
+#define LPAIF_SEC_MODE_MUXSEL (LPAIF_OFFSET + 0x2C000)
+#define LPAIF_TER_MODE_MUXSEL (LPAIF_OFFSET + 0x2D000)
+#define LPAIF_QUAD_MODE_MUXSEL (LPAIF_OFFSET + 0x2E000)
+
+#define I2S_PCM_SEL 1
+#define I2S_PCM_SEL_OFFSET 1
+
void *def_tapan_mbhc_cal(void);
static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
bool dapm);
@@ -58,15 +72,40 @@
.gpio_irq = 0,
.gpio_level_insert = 0,
.detect_extn_cable = true,
+ .micbias_enable_flags = 0,
.insert_detect = true,
.swap_gnd_mic = NULL,
};
+struct msm_auxpcm_gpio {
+ unsigned gpio_no;
+ const char *gpio_name;
+};
+
+struct msm_auxpcm_ctrl {
+ struct msm_auxpcm_gpio *pin_data;
+ u32 cnt;
+};
+
struct msm8226_asoc_mach_data {
int mclk_gpio;
u32 mclk_freq;
+ struct msm_auxpcm_ctrl *auxpcm_ctrl;
+ u32 us_euro_gpio;
};
+#define GPIO_NAME_INDEX 0
+#define DT_PARSE_INDEX 1
+
+static char *msm_auxpcm_gpio_name[][2] = {
+ {"PRIM_AUXPCM_CLK", "qcom,prim-auxpcm-gpio-clk"},
+ {"PRIM_AUXPCM_SYNC", "qcom,prim-auxpcm-gpio-sync"},
+ {"PRIM_AUXPCM_DIN", "qcom,prim-auxpcm-gpio-din"},
+ {"PRIM_AUXPCM_DOUT", "qcom,prim-auxpcm-gpio-dout"},
+};
+
+void *lpaif_pri_muxsel_virt_addr;
+
/* Shared channel numbers for Slimbus ports that connect APQ to MDM. */
enum {
SLIM_1_RX_1 = 145, /* BT-SCO and USB TX */
@@ -108,7 +147,6 @@
if (clk_users != 1)
goto exit;
if (codec_clk) {
- clk_set_rate(codec_clk, TAPAN_EXT_CLK_RATE);
clk_prepare_enable(codec_clk);
tapan_mclk_enable(codec, 1, dapm);
} else {
@@ -257,10 +295,10 @@
struct snd_ctl_elem_value *ucontrol)
{
switch (ucontrol->value.integer.value[0]) {
- case 0:
+ case 8000:
msm_btsco_rate = BTSCO_RATE_8KHZ;
break;
- case 1:
+ case 16000:
msm_btsco_rate = BTSCO_RATE_16KHZ;
break;
default:
@@ -272,11 +310,6 @@
return 0;
}
-static const struct snd_kcontrol_new int_btsco_rate_mixer_controls[] = {
- SOC_ENUM_EXT("Internal BTSCO SampleRate", msm_btsco_enum[0],
- msm_btsco_rate_get, msm_btsco_rate_put),
-};
-
static int msm_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -292,6 +325,45 @@
return 0;
}
+static int msm8226_auxpcm_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm8226_auxpcm_rate;
+ return 0;
+}
+
+static int msm8226_auxpcm_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm8226_auxpcm_rate = 8000;
+ break;
+ case 1:
+ msm8226_auxpcm_rate = 16000;
+ break;
+ default:
+ msm8226_auxpcm_rate = 8000;
+ break;
+ }
+ return 0;
+}
+
+static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = msm8226_auxpcm_rate;
+ channels->min = channels->max = 1;
+
+ return 0;
+}
+
static int msm_proxy_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -304,6 +376,118 @@
return 0;
}
+static int msm_aux_pcm_get_gpios(struct msm_auxpcm_ctrl *auxpcm_ctrl)
+{
+ struct msm_auxpcm_gpio *pin_data = NULL;
+ int ret = 0;
+ int i;
+ int j;
+
+ pin_data = auxpcm_ctrl->pin_data;
+ if (!pin_data) {
+ pr_err("%s: Invalid control data for AUXPCM\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ for (i = 0; i < auxpcm_ctrl->cnt; i++, pin_data++) {
+ ret = gpio_request(pin_data->gpio_no,
+ pin_data->gpio_name);
+ pr_debug("%s: gpio = %d, gpio name = %s\n"
+ "ret = %d\n", __func__,
+ pin_data->gpio_no,
+ pin_data->gpio_name,
+ ret);
+ if (ret) {
+ pr_err("%s: Failed to request gpio %d\n",
+ __func__, pin_data->gpio_no);
+ /* Release all GPIOs on failure */
+ if (i > 0) {
+ for (j = i; j >= 0; j--)
+ gpio_free(pin_data->gpio_no);
+ }
+ goto err;
+ }
+ }
+err:
+ return ret;
+}
+
+static int msm_aux_pcm_free_gpios(struct msm_auxpcm_ctrl *auxpcm_ctrl)
+{
+ struct msm_auxpcm_gpio *pin_data = NULL;
+ int i;
+ int ret = 0;
+
+ if (auxpcm_ctrl == NULL || auxpcm_ctrl->pin_data == NULL) {
+ pr_err("%s: Invalid control data for AUXPCM\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ pin_data = auxpcm_ctrl->pin_data;
+ for (i = 0; i < auxpcm_ctrl->cnt; i++, pin_data++) {
+ gpio_free(pin_data->gpio_no);
+ pr_debug("%s: gpio = %d, gpio_name = %s\n",
+ __func__, pin_data->gpio_no,
+ pin_data->gpio_name);
+ }
+err:
+ return ret;
+}
+
+static int msm_auxpcm_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct msm8226_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct msm_auxpcm_ctrl *auxpcm_ctrl = NULL;
+ int ret = 0;
+
+ pr_debug("%s(): substream = %s, auxpcm_rsc_ref counter = %d\n",
+ __func__, substream->name, atomic_read(&auxpcm_rsc_ref));
+
+ auxpcm_ctrl = pdata->auxpcm_ctrl;
+
+ if (auxpcm_ctrl == NULL || auxpcm_ctrl->pin_data == NULL ||
+ lpaif_pri_muxsel_virt_addr == NULL) {
+ pr_err("%s: Invalid control data for AUXPCM\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (atomic_inc_return(&auxpcm_rsc_ref) == 1) {
+ iowrite32(I2S_PCM_SEL << I2S_PCM_SEL_OFFSET,
+ lpaif_pri_muxsel_virt_addr);
+ ret = msm_aux_pcm_get_gpios(auxpcm_ctrl);
+ }
+ if (ret < 0) {
+ pr_err("%s: Aux PCM GPIO request failed\n", __func__);
+ return -EINVAL;
+ }
+err:
+ return ret;
+}
+
+static void msm_auxpcm_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct msm8226_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct msm_auxpcm_ctrl *auxpcm_ctrl = NULL;
+
+ pr_debug("%s(): substream = %s, auxpcm_rsc_ref counter = %d\n",
+ __func__, substream->name, atomic_read(&auxpcm_rsc_ref));
+
+ auxpcm_ctrl = pdata->auxpcm_ctrl;
+
+ if (atomic_dec_return(&auxpcm_rsc_ref) == 0)
+ msm_aux_pcm_free_gpios(auxpcm_ctrl);
+}
+
+static struct snd_soc_ops msm_auxpcm_be_ops = {
+ .startup = msm_auxpcm_startup,
+ .shutdown = msm_auxpcm_shutdown,
+};
+
static int msm_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -358,6 +542,10 @@
msm_slim_0_rx_ch_get, msm_slim_0_rx_ch_put),
SOC_ENUM_EXT("SLIM_0_TX Channels", msm_snd_enum[1],
msm_slim_0_tx_ch_get, msm_slim_0_tx_ch_put),
+ SOC_ENUM_EXT("AUX PCM SampleRate", msm8226_auxpcm_enum[0],
+ msm8226_auxpcm_rate_get, msm8226_auxpcm_rate_put),
+ SOC_ENUM_EXT("Internal BTSCO SampleRate", msm_btsco_enum[0],
+ msm_btsco_rate_get, msm_btsco_rate_put),
};
static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
@@ -448,7 +636,7 @@
#undef S
#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(tapan_cal)->X) = (Y))
S(v_no_mic, 30);
- S(v_hs_max, 1650);
+ S(v_hs_max, 2450);
#undef S
#define S(X, Y) ((WCD9XXX_MBHC_CAL_BTN_DET_PTR(tapan_cal)->X) = (Y))
S(c[0], 62);
@@ -893,6 +1081,35 @@
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
+ /* AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_AUXPCM_RX,
+ .stream_name = "AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6.4106",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_RX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ .ops = &msm_auxpcm_be_ops,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1
+ /* this dainlink has playback support */
+ },
+ {
+ .name = LPASS_BE_AUXPCM_TX,
+ .stream_name = "AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6.4107",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_TX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ .ops = &msm_auxpcm_be_ops,
+ .ignore_suspend = 1
+ },
/* Backend DAI Links */
{
.name = LPASS_BE_SLIMBUS_0_RX,
@@ -1053,6 +1270,70 @@
.num_links = ARRAY_SIZE(msm8226_dai),
};
+static int msm8226_dtparse_auxpcm(struct platform_device *pdev,
+ struct msm_auxpcm_ctrl **auxpcm_ctrl,
+ char *msm_auxpcm_gpio_name[][2])
+{
+ int ret = 0;
+ int i = 0;
+ struct msm_auxpcm_gpio *pin_data = NULL;
+ struct msm_auxpcm_ctrl *ctrl;
+ unsigned int gpio_no[NUM_OF_AUXPCM_GPIOS];
+ enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;
+ int auxpcm_cnt = 0;
+
+ pin_data = devm_kzalloc(&pdev->dev, (ARRAY_SIZE(gpio_no) *
+ sizeof(struct msm_auxpcm_gpio)),
+ GFP_KERNEL);
+ if (!pin_data) {
+ dev_err(&pdev->dev, "No memory for gpio\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(gpio_no); i++) {
+ gpio_no[i] = of_get_named_gpio_flags(pdev->dev.of_node,
+ msm_auxpcm_gpio_name[i][DT_PARSE_INDEX],
+ 0, &flags);
+
+ if (gpio_no[i] > 0) {
+ pin_data[i].gpio_name =
+ msm_auxpcm_gpio_name[auxpcm_cnt][GPIO_NAME_INDEX];
+ pin_data[i].gpio_no = gpio_no[i];
+ dev_dbg(&pdev->dev, "%s:GPIO gpio[%s] =\n"
+ "0x%x\n", __func__,
+ pin_data[i].gpio_name,
+ pin_data[i].gpio_no);
+ auxpcm_cnt++;
+ } else {
+ dev_err(&pdev->dev, "%s:Invalid AUXPCM GPIO[%s]= %x\n",
+ __func__,
+ msm_auxpcm_gpio_name[i][GPIO_NAME_INDEX],
+ gpio_no[i]);
+ ret = -ENODEV;
+ goto err;
+ }
+ }
+
+ ctrl = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_auxpcm_ctrl), GFP_KERNEL);
+ if (!ctrl) {
+ dev_err(&pdev->dev, "No memory for gpio\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ctrl->pin_data = pin_data;
+ ctrl->cnt = auxpcm_cnt;
+ *auxpcm_ctrl = ctrl;
+ return ret;
+
+err:
+ if (pin_data)
+ devm_kfree(&pdev->dev, pin_data);
+ return ret;
+}
+
static int msm8226_prepare_codec_mclk(struct snd_soc_card *card)
{
struct msm8226_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
@@ -1069,11 +1350,50 @@
return 0;
}
+static bool msm8226_swap_gnd_mic(struct snd_soc_codec *codec)
+{
+ struct snd_soc_card *card = codec->card;
+ struct msm8226_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ int value = gpio_get_value_cansleep(pdata->us_euro_gpio);
+
+ pr_debug("%s: swap select switch %d to %d\n", __func__, value, !value);
+ gpio_direction_output(pdata->us_euro_gpio, !value);
+
+ return true;
+}
+
+static int msm8226_setup_hs_jack(struct platform_device *pdev,
+ struct msm8226_asoc_mach_data *pdata)
+{
+ int rc;
+
+ pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,cdc-us-euro-gpios", 0);
+ if (pdata->us_euro_gpio < 0) {
+ dev_info(&pdev->dev,
+ "property %s in node %s not found %d\n",
+ "qcom,cdc-us-euro-gpios", pdev->dev.of_node->full_name,
+ pdata->us_euro_gpio);
+ } else {
+ rc = gpio_request(pdata->us_euro_gpio,
+ "TAPAN_CODEC_US_EURO_GPIO");
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: Failed to request tapan us-euro gpio %d\n",
+ __func__, pdata->us_euro_gpio);
+ } else {
+ mbhc_cfg.swap_gnd_mic = msm8226_swap_gnd_mic;
+ }
+ }
+ return 0;
+}
+
static __devinit int msm8226_asoc_machine_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &snd_soc_card_msm8226;
struct msm8226_asoc_mach_data *pdata;
int ret;
+ const char *auxpcm_pri_gpio_set = NULL;
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "No platform supplied from device tree\n");
@@ -1088,6 +1408,15 @@
goto err;
}
+ /* Parse AUXPCM info from DT */
+ ret = msm8226_dtparse_auxpcm(pdev, &pdata->auxpcm_ctrl,
+ msm_auxpcm_gpio_name);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: Auxpcm pin data parse failed\n", __func__);
+ goto err;
+ }
+
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, pdata);
@@ -1146,6 +1475,10 @@
}
}
+ mbhc_cfg.gpio_level_insert = of_property_read_bool(pdev->dev.of_node,
+ "qcom,headset-jack-type-NO");
+ msm8226_setup_hs_jack(pdev, pdata);
+
ret = msm8226_prepare_codec_mclk(card);
if (ret)
goto err_vdd_spkr;
@@ -1158,6 +1491,30 @@
}
mutex_init(&cdc_mclk_mutex);
+ ret = of_property_read_string(pdev->dev.of_node,
+ "qcom,prim-auxpcm-gpio-set", &auxpcm_pri_gpio_set);
+ if (ret) {
+ dev_err(&pdev->dev, "Looking up %s property in node %s failed",
+ "qcom,prim-auxpcm-gpio-set",
+ pdev->dev.of_node->full_name);
+ goto err_vdd_spkr;
+ }
+ if (!strcmp(auxpcm_pri_gpio_set, "prim-gpio-prim")) {
+ lpaif_pri_muxsel_virt_addr = ioremap(LPAIF_PRI_MODE_MUXSEL, 4);
+ } else if (!strcmp(auxpcm_pri_gpio_set, "prim-gpio-tert")) {
+ lpaif_pri_muxsel_virt_addr = ioremap(LPAIF_TER_MODE_MUXSEL, 4);
+ } else {
+ dev_err(&pdev->dev, "Invalid value %s for AUXPCM GPIO set\n",
+ auxpcm_pri_gpio_set);
+ ret = -EINVAL;
+ goto err_vdd_spkr;
+ }
+ if (lpaif_pri_muxsel_virt_addr == NULL) {
+ pr_err("%s Pri muxsel virt addr is null\n", __func__);
+ ret = -EINVAL;
+ goto err_vdd_spkr;
+ }
+
return 0;
err_vdd_spkr:
@@ -1184,6 +1541,9 @@
gpio_free(pdata->mclk_gpio);
gpio_free(vdd_spkr_gpio);
+ if (pdata->us_euro_gpio > 0)
+ gpio_free(pdata->us_euro_gpio);
+
vdd_spkr_gpio = -1;
snd_soc_unregister_card(card);
diff --git a/sound/soc/msm/msm8930.c b/sound/soc/msm/msm8930.c
index 737317c..dd92e34 100644
--- a/sound/soc/msm/msm8930.c
+++ b/sound/soc/msm/msm8930.c
@@ -1143,6 +1143,22 @@
.ignore_pmdown_time = 1,
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,
},
+ {
+ .name = "MSM8960 FM",
+ .stream_name = "MultiMedia6",
+ .cpu_dai_name = "MultiMedia6",
+ .platform_name = "msm-pcm-loopback",
+ .dynamic = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6,
+ },
/* Backend DAI Links */
{
.name = LPASS_BE_SLIMBUS_0_RX,
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index 778dc10..a39a18b 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -19,18 +19,20 @@
#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/qpnp/clkdiv.h>
#include <linux/regulator/consumer.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/pcm.h>
#include <sound/jack.h>
#include <sound/q6afe-v2.h>
-#include <asm/mach-types.h>
-#include <mach/socinfo.h>
#include <sound/pcm_params.h>
+#include <asm/mach-types.h>
+#include <mach/subsystem_notif.h>
#include "qdsp6v2/msm-pcm-routing-v2.h"
+#include "qdsp6v2/q6core.h"
+#include "../codecs/wcd9xxx-common.h"
#include "../codecs/wcd9320.h"
-#include <linux/io.h>
#define DRV_NAME "msm8974-asoc-taiko"
@@ -75,8 +77,17 @@
#define EXT_CLASS_D_DIS_DELAY 3000
#define EXT_CLASS_D_DELAY_DELTA 2000
+/* It takes about 13ms for Class-AB PAs to ramp-up */
+#define EXT_CLASS_AB_EN_DELAY 10000
+#define EXT_CLASS_AB_DIS_DELAY 1000
+#define EXT_CLASS_AB_DELAY_DELTA 1000
+
#define NUM_OF_AUXPCM_GPIOS 4
+static void *adsp_state_notifier;
+
+#define ADSP_STATE_READY_TIMEOUT_MS 3000
+
static inline int param_is_mask(int p)
{
return ((p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
@@ -119,6 +130,7 @@
.gpio_irq = 0,
.gpio_level_insert = 1,
.detect_extn_cable = true,
+ .micbias_enable_flags = 1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET,
.insert_detect = true,
.swap_gnd_mic = NULL,
};
@@ -184,6 +196,8 @@
static struct platform_device *spdev;
static struct regulator *ext_spk_amp_regulator;
static int ext_spk_amp_gpio = -1;
+static int ext_ult_spk_amp_gpio = -1;
+static int ext_ult_lo_amp_gpio = -1;
static int msm8974_spk_control = 1;
static int msm8974_ext_spk_pamp;
static int msm_slim_0_rx_ch = 1;
@@ -231,9 +245,43 @@
}
}
+ ext_ult_spk_amp_gpio = of_get_named_gpio(spdev->dev.of_node,
+ "qcom,ext-ult-spk-amp-gpio", 0);
+
+ if (ext_ult_spk_amp_gpio >= 0) {
+ ret = gpio_request(ext_ult_spk_amp_gpio,
+ "ext_ult_spk_amp_gpio");
+ if (ret) {
+ pr_err("%s: gpio_request failed for ext-ult_spk-amp-gpio.\n",
+ __func__);
+ return -EINVAL;
+ }
+ gpio_direction_output(ext_ult_spk_amp_gpio, 0);
+ }
+
return 0;
}
+static void msm8974_liquid_ext_ult_spk_power_amp_enable(u32 on)
+{
+ if (on) {
+ regulator_enable(ext_spk_amp_regulator);
+ gpio_direction_output(ext_ult_spk_amp_gpio, 1);
+ /* time takes enable the external power class AB amplifier */
+ usleep_range(EXT_CLASS_AB_EN_DELAY,
+ EXT_CLASS_AB_EN_DELAY + EXT_CLASS_AB_DELAY_DELTA);
+ } else {
+ gpio_direction_output(ext_ult_spk_amp_gpio, 0);
+ regulator_disable(ext_spk_amp_regulator);
+ /* time takes disable the external power class AB amplifier */
+ usleep_range(EXT_CLASS_AB_DIS_DELAY,
+ EXT_CLASS_AB_DIS_DELAY + EXT_CLASS_AB_DELAY_DELTA);
+ }
+
+ pr_debug("%s: %s external ultrasound SPKR_DRV PAs.\n", __func__,
+ on ? "Enable" : "Disable");
+}
+
static void msm8974_liquid_ext_spk_power_amp_enable(u32 on)
{
if (on) {
@@ -287,7 +335,6 @@
}
-
static irqreturn_t msm8974_liquid_docking_irq_handler(int irq, void *dev)
{
struct msm8974_liquid_dock_dev *dock_dev = dev;
@@ -351,41 +398,82 @@
return 0;
}
-
-
-
-static void msm8974_ext_spk_power_amp_on(u32 spk)
+static int msm8974_liquid_ext_spk_power_amp_on(u32 spk)
{
- if (spk & (LO_1_SPK_AMP |
- LO_3_SPK_AMP |
- LO_2_SPK_AMP |
- LO_4_SPK_AMP)) {
+ int rc;
- pr_debug("%s() External Left/Right Speakers already turned on. spk = 0x%08x\n",
- __func__, spk);
+ if (spk & (LO_1_SPK_AMP | LO_3_SPK_AMP | LO_2_SPK_AMP | LO_4_SPK_AMP)) {
+ pr_debug("%s: External speakers are already on. spk = 0x%x\n",
+ __func__, spk);
msm8974_ext_spk_pamp |= spk;
-
if ((msm8974_ext_spk_pamp & LO_1_SPK_AMP) &&
- (msm8974_ext_spk_pamp & LO_3_SPK_AMP) &&
- (msm8974_ext_spk_pamp & LO_2_SPK_AMP) &&
- (msm8974_ext_spk_pamp & LO_4_SPK_AMP)) {
-
+ (msm8974_ext_spk_pamp & LO_3_SPK_AMP) &&
+ (msm8974_ext_spk_pamp & LO_2_SPK_AMP) &&
+ (msm8974_ext_spk_pamp & LO_4_SPK_AMP))
if (ext_spk_amp_gpio >= 0 &&
- msm8974_liquid_dock_dev != NULL &&
- msm8974_liquid_dock_dev->dock_plug_det == 0)
+ msm8974_liquid_dock_dev &&
+ msm8974_liquid_dock_dev->dock_plug_det == 0)
msm8974_liquid_ext_spk_power_amp_enable(1);
- }
+ rc = 0;
} else {
+ pr_err("%s: Invalid external speaker ampl. spk = 0x%x\n",
+ __func__, spk);
+ rc = -EINVAL;
+ }
- pr_err("%s: ERROR : Invalid External Speaker Ampl. spk = 0x%08x\n",
+ return rc;
+}
+
+static void msm8974_fluid_ext_us_amp_on(u32 spk)
+{
+ if (!gpio_is_valid(ext_ult_lo_amp_gpio)) {
+ pr_err("%s: ext_ult_lo_amp_gpio isn't configured\n", __func__);
+ } else if (spk & (LO_1_SPK_AMP | LO_3_SPK_AMP)) {
+ pr_debug("%s: External US amp is already on. spk = 0x%x\n",
+ __func__, spk);
+ msm8974_ext_spk_pamp |= spk;
+ if ((msm8974_ext_spk_pamp & LO_1_SPK_AMP) &&
+ (msm8974_ext_spk_pamp & LO_3_SPK_AMP))
+ pr_debug("%s: Turn on US amp. spk = 0x%x\n",
+ __func__, spk);
+ gpio_direction_output(ext_ult_lo_amp_gpio, 1);
+
+ } else {
+ pr_err("%s: Invalid external speaker ampl. spk = 0x%x\n",
__func__, spk);
- return;
}
}
-static void msm8974_ext_spk_power_amp_off(u32 spk)
+static void msm8974_fluid_ext_us_amp_off(u32 spk)
{
+ if (!gpio_is_valid(ext_ult_lo_amp_gpio)) {
+ pr_err("%s: ext_ult_lo_amp_gpio isn't configured\n", __func__);
+ } else if (spk & (LO_1_SPK_AMP | LO_3_SPK_AMP)) {
+ pr_debug("%s LO1 and LO3. spk = 0x%x", __func__, spk);
+ if (!msm8974_ext_spk_pamp) {
+ pr_debug("%s: Turn off US amp. spk = 0x%x\n",
+ __func__, spk);
+ gpio_direction_output(ext_ult_lo_amp_gpio, 0);
+ msm8974_ext_spk_pamp = 0;
+ }
+ } else {
+ pr_err("%s: Invalid external speaker ampl. spk = 0x%x\n",
+ __func__, spk);
+ }
+}
+
+static void msm8974_ext_spk_power_amp_on(u32 spk)
+{
+ if (gpio_is_valid(ext_spk_amp_gpio))
+ msm8974_liquid_ext_spk_power_amp_on(spk);
+ else if (gpio_is_valid(ext_ult_lo_amp_gpio))
+ msm8974_fluid_ext_us_amp_on(spk);
+}
+
+static void msm8974_liquid_ext_spk_power_amp_off(u32 spk)
+{
+
if (spk & (LO_1_SPK_AMP |
LO_3_SPK_AMP |
LO_2_SPK_AMP |
@@ -410,6 +498,14 @@
}
}
+static void msm8974_ext_spk_power_amp_off(u32 spk)
+{
+ if (gpio_is_valid(ext_spk_amp_gpio))
+ msm8974_liquid_ext_spk_power_amp_off(spk);
+ else if (gpio_is_valid(ext_ult_lo_amp_gpio))
+ msm8974_fluid_ext_us_amp_off(spk);
+}
+
static void msm8974_ext_control(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
@@ -495,6 +591,33 @@
}
+static int msm_ext_spkramp_ultrasound_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+
+ pr_debug("%s()\n", __func__);
+
+ if (!strncmp(w->name, "SPK_ultrasound amp", 19)) {
+ if (!gpio_is_valid(ext_ult_spk_amp_gpio)) {
+ pr_err("%s: ext_ult_spk_amp_gpio isn't configured\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ msm8974_liquid_ext_ult_spk_power_amp_enable(1);
+ else
+ msm8974_liquid_ext_ult_spk_power_amp_enable(0);
+
+ } else {
+ pr_err("%s() Invalid Speaker Widget = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
bool dapm)
{
@@ -516,7 +639,6 @@
goto exit;
if (codec_clk) {
- clk_set_rate(codec_clk, TAIKO_EXT_CLK_RATE);
clk_prepare_enable(codec_clk);
taiko_mclk_enable(codec, 1, dapm);
} else {
@@ -567,6 +689,8 @@
SND_SOC_DAPM_SPK("Lineout_2 amp", msm_ext_spkramp_event),
SND_SOC_DAPM_SPK("Lineout_4 amp", msm_ext_spkramp_event),
+ SND_SOC_DAPM_SPK("SPK_ultrasound amp",
+ msm_ext_spkramp_ultrasound_event),
SND_SOC_DAPM_MIC("Handset Mic", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
@@ -738,10 +862,10 @@
struct snd_ctl_elem_value *ucontrol)
{
switch (ucontrol->value.integer.value[0]) {
- case 0:
+ case 8000:
msm_btsco_rate = BTSCO_RATE_8KHZ;
break;
- case 1:
+ case 16000:
msm_btsco_rate = BTSCO_RATE_16KHZ;
break;
default:
@@ -816,11 +940,6 @@
return 1;
}
-static const struct snd_kcontrol_new int_btsco_rate_mixer_controls[] = {
- SOC_ENUM_EXT("Internal BTSCO SampleRate", msm_btsco_enum[0],
- msm_btsco_rate_get, msm_btsco_rate_put),
-};
-
static int msm_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -1142,6 +1261,22 @@
return 0;
}
+static int msm_slim_4_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s()\n", __func__);
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ return 0;
+}
+
static int msm_slim_5_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -1210,6 +1345,8 @@
hdmi_rx_bit_format_get, hdmi_rx_bit_format_put),
SOC_ENUM_EXT("PROXY_RX Channels", msm_snd_enum[6],
msm_proxy_rx_ch_get, msm_proxy_rx_ch_put),
+ SOC_ENUM_EXT("Internal BTSCO SampleRate", msm_btsco_enum[0],
+ msm_btsco_rate_get, msm_btsco_rate_put),
};
static bool msm8974_swap_gnd_mic(struct snd_soc_codec *codec)
@@ -1222,6 +1359,101 @@
return true;
}
+static int msm_afe_set_config(struct snd_soc_codec *codec)
+{
+ int rc;
+ void *config_data;
+
+ pr_debug("%s: enter\n", __func__);
+ config_data = taiko_get_afe_config(codec, AFE_CDC_REGISTERS_CONFIG);
+ rc = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0);
+ if (rc) {
+ pr_err("%s: Failed to set codec registers config %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ config_data = taiko_get_afe_config(codec, AFE_SLIMBUS_SLAVE_CONFIG);
+ rc = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0);
+ if (rc) {
+ pr_err("%s: Failed to set slimbus slave config %d\n", __func__,
+ rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static void msm_afe_clear_config(void)
+{
+ afe_clear_config(AFE_CDC_REGISTERS_CONFIG);
+ afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG);
+}
+
+static int msm8974_adsp_state_callback(struct notifier_block *nb,
+ unsigned long value, void *priv)
+{
+ if (value == SUBSYS_BEFORE_SHUTDOWN) {
+ pr_debug("%s: ADSP is about to shutdown. Clearing AFE config\n",
+ __func__);
+ msm_afe_clear_config();
+ } else if (value == SUBSYS_AFTER_POWERUP) {
+ pr_debug("%s: ADSP is up\n", __func__);
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block adsp_state_notifier_block = {
+ .notifier_call = msm8974_adsp_state_callback,
+ .priority = -INT_MAX,
+};
+
+static int msm8974_taiko_codec_up(struct snd_soc_codec *codec)
+{
+ int err;
+ unsigned long timeout;
+ int adsp_ready = 0;
+
+ timeout = jiffies +
+ msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
+
+ do {
+ if (!q6core_is_adsp_ready()) {
+ pr_err("%s: ADSP Audio isn't ready\n", __func__);
+ } else {
+ pr_debug("%s: ADSP Audio is ready\n", __func__);
+ adsp_ready = 1;
+ break;
+ }
+ } while (time_after(timeout, jiffies));
+
+ if (!adsp_ready) {
+ pr_err("%s: timed out waiting for ADSP Audio\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ err = msm_afe_set_config(codec);
+ if (err)
+ pr_err("%s: Failed to set AFE config. err %d\n",
+ __func__, err);
+ return err;
+}
+
+static int msm8974_taiko_event_cb(struct snd_soc_codec *codec,
+ enum wcd9xxx_codec_event codec_event)
+{
+ switch (codec_event) {
+ case WCD9XXX_CODEC_EVENT_CODEC_UP:
+ return msm8974_taiko_codec_up(codec);
+ break;
+ default:
+ pr_err("%s: UnSupported codec event %d\n",
+ __func__, codec_event);
+ return -EINVAL;
+ }
+}
+
static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
{
int err;
@@ -1283,19 +1515,9 @@
tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
- config_data = taiko_get_afe_config(codec, AFE_CDC_REGISTERS_CONFIG);
- err = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0);
+ err = msm_afe_set_config(codec);
if (err) {
- pr_err("%s: Failed to set codec registers config %d\n",
- __func__, err);
- goto out;
- }
-
- config_data = taiko_get_afe_config(codec, AFE_SLIMBUS_SLAVE_CONFIG);
- err = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0);
- if (err) {
- pr_err("%s: Failed to set slimbus slave config %d\n", __func__,
- err);
+ pr_err("%s: Failed to set AFE config %d\n", __func__, err);
goto out;
}
@@ -1306,19 +1528,48 @@
__func__, err);
goto out;
}
-
+ config_data = taiko_get_afe_config(codec,
+ AFE_CDC_CLIP_REGISTERS_CONFIG);
+ if (config_data) {
+ err = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG,
+ config_data, 0);
+ if (err) {
+ pr_err("%s: Failed to set clip registers %d\n",
+ __func__, err);
+ return err;
+ }
+ }
+ config_data = taiko_get_afe_config(codec, AFE_CLIP_BANK_SEL);
+ if (config_data) {
+ err = afe_set_config(AFE_CLIP_BANK_SEL, config_data, 0);
+ if (err) {
+ pr_err("%s: Failed to set AFE bank selection %d\n",
+ __func__, err);
+ return err;
+ }
+ }
/* start mbhc */
mbhc_cfg.calibration = def_taiko_mbhc_cal();
if (mbhc_cfg.calibration) {
err = taiko_hs_detect(codec, &mbhc_cfg);
if (err)
goto out;
- else
- return err;
} else {
err = -ENOMEM;
goto out;
}
+ adsp_state_notifier =
+ subsys_notif_register_notifier("adsp",
+ &adsp_state_notifier_block);
+ if (!adsp_state_notifier) {
+ pr_err("%s: Failed to register adsp state notifier\n",
+ __func__);
+ err = -EFAULT;
+ goto out;
+ }
+
+ taiko_event_register(msm8974_taiko_event_cb, rtd->codec);
+ return 0;
out:
clk_put(codec_clk);
return err;
@@ -1796,6 +2047,104 @@
.codec_name = "snd-soc-dummy",
.be_id = MSM_FRONTEND_DAI_LSM1,
},
+ /* Multiple Tunnel instances */
+ {
+ .name = "MSM8974 Compr2",
+ .stream_name = "COMPR2",
+ .cpu_dai_name = "MultiMedia6",
+ .platform_name = "msm-compr-dsp",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6,
+ },
+ {
+ .name = "MSM8974 Compr3",
+ .stream_name = "COMPR3",
+ .cpu_dai_name = "MultiMedia7",
+ .platform_name = "msm-compr-dsp",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7,
+ },
+ {
+ .name = "MSM8974 Compr4",
+ .stream_name = "COMPR4",
+ .cpu_dai_name = "MultiMedia8",
+ .platform_name = "msm-compr-dsp",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8,
+ },
+ /* HDMI Hostless */
+ {
+ .name = "HDMI_RX_HOSTLESS",
+ .stream_name = "HDMI_RX_HOSTLESS",
+ .cpu_dai_name = "HDMI_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_4_TX,
+ .stream_name = "Slimbus4 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16393",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "taiko_codec",
+ .codec_dai_name = "taiko_vifeedback",
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ .be_hw_params_fixup = msm_slim_4_tx_be_hw_params_fixup,
+ .ops = &msm8974_be_ops,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ },
+ /* Ultrasound RX Back End DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Playback",
+ .stream_name = "SLIMBUS_2 Hostless Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16388",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "taiko_codec",
+ .codec_dai_name = "taiko_rx2",
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &msm8974_slimbus_2_be_ops,
+ },
+ /* Ultrasound TX Back End DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Capture",
+ .stream_name = "SLIMBUS_2 Hostless Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16389",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "taiko_codec",
+ .codec_dai_name = "taiko_tx2",
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &msm8974_slimbus_2_be_ops,
+ },
/* Backend BT/FM DAI Links */
{
.name = LPASS_BE_INT_BT_SCO_RX,
@@ -1876,21 +2225,6 @@
.be_hw_params_fixup = msm_proxy_tx_be_hw_params_fixup,
.ignore_suspend = 1,
},
- /* HDMI Hostless */
- {
- .name = "HDMI_RX_HOSTLESS",
- .stream_name = "HDMI_RX_HOSTLESS",
- .cpu_dai_name = "HDMI_HOSTLESS",
- .platform_name = "msm-pcm-hostless",
- .dynamic = 1,
- .trigger = {SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST},
- .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
- .ignore_suspend = 1,
- .ignore_pmdown_time = 1,
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- },
/* Primary AUX PCM Backend DAI Links */
{
.name = LPASS_BE_AUXPCM_RX,
@@ -2050,19 +2384,6 @@
.ignore_pmdown_time = 1,
.ignore_suspend = 1,
},
- {
- .name = LPASS_BE_SLIMBUS_4_TX,
- .stream_name = "Slimbus4 Capture",
- .cpu_dai_name = "msm-dai-q6-dev.16393",
- .platform_name = "msm-pcm-hostless",
- .codec_name = "taiko_codec",
- .codec_dai_name = "taiko_vifeedback",
- .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
- .be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
- .ops = &msm8974_be_ops,
- .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
- .ignore_suspend = 1,
- },
/* Incall Record Uplink BACK END DAI Link */
{
.name = LPASS_BE_INCALL_RECORD_TX,
@@ -2115,30 +2436,6 @@
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_suspend = 1,
},
- /* Ultrasound RX Back End DAI Link */
- {
- .name = "SLIMBUS_2 Hostless Playback",
- .stream_name = "SLIMBUS_2 Hostless Playback",
- .cpu_dai_name = "msm-dai-q6-dev.16388",
- .platform_name = "msm-pcm-hostless",
- .codec_name = "taiko_codec",
- .codec_dai_name = "taiko_rx2",
- .ignore_suspend = 1,
- .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
- .ops = &msm8974_slimbus_2_be_ops,
- },
- /* Ultrasound TX Back End DAI Link */
- {
- .name = "SLIMBUS_2 Hostless Capture",
- .stream_name = "SLIMBUS_2 Hostless Capture",
- .cpu_dai_name = "msm-dai-q6-dev.16389",
- .platform_name = "msm-pcm-hostless",
- .codec_name = "taiko_codec",
- .codec_dai_name = "taiko_tx2",
- .ignore_suspend = 1,
- .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
- .ops = &msm8974_slimbus_2_be_ops,
- },
};
static struct snd_soc_dai_link msm8974_hdmi_dai_link[] = {
@@ -2251,7 +2548,7 @@
{
struct msm8974_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
int ret;
- if (pdata->us_euro_gpio) {
+ if (pdata->us_euro_gpio >= 0) {
dev_dbg(card->dev, "%s : us_euro gpio request %d", __func__,
pdata->us_euro_gpio);
ret = gpio_request(pdata->us_euro_gpio, "TAIKO_CODEC_US_EURO");
@@ -2271,6 +2568,8 @@
struct snd_soc_card *card = &snd_soc_card_msm8974;
struct msm8974_asoc_mach_data *pdata;
int ret;
+ const char *auxpcm_pri_gpio_set = NULL;
+ const char *prop_name_ult_lo_gpio = "qcom,ext-ult-lo-amp-gpio";
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "No platform supplied from device tree\n");
@@ -2342,9 +2641,26 @@
goto err;
}
+ ext_ult_lo_amp_gpio = of_get_named_gpio(pdev->dev.of_node,
+ prop_name_ult_lo_gpio, 0);
+ if (!gpio_is_valid(ext_ult_lo_amp_gpio)) {
+ dev_dbg(&pdev->dev,
+ "Couldn't find %s property in node %s, %d\n",
+ prop_name_ult_lo_gpio, pdev->dev.of_node->full_name,
+ ext_ult_lo_amp_gpio);
+ } else {
+ ret = gpio_request(ext_ult_lo_amp_gpio, "US_AMP_GPIO");
+ if (ret) {
+ dev_err(card->dev,
+ "%s: Failed to request US amp gpio %d\n",
+ __func__, ext_ult_lo_amp_gpio);
+ goto err;
+ }
+ }
+
ret = msm8974_prepare_codec_mclk(card);
if (ret)
- goto err;
+ goto err1;
if (of_property_read_bool(pdev->dev.of_node, "qcom,hdmi-audio-rx")) {
dev_info(&pdev->dev, "%s(): hdmi audio support present\n",
@@ -2368,7 +2684,7 @@
pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,us-euro-gpios", 0);
if (pdata->us_euro_gpio < 0) {
- dev_err(&pdev->dev, "Looking up %s property in node %s failed",
+ dev_info(&pdev->dev, "property %s not detected in node %s",
"qcom,us-euro-gpios",
pdev->dev.of_node->full_name);
} else {
@@ -2393,22 +2709,43 @@
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
ret);
- goto err;
+ goto err1;
}
- lpaif_pri_muxsel_virt_addr = ioremap(LPAIF_PRI_MODE_MUXSEL, 4);
+ ret = of_property_read_string(pdev->dev.of_node,
+ "qcom,prim-auxpcm-gpio-set", &auxpcm_pri_gpio_set);
+ if (ret) {
+ dev_err(&pdev->dev, "Looking up %s property in node %s failed",
+ "qcom,prim-auxpcm-gpio-set",
+ pdev->dev.of_node->full_name);
+ goto err1;
+ }
+ if (!strcmp(auxpcm_pri_gpio_set, "prim-gpio-prim")) {
+ lpaif_pri_muxsel_virt_addr = ioremap(LPAIF_PRI_MODE_MUXSEL, 4);
+ } else if (!strcmp(auxpcm_pri_gpio_set, "prim-gpio-tert")) {
+ lpaif_pri_muxsel_virt_addr = ioremap(LPAIF_TER_MODE_MUXSEL, 4);
+ } else {
+ dev_err(&pdev->dev, "Invalid value %s for AUXPCM GPIO set\n",
+ auxpcm_pri_gpio_set);
+ ret = -EINVAL;
+ goto err1;
+ }
if (lpaif_pri_muxsel_virt_addr == NULL) {
pr_err("%s Pri muxsel virt addr is null\n", __func__);
ret = -EINVAL;
- goto err;
+ goto err1;
}
lpaif_sec_muxsel_virt_addr = ioremap(LPAIF_SEC_MODE_MUXSEL, 4);
if (lpaif_sec_muxsel_virt_addr == NULL) {
pr_err("%s Sec muxsel virt addr is null\n", __func__);
ret = -EINVAL;
- goto err;
+ goto err1;
}
return 0;
+
+err1:
+ gpio_free(ext_ult_lo_amp_gpio);
+ ext_ult_lo_amp_gpio = -1;
err:
if (pdata->mclk_gpio > 0) {
dev_dbg(&pdev->dev, "%s free gpio %d\n",
@@ -2434,9 +2771,15 @@
if (ext_spk_amp_regulator)
regulator_put(ext_spk_amp_regulator);
+ if (gpio_is_valid(ext_ult_spk_amp_gpio))
+ gpio_free(ext_ult_spk_amp_gpio);
+
+ if (gpio_is_valid(ext_ult_lo_amp_gpio))
+ gpio_free(ext_ult_lo_amp_gpio);
+
gpio_free(pdata->mclk_gpio);
gpio_free(pdata->us_euro_gpio);
- if (ext_spk_amp_gpio >= 0)
+ if (gpio_is_valid(ext_spk_amp_gpio))
gpio_free(ext_spk_amp_gpio);
if (msm8974_liquid_dock_dev != NULL) {
diff --git a/sound/soc/msm/msm8x10.c b/sound/soc/msm/msm8x10.c
index 4dd85fc..ce6cfea 100644
--- a/sound/soc/msm/msm8x10.c
+++ b/sound/soc/msm/msm8x10.c
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/qpnp/clkdiv.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
@@ -26,8 +27,187 @@
#include <asm/mach-types.h>
#include <mach/socinfo.h>
#include <qdsp6v2/msm-pcm-routing-v2.h>
+#include <sound/q6afe-v2.h>
#include <linux/module.h>
+#include "../codecs/msm8x10-wcd.h"
#define DRV_NAME "msm8x10-asoc-wcd"
+#define BTSCO_RATE_8KHZ 8000
+#define BTSCO_RATE_16KHZ 16000
+
+static int msm_btsco_rate = BTSCO_RATE_8KHZ;
+static int msm_btsco_ch = 1;
+
+static int msm_proxy_rx_ch = 2;
+static struct snd_soc_jack hs_jack;
+
+#define MSM8X10_DINO_LPASS_AUDIO_CORE_DIG_CODEC_CLK_SEL 0xFE03B004
+#define MSM8X10_DINO_LPASS_DIGCODEC_CMD_RCGR 0xFE02C000
+#define MSM8X10_DINO_LPASS_DIGCODEC_CFG_RCGR 0xFE02C004
+#define MSM8X10_DINO_LPASS_DIGCODEC_M 0xFE02C008
+#define MSM8X10_DINO_LPASS_DIGCODEC_N 0xFE02C00C
+#define MSM8X10_DINO_LPASS_DIGCODEC_D 0xFE02C010
+#define MSM8X10_DINO_LPASS_DIGCODEC_CBCR 0xFE02C014
+#define MSM8X10_DINO_LPASS_DIGCODEC_AHB_CBCR 0xFE02C018
+
+/*
+ * There is limitation for the clock root selection from
+ * either MI2S or DIG_CODEC.
+ * If DIG_CODEC root can only provide 9.6MHz clock
+ * to codec while MI2S only can provide
+ * 12.288MHz.
+ */
+enum {
+ DIG_CDC_CLK_SEL_DIG_CODEC,
+ DIG_CDC_CLK_SEL_PRI_MI2S,
+ DIG_CDC_CLK_SEL_SEC_MI2S,
+};
+
+static struct afe_clk_cfg mi2s_rx_clk = {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_OSR_CLK_12_P288_MHZ,
+ Q6AFE_LPASS_CLK_SRC_INTERNAL,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ Q6AFE_LPASS_MODE_BOTH_VALID,
+ 0,
+};
+
+static struct afe_clk_cfg mi2s_tx_clk = {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_OSR_CLK_12_P288_MHZ,
+ Q6AFE_LPASS_CLK_SRC_INTERNAL,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ Q6AFE_LPASS_MODE_BOTH_VALID,
+ 0,
+};
+
+static struct afe_digital_clk_cfg digital_cdc_clk = {
+ AFE_API_VERSION_I2S_CONFIG,
+ 9600000,
+ 5, /* Digital Codec root */
+ 0,
+};
+
+static atomic_t aud_init_rsc_ref;
+
+static int msm8x10_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+static const struct snd_soc_dapm_widget msm8x10_dapm_widgets[] = {
+
+ SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
+ msm8x10_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIC("Handset Mic", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+
+};
+
+/*
+ * This function will be replaced by
+ * afe_set_lpass_internal_digital_codec_clock(port_id, cfg)
+ * in the future after LPASS API fix
+ */
+static int msm_enable_lpass_mclk(void)
+{
+ /* Select the codec root */
+ iowrite32(DIG_CDC_CLK_SEL_DIG_CODEC,
+ ioremap(MSM8X10_DINO_LPASS_AUDIO_CORE_DIG_CODEC_CLK_SEL,
+ 4));
+ /* Div-2 */
+ iowrite32(0x3, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_CFG_RCGR, 4));
+ iowrite32(0x0, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_M, 4));
+ iowrite32(0x0, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_N, 4));
+ iowrite32(0x0, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_D, 4));
+ /* Digital codec clock enable */
+ iowrite32(0x1, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_CBCR, 4));
+ /* AHB clock enable */
+ iowrite32(0x1, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_AHB_CBCR, 4));
+ /* Set the update bit to make the settings go through */
+ iowrite32(0x1, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_CMD_RCGR, 4));
+
+ return 0;
+}
+
+static int msm_enable_mclk_root(u16 port_id, struct afe_digital_clk_cfg *cfg)
+{
+ int ret = 0;
+ /*
+ * msm_enable_lpass_mclk() function call will be replaced by
+ * ret = afe_set_lpass_internal_digital_codec_clock(port_id, cfg)
+ * in the future. Currentlt there is a bug in LPASS plan which
+ * doesn't consider the digital codec clock. It will be fixed soon
+ * in new Q6 image
+ */
+ msm_enable_lpass_mclk();
+ pr_debug("%s(): return = %d\n", __func__, ret);
+ return ret;
+}
+
+static int msm_config_mclk(u16 port_id, struct afe_digital_clk_cfg *cfg)
+{
+ /* Select the codec root */
+ iowrite32(DIG_CDC_CLK_SEL_DIG_CODEC,
+ ioremap(MSM8X10_DINO_LPASS_AUDIO_CORE_DIG_CODEC_CLK_SEL,
+ 4));
+ /* Div-2 */
+ iowrite32(0x3, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_CFG_RCGR, 4));
+ iowrite32(0x0, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_M, 4));
+ iowrite32(0x0, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_N, 4));
+ iowrite32(0x0, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_D, 4));
+ /* Digital codec clock enable */
+ if (cfg->clk_val == 0) {
+ iowrite32(0x0, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_CBCR, 4));
+ pr_debug("%s(line %d)\n", __func__, __LINE__);
+ } else {
+ iowrite32(0x1, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_CBCR, 4));
+ pr_debug("%s(line %d)\n", __func__, __LINE__);
+ }
+ iowrite32(0x1, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_CBCR, 4));
+ /* AHB clock enable */
+ iowrite32(0x1, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_AHB_CBCR, 4));
+ /* Set the update bit to make the settings go through */
+ iowrite32(0x1, ioremap(MSM8X10_DINO_LPASS_DIGCODEC_CMD_RCGR, 4));
+
+ return 0;
+
+}
+
+static int msm_config_mi2s_clk(int enable)
+{
+ int ret = 0;
+ pr_debug("%s(line %d):enable = %x\n", __func__, __LINE__, enable);
+ if (enable) {
+ digital_cdc_clk.clk_val = 9600000;
+ mi2s_rx_clk.clk_val2 = Q6AFE_LPASS_OSR_CLK_12_P288_MHZ;
+ mi2s_rx_clk.clk_val1 = Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ;
+ ret = afe_set_lpass_clock(AFE_PORT_ID_SECONDARY_MI2S_RX,
+ &mi2s_rx_clk);
+ mi2s_tx_clk.clk_val2 = Q6AFE_LPASS_OSR_CLK_12_P288_MHZ;
+ mi2s_tx_clk.clk_val1 = Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ;
+ ret = afe_set_lpass_clock(AFE_PORT_ID_PRIMARY_MI2S_RX,
+ &mi2s_tx_clk);
+ if (ret < 0)
+ pr_err("%s:afe_set_lpass_clock failed\n", __func__);
+
+ } else {
+ digital_cdc_clk.clk_val = 0;
+ mi2s_rx_clk.clk_val2 = Q6AFE_LPASS_OSR_CLK_DISABLE;
+ mi2s_rx_clk.clk_val1 = Q6AFE_LPASS_IBIT_CLK_DISABLE;
+ ret = afe_set_lpass_clock(AFE_PORT_ID_SECONDARY_MI2S_RX,
+ &mi2s_rx_clk);
+ mi2s_tx_clk.clk_val2 = Q6AFE_LPASS_OSR_CLK_DISABLE;
+ mi2s_tx_clk.clk_val1 = Q6AFE_LPASS_IBIT_CLK_DISABLE;
+ ret = afe_set_lpass_clock(AFE_PORT_ID_PRIMARY_MI2S_RX,
+ &mi2s_tx_clk);
+ if (ret < 0)
+ pr_err("%s:afe_set_lpass_clock failed\n", __func__);
+
+ }
+ ret = msm_config_mclk(AFE_PORT_ID_SECONDARY_MI2S_RX, &digital_cdc_clk);
+ return ret;
+}
+
static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
@@ -41,13 +221,22 @@
return 0;
}
-static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
+static int msm_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
{
- pr_info("%s(), dev_name%s\n", __func__, dev_name(rtd->cpu_dai->dev));
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = msm_btsco_rate;
+ channels->min = channels->max = msm_btsco_ch;
+
return 0;
}
-static int msm_snd_hw_params(struct snd_pcm_substream *substream,
+static int msm_mi2s_snd_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
pr_debug("%s(): substream = %s stream = %d\n", __func__,
@@ -55,34 +244,190 @@
return 0;
}
-static int msm_snd_startup(struct snd_pcm_substream *substream)
+static int mi2s_clk_ctl(struct snd_pcm_substream *substream, bool enable)
{
+ int ret = 0;
+ if (enable) {
+ digital_cdc_clk.clk_val = 9600000;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mi2s_rx_clk.clk_val2 = Q6AFE_LPASS_OSR_CLK_12_P288_MHZ;
+ mi2s_rx_clk.clk_val1 = Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ;
+ ret = afe_set_lpass_clock(AFE_PORT_ID_SECONDARY_MI2S_RX,
+ &mi2s_rx_clk);
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mi2s_tx_clk.clk_val2 = Q6AFE_LPASS_OSR_CLK_12_P288_MHZ;
+ mi2s_tx_clk.clk_val1 = Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ;
+ ret = afe_set_lpass_clock(AFE_PORT_ID_PRIMARY_MI2S_RX,
+ &mi2s_tx_clk);
+ } else
+ pr_err("%s:Not valid substream.\n", __func__);
+
+ if (ret < 0)
+ pr_err("%s:afe_set_lpass_clock failed\n", __func__);
+
+ } else {
+ digital_cdc_clk.clk_val = 0;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mi2s_rx_clk.clk_val2 = Q6AFE_LPASS_OSR_CLK_DISABLE;
+ mi2s_rx_clk.clk_val1 = Q6AFE_LPASS_IBIT_CLK_DISABLE;
+ ret = afe_set_lpass_clock(AFE_PORT_ID_SECONDARY_MI2S_RX,
+ &mi2s_rx_clk);
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mi2s_tx_clk.clk_val2 = Q6AFE_LPASS_OSR_CLK_DISABLE;
+ mi2s_tx_clk.clk_val1 = Q6AFE_LPASS_IBIT_CLK_DISABLE;
+ ret = afe_set_lpass_clock(AFE_PORT_ID_PRIMARY_MI2S_RX,
+ &mi2s_tx_clk);
+ } else
+ pr_err("%s:Not valid substream.\n", __func__);
+
+ if (ret < 0)
+ pr_err("%s:afe_set_lpass_clock failed\n", __func__);
+
+ }
+ ret = msm_config_mclk(AFE_PORT_ID_SECONDARY_MI2S_RX, &digital_cdc_clk);
+ return ret;
+}
+
+static int msm8x10_enable_codec_ext_clk(struct snd_soc_codec *codec,
+ int enable, bool dapm)
+{
+ int ret = 0;
+
+ pr_debug("%s: enable = %d codec name %s enable %x\n",
+ __func__, enable, codec->name, enable);
+ if (enable) {
+ digital_cdc_clk.clk_val = 9600000;
+ msm_config_mi2s_clk(1);
+ ret = msm_config_mclk(AFE_PORT_ID_SECONDARY_MI2S_RX,
+ &digital_cdc_clk);
+ msm8x10_wcd_mclk_enable(codec, 1, dapm);
+ } else {
+ msm8x10_wcd_mclk_enable(codec, 0, dapm);
+ ret = msm_config_mclk(AFE_PORT_ID_SECONDARY_MI2S_RX,
+ &digital_cdc_clk);
+ msm_config_mi2s_clk(0);
+ }
+ return ret;
+}
+
+static int msm8x10_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ pr_debug("%s: event = %d\n", __func__, event);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return msm8x10_enable_codec_ext_clk(w->codec, 1, true);
+ case SND_SOC_DAPM_POST_PMD:
+ return msm8x10_enable_codec_ext_clk(w->codec, 0, true);
+ default:
+ return -EINVAL;
+ }
+}
+
+static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ int ret;
+
pr_debug("%s(): substream = %s stream = %d\n", __func__,
substream->name, substream->stream);
+ ret = mi2s_clk_ctl(substream, false);
+ if (ret < 0)
+ pr_err("%s:clock disable failed\n", __func__);
+}
+
+static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ ret = mi2s_clk_ctl(substream, true);
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ pr_err("set fmt cpu dai failed\n");
+
+ return ret;
+}
+
+static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+
+ pr_debug("%s(),dev_name%s\n", __func__, dev_name(cpu_dai->dev));
+
+ pr_debug("%s(): aud_init_rsc_ref counter = %d\n",
+ __func__, atomic_read(&aud_init_rsc_ref));
+ if (atomic_inc_return(&aud_init_rsc_ref) != 1)
+ goto exit;
+
+ snd_soc_dapm_new_controls(dapm, msm8x10_dapm_widgets,
+ ARRAY_SIZE(msm8x10_dapm_widgets));
+
+ snd_soc_dapm_sync(dapm);
+ ret = msm_enable_mclk_root(AFE_PORT_ID_SECONDARY_MI2S_RX,
+ &digital_cdc_clk);
+
+ ret = snd_soc_jack_new(codec, "Headset Jack",
+ SND_JACK_HEADSET, &hs_jack);
+
+ if (ret) {
+ pr_err("%s: Failed to create headset jack\n", __func__);
+ return ret;
+ }
+
+exit:
+ return ret;
+}
+
+static int msm_proxy_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s: msm_proxy_rx_ch =%d\n", __func__, msm_proxy_rx_ch);
+
+ if (channels->max < 2)
+ channels->min = channels->max = 2;
+ channels->min = channels->max = msm_proxy_rx_ch;
+ rate->min = rate->max = 48000;
return 0;
}
-
-static void msm_snd_shutdown(struct snd_pcm_substream *substream)
+static int msm_proxy_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
{
- pr_debug("%s(): substream = %s stream = %d\n", __func__,
- substream->name, substream->stream);
-}
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
-static struct snd_soc_ops msm8x10_be_ops = {
- .startup = msm_snd_startup,
- .hw_params = msm_snd_hw_params,
- .shutdown = msm_snd_shutdown,
+ rate->min = rate->max = 48000;
+ return 0;
+}
+static struct snd_soc_ops msm8x10_mi2s_be_ops = {
+ .startup = msm_mi2s_snd_startup,
+ .hw_params = msm_mi2s_snd_hw_params,
+ .shutdown = msm_mi2s_snd_shutdown,
};
/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link msm8x10_dai[] = {
/* FrontEnd DAI Links */
- {
+ {/* hw:x,0 */
.name = "MSM8X10 Media1",
.stream_name = "MultiMedia1",
.cpu_dai_name = "MultiMedia1",
- .platform_name = "msm-pcm-dsp",
+ .platform_name = "msm-pcm-dsp.0",
.dynamic = 1,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
@@ -93,11 +438,11 @@
.ignore_pmdown_time = 1,
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
},
- {
+ {/* hw:x,1 */
.name = "MSM8X10 Media2",
.stream_name = "MultiMedia2",
.cpu_dai_name = "MultiMedia2",
- .platform_name = "msm-pcm-dsp",
+ .platform_name = "msm-pcm-dsp.0",
.dynamic = 1,
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
@@ -108,31 +453,322 @@
.ignore_pmdown_time = 1,
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
},
+ {/* hw:x,2 */
+ .name = "Circuit-Switch Voice",
+ .stream_name = "CS-Voice",
+ .cpu_dai_name = "CS-VOICE",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_CS_VOICE,
+ },
+ {/* hw:x,3 */
+ .name = "MSM VoIP",
+ .stream_name = "VoIP",
+ .cpu_dai_name = "VoIP",
+ .platform_name = "msm-voip-dsp",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_VOIP,
+ },
+ {/* hw:x,4 */
+ .name = "MSM8X10 LPA",
+ .stream_name = "LPA",
+ .cpu_dai_name = "MultiMedia3",
+ .platform_name = "msm-pcm-lpa",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+ },
+ /* Hostless PCM purpose */
+ {/* hw:x,5 */
+ .name = "Secondary MI2S RX Hostless",
+ .stream_name = "Secondary MI2S_RX Hostless Playback",
+ .cpu_dai_name = "SEC_MI2S_RX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* This dainlink has MI2S support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,6 */
+ .name = "INT_FM Hostless",
+ .stream_name = "INT_FM Hostless",
+ .cpu_dai_name = "INT_FM_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,7 */
+ .name = "MSM AFE-PCM RX",
+ .stream_name = "AFE-PROXY RX",
+ .cpu_dai_name = "msm-dai-q6-dev.241",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ },
+ {/* hw:x,8 */
+ .name = "MSM AFE-PCM TX",
+ .stream_name = "AFE-PROXY TX",
+ .cpu_dai_name = "msm-dai-q6-dev.240",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ },
+ {/* hw:x,9 */
+ .name = "MSM8X10 Compr",
+ .stream_name = "COMPR",
+ .cpu_dai_name = "MultiMedia4",
+ .platform_name = "msm-compr-dsp",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4,
+ },
+ {/* hw:x,10 */
+ .name = "AUXPCM Hostless",
+ .stream_name = "AUXPCM Hostless",
+ .cpu_dai_name = "AUXPCM_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,11 */
+ .name = "Primary MI2S TX Hostless",
+ .stream_name = "Primary MI2S_TX Hostless Capture",
+ .cpu_dai_name = "PRI_MI2S_TX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* This dainlink has MI2S support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,12 */
+ .name = "MSM8x10 LowLatency",
+ .stream_name = "MultiMedia5",
+ .cpu_dai_name = "MultiMedia5",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,
+ },
+ {/* hw:x,13 */
+ .name = "Voice2",
+ .stream_name = "Voice2",
+ .cpu_dai_name = "Voice2",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
/* Backend I2S DAI Links */
{
- .name = LPASS_BE_MI2S_RX,
- .stream_name = "Primary MI2S Playback",
- .cpu_dai_name = "msm-dai-q6-mi2s.0",
- .platform_name = "msm-pcm-routing",
- .codec_name = "msm8x10-wcd-i2c-core.1-000d",
- .codec_dai_name = "msm8x10_wcd_i2s_rx1",
- .no_pcm = 1,
- .be_id = MSM_BACKEND_DAI_MI2S_RX,
- .init = &msm_audrx_init,
- .be_hw_params_fixup = msm_be_hw_params_fixup,
- .ops = &msm8x10_be_ops,
- },
- {
- .name = LPASS_BE_SEC_MI2S_TX,
- .stream_name = "Secondary MI2S Capture",
+ .name = LPASS_BE_SEC_MI2S_RX,
+ .stream_name = "Secondary MI2S Playback",
.cpu_dai_name = "msm-dai-q6-mi2s.1",
.platform_name = "msm-pcm-routing",
- .codec_name = "msm8x10-wcd-i2c-core.1-000d",
+ .codec_name = "msm8x10-wcd-i2c-core.5-000d",
+ .codec_dai_name = "msm8x10_wcd_i2s_rx1",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ .init = &msm_audrx_init,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm8x10_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_MI2S_TX,
+ .stream_name = "Primary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.0",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm8x10-wcd-i2c-core.5-000d",
.codec_dai_name = "msm8x10_wcd_i2s_tx1",
.no_pcm = 1,
- .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX,
.be_hw_params_fixup = msm_be_hw_params_fixup,
- .ops = &msm8x10_be_ops,
+ .ops = &msm8x10_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_INT_BT_SCO_RX,
+ .stream_name = "Internal BT-SCO Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.12288",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ .be_hw_params_fixup = msm_btsco_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_INT_BT_SCO_TX,
+ .stream_name = "Internal BT-SCO Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.12289",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ .be_hw_params_fixup = msm_btsco_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_INT_FM_RX,
+ .stream_name = "Internal FM Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.12292",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_FM_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_INT_FM_TX,
+ .stream_name = "Internal FM Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.12293",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_FM_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_AFE_PCM_RX,
+ .stream_name = "AFE Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.224",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
+ .be_hw_params_fixup = msm_proxy_rx_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_AFE_PCM_TX,
+ .stream_name = "AFE Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.225",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
+ .be_hw_params_fixup = msm_proxy_tx_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Uplink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_TX,
+ .stream_name = "Voice Uplink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32772",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Downlink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_RX,
+ .stream_name = "Voice Downlink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32771",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Music BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE_PLAYBACK_TX,
+ .stream_name = "Voice Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32773",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
},
};
@@ -157,6 +793,10 @@
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
+ ret = snd_soc_of_parse_audio_routing(card,
+ "qcom,audio-routing");
+ if (ret)
+ goto err;
ret = snd_soc_register_card(card);
if (ret) {
@@ -165,6 +805,8 @@
goto err;
}
+ atomic_set(&aud_init_rsc_ref, 0);
+
return 0;
err:
return ret;
diff --git a/sound/soc/msm/qdsp6/q6afe.c b/sound/soc/msm/qdsp6/q6afe.c
index 9558fa4..c4851f3 100644
--- a/sound/soc/msm/qdsp6/q6afe.c
+++ b/sound/soc/msm/qdsp6/q6afe.c
@@ -840,7 +840,7 @@
ret = wait_event_timeout(this_afe.wait,
(atomic_read(&this_afe.state) == 0),
msecs_to_jiffies(TIMEOUT_MS));
- if (ret < 0) {
+ if (!ret) {
pr_err("%s: wait_event timeout\n", __func__);
ret = -EINVAL;
goto fail_cmd;
@@ -918,7 +918,7 @@
ret = wait_event_timeout(this_afe.wait,
(atomic_read(&this_afe.state) == 0),
msecs_to_jiffies(TIMEOUT_MS));
- if (ret < 0) {
+ if (!ret) {
pr_err("%s: wait_event timeout\n", __func__);
ret = -EINVAL;
goto fail_cmd;
@@ -979,7 +979,7 @@
ret = wait_event_timeout(this_afe.wait,
(atomic_read(&this_afe.state) == 0),
msecs_to_jiffies(TIMEOUT_MS));
- if (ret < 0) {
+ if (!ret) {
pr_err("%s: wait_event timeout\n", __func__);
ret = -EINVAL;
goto fail_cmd;
@@ -1731,7 +1731,7 @@
ret = wait_event_timeout(this_afe.wait,
(atomic_read(&this_afe.state) == 0),
msecs_to_jiffies(TIMEOUT_MS));
- if (ret < 0) {
+ if (!ret) {
pr_err("%s: wait_event timeout\n", __func__);
ret = -EINVAL;
goto fail_cmd;
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
index f15f4d1..659d5a2 100644
--- a/sound/soc/msm/qdsp6/q6asm.c
+++ b/sound/soc/msm/qdsp6/q6asm.c
@@ -912,6 +912,7 @@
case ASM_STREAM_CMD_OPEN_WRITE:
case ASM_STREAM_CMD_OPEN_WRITE_V2_1:
case ASM_STREAM_CMD_OPEN_READWRITE:
+ case ASM_STREAM_CMD_OPEN_LOOPBACK:
case ASM_DATA_CMD_MEDIA_FORMAT_UPDATE:
case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
case ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED:
@@ -1852,6 +1853,45 @@
return -EINVAL;
}
+int q6asm_open_loopack(struct audio_client *ac)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_loopback open;
+
+ if ((ac == NULL) || (ac->apr == NULL)) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d]", __func__, ac->session);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_LOOPBACK;
+
+ open.mode_flags = 0;
+ open.src_endpointype = 0;
+ open.sink_endpointype = 0;
+ /* source endpoint : matrix */
+ open.postprocopo_id = get_asm_topology();
+ if (open.postprocopo_id == 0)
+ open.postprocopo_id = DEFAULT_POPP_TOPOLOGY;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("open failed op[0x%x]rc[%d]\n", \
+ open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for OPEN_WRITE rc[%d]\n", rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
int q6asm_run(struct audio_client *ac, uint32_t flags,
uint32_t msw_ts, uint32_t lsw_ts)
{
diff --git a/sound/soc/msm/qdsp6/q6voice.c b/sound/soc/msm/qdsp6/q6voice.c
index bb13695..60f4669 100644
--- a/sound/soc/msm/qdsp6/q6voice.c
+++ b/sound/soc/msm/qdsp6/q6voice.c
@@ -1058,7 +1058,9 @@
}
/* Set encoder properties. */
switch (common.mvs_info.media_type) {
- case VSS_MEDIA_ID_EVRC_MODEM: {
+ case VSS_MEDIA_ID_EVRC_MODEM:
+ case VSS_MEDIA_ID_4GV_NB_MODEM:
+ case VSS_MEDIA_ID_4GV_WB_MODEM: {
struct cvs_set_cdma_enc_minmax_rate_cmd cvs_set_cdma_rate;
pr_debug("Setting EVRC min-max rate\n");
diff --git a/sound/soc/msm/qdsp6v2/audio_acdb.c b/sound/soc/msm/qdsp6v2/audio_acdb.c
index a2e0b87..3b6a415 100644
--- a/sound/soc/msm/qdsp6v2/audio_acdb.c
+++ b/sound/soc/msm/qdsp6v2/audio_acdb.c
@@ -1010,10 +1010,11 @@
case AUDIO_GET_SPEAKER_PROT:
mutex_lock(&acdb_data.acdb_mutex);
/*Indicates calibration was succesfull*/
- if (!acdb_data.spk_prot_cfg.mode) {
+ if (acdb_data.spk_prot_cfg.mode == MSM_SPKR_PROT_CALIBRATED) {
prot_status.r0 = acdb_data.spk_prot_cfg.r0;
prot_status.status = 0;
- } else if (acdb_data.spk_prot_cfg.mode == 1) {
+ } else if (acdb_data.spk_prot_cfg.mode ==
+ MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS) {
/*Call AFE to query the status*/
acdb_spk_status.status = -EINVAL;
acdb_spk_status.r0 = -1;
@@ -1021,7 +1022,8 @@
prot_status.r0 = acdb_spk_status.r0;
prot_status.status = acdb_spk_status.status;
if (!acdb_spk_status.status) {
- acdb_data.spk_prot_cfg.mode = 0;
+ acdb_data.spk_prot_cfg.mode =
+ MSM_SPKR_PROT_CALIBRATED;
acdb_data.spk_prot_cfg.r0 = prot_status.r0;
}
} else {
@@ -1206,7 +1208,7 @@
{
memset(&acdb_data, 0, sizeof(acdb_data));
/*Speaker protection disabled*/
- acdb_data.spk_prot_cfg.mode = -1;
+ acdb_data.spk_prot_cfg.mode = MSM_SPKR_PROT_DISABLED;
mutex_init(&acdb_data.acdb_mutex);
atomic_set(&usage_count, 0);
atomic_set(&acdb_data.valid_adm_custom_top, 1);
diff --git a/sound/soc/msm/qdsp6v2/audio_ocmem.c b/sound/soc/msm/qdsp6v2/audio_ocmem.c
index 08d7277..bedaba0 100644
--- a/sound/soc/msm/qdsp6v2/audio_ocmem.c
+++ b/sound/soc/msm/qdsp6v2/audio_ocmem.c
@@ -243,7 +243,7 @@
struct ocmem_buf *buf = NULL;
struct avcs_cmd_rsp_get_low_power_segments_info_t *lp_segptr;
- pr_debug("%s\n", __func__);
+ pr_debug("%s, %p\n", __func__, &audio_ocmem_lcl);
atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_DEFAULT);
if (audio_ocmem_lcl.lp_memseg_ptr == NULL) {
/* Retrieve low power segments */
@@ -329,6 +329,7 @@
if (ret) {
pr_err("%s: ocmem_map failed\n", __func__);
atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_MAP_FAIL);
+ goto fail_cmd1;
}
wait_event_interruptible(audio_ocmem_lcl.audio_wait,
@@ -372,7 +373,7 @@
pr_err("%s: ocmem_unmap failed, state[%d]\n",
__func__,
atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd;
+ goto fail_cmd1;
}
wait_event_interruptible(audio_ocmem_lcl.audio_wait,
@@ -384,7 +385,7 @@
pr_err("%s: ocmem_shrink failed, state[%d]\n",
__func__,
atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd;
+ goto fail_cmd1;
}
atomic_set(&audio_ocmem_lcl.audio_cond, 1);
clear_bit_pos(audio_ocmem_lcl.audio_state,
@@ -405,7 +406,7 @@
pr_err("%s: ocmem_map failed, state[%d]\n",
__func__,
atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd;
+ goto fail_cmd1;
}
wait_event_interruptible(audio_ocmem_lcl.audio_wait,
(atomic_read(&audio_ocmem_lcl.audio_state) &
@@ -428,7 +429,7 @@
pr_err("%s: ocmem_unmap failed, state[0x%x]\n",
__func__,
atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd;
+ goto fail_cmd1;
}
wait_event_interruptible(
audio_ocmem_lcl.audio_wait,
@@ -446,14 +447,16 @@
pr_err("%s: ocmem_shrink failed, state[0x%x]\n",
__func__,
atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd;
+ goto fail_cmd1;
}
clear_bit_pos(audio_ocmem_lcl.audio_state,
OCMEM_STATE_SHRINK);
}
- pr_debug("%s: calling ocmem free\n", __func__);
+ pr_debug("%s: calling ocmem free, state:0x%x\n",
+ __func__,
+ atomic_read(&audio_ocmem_lcl.audio_state));
ret = ocmem_free(OCMEM_LP_AUDIO, audio_ocmem_lcl.buf);
if (ret == -EAGAIN) {
pr_debug("%s: received EAGAIN\n", __func__);
@@ -466,7 +469,7 @@
pr_err("%s: ocmem_shrink failed, state[0x%x]\n",
__func__,
atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd;
+ goto fail_cmd1;
}
pr_debug("calling free after EAGAIN");
ret = ocmem_free(OCMEM_LP_AUDIO,
@@ -474,19 +477,19 @@
if (ret) {
pr_err("%s: ocmem_free failed\n",
__func__);
- goto fail_cmd;
+ goto fail_cmd2;
}
} else {
pr_debug("%s: shrink callback already processed\n",
__func__);
- goto fail_cmd;
+ goto fail_cmd1;
}
} else if (ret) {
pr_err("%s: ocmem_free failed, state[0x%x], ret:%d\n",
__func__,
atomic_read(&audio_ocmem_lcl.audio_state),
ret);
- goto fail_cmd;
+ goto fail_cmd2;
}
pr_debug("%s: ocmem_free success\n", __func__);
/* Fall through */
@@ -508,6 +511,14 @@
mutex_unlock(&audio_ocmem_lcl.state_process_lock);
}
ret = 0;
+ goto fail_cmd;
+
+fail_cmd1:
+ ret = ocmem_free(OCMEM_LP_AUDIO, audio_ocmem_lcl.buf);
+ if (ret)
+ pr_err("%s: ocmem_free failed\n", __func__);
+fail_cmd2:
+ mutex_unlock(&audio_ocmem_lcl.state_process_lock);
fail_cmd:
pr_debug("%s: exit\n", __func__);
audio_ocmem_lcl.audio_ocmem_running = false;
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index 9359ed7..4a20af1 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -29,6 +29,7 @@
#include <sound/pcm_params.h>
#include <asm/dma.h>
#include <linux/dma-mapping.h>
+#include <linux/msm_audio_ion.h>
#include <sound/timer.h>
@@ -39,7 +40,10 @@
#define COMPRE_CAPTURE_NUM_PERIODS 16
/* Allocate the worst case frame size for compressed audio */
#define COMPRE_CAPTURE_HEADER_SIZE (sizeof(struct snd_compr_audio_info))
-#define COMPRE_CAPTURE_MAX_FRAME_SIZE (6144)
+/* Changing period size to 4032. 4032 will make sure COMPRE_CAPTURE_PERIOD_SIZE
+ * is 4096 with meta data size of 64 and MAX_NUM_FRAMES_PER_BUFFER 1
+ */
+#define COMPRE_CAPTURE_MAX_FRAME_SIZE (4032)
#define COMPRE_CAPTURE_PERIOD_SIZE ((COMPRE_CAPTURE_MAX_FRAME_SIZE + \
COMPRE_CAPTURE_HEADER_SIZE) * \
MAX_NUM_FRAMES_PER_BUFFER)
@@ -121,7 +125,6 @@
int i = 0;
int time_stamp_flag = 0;
int buffer_length = 0;
- int stop_playback = 0;
pr_debug("%s opcode =%08x\n", __func__, opcode);
switch (opcode) {
@@ -146,15 +149,9 @@
/*
* check for underrun
*/
- snd_pcm_stream_lock_irq(substream);
- if (snd_pcm_playback_empty(substream)) {
+ if (runtime->status->hw_ptr >= runtime->control->appl_ptr) {
+ pr_info("render stopped");
runtime->render_flag |= SNDRV_RENDER_STOPPED;
- stop_playback = 1;
- }
- snd_pcm_stream_unlock_irq(substream);
-
- if (stop_playback) {
- pr_err("%s empty buffer, stop writes\n", __func__);
break;
}
@@ -226,7 +223,7 @@
prtd->pcm_irq_pos);
memcpy(prtd->audio_client->port[OUT].buf->data +
- prtd->pcm_irq_pos, (ptrmem + 2),
+ prtd->pcm_irq_pos, (ptrmem + READDONE_IDX_SIZE),
COMPRE_CAPTURE_HEADER_SIZE);
pr_debug("buf = %p, updated data = 0x%X, *data = %p\n",
prtd->audio_client->port[OUT].buf,
@@ -235,9 +232,10 @@
prtd->audio_client->port[OUT].buf->data);
if (!atomic_read(&prtd->start))
break;
- pr_debug("frame size=%d, buffer = 0x%X\n", ptrmem[2],
- ptrmem[1]);
- if (ptrmem[2] > COMPRE_CAPTURE_MAX_FRAME_SIZE) {
+ pr_debug("frame size=%d, buffer = 0x%X\n",
+ ptrmem[READDONE_IDX_SIZE],
+ ptrmem[READDONE_IDX_BUFADD_LSW]);
+ if (ptrmem[READDONE_IDX_SIZE] > COMPRE_CAPTURE_MAX_FRAME_SIZE) {
pr_err("Frame length exceeded the max length");
break;
}
@@ -488,11 +486,6 @@
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
prtd->pcm_irq_pos = 0;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (!atomic_cmpxchg(&compressed_audio.audio_ocmem_req,
- 0, 1))
- audio_ocmem_process_req(AUDIO, true);
- }
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
switch (compr->info.codec_param.codec.id) {
@@ -526,12 +519,14 @@
}
}
atomic_set(&prtd->start, 0);
+ runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
atomic_set(&prtd->start, 0);
+ runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
break;
default:
ret = -EINVAL;
@@ -546,7 +541,7 @@
{
pr_debug("%s\n", __func__);
/* MP3 Block */
- compr->info.compr_cap.num_codecs = 4;
+ compr->info.compr_cap.num_codecs = 5;
compr->info.compr_cap.min_fragment_size = runtime->hw.period_bytes_min;
compr->info.compr_cap.max_fragment_size = runtime->hw.period_bytes_max;
compr->info.compr_cap.min_fragments = runtime->hw.periods_min;
@@ -555,6 +550,7 @@
compr->info.compr_cap.codecs[1] = SND_AUDIOCODEC_AAC;
compr->info.compr_cap.codecs[2] = SND_AUDIOCODEC_AC3;
compr->info.compr_cap.codecs[3] = SND_AUDIOCODEC_EAC3;
+ compr->info.compr_cap.codecs[4] = SND_AUDIOCODEC_AMRWB;
/* Add new codecs here */
}
@@ -613,19 +609,33 @@
populate_codec_list(compr, runtime);
runtime->private_data = compr;
atomic_set(&prtd->eos, 0);
- atomic_set(&compressed_audio.audio_ocmem_req, 0);
compressed_audio.prtd = &compr->prtd;
-
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (!atomic_cmpxchg(&compressed_audio.audio_ocmem_req, 0, 1))
+ audio_ocmem_process_req(AUDIO, true);
+ else
+ atomic_inc(&compressed_audio.audio_ocmem_req);
+ pr_debug("%s: req: %d\n", __func__,
+ atomic_read(&compressed_audio.audio_ocmem_req));
+ }
return 0;
}
int compressed_set_volume(unsigned volume)
{
int rc = 0;
+ int avg_vol = 0;
if (compressed_audio.prtd && compressed_audio.prtd->audio_client) {
- rc = q6asm_set_lrgain(compressed_audio.prtd->audio_client,
- (volume >> 16) & 0xFFFF,
- volume & 0xFFFF);
+ if (compressed_audio.prtd->channel_mode > 2) {
+ avg_vol = (((volume >> 16) & 0xFFFF) +
+ (volume & 0xFFFF)) / 2;
+ rc = q6asm_set_volume(
+ compressed_audio.prtd->audio_client, avg_vol);
+ } else {
+ rc = q6asm_set_lrgain(
+ compressed_audio.prtd->audio_client,
+ (volume >> 16) & 0xFFFF, volume & 0xFFFF);
+ }
if (rc < 0) {
pr_err("%s: Send Volume command failed rc=%d\n",
__func__, rc);
@@ -647,8 +657,14 @@
dir = IN;
atomic_set(&prtd->pending_buffer, 0);
- if (atomic_cmpxchg(&compressed_audio.audio_ocmem_req, 1, 0))
+
+ if (atomic_read(&compressed_audio.audio_ocmem_req) > 1)
+ atomic_dec(&compressed_audio.audio_ocmem_req);
+ else if (atomic_cmpxchg(&compressed_audio.audio_ocmem_req, 1, 0))
audio_ocmem_process_req(AUDIO, false);
+
+ pr_debug("%s: req: %d\n", __func__,
+ atomic_read(&compressed_audio.audio_ocmem_req));
prtd->pcm_irq_pos = 0;
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
compressed_audio.prtd = NULL;
@@ -723,25 +739,22 @@
static int msm_compr_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
- int result = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct compr_audio *compr = runtime->private_data;
- struct msm_audio *prtd = &compr->prtd;
+ struct msm_audio *prtd = runtime->private_data;
+ struct audio_client *ac = prtd->audio_client;
+ struct audio_port_data *apd = ac->port;
+ struct audio_buffer *ab;
+ int dir = -1;
- pr_debug("%s\n", __func__);
prtd->mmap_flag = 1;
runtime->render_flag = SNDRV_NON_DMA_MODE;
- if (runtime->dma_addr && runtime->dma_bytes) {
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- result = remap_pfn_range(vma, vma->vm_start,
- runtime->dma_addr >> PAGE_SHIFT,
- runtime->dma_bytes,
- vma->vm_page_prot);
- } else {
- pr_err("Physical address or size of buf is NULL");
- return -EINVAL;
- }
- return result;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+ ab = &(apd[dir].buf[0]);
+
+ return msm_audio_ion_mmap(ab, vma);
}
static int msm_compr_hw_params(struct snd_pcm_substream *substream,
@@ -790,6 +803,11 @@
prtd->audio_client->perf_mode,
prtd->session_id,
substream->stream);
+ /* the number of channels are required to call volume api
+ accoridngly. So, get channels from hw params */
+ if ((params_channels(params) > 0) &&
+ (params_periods(params) <= runtime->hw.channels_max))
+ prtd->channel_mode = params_channels(params);
ret = compressed_set_volume(0);
if (ret < 0)
@@ -1043,7 +1061,7 @@
}
rc = wait_event_timeout(the_locks.flush_wait,
prtd->cmd_ack, 5 * HZ);
- if (rc < 0)
+ if (!rc)
pr_err("Flush cmd timeout\n");
prtd->pcm_irq_pos = 0;
}
@@ -1176,6 +1194,8 @@
dev_info(&pdev->dev, "%s: dev name %s\n",
__func__, dev_name(&pdev->dev));
+
+ atomic_set(&compressed_audio.audio_ocmem_req, 0);
return snd_soc_register_platform(&pdev->dev,
&msm_soc_platform);
}
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index 8bb3eaf..b07e91e 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -64,8 +64,6 @@
struct msm_dai_q6_mi2s_dai_data {
struct msm_dai_q6_mi2s_dai_config tx_dai;
struct msm_dai_q6_mi2s_dai_config rx_dai;
- struct snd_pcm_hw_constraint_list rate_constraint;
- struct snd_pcm_hw_constraint_list bitwidth_constraint;
};
/* MI2S format field for AFE_PORT_CMD_I2S_CONFIG command
@@ -190,8 +188,8 @@
if (dai->id == AFE_PORT_ID_PRIMARY_PCM_RX
|| dai->id == AFE_PORT_ID_PRIMARY_PCM_TX) {
- rx_port = PCM_RX;
- tx_port = PCM_TX;
+ rx_port = AFE_PORT_ID_PRIMARY_PCM_RX;
+ tx_port = AFE_PORT_ID_PRIMARY_PCM_TX;
} else if (dai->id == AFE_PORT_ID_SECONDARY_PCM_RX
|| dai->id == AFE_PORT_ID_SECONDARY_PCM_TX) {
rx_port = AFE_PORT_ID_SECONDARY_PCM_RX;
@@ -295,8 +293,8 @@
if (dai->id == AFE_PORT_ID_PRIMARY_PCM_RX ||
dai->id == AFE_PORT_ID_PRIMARY_PCM_TX) {
- rx_port = PCM_RX;
- tx_port = PCM_TX;
+ rx_port = AFE_PORT_ID_PRIMARY_PCM_RX;
+ tx_port = AFE_PORT_ID_PRIMARY_PCM_TX;
} else if (dai->id == AFE_PORT_ID_SECONDARY_PCM_RX ||
dai->id == AFE_PORT_ID_SECONDARY_PCM_TX) {
rx_port = AFE_PORT_ID_SECONDARY_PCM_RX;
@@ -420,8 +418,8 @@
if (dai->id == AFE_PORT_ID_PRIMARY_PCM_RX ||
dai->id == AFE_PORT_ID_PRIMARY_PCM_TX) {
- rx_port = PCM_RX;
- tx_port = PCM_TX;
+ rx_port = AFE_PORT_ID_PRIMARY_PCM_RX;
+ tx_port = AFE_PORT_ID_PRIMARY_PCM_TX;
} else if (dai->id == AFE_PORT_ID_SECONDARY_PCM_RX ||
dai->id == AFE_PORT_ID_SECONDARY_PCM_TX) {
rx_port = AFE_PORT_ID_SECONDARY_PCM_RX;
@@ -1358,16 +1356,25 @@
SOC_ENUM_EXT("PRI MI2S RX Format", mi2s_config_enum[0],
msm_dai_q6_mi2s_format_get,
msm_dai_q6_mi2s_format_put),
- SOC_ENUM_EXT("SEC RX Format", mi2s_config_enum[0],
+ SOC_ENUM_EXT("SEC MI2S RX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("TERT MI2S RX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("QUAT MI2S RX Format", mi2s_config_enum[0],
msm_dai_q6_mi2s_format_get,
msm_dai_q6_mi2s_format_put),
SOC_ENUM_EXT("PRI MI2S TX Format", mi2s_config_enum[0],
msm_dai_q6_mi2s_format_get,
msm_dai_q6_mi2s_format_put),
- SOC_ENUM_EXT("SEC MI2S RX Format", mi2s_config_enum[0],
+ SOC_ENUM_EXT("SEC MI2S TX Format", mi2s_config_enum[0],
msm_dai_q6_mi2s_format_get,
msm_dai_q6_mi2s_format_put),
- SOC_ENUM_EXT("SEC MI2S TX Format", mi2s_config_enum[0],
+ SOC_ENUM_EXT("TERT MI2S TX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("QUAT MI2S TX Format", mi2s_config_enum[0],
msm_dai_q6_mi2s_format_get,
msm_dai_q6_mi2s_format_put),
};
@@ -1384,6 +1391,10 @@
if (!strncmp(dai->name, "msm-dai-q6-mi2s.0", 17))
ctrl = &mi2s_config_controls[0];
if (!strncmp(dai->name, "msm-dai-q6-mi2s.1", 17))
+ ctrl = &mi2s_config_controls[1];
+ if (!strncmp(dai->name, "msm-dai-q6-mi2s.2", 17))
+ ctrl = &mi2s_config_controls[2];
+ if (!strncmp(dai->name, "msm-dai-q6-mi2s.3", 17))
ctrl = &mi2s_config_controls[3];
}
@@ -1402,9 +1413,13 @@
ctrl = NULL;
if (mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode) {
if (!strncmp(dai->name, "msm-dai-q6-mi2s.0", 17))
- ctrl = &mi2s_config_controls[2];
- if (!strncmp(dai->name, "msm-dai-q6-mi2s.1", 17))
ctrl = &mi2s_config_controls[4];
+ if (!strncmp(dai->name, "msm-dai-q6-mi2s.1", 17))
+ ctrl = &mi2s_config_controls[5];
+ if (!strncmp(dai->name, "msm-dai-q6-mi2s.2", 17))
+ ctrl = &mi2s_config_controls[6];
+ if (!strncmp(dai->name, "msm-dai-q6-mi2s.3", 17))
+ ctrl = &mi2s_config_controls[7];
}
if (ctrl) {
@@ -1455,20 +1470,6 @@
static int msm_dai_q6_mi2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
- dev_get_drvdata(dai->dev);
-
- dev_dbg(dai->dev, "%s: cnst list %p\n", __func__,
- mi2s_dai_data->rate_constraint.list);
-
- if (mi2s_dai_data->rate_constraint.list) {
- snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &mi2s_dai_data->rate_constraint);
- snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- &mi2s_dai_data->bitwidth_constraint);
- }
return 0;
}
@@ -1482,7 +1483,7 @@
case SNDRV_PCM_STREAM_PLAYBACK:
switch (mi2s_id) {
case MSM_PRIM_MI2S:
- *port_id = MI2S_RX;
+ *port_id = AFE_PORT_ID_PRIMARY_MI2S_RX;
break;
case MSM_SEC_MI2S:
*port_id = AFE_PORT_ID_SECONDARY_MI2S_RX;
@@ -1502,7 +1503,7 @@
case SNDRV_PCM_STREAM_CAPTURE:
switch (mi2s_id) {
case MSM_PRIM_MI2S:
- *port_id = MI2S_TX;
+ *port_id = AFE_PORT_ID_PRIMARY_MI2S_TX;
break;
case MSM_SEC_MI2S:
*port_id = AFE_PORT_ID_SECONDARY_MI2S_TX;
@@ -1642,11 +1643,25 @@
dai_data->port_config.i2s.i2s_cfg_minor_version =
AFE_API_VERSION_I2S_CONFIG;
dai_data->port_config.i2s.sample_rate = dai_data->rate;
- if (!mi2s_dai_data->rate_constraint.list) {
- mi2s_dai_data->rate_constraint.list = &dai_data->rate;
- mi2s_dai_data->bitwidth_constraint.list = &dai_data->bitwidth;
+ if (test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) ||
+ test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask)) {
+ if ((mi2s_dai_data->tx_dai.mi2s_dai_data.rate !=
+ mi2s_dai_data->rx_dai.mi2s_dai_data.rate) ||
+ (mi2s_dai_data->rx_dai.mi2s_dai_data.bitwidth !=
+ mi2s_dai_data->tx_dai.mi2s_dai_data.bitwidth)) {
+ dev_err(dai->dev, "%s: Error mismatch in HW params\n"
+ "Tx sample_rate = %u bit_width = %hu\n"
+ "Rx sample_rate = %u bit_width = %hu\n"
+ , __func__,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.rate,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.bitwidth,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.rate,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.bitwidth);
+ return -EINVAL;
+ }
}
-
dev_dbg(dai->dev, "%s: dai id %d dai_data->channels = %d\n"
"sample_rate = %u i2s_cfg_minor_version = %#x\n"
"bit_width = %hu channel_mode = %#x mono_stereo = %#x\n"
@@ -1722,14 +1737,6 @@
dev_err(dai->dev, "fail to close AFE port\n");
clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
}
-
- if (!test_bit(STATUS_PORT_STARTED,
- mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) &&
- !test_bit(STATUS_PORT_STARTED,
- mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask)) {
- mi2s_dai_data->rate_constraint.list = NULL;
- mi2s_dai_data->bitwidth_constraint.list = NULL;
- }
}
static struct snd_soc_dai_ops msm_dai_q6_mi2s_ops = {
@@ -1985,8 +1992,6 @@
if (IS_ERR_VALUE(rc))
goto free_dai;
- dai_data->rate_constraint.count = 1;
- dai_data->bitwidth_constraint.count = 1;
rc = snd_soc_register_dai(&pdev->dev, mi2s_dai);
if (IS_ERR_VALUE(rc))
goto err_register;
diff --git a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c
index f77ec0f..9ace410 100644
--- a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c
+++ b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c
@@ -437,7 +437,8 @@
if (device == DEVICE_OUT_ALL) {
port_id = PRIMARY_I2S_RX | SLIMBUS_0_RX | HDMI_RX |
INT_BT_SCO_RX | INT_FM_RX |
- RT_PROXY_PORT_001_RX | PCM_RX |
+ RT_PROXY_PORT_001_RX |
+ AFE_PORT_ID_PRIMARY_PCM_RX |
MI2S_RX | SECONDARY_I2S_RX |
SLIMBUS_1_RX | SLIMBUS_4_RX | SLIMBUS_3_RX |
AFE_PORT_ID_SECONDARY_MI2S_RX;
@@ -640,7 +641,7 @@
DOLBY_PARAM_PAYLOAD_SIZE * sizeof(uint32_t);
int port_id = dolby_dap_params_states.port_id;
if (port_id == DOLBY_INVALID_PORT_ID) {
- pr_err("%s, port_id not set, returning error", __func__);
+ pr_debug("%s, port_id not set, returning error", __func__);
ucontrol->value.integer.value[0] = 0;
return -EINVAL;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-lsm-client.c b/sound/soc/msm/qdsp6v2/msm-lsm-client.c
index 363fb15..f76d455 100644
--- a/sound/soc/msm/qdsp6v2/msm-lsm-client.c
+++ b/sound/soc/msm/qdsp6v2/msm-lsm-client.c
@@ -117,8 +117,8 @@
if (copy_from_user(prtd->lsm_client->sound_model.data,
snd_model.data, snd_model.data_size)) {
- pr_err("%s: copy from user data failed size %d\n",
- __func__, snd_model.data_size);
+ pr_err("%s: copy from user data failed data %p size %d\n",
+ __func__, snd_model.data, snd_model.data_size);
rc = -EFAULT;
break;
}
@@ -128,9 +128,12 @@
snd_model.min_keyw_confidence,
snd_model.min_user_confidence,
snd_model.detect_failure);
- if (rc < 0)
+ if (rc < 0) {
pr_err("%s: q6lsm_register_sound_model failed =%d\n",
__func__, rc);
+ q6lsm_snd_model_buf_free(prtd->lsm_client);
+ }
+
break;
case SNDRV_LSM_DEREG_SND_MODEL:
@@ -213,11 +216,10 @@
pr_debug("%s: Stopping LSM client session\n", __func__);
if (prtd->lsm_client->started) {
ret = q6lsm_stop(prtd->lsm_client, true);
- if (!ret) {
- prtd->lsm_client->started = false;
- pr_debug("%s: LSM client session stopped\n",
- __func__);
- }
+ if (!ret)
+ pr_debug("%s: LSM client session stopped %d\n",
+ __func__, ret);
+ prtd->lsm_client->started = false;
}
break;
@@ -258,10 +260,10 @@
}
ret = q6lsm_open(prtd->lsm_client);
if (ret < 0) {
- pr_err("%s: lsm out open failed\n", __func__);
+ pr_err("%s: lsm open failed, %d\n", __func__, ret);
q6lsm_client_free(prtd->lsm_client);
kfree(prtd);
- return -ENOMEM;
+ return ret;
}
pr_debug("%s: Session ID %d\n", __func__, prtd->lsm_client->session);
diff --git a/sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c
index a078042..4297ddb 100644
--- a/sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c
@@ -454,7 +454,7 @@
__func__, atomic_read(&prtd->out_count));
ret = wait_event_timeout(the_locks.write_wait,
(atomic_read(&prtd->out_count)), 5 * HZ);
- if (ret < 0) {
+ if (!ret) {
pr_err("%s: wait_event_timeout failed\n", __func__);
goto fail;
}
@@ -507,7 +507,7 @@
dir = IN;
ret = wait_event_timeout(the_locks.eos_wait,
prtd->cmd_ack, 5 * HZ);
- if (ret < 0)
+ if (!ret)
pr_err("%s: CMD_EOS failed\n", __func__);
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
q6asm_audio_client_buf_free_contiguous(dir,
@@ -546,7 +546,7 @@
ret = wait_event_timeout(the_locks.read_wait,
(atomic_read(&prtd->in_count)), 5 * HZ);
- if (ret < 0) {
+ if (!ret) {
pr_debug("%s: wait_event_timeout failed\n", __func__);
goto fail;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
index 2a64ae2..c4b44fe 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
@@ -20,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
+#include <linux/msm_audio_ion.h>
#include <sound/core.h>
#include <sound/soc.h>
@@ -517,21 +518,21 @@
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct pcm_afe_info *prtd = runtime->private_data;
- int result = 0;
+ struct afe_audio_client *ac = prtd->audio_client;
+ struct afe_audio_port_data *apd = ac->port;
+ struct afe_audio_buffer *ab;
+ int dir = -1;
pr_debug("%s\n", __func__);
prtd->mmap_flag = 1;
- if (runtime->dma_addr && runtime->dma_bytes) {
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- result = remap_pfn_range(vma, vma->vm_start,
- runtime->dma_addr >> PAGE_SHIFT,
- runtime->dma_bytes,
- vma->vm_page_prot);
- } else {
- pr_err("Physical address or size of buf is NULL");
- return -EINVAL;
- }
- return result;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+ ab = &(apd[dir].buf[0]);
+
+ return msm_audio_ion_mmap((struct audio_buffer *)ab, vma);
}
static int msm_afe_trigger(struct snd_pcm_substream *substream, int cmd)
{
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
index a163f6a..4459bc8 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
@@ -29,10 +29,13 @@
#include <linux/dma-mapping.h>
#include <linux/of_device.h>
+#include <linux/msm_audio_ion.h>
+
#include <sound/compress_params.h>
#include <sound/compress_offload.h>
#include <sound/compress_driver.h>
#include <sound/timer.h>
+#include <sound/pcm_params.h>
#include "msm-pcm-q6-v2.h"
#include "msm-pcm-routing-v2.h"
@@ -55,9 +58,10 @@
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
- .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT,
+ .rates = SNDRV_PCM_RATE_8000_192000 |
+ SNDRV_PCM_RATE_KNOT,
.rate_min = 8000,
- .rate_max = 48000,
+ .rate_max = 192000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 1024 * 1024,
@@ -70,7 +74,8 @@
/* Conventional and unconventional sample rate supported */
static unsigned int supported_sample_rates[] = {
- 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
+ 96000, 192000
};
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
@@ -277,19 +282,7 @@
static int msm_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
struct msm_audio *prtd;
- struct asm_softpause_params softpause = {
- .enable = SOFT_PAUSE_ENABLE,
- .period = SOFT_PAUSE_PERIOD,
- .step = SOFT_PAUSE_STEP,
- .rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
- };
- struct asm_softvolume_params softvol = {
- .period = SOFT_VOLUME_PERIOD,
- .step = SOFT_VOLUME_STEP,
- .rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
- };
int ret = 0;
pr_debug("%s\n", __func__);
@@ -307,31 +300,9 @@
kfree(prtd);
return -ENOMEM;
}
- prtd->audio_client->perf_mode = false;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
- if (ret < 0) {
- pr_err("%s: pcm out open failed\n", __func__);
- q6asm_audio_client_free(prtd->audio_client);
- kfree(prtd);
- return -ENOMEM;
- }
- ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
- if (ret < 0) {
- pr_err("%s: Set IO mode failed\n", __func__);
- q6asm_audio_client_free(prtd->audio_client);
- kfree(prtd);
- return -ENOMEM;
- }
- }
/* Capture path */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
return -EPERM;
- pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
- prtd->session_id = prtd->audio_client->session;
- msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
- prtd->audio_client->perf_mode,
- prtd->session_id, substream->stream);
ret = snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
@@ -350,15 +321,6 @@
atomic_set(&lpa_audio.audio_ocmem_req, 0);
runtime->private_data = prtd;
lpa_audio.prtd = prtd;
- lpa_set_volume(0);
- ret = q6asm_set_softpause(lpa_audio.prtd->audio_client, &softpause);
- if (ret < 0)
- pr_err("%s: Send SoftPause Param failed ret=%d\n",
- __func__, ret);
- ret = q6asm_set_softvolume(lpa_audio.prtd->audio_client, &softvol);
- if (ret < 0)
- pr_err("%s: Send SoftVolume Param failed ret=%d\n",
- __func__, ret);
return 0;
}
@@ -402,27 +364,29 @@
pr_debug("%s\n", __func__);
rc = wait_event_timeout(the_locks.eos_wait,
prtd->cmd_ack, 5 * HZ);
- if (rc < 0)
+ if (!rc)
pr_err("EOS cmd timeout\n");
prtd->pcm_irq_pos = 0;
}
- dir = IN;
- atomic_set(&prtd->pending_buffer, 0);
+ if (prtd->audio_client) {
+ dir = IN;
+ atomic_set(&prtd->pending_buffer, 0);
- if (atomic_cmpxchg(&lpa_audio.audio_ocmem_req, 1, 0))
- audio_ocmem_process_req(AUDIO, false);
- lpa_audio.prtd = NULL;
- q6asm_cmd(prtd->audio_client, CMD_CLOSE);
- q6asm_audio_client_buf_free_contiguous(dir,
+ if (atomic_cmpxchg(&lpa_audio.audio_ocmem_req, 1, 0))
+ audio_ocmem_process_req(AUDIO, false);
+ lpa_audio.prtd = NULL;
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
prtd->audio_client);
- atomic_set(&prtd->stop, 1);
- pr_debug("%s\n", __func__);
+ atomic_set(&prtd->stop, 1);
+ q6asm_audio_client_free(prtd->audio_client);
+ pr_debug("%s\n", __func__);
+ }
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
SNDRV_PCM_STREAM_PLAYBACK);
pr_debug("%s\n", __func__);
- q6asm_audio_client_free(prtd->audio_client);
kfree(prtd);
return 0;
@@ -459,24 +423,22 @@
static int msm_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
- int result = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
struct msm_audio *prtd = runtime->private_data;
+ struct audio_client *ac = prtd->audio_client;
+ struct audio_port_data *apd = ac->port;
+ struct audio_buffer *ab;
+ int dir = -1;
- pr_debug("%s\n", __func__);
prtd->mmap_flag = 1;
- if (runtime->dma_addr && runtime->dma_bytes) {
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- result = remap_pfn_range(vma, vma->vm_start,
- runtime->dma_addr >> PAGE_SHIFT,
- runtime->dma_bytes,
- vma->vm_page_prot);
- } else {
- pr_err("Physical address or size of buf is NULL");
- return -EINVAL;
- }
- return result;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+ ab = &(apd[dir].buf[0]);
+
+ return msm_audio_ion_mmap(ab, vma);
}
static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -484,9 +446,59 @@
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct msm_audio *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
struct audio_buffer *buf;
+ uint16_t bits_per_sample = 16;
int dir, ret;
+ struct asm_softpause_params softpause = {
+ .enable = SOFT_PAUSE_ENABLE,
+ .period = SOFT_PAUSE_PERIOD,
+ .step = SOFT_PAUSE_STEP,
+ .rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
+ };
+ struct asm_softvolume_params softvol = {
+ .period = SOFT_VOLUME_PERIOD,
+ .step = SOFT_VOLUME_STEP,
+ .rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
+ };
+
+ prtd->audio_client->perf_mode = false;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (params_format(params) == SNDRV_PCM_FORMAT_S24_LE)
+ bits_per_sample = 24;
+ ret = q6asm_open_write_v2(prtd->audio_client,
+ FORMAT_LINEAR_PCM, bits_per_sample);
+ if (ret < 0) {
+ pr_err("%s: pcm out open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ prtd->audio_client = NULL;
+ return -ENOMEM;
+ }
+ ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
+ if (ret < 0) {
+ pr_err("%s: Set IO mode failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ prtd->audio_client = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
+ prtd->session_id = prtd->audio_client->session;
+ msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->audio_client->perf_mode,
+ prtd->session_id, substream->stream);
+
+ lpa_set_volume(0);
+ ret = q6asm_set_softpause(lpa_audio.prtd->audio_client, &softpause);
+ if (ret < 0)
+ pr_err("%s: Send SoftPause Param failed ret=%d\n",
+ __func__, ret);
+ ret = q6asm_set_softvolume(lpa_audio.prtd->audio_client, &softvol);
+ if (ret < 0)
+ pr_err("%s: Send SoftVolume Param failed ret=%d\n",
+ __func__, ret);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
dir = IN;
@@ -561,7 +573,7 @@
pr_err("%s: flush cmd failed rc=%d\n", __func__, rc);
rc = wait_event_timeout(the_locks.eos_wait,
prtd->cmd_ack, 5 * HZ);
- if (rc < 0)
+ if (!rc)
pr_err("Flush cmd timeout\n");
prtd->pcm_irq_pos = 0;
break;
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
index 717e63b..461fca4 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -27,6 +27,7 @@
#include <sound/control.h>
#include <asm/dma.h>
#include <linux/dma-mapping.h>
+#include <linux/msm_audio_ion.h>
#include <linux/of_device.h>
#include <sound/pcm_params.h>
@@ -294,7 +295,7 @@
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
pr_debug("%s: Trigger start\n", __func__);
- q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
break;
case SNDRV_PCM_TRIGGER_STOP:
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
@@ -302,12 +303,12 @@
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
break;
prtd->cmd_ack = 0;
- q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
- q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
atomic_set(&prtd->start, 0);
break;
default:
@@ -409,7 +410,7 @@
__func__, atomic_read(&prtd->out_count));
ret = wait_event_timeout(the_locks.write_wait,
(atomic_read(&prtd->out_count)), 5 * HZ);
- if (ret < 0) {
+ if (!ret) {
pr_err("%s: wait_event_timeout failed\n", __func__);
goto fail;
}
@@ -420,6 +421,12 @@
}
data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size, &idx);
+ if (size < fbytes) {
+ pr_err("%s: size mismatch error size %d fbytes %d\n",
+ __func__ , size , fbytes);
+ ret = -EFAULT;
+ goto fail;
+ }
bufptr = data;
if (bufptr) {
pr_debug("%s:fbytes =%d: xfer=%d size=%d\n",
@@ -463,7 +470,7 @@
dir = IN;
ret = wait_event_timeout(the_locks.eos_wait,
prtd->cmd_ack, 5 * HZ);
- if (ret < 0)
+ if (!ret)
pr_err("%s: CMD_EOS failed\n", __func__);
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
q6asm_audio_client_buf_free_contiguous(dir,
@@ -502,7 +509,7 @@
ret = wait_event_timeout(the_locks.read_wait,
(atomic_read(&prtd->in_count)), 5 * HZ);
- if (ret < 0) {
+ if (!ret) {
pr_debug("%s: wait_event_timeout failed\n", __func__);
goto fail;
}
@@ -622,25 +629,22 @@
static int msm_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
- int result = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
struct msm_audio *prtd = runtime->private_data;
+ struct audio_client *ac = prtd->audio_client;
+ struct audio_port_data *apd = ac->port;
+ struct audio_buffer *ab;
+ int dir = -1;
- pr_debug("%s\n", __func__);
prtd->mmap_flag = 1;
- if (runtime->dma_addr && runtime->dma_bytes) {
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- result = remap_pfn_range(vma, vma->vm_start,
- runtime->dma_addr >> PAGE_SHIFT,
- runtime->dma_bytes,
- vma->vm_page_prot);
- } else {
- pr_err("Physical address or size of buf is NULL");
- return -EINVAL;
- }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+ ab = &(apd[dir].buf[0]);
- return result;
+ return msm_audio_ion_mmap(ab, vma);
}
static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index 17934eb..97803b3 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -44,7 +44,7 @@
unsigned int sample_rate;
unsigned int channel;
unsigned int format;
- bool perf_mode;
+ unsigned long perf_mode;
};
#define INVALID_SESSION -1
@@ -58,6 +58,7 @@
static int srs_alsa_ctrl_ever_called;
static int lsm_mux_slim_port;
static int slim0_rx_aanc_fb_port;
+static int msm_route_ec_ref_rx = 3; /* NONE */
enum {
MADNONE,
@@ -213,8 +214,8 @@
{ INT_FM_TX, 0, 0, 0, 0, 0},
{ RT_PROXY_PORT_001_RX, 0, 0, 0, 0, 0},
{ RT_PROXY_PORT_001_TX, 0, 0, 0, 0, 0},
- { PCM_RX, 0, 0, 0, 0, 0},
- { PCM_TX, 0, 0, 0, 0, 0},
+ { AFE_PORT_ID_PRIMARY_PCM_RX, 0, 0, 0, 0, 0},
+ { AFE_PORT_ID_PRIMARY_PCM_TX, 0, 0, 0, 0, 0},
{ VOICE_PLAYBACK_TX, 0, 0, 0, 0, 0},
{ VOICE_RECORD_RX, 0, 0, 0, 0, 0},
{ VOICE_RECORD_TX, 0, 0, 0, 0, 0},
@@ -257,6 +258,12 @@
{INVALID_SESSION, INVALID_SESSION},
/* MULTIMEDIA5 */
{INVALID_SESSION, INVALID_SESSION},
+ /* MULTIMEDIA6 */
+ {INVALID_SESSION, INVALID_SESSION},
+ /* MULTIMEDIA7 */
+ {INVALID_SESSION, INVALID_SESSION},
+ /* MULTIMEDIA8 */
+ {INVALID_SESSION, INVALID_SESSION},
};
static uint8_t is_be_dai_extproc(int be_dai)
@@ -270,7 +277,7 @@
}
static void msm_pcm_routing_build_matrix(int fedai_id, int dspst_id,
- int path_type)
+ int path_type, bool perf_mode)
{
int i, port_type;
struct route_payload payload;
@@ -290,7 +297,7 @@
if (payload.num_copps)
adm_matrix_map(dspst_id, path_type,
- payload.num_copps, payload.copp_ids, 0);
+ payload.num_copps, payload.copp_ids, 0, perf_mode);
}
void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id,
@@ -365,8 +372,8 @@
msm_send_eq_values(fedai_id);
topology = get_topology(path_type);
for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
- if (test_bit(fedai_id, &msm_bedais[i].fe_sessions))
- msm_bedais[i].perf_mode = perf_mode;
+ if (test_bit(fedai_id, &msm_bedais[i].fe_sessions) && perf_mode)
+ set_bit(fedai_id, &msm_bedais[i].perf_mode);
if (!is_be_dai_extproc(i) &&
(afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
(msm_bedais[i].active) &&
@@ -379,13 +386,18 @@
else if (msm_bedais[i].format ==
SNDRV_PCM_FORMAT_S24_LE)
bits_per_sample = 24;
+
+ if (msm_bedais[i].port_id == VOICE_RECORD_RX ||
+ msm_bedais[i].port_id == VOICE_RECORD_TX)
+ topology = DEFAULT_COPP_TOPOLOGY;
if ((stream_type == SNDRV_PCM_STREAM_PLAYBACK) &&
(channels > 0))
adm_multi_ch_copp_open(msm_bedais[i].port_id,
path_type,
msm_bedais[i].sample_rate,
msm_bedais[i].channel,
- topology, msm_bedais[i].perf_mode,
+ topology,
+ test_bit(fedai_id, &msm_bedais[i].perf_mode),
bits_per_sample);
else
adm_open(msm_bedais[i].port_id,
@@ -399,7 +411,8 @@
msm_bedais[i].port_id;
port_id = srs_port_id = msm_bedais[i].port_id;
srs_send_params(srs_port_id, 1, 0);
- if (DOLBY_ADM_COPP_TOPOLOGY_ID == topology)
+ if ((DOLBY_ADM_COPP_TOPOLOGY_ID == topology) &&
+ (!perf_mode))
if (dolby_dap_init(port_id,
msm_bedais[i].channel) < 0)
pr_err("%s: Err init dolby dap\n",
@@ -408,7 +421,7 @@
}
if (payload.num_copps)
adm_matrix_map(dspst_id, path_type,
- payload.num_copps, payload.copp_ids, 0);
+ payload.num_copps, payload.copp_ids, 0, perf_mode);
mutex_unlock(&routing_lock);
}
@@ -440,8 +453,10 @@
(afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
(msm_bedais[i].active) &&
(test_bit(fedai_id, &msm_bedais[i].fe_sessions))) {
- adm_close(msm_bedais[i].port_id);
- if (DOLBY_ADM_COPP_TOPOLOGY_ID == topology)
+ adm_close(msm_bedais[i].port_id,
+ test_bit(fedai_id, &msm_bedais[i].perf_mode));
+ if ((DOLBY_ADM_COPP_TOPOLOGY_ID == topology) &&
+ (!test_bit(fedai_id, &msm_bedais[i].perf_mode)))
dolby_dap_deinit(msm_bedais[i].port_id);
}
}
@@ -473,6 +488,7 @@
int session_type, path_type, port_id, topology;
u32 channels;
uint16_t bits_per_sample = 16;
+ bool perf_mode = false;
pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
@@ -505,14 +521,20 @@
if (msm_bedais[reg].format == SNDRV_PCM_FORMAT_S24_LE)
bits_per_sample = 24;
+ if (msm_bedais[reg].port_id == VOICE_RECORD_RX ||
+ msm_bedais[reg].port_id == VOICE_RECORD_TX)
+ topology = DEFAULT_COPP_TOPOLOGY;
+
if ((session_type == SESSION_TYPE_RX) &&
(channels > 0)) {
+ perf_mode = test_bit(val,
+ &msm_bedais[reg].perf_mode);
adm_multi_ch_copp_open(msm_bedais[reg].port_id,
path_type,
msm_bedais[reg].sample_rate,
channels,
topology,
- msm_bedais[reg].perf_mode,
+ perf_mode,
bits_per_sample);
} else
adm_open(msm_bedais[reg].port_id,
@@ -521,10 +543,12 @@
topology, false, bits_per_sample);
msm_pcm_routing_build_matrix(val,
- fe_dai_map[val][session_type], path_type);
+ fe_dai_map[val][session_type], path_type,
+ perf_mode);
port_id = srs_port_id = msm_bedais[reg].port_id;
srs_send_params(srs_port_id, 1, 0);
- if (DOLBY_ADM_COPP_TOPOLOGY_ID == topology)
+ if ((DOLBY_ADM_COPP_TOPOLOGY_ID == topology) &&
+ (!perf_mode))
if (dolby_dap_init(port_id, channels) < 0)
pr_err("%s: Err init dolby dap\n",
__func__);
@@ -536,11 +560,14 @@
clear_bit(val, &msm_bedais[reg].fe_sessions);
if (msm_bedais[reg].active && fe_dai_map[val][session_type] !=
INVALID_SESSION) {
- adm_close(msm_bedais[reg].port_id);
- if (DOLBY_ADM_COPP_TOPOLOGY_ID == topology)
+ perf_mode = test_bit(val, &msm_bedais[reg].perf_mode);
+ adm_close(msm_bedais[reg].port_id, perf_mode);
+ if ((DOLBY_ADM_COPP_TOPOLOGY_ID == topology) &&
+ (!perf_mode))
dolby_dap_deinit(msm_bedais[reg].port_id);
msm_pcm_routing_build_matrix(val,
- fe_dai_map[val][session_type], path_type);
+ fe_dai_map[val][session_type], path_type,
+ perf_mode);
}
}
if ((msm_bedais[reg].port_id == VOICE_RECORD_RX)
@@ -591,7 +618,7 @@
static void msm_pcm_routing_process_voice(u16 reg, u16 val, int set)
{
- u16 session_id = 0;
+ u32 session_id = 0;
pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
@@ -1321,6 +1348,51 @@
return 0;
}
+static int msm_routing_ec_ref_rx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: ec_ref_rx = %d", __func__, msm_route_ec_ref_rx);
+ ucontrol->value.integer.value[0] = msm_route_ec_ref_rx;
+ return 0;
+}
+
+static int msm_routing_ec_ref_rx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ec_ref_port_id;
+ mutex_lock(&routing_lock);
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_route_ec_ref_rx = 0;
+ ec_ref_port_id = SLIMBUS_0_RX;
+ break;
+ case 1:
+ msm_route_ec_ref_rx = 1;
+ ec_ref_port_id = PRIMARY_I2S_RX;
+ break;
+ default:
+ msm_route_ec_ref_rx = 3; /* NONE */
+ ec_ref_port_id = -1;
+ break;
+ }
+ adm_ec_ref_rx_id(ec_ref_port_id);
+ pr_debug("%s: msm_route_ec_ref_rx = %d\n",
+ __func__, msm_route_ec_ref_rx);
+ mutex_unlock(&routing_lock);
+ return 0;
+}
+
+static const char *const ec_ref_rx[] = { "SLIM_RX", "I2S_RX", "PROXY_RX",
+ "NONE" };
+static const struct soc_enum msm_route_ec_ref_rx_enum[] = {
+ SOC_ENUM_SINGLE_EXT(4, ec_ref_rx),
+};
+
+static const struct snd_kcontrol_new ec_ref_rx_mixer_controls[] = {
+ SOC_ENUM_EXT("EC_REF_RX", msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put),
+};
+
static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_I2S_RX ,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
@@ -1337,6 +1409,15 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_PRI_I2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = {
@@ -1355,6 +1436,15 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SEC_I2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
@@ -1373,6 +1463,15 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SLIMBUS_0_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = {
@@ -1391,6 +1490,15 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = {
@@ -1406,6 +1514,33 @@
SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERTIARY_MI2S_RX ,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new secondary_mi2s_rx_mixer_controls[] = {
@@ -1421,6 +1556,27 @@
SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mi2s_hl_mixer_controls[] = {
+ SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
};
static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = {
@@ -1436,6 +1592,18 @@
SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_PRI_MI2S_RX,
MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
@@ -1454,6 +1622,15 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_HDMI_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
/* incall music delivery mixer */
static const struct snd_kcontrol_new incall_music_delivery_mixer_controls[] = {
@@ -1490,6 +1667,15 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_INT_BT_SCO_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
@@ -1508,6 +1694,15 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_INT_FM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = {
@@ -1526,6 +1721,15 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_AFE_PCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = {
@@ -1544,6 +1748,15 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_AUXPCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = {
@@ -1562,6 +1775,15 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
@@ -1571,9 +1793,15 @@
SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
@@ -1618,6 +1846,12 @@
msm_routing_put_audio_mixer),
};
+static const struct snd_kcontrol_new mmul4_mixer_controls[] = {
+ SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
static const struct snd_kcontrol_new mmul5_mixer_controls[] = {
SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
@@ -1640,6 +1874,9 @@
SOC_SINGLE_EXT("SEC_AUX_PCM_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
@@ -1678,6 +1915,24 @@
msm_routing_put_voice_mixer),
};
+static const struct snd_kcontrol_new sec_mi2s_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = {
SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SLIMBUS_0_RX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
@@ -1688,6 +1943,9 @@
SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -1859,6 +2117,9 @@
SOC_SINGLE_EXT("SEC_AUX_PCM_TX_Voice", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("PRI_MI2S_TX_Voice", MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new tx_voice2_mixer_controls[] = {
@@ -1928,6 +2189,9 @@
SOC_SINGLE_EXT("SEC_AUX_PCM_TX_Voip", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("PRI_MI2S_TX_Voip", MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new tx_voice_stub_mixer_controls[] = {
@@ -1946,6 +2210,15 @@
SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
};
static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = {
@@ -1964,6 +2237,9 @@
SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
};
static const struct snd_kcontrol_new auxpcm_rx_port_mixer_controls[] = {
@@ -1973,6 +2249,9 @@
SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_AUXPCM_RX,
MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
};
static const struct snd_kcontrol_new sec_auxpcm_rx_port_mixer_controls[] = {
@@ -1982,6 +2261,9 @@
SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
};
static const struct snd_kcontrol_new sbus_1_rx_port_mixer_controls[] = {
@@ -2002,6 +2284,9 @@
SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_INT_BT_SCO_RX,
MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
};
static const struct snd_kcontrol_new afe_pcm_rx_port_mixer_controls[] = {
@@ -2420,15 +2705,21 @@
__func__, e->shift_l , e->values[item]);
if (e->shift_l < MSM_BACKEND_DAI_MAX &&
e->values[item] < MSM_BACKEND_DAI_MAX)
+ /* Enable feedback TX path */
ret = afe_spk_prot_feed_back_cfg(
msm_bedais[e->values[item]].port_id,
- msm_bedais[e->shift_l].port_id, 1, 0);
+ msm_bedais[e->shift_l].port_id, 1, 0, 1);
else {
- pr_err("%s values are out of range\n", __func__);
- ret = -EINVAL;
+ pr_debug("%s values are out of range item %d\n",
+ __func__, e->values[item]);
+ /* Disable feedback TX path */
+ if (e->values[item] == MSM_BACKEND_DAI_MAX)
+ ret = afe_spk_prot_feed_back_cfg(0, 0, 0, 0, 0);
+ else
+ ret = -EINVAL;
}
} else {
- pr_err("%s item value is out of range\n", __func__);
+ pr_err("%s item value is out of range item\n", __func__);
ret = -EINVAL;
}
mutex_unlock(&routing_lock);
@@ -2443,11 +2734,11 @@
}
static const char * const slim0_rx_vi_fb_tx_lch_mux_text[] = {
- "SLIM4_TX",
+ "ZERO", "SLIM4_TX"
};
static const int const slim0_rx_vi_fb_tx_lch_value[] = {
- MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SLIMBUS_4_TX
};
static const struct soc_enum slim0_rx_vi_fb_lch_mux_enum =
SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_SLIMBUS_0_RX, 0, 0,
@@ -2469,9 +2760,13 @@
SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("MM_DL5", "MultiMedia5 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL4", "MultiMedia4 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
@@ -2503,12 +2798,19 @@
SND_SOC_DAPM_AIF_IN("HDMI_DL_HL", "HDMI_HOSTLESS Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("SEC_I2S_DL_HL", "SEC_I2S_RX_HOSTLESS Playback",
0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_MI2S_DL_HL",
+ "Secondary MI2S_RX Hostless Playback",
+ 0, 0, 0, 0),
+
SND_SOC_DAPM_AIF_IN("AUXPCM_DL_HL", "AUXPCM_HOSTLESS Playback",
0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("AUXPCM_UL_HL", "AUXPCM_HOSTLESS Capture",
0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MI2S_UL_HL", "MI2S_TX_HOSTLESS Capture",
0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_MI2S_UL_HL",
+ "Primary MI2S_TX Hostless Capture",
+ 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MI2S_DL_HL", "MI2S_RX_HOSTLESS Playback",
0, 0, 0, 0),
@@ -2528,6 +2830,8 @@
SND_SOC_DAPM_AIF_OUT("MI2S_RX", "MI2S Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX", "Quaternary MI2S Playback",
0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_MI2S_RX", "Tertiary MI2S Playback",
+ 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX", "Secondary MI2S Playback",
0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("PRI_MI2S_RX", "Primary MI2S Playback",
@@ -2536,6 +2840,10 @@
SND_SOC_DAPM_AIF_IN("MI2S_TX", "MI2S Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX", "Quaternary MI2S Capture",
0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_MI2S_TX", "Primary MI2S Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_MI2S_TX", "Tertiary MI2S Capture",
+ 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("SEC_MI2S_TX", "Secondary MI2S Capture",
0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
@@ -2611,9 +2919,15 @@
SND_SOC_DAPM_MIXER("QUAT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
quaternary_mi2s_rx_mixer_controls,
ARRAY_SIZE(quaternary_mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tertiary_mi2s_rx_mixer_controls,
+ ARRAY_SIZE(tertiary_mi2s_rx_mixer_controls)),
SND_SOC_DAPM_MIXER("SEC_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
secondary_mi2s_rx_mixer_controls,
ARRAY_SIZE(secondary_mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+ mi2s_hl_mixer_controls,
+ ARRAY_SIZE(mi2s_hl_mixer_controls)),
SND_SOC_DAPM_MIXER("PRI_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
primary_mi2s_rx_mixer_controls,
ARRAY_SIZE(primary_mi2s_rx_mixer_controls)),
@@ -2621,6 +2935,8 @@
mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)),
SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0,
mmul2_mixer_controls, ARRAY_SIZE(mmul2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia4 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul4_mixer_controls, ARRAY_SIZE(mmul4_mixer_controls)),
SND_SOC_DAPM_MIXER("MultiMedia5 Mixer", SND_SOC_NOPM, 0, 0,
mmul5_mixer_controls, ARRAY_SIZE(mmul5_mixer_controls)),
SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
@@ -2642,6 +2958,10 @@
SND_SOC_NOPM, 0, 0,
sec_i2s_rx_voice_mixer_controls,
ARRAY_SIZE(sec_i2s_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_MI2S_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ sec_mi2s_rx_voice_mixer_controls,
+ ARRAY_SIZE(sec_mi2s_rx_voice_mixer_controls)),
SND_SOC_DAPM_MIXER("SLIM_0_RX_Voice Mixer",
SND_SOC_NOPM, 0, 0,
slimbus_rx_voice_mixer_controls,
@@ -2743,6 +3063,9 @@
{"PRI_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"PRI_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"PRI_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"PRI_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"PRI_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"PRI_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
{"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"},
{"SEC_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -2750,6 +3073,9 @@
{"SEC_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"SEC_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"SEC_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SEC_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SEC_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SEC_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
{"SEC_I2S_RX", NULL, "SEC_RX Audio Mixer"},
{"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -2757,6 +3083,9 @@
{"SLIMBUS_0_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"SLIMBUS_0_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"SLIMBUS_0_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
{"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"},
{"HDMI Mixer", "MultiMedia1", "MM_DL1"},
@@ -2764,6 +3093,9 @@
{"HDMI Mixer", "MultiMedia3", "MM_DL3"},
{"HDMI Mixer", "MultiMedia4", "MM_DL4"},
{"HDMI Mixer", "MultiMedia5", "MM_DL5"},
+ {"HDMI Mixer", "MultiMedia6", "MM_DL6"},
+ {"HDMI Mixer", "MultiMedia7", "MM_DL7"},
+ {"HDMI Mixer", "MultiMedia8", "MM_DL8"},
{"HDMI", NULL, "HDMI Mixer"},
/* incall */
@@ -2777,37 +3109,54 @@
{"MultiMedia1 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
{"MultiMedia1 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
{"MultiMedia1 Mixer", "SLIM_4_TX", "SLIMBUS_4_TX"},
+ {"MultiMedia4 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
{"MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
{"MI2S_RX", NULL, "MI2S_RX Audio Mixer"},
{"QUAT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"QUAT_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
{"QUAT_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"QUAT_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
{"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Audio Mixer"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX Audio Mixer"},
{"SEC_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"SEC_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
{"SEC_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"SEC_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
{"SEC_MI2S_RX", NULL, "SEC_MI2S_RX Audio Mixer"},
+ {"SEC_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"SEC_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+
{"PRI_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"PRI_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
{"PRI_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"PRI_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
{"PRI_MI2S_RX", NULL, "PRI_MI2S_RX Audio Mixer"},
{"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
{"MultiMedia1 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"},
+ {"MultiMedia5 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia1 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
{"MultiMedia5 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"},
@@ -2815,12 +3164,16 @@
{"MultiMedia5 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"},
{"MultiMedia2 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MultiMedia1 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"MultiMedia1 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
{"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"},
{"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -2828,6 +3181,9 @@
{"INTERNAL_FM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"INTERNAL_FM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"INTERNAL_FM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
{"INT_FM_RX", NULL, "INTERNAL_FM_RX Audio Mixer"},
{"AFE_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -2835,6 +3191,9 @@
{"AFE_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"AFE_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"AFE_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
{"PCM_RX", NULL, "AFE_PCM_RX Audio Mixer"},
{"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
@@ -2847,6 +3206,7 @@
{"MM_UL1", NULL, "MultiMedia1 Mixer"},
{"MultiMedia2 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"MM_UL2", NULL, "MultiMedia2 Mixer"},
+ {"MM_UL4", NULL, "MultiMedia4 Mixer"},
{"MM_UL5", NULL, "MultiMedia5 Mixer"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -2854,6 +3214,9 @@
{"AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
{"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"},
{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -2861,6 +3224,9 @@
{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
{"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX Audio Mixer"},
{"MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
@@ -2884,11 +3250,19 @@
{"SEC_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"SEC_I2S_RX", NULL, "SEC_RX_Voice Mixer"},
+ {"SEC_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"SEC_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"SEC_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SEC_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SEC_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX_Voice Mixer"},
+
{"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"SLIM_0_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
{"SLIM_0_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
{"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"SLIM_0_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"SLIM_0_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
{"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
@@ -2910,12 +3284,14 @@
{"AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
{"AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
{"AUX_PCM_RX", NULL, "AUX_PCM_RX_Voice Mixer"},
{"SEC_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"SEC_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
{"SEC_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"SEC_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"SEC_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
{"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX_Voice Mixer"},
{"HDMI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
@@ -2934,6 +3310,7 @@
{"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"},
{"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
+ {"Voice_Tx Mixer", "PRI_MI2S_TX_Voice", "PRI_MI2S_TX"},
{"Voice_Tx Mixer", "MI2S_TX_Voice", "MI2S_TX"},
{"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
{"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
@@ -2965,6 +3342,7 @@
{"Voip_Tx Mixer", "AFE_PCM_TX_Voip", "PCM_TX"},
{"Voip_Tx Mixer", "AUX_PCM_TX_Voip", "AUX_PCM_TX"},
{"Voip_Tx Mixer", "SEC_AUX_PCM_TX_Voip", "SEC_AUX_PCM_TX"},
+ {"Voip_Tx Mixer", "PRI_MI2S_TX_Voip", "PRI_MI2S_TX"},
{"VOIP_UL", NULL, "Voip_Tx Mixer"},
{"SLIMBUS_DL_HL", "Switch", "SLIM0_DL_HL"},
@@ -2995,21 +3373,27 @@
{"MI2S_UL_HL", NULL, "MI2S_TX"},
{"PCM_RX_DL_HL", "Switch", "SLIM0_DL_HL"},
{"PCM_RX", NULL, "PCM_RX_DL_HL"},
- {"MI2S_UL_HL", NULL, "MI2S_TX"},
+ {"MI2S_UL_HL", NULL, "TERT_MI2S_TX"},
{"SEC_I2S_RX", NULL, "SEC_I2S_DL_HL"},
+ {"PRI_MI2S_UL_HL", NULL, "PRI_MI2S_TX"},
+ {"SEC_MI2S_RX", NULL, "SEC_MI2S_DL_HL"},
+
{"SLIMBUS_0_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"SLIMBUS_0_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"SLIMBUS_0_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
{"SLIMBUS_0_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
{"SLIMBUS_0_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
{"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Port Mixer"},
{"AFE_PCM_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"PCM_RX", NULL, "AFE_PCM_RX Port Mixer"},
{"AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
{"AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"AUXPCM_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
{"AUX_PCM_RX", NULL, "AUXPCM_RX Port Mixer"},
+ {"SEC_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
{"SEC_AUXPCM_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
{"SEC_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"SEC_AUX_PCM_RX", NULL, "SEC_AUXPCM_RX Port Mixer"},
@@ -3018,7 +3402,10 @@
{"Voice Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"Voice Stub Tx Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
{"Voice Stub Tx Mixer", "STUB_1_TX_HL", "STUB_1_TX"},
+ {"Voice Stub Tx Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"Voice Stub Tx Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
{"Voice Stub Tx Mixer", "MI2S_TX", "MI2S_TX"},
+ {"Voice Stub Tx Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"VOICE_STUB_UL", NULL, "Voice Stub Tx Mixer"},
{"STUB_RX Mixer", "Voice Stub", "VOICE_STUB_DL"},
@@ -3032,6 +3419,7 @@
{"SLIMBUS_1_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
{"SLIMBUS_1_RX", NULL, "SLIMBUS_1_RX Port Mixer"},
{"INTERNAL_BT_SCO_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"INTERNAL_BT_SCO_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Port Mixer"},
{"SLIMBUS_3_RX Port Mixer", "INTERNAL_BT_SCO_RX", "INT_BT_SCO_RX"},
{"SLIMBUS_3_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
@@ -3062,6 +3450,7 @@
{"BE_OUT", NULL, "HDMI"},
{"BE_OUT", NULL, "MI2S_RX"},
{"BE_OUT", NULL, "QUAT_MI2S_RX"},
+ {"BE_OUT", NULL, "TERT_MI2S_RX"},
{"BE_OUT", NULL, "SEC_MI2S_RX"},
{"BE_OUT", NULL, "PRI_MI2S_RX"},
{"BE_OUT", NULL, "INT_BT_SCO_RX"},
@@ -3070,30 +3459,32 @@
{"BE_OUT", NULL, "SLIMBUS_3_RX"},
{"BE_OUT", NULL, "AUX_PCM_RX"},
{"BE_OUT", NULL, "SEC_AUX_PCM_RX"},
+ {"BE_OUT", NULL, "INT_BT_SCO_RX"},
+ {"BE_OUT", NULL, "INT_FM_RX"},
+ {"BE_OUT", NULL, "PCM_RX"},
+ {"BE_OUT", NULL, "SLIMBUS_3_RX"},
+ {"BE_OUT", NULL, "AUX_PCM_RX"},
+ {"BE_OUT", NULL, "SEC_AUX_PCM_RX"},
+ {"BE_OUT", NULL, "VOICE_PLAYBACK_TX"},
{"PRI_I2S_TX", NULL, "BE_IN"},
{"MI2S_TX", NULL, "BE_IN"},
{"QUAT_MI2S_TX", NULL, "BE_IN"},
+ {"PRI_MI2S_TX", NULL, "BE_IN"},
+ {"TERT_MI2S_TX", NULL, "BE_IN"},
{"SEC_MI2S_TX", NULL, "BE_IN"},
{"SLIMBUS_0_TX", NULL, "BE_IN" },
{"SLIMBUS_1_TX", NULL, "BE_IN" },
{"SLIMBUS_3_TX", NULL, "BE_IN" },
{"SLIMBUS_4_TX", NULL, "BE_IN" },
{"SLIMBUS_5_TX", NULL, "BE_IN" },
- {"BE_OUT", NULL, "INT_BT_SCO_RX"},
{"INT_BT_SCO_TX", NULL, "BE_IN"},
- {"BE_OUT", NULL, "INT_FM_RX"},
{"INT_FM_TX", NULL, "BE_IN"},
- {"BE_OUT", NULL, "PCM_RX"},
{"PCM_TX", NULL, "BE_IN"},
- {"BE_OUT", NULL, "SLIMBUS_3_RX"},
- {"BE_OUT", NULL, "AUX_PCM_RX"},
{"AUX_PCM_TX", NULL, "BE_IN"},
- {"BE_OUT", NULL, "SEC_AUX_PCM_RX"},
{"SEC_AUX_PCM_TX", NULL, "BE_IN"},
{"INCALL_RECORD_TX", NULL, "BE_IN"},
{"INCALL_RECORD_RX", NULL, "BE_IN"},
- {"BE_OUT", NULL, "VOICE_PLAYBACK_TX"},
{"SLIM0_RX_VI_FB_LCH_MUX", "SLIM4_TX", "SLIMBUS_4_TX"},
{"SLIMBUS_0_RX", NULL, "SLIM0_RX_VI_FB_LCH_MUX"},
};
@@ -3141,17 +3532,19 @@
topology = get_topology(path_type);
for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
if (fe_dai_map[i][session_type] != INVALID_SESSION) {
- adm_close(bedai->port_id);
+ adm_close(bedai->port_id,
+ test_bit(i, &(bedai->perf_mode)));
srs_port_id = -1;
- if (DOLBY_ADM_COPP_TOPOLOGY_ID == topology)
+ if ((DOLBY_ADM_COPP_TOPOLOGY_ID == topology) &&
+ (!test_bit(i, &(bedai->perf_mode))))
dolby_dap_deinit(bedai->port_id);
+ clear_bit(i, &(bedai->perf_mode));
}
}
bedai->active = 0;
bedai->sample_rate = 0;
bedai->channel = 0;
- bedai->perf_mode = false;
mutex_unlock(&routing_lock);
return 0;
@@ -3166,6 +3559,7 @@
u32 channels;
bool playback, capture;
uint16_t bits_per_sample = 16;
+ bool perf_mode = false;
if (be_id >= MSM_BACKEND_DAI_MAX) {
pr_err("%s: unexpected be_id %d\n", __func__, be_id);
@@ -3203,12 +3597,18 @@
if (bedai->format == SNDRV_PCM_FORMAT_S24_LE)
bits_per_sample = 24;
+ if (bedai->port_id == VOICE_RECORD_RX ||
+ bedai->port_id == VOICE_RECORD_TX)
+ topology = DEFAULT_COPP_TOPOLOGY;
+
if ((playback) && (channels > 0)) {
+ perf_mode = test_bit(i, &(bedai->perf_mode));
adm_multi_ch_copp_open(bedai->port_id,
path_type,
bedai->sample_rate,
channels,
- topology, bedai->perf_mode,
+ topology,
+ perf_mode,
bits_per_sample);
} else if (capture) {
adm_open(bedai->port_id,
@@ -3220,10 +3620,12 @@
}
msm_pcm_routing_build_matrix(i,
- fe_dai_map[i][session_type], path_type);
+ fe_dai_map[i][session_type], path_type,
+ perf_mode);
port_id = srs_port_id = bedai->port_id;
srs_send_params(srs_port_id, 1, 0);
- if (DOLBY_ADM_COPP_TOPOLOGY_ID == topology)
+ if ((DOLBY_ADM_COPP_TOPOLOGY_ID == topology) &&
+ (!perf_mode))
if (dolby_dap_init(port_id, channels) < 0)
pr_err("%s: Err init dolby dap\n",
__func__);
@@ -3342,6 +3744,9 @@
dolby_dap_param_end_point_controls,
ARRAY_SIZE(dolby_dap_param_end_point_controls));
+ snd_soc_add_platform_controls(platform,
+ ec_ref_rx_mixer_controls,
+ ARRAY_SIZE(ec_ref_rx_mixer_controls));
return 0;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
index 4a58369..9750756 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
@@ -67,6 +67,9 @@
MSM_FRONTEND_DAI_MULTIMEDIA3,
MSM_FRONTEND_DAI_MULTIMEDIA4,
MSM_FRONTEND_DAI_MULTIMEDIA5,
+ MSM_FRONTEND_DAI_MULTIMEDIA6,
+ MSM_FRONTEND_DAI_MULTIMEDIA7,
+ MSM_FRONTEND_DAI_MULTIMEDIA8,
MSM_FRONTEND_DAI_CS_VOICE,
MSM_FRONTEND_DAI_VOIP,
MSM_FRONTEND_DAI_AFE_RX,
@@ -79,8 +82,8 @@
MSM_FRONTEND_DAI_MAX,
};
-#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA5 + 1)
-#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA5
+#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA8 + 1)
+#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA8
enum {
MSM_BACKEND_DAI_PRI_I2S_RX = 0,
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
index 25bb72f..5c97403 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
@@ -67,9 +67,9 @@
return false;
}
-static uint16_t get_session_id(struct msm_voice *pvoc)
+static uint32_t get_session_id(struct msm_voice *pvoc)
{
- uint16_t session_id = 0;
+ uint32_t session_id = 0;
if (is_volte(pvoc))
session_id = voc_get_session_id(VOLTE_SESSION_NAME);
@@ -175,7 +175,7 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct msm_voice *prtd = runtime->private_data;
- uint16_t session_id = 0;
+ uint32_t session_id = 0;
int ret = 0;
mutex_lock(&prtd->lock);
@@ -201,7 +201,7 @@
int ret = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
struct msm_voice *prtd = runtime->private_data;
- uint16_t session_id = 0;
+ uint32_t session_id = 0;
mutex_lock(&prtd->lock);
@@ -236,7 +236,7 @@
int ret = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
struct msm_voice *prtd = runtime->private_data;
- uint16_t session_id = 0;
+ uint32_t session_id = 0;
pr_debug("%s: cmd = %d\n", __func__, cmd);
@@ -281,6 +281,58 @@
return ret;
}
+static int msm_pcm_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_voice *prtd = runtime->private_data;
+ uint16_t session_id = get_session_id(prtd);
+ enum voice_lch_mode lch_mode;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_VOICE_IOCTL_LCH:
+ if (copy_from_user(&lch_mode, (void *)arg,
+ sizeof(enum voice_lch_mode))) {
+ pr_err("%s: Copy from user failed, size %d\n", __func__,
+ sizeof(enum voice_lch_mode));
+
+ ret = -EFAULT;
+ break;
+ }
+
+ pr_debug("%s: %s lch_mode:%d\n",
+ __func__, substream->pcm->id, lch_mode);
+
+ switch (lch_mode) {
+ case VOICE_LCH_START:
+ case VOICE_LCH_STOP:
+ ret = voc_set_lch(session_id, lch_mode);
+ break;
+
+ default:
+ pr_err("%s: Invalid LCH MODE %d\n", __func__, lch_mode);
+
+ ret = -EFAULT;
+ }
+
+ break;
+ default:
+ pr_debug("%s: Falling into default snd_lib_ioctl cmd 0x%x\n",
+ __func__, cmd);
+
+ ret = snd_pcm_lib_ioctl(substream, cmd, arg);
+ break;
+ }
+
+ if (!ret)
+ pr_debug("%s: ret %d\n", __func__, ret);
+ else
+ pr_err("%s: cmd 0x%x failed %d\n", __func__, cmd, ret);
+
+ return ret;
+}
+
static int msm_voice_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -476,29 +528,6 @@
return 0;
}
-static int msm_voice_widevoice_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int wv_enable = ucontrol->value.integer.value[0];
-
- pr_debug("%s: wv enable=%d\n", __func__, wv_enable);
-
- voc_set_widevoice_enable(voc_get_session_id(VOICE_SESSION_NAME),
- wv_enable);
- voc_set_widevoice_enable(voc_get_session_id(VOICE2_SESSION_NAME),
- wv_enable);
-
- return 0;
-}
-
-static int msm_voice_widevoice_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] =
- voc_get_widevoice_enable(voc_get_session_id(VOICE_SESSION_NAME));
- return 0;
-}
-
static int msm_voice_slowtalk_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -524,30 +553,6 @@
return 0;
}
-static int msm_voice_fens_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int fens_enable = ucontrol->value.integer.value[0];
-
- pr_debug("%s: fens enable=%d\n", __func__, fens_enable);
-
- voc_set_pp_enable(voc_get_session_id(VOICE_SESSION_NAME),
- MODULE_ID_VOICE_MODULE_FENS, fens_enable);
- voc_set_pp_enable(voc_get_session_id(VOICE2_SESSION_NAME),
- MODULE_ID_VOICE_MODULE_FENS, fens_enable);
-
- return 0;
-}
-
-static int msm_voice_fens_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] =
- voc_get_pp_enable(voc_get_session_id(VOICE_SESSION_NAME),
- MODULE_ID_VOICE_MODULE_FENS);
- return 0;
-}
-
static struct snd_kcontrol_new msm_voice_controls[] = {
SOC_SINGLE_EXT("Voice Rx Device Mute", SND_SOC_NOPM, 0, 1, 0,
msm_voice_rx_device_mute_get,
@@ -558,12 +563,8 @@
msm_voice_volume_get, msm_voice_volume_put),
SOC_ENUM_EXT("TTY Mode", msm_tty_mode_enum[0], msm_voice_tty_mode_get,
msm_voice_tty_mode_put),
- SOC_SINGLE_EXT("Widevoice Enable", SND_SOC_NOPM, 0, 1, 0,
- msm_voice_widevoice_get, msm_voice_widevoice_put),
SOC_SINGLE_EXT("Slowtalk Enable", SND_SOC_NOPM, 0, 1, 0,
msm_voice_slowtalk_get, msm_voice_slowtalk_put),
- SOC_SINGLE_EXT("FENS Enable", SND_SOC_NOPM, 0, 1, 0,
- msm_voice_fens_get, msm_voice_fens_put),
SOC_SINGLE_EXT("VoLTE Rx Device Mute", SND_SOC_NOPM, 0, 1, 0,
msm_volte_rx_device_mute_get,
msm_volte_rx_device_mute_put),
@@ -581,11 +582,12 @@
};
static struct snd_pcm_ops msm_pcm_ops = {
- .open = msm_pcm_open,
- .hw_params = msm_pcm_hw_params,
- .close = msm_pcm_close,
- .prepare = msm_pcm_prepare,
- .trigger = msm_pcm_trigger,
+ .open = msm_pcm_open,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .ioctl = msm_pcm_ioctl,
};
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
index b5ce28f..f17fe5b 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
@@ -109,10 +109,9 @@
wait_queue_head_t in_wait;
struct mutex lock;
- struct mutex in_lock;
- struct mutex out_lock;
spinlock_t dsp_lock;
+ spinlock_t dsp_ul_lock;
uint32_t mode;
uint32_t rate_type;
@@ -272,7 +271,7 @@
return;
/* Copy up-link packet into out_queue. */
- spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags);
/* discarding UL packets till start is received */
if (!list_empty(&prtd->free_out_queue) && prtd->capture_start) {
@@ -325,10 +324,10 @@
pr_debug("ul_pkt: pkt_len =%d, frame.len=%d\n", pkt_len,
buf_node->frame.len);
prtd->pcm_capture_irq_pos += prtd->pcm_capture_count;
- spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags);
snd_pcm_period_elapsed(prtd->capture_substream);
} else {
- spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags);
pr_err("UL data dropped\n");
}
@@ -526,6 +525,7 @@
struct voip_buf_node *buf_node = NULL;
struct snd_pcm_runtime *runtime = substream->runtime;
struct voip_drv_info *prtd = runtime->private_data;
+ unsigned long dsp_flags;
int count = frames_to_bytes(runtime, frames);
pr_debug("%s: count = %d, frames=%d\n", __func__, count, (int)frames);
@@ -535,12 +535,13 @@
prtd->state == VOIP_STOPPED),
1 * HZ);
if (ret > 0) {
- mutex_lock(&prtd->in_lock);
if (count <= VOIP_MAX_VOC_PKT_SIZE) {
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
buf_node =
list_first_entry(&prtd->free_in_queue,
struct voip_buf_node, list);
list_del(&buf_node->list);
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
if (prtd->mode == MODE_PCM) {
ret = copy_from_user(&buf_node->frame.voc_pkt,
buf, count);
@@ -548,14 +549,15 @@
} else
ret = copy_from_user(&buf_node->frame,
buf, count);
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
list_add_tail(&buf_node->list, &prtd->in_queue);
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
} else {
pr_err("%s: Write cnt %d is > VOIP_MAX_VOC_PKT_SIZE\n",
__func__, count);
ret = -ENOMEM;
}
- mutex_unlock(&prtd->in_lock);
} else if (ret == 0) {
pr_err("%s: No free DL buffs\n", __func__);
ret = -ETIMEDOUT;
@@ -574,6 +576,7 @@
struct voip_buf_node *buf_node = NULL;
struct snd_pcm_runtime *runtime = substream->runtime;
struct voip_drv_info *prtd = runtime->private_data;
+ unsigned long dsp_flags;
count = frames_to_bytes(runtime, frames);
@@ -585,12 +588,13 @@
1 * HZ);
if (ret > 0) {
- mutex_lock(&prtd->out_lock);
if (count <= VOIP_MAX_VOC_PKT_SIZE) {
+ spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags);
buf_node = list_first_entry(&prtd->out_queue,
struct voip_buf_node, list);
list_del(&buf_node->list);
+ spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags);
if (prtd->mode == MODE_PCM)
ret = copy_to_user(buf,
&buf_node->frame.voc_pkt,
@@ -604,15 +608,17 @@
__func__, ret);
ret = -EFAULT;
}
+ spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags);
list_add_tail(&buf_node->list,
&prtd->free_out_queue);
+ spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags);
+
} else {
pr_err("%s: Read count %d > VOIP_MAX_VOC_PKT_SIZE\n",
__func__, count);
ret = -ENOMEM;
}
- mutex_unlock(&prtd->out_lock);
} else if (ret == 0) {
pr_err("%s: No UL data available\n", __func__);
@@ -646,6 +652,7 @@
struct snd_pcm_substream *p_substream, *c_substream;
struct snd_pcm_runtime *runtime;
struct voip_drv_info *prtd;
+ unsigned long dsp_flags;
if (substream == NULL) {
pr_err("substream is NULL\n");
@@ -684,7 +691,7 @@
goto capt;
}
if (p_dma_buf->area != NULL) {
- mutex_lock(&prtd->in_lock);
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
list_for_each_safe(ptr, next, &prtd->in_queue) {
buf_node = list_entry(ptr,
struct voip_buf_node, list);
@@ -695,11 +702,11 @@
struct voip_buf_node, list);
list_del(&buf_node->list);
}
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
dma_free_coherent(p_substream->pcm->card->dev,
runtime->hw.buffer_bytes_max, p_dma_buf->area,
p_dma_buf->addr);
p_dma_buf->area = NULL;
- mutex_unlock(&prtd->in_lock);
}
/* release out_queue and free_out_queue */
capt: c_substream = prtd->capture_substream;
@@ -713,7 +720,7 @@
goto done;
}
if (c_dma_buf->area != NULL) {
- mutex_lock(&prtd->out_lock);
+ spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags);
list_for_each_safe(ptr, next, &prtd->out_queue) {
buf_node = list_entry(ptr,
struct voip_buf_node, list);
@@ -724,11 +731,11 @@
struct voip_buf_node, list);
list_del(&buf_node->list);
}
+ spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags);
dma_free_coherent(c_substream->pcm->card->dev,
runtime->hw.buffer_bytes_max, c_dma_buf->area,
c_dma_buf->addr);
c_dma_buf->area = NULL;
- mutex_unlock(&prtd->out_lock);
}
done:
prtd->capture_substream = NULL;
@@ -899,19 +906,15 @@
for (i = 0; i < VOIP_MAX_Q_LEN; i++) {
buf_node = (void *)dma_buf->area + offset;
- mutex_lock(&voip_info.in_lock);
list_add_tail(&buf_node->list,
&voip_info.free_in_queue);
- mutex_unlock(&voip_info.in_lock);
offset = offset + sizeof(struct voip_buf_node);
}
} else {
for (i = 0; i < VOIP_MAX_Q_LEN; i++) {
buf_node = (void *) dma_buf->area + offset;
- mutex_lock(&voip_info.out_lock);
list_add_tail(&buf_node->list,
&voip_info.free_out_queue);
- mutex_unlock(&voip_info.out_lock);
offset = offset + sizeof(struct voip_buf_node);
}
}
@@ -1145,6 +1148,7 @@
static const struct of_device_id msm_voip_dt_match[] = {
{.compatible = "qcom,msm-voip-dsp"},
+ {}
};
MODULE_DEVICE_TABLE(of, msm_voip_dt_match);
@@ -1163,10 +1167,9 @@
memset(&voip_info, 0, sizeof(voip_info));
voip_info.mode = MODE_PCM;
mutex_init(&voip_info.lock);
- mutex_init(&voip_info.in_lock);
- mutex_init(&voip_info.out_lock);
spin_lock_init(&voip_info.dsp_lock);
+ spin_lock_init(&voip_info.dsp_ul_lock);
init_waitqueue_head(&voip_info.out_wait);
init_waitqueue_head(&voip_info.in_wait);
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 1bd3eac..bdb0e13 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -39,6 +39,9 @@
void *apr;
atomic_t copp_id[AFE_MAX_PORTS];
atomic_t copp_cnt[AFE_MAX_PORTS];
+ atomic_t copp_low_latency_id[AFE_MAX_PORTS];
+ atomic_t copp_low_latency_cnt[AFE_MAX_PORTS];
+ atomic_t copp_perf_mode[AFE_MAX_PORTS];
atomic_t copp_stat[AFE_MAX_PORTS];
wait_queue_head_t wait[AFE_MAX_PORTS];
@@ -52,6 +55,7 @@
atomic_t mem_map_cal_index;
int set_custom_topology;
+ int ec_ref_rx;
};
static struct adm_ctl this_adm;
@@ -445,7 +449,12 @@
for (i = 0; i < AFE_MAX_PORTS; i++) {
atomic_set(&this_adm.copp_id[i],
RESET_COPP_ID);
+ atomic_set(&this_adm.copp_low_latency_id[i],
+ RESET_COPP_ID);
atomic_set(&this_adm.copp_cnt[i], 0);
+ atomic_set(&this_adm.copp_low_latency_cnt[i],
+ 0);
+ atomic_set(&this_adm.copp_perf_mode[i], 0);
atomic_set(&this_adm.copp_stat[i], 0);
}
this_adm.apr = NULL;
@@ -545,7 +554,13 @@
wake_up(&this_adm.wait[index]);
break;
}
- atomic_set(&this_adm.copp_id[index], open->copp_id);
+ if (atomic_read(&this_adm.copp_perf_mode[index])) {
+ atomic_set(&this_adm.copp_low_latency_id[index],
+ open->copp_id);
+ } else {
+ atomic_set(&this_adm.copp_id[index],
+ open->copp_id);
+ }
atomic_set(&this_adm.copp_stat[index], 1);
pr_debug("%s: coppid rxed=%d\n", __func__,
open->copp_id);
@@ -892,8 +907,8 @@
int index;
int tmp_port = q6audio_get_port_id(port_id);
- pr_debug("%s: port %#x path:%d rate:%d mode:%d\n", __func__,
- port_id, path, rate, channel_mode);
+ pr_debug("%s: port %#x path:%d rate:%d mode:%d perf_mode:%d\n",
+ __func__, port_id, path, rate, channel_mode, perf_mode);
port_id = q6audio_convert_virtual_to_portid(port_id);
@@ -916,11 +931,19 @@
rtac_set_adm_handle(this_adm.apr);
}
- send_adm_custom_topology(port_id);
+ if (!perf_mode) {
+ atomic_set(&this_adm.copp_perf_mode[index], 0);
+ send_adm_custom_topology(port_id);
+ } else {
+ atomic_set(&this_adm.copp_perf_mode[index], 1);
+ }
/* Create a COPP if port id are not enabled */
- if (atomic_read(&this_adm.copp_cnt[index]) == 0) {
-
+ if ((!perf_mode && (atomic_read(&this_adm.copp_cnt[index]) == 0)) ||
+ (perf_mode &&
+ (atomic_read(&this_adm.copp_low_latency_cnt[index]) == 0))) {
+ pr_debug("%s:opening ADM: perf_mode: %d\n", __func__,
+ perf_mode);
open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
open.hdr.pkt_size = sizeof(open);
@@ -943,13 +966,22 @@
open.mode_of_operation = path;
open.endpoint_id_1 = tmp_port;
- open.endpoint_id_2 = 0xFFFF;
+
+ if (this_adm.ec_ref_rx == -1) {
+ open.endpoint_id_2 = 0xFFFF;
+ } else if (this_adm.ec_ref_rx && (path != 1)) {
+ open.endpoint_id_2 = this_adm.ec_ref_rx;
+ this_adm.ec_ref_rx = -1;
+ }
open.topology_id = topology;
if ((open.topology_id == VPM_TX_SM_ECNS_COPP_TOPOLOGY) ||
(open.topology_id == VPM_TX_DM_FLUENCE_COPP_TOPOLOGY))
rate = 16000;
+ if (perf_mode)
+ open.topology_id = NULL_COPP_TOPOLOGY;
+
open.dev_num_channel = channel_mode & 0x00FF;
open.bit_width = bits_per_sample;
open.sample_rate = rate;
@@ -978,8 +1010,8 @@
} else if (channel_mode == 6) {
open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
- open.dev_channel_mapping[2] = PCM_CHANNEL_LFE;
- open.dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ open.dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ open.dev_channel_mapping[3] = PCM_CHANNEL_LFE;
open.dev_channel_mapping[4] = PCM_CHANNEL_LB;
open.dev_channel_mapping[5] = PCM_CHANNEL_RB;
} else if (channel_mode == 8) {
@@ -1026,7 +1058,15 @@
goto fail_cmd;
}
}
- atomic_inc(&this_adm.copp_cnt[index]);
+ if (perf_mode) {
+ atomic_inc(&this_adm.copp_low_latency_cnt[index]);
+ pr_debug("%s: index: %d coppid: %d", __func__, index,
+ atomic_read(&this_adm.copp_low_latency_id[index]));
+ } else {
+ atomic_inc(&this_adm.copp_cnt[index]);
+ pr_debug("%s: index: %d coppid: %d", __func__, index,
+ atomic_read(&this_adm.copp_id[index]));
+ }
return 0;
fail_cmd:
@@ -1046,7 +1086,7 @@
}
int adm_matrix_map(int session_id, int path, int num_copps,
- unsigned int *port_id, int copp_id)
+ unsigned int *port_id, int copp_id, bool perf_mode)
{
struct adm_cmd_matrix_map_routings_v5 *route;
struct adm_session_map_node_v5 *node;
@@ -1085,7 +1125,12 @@
route->hdr.src_port = copp_id;
route->hdr.dest_svc = APR_SVC_ADM;
route->hdr.dest_domain = APR_DOMAIN_ADSP;
- route->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+ if (perf_mode) {
+ route->hdr.dest_port =
+ atomic_read(&this_adm.copp_low_latency_id[index]);
+ } else {
+ route->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+ }
route->hdr.token = copp_id;
route->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5;
route->num_sessions = 1;
@@ -1117,9 +1162,14 @@
tmp = q6audio_get_port_index(port_id[i]);
- if (tmp >= 0 && tmp < AFE_MAX_PORTS)
- copps_list[i] =
+ if (tmp >= 0 && tmp < AFE_MAX_PORTS) {
+ if (perf_mode)
+ copps_list[i] =
+ atomic_read(&this_adm.copp_low_latency_id[tmp]);
+ else
+ copps_list[i] =
atomic_read(&this_adm.copp_id[tmp]);
+ }
else
continue;
pr_debug("%s: port_id[%#x]: %d, index: %d act coppid[0x%x]\n",
@@ -1144,21 +1194,25 @@
ret = -EINVAL;
goto fail_cmd;
}
- for (i = 0; i < num_copps; i++)
- send_adm_cal(port_id[i], path);
+ if (!perf_mode) {
+ for (i = 0; i < num_copps; i++)
+ send_adm_cal(port_id[i], path);
- for (i = 0; i < num_copps; i++) {
- int tmp;
- tmp = afe_get_port_index(port_id[i]);
- if (tmp >= 0 && tmp < AFE_MAX_PORTS)
- rtac_add_adm_device(port_id[i],
- atomic_read(&this_adm.copp_id[tmp]),
- path, session_id);
- else
- pr_debug("%s: Invalid port index %d",
- __func__, tmp);
+ for (i = 0; i < num_copps; i++) {
+ int tmp;
+ tmp = afe_get_port_index(port_id[i]);
+ if (tmp >= 0 && tmp < AFE_MAX_PORTS) {
+ rtac_add_adm_device(port_id[i],
+ atomic_read(&this_adm.copp_id[tmp]),
+ path, session_id);
+ pr_debug("%s, copp_id: %d\n", __func__,
+ atomic_read(&this_adm.copp_id[tmp]));
+ } else {
+ pr_debug("%s: Invalid port index %d",
+ __func__, tmp);
+ }
+ }
}
-
fail_cmd:
kfree(matrix_map);
return ret;
@@ -1319,7 +1373,13 @@
return atomic_read(&this_adm.copp_id[port_index]);
}
-int adm_close(int port_id)
+void adm_ec_ref_rx_id(int port_id)
+{
+ this_adm.ec_ref_rx = port_id;
+ pr_debug("%s ec_ref_rx:%d", __func__, this_adm.ec_ref_rx);
+}
+
+int adm_close(int port_id, bool perf_mode)
{
struct apr_hdr close;
@@ -1332,16 +1392,30 @@
if (q6audio_validate_port(port_id) < 0)
return -EINVAL;
- pr_debug("%s port_id=%#x index %d\n", __func__, port_id, index);
+ pr_debug("%s port_id=%#x index %d perf_mode: %d\n", __func__, port_id,
+ index, perf_mode);
- if (!(atomic_read(&this_adm.copp_cnt[index]))) {
- pr_err("%s: copp count for port[%#x]is 0\n", __func__, port_id);
-
- goto fail_cmd;
+ if (perf_mode) {
+ if (!(atomic_read(&this_adm.copp_low_latency_cnt[index]))) {
+ pr_err("%s: copp count for port[%#x]is 0\n", __func__,
+ port_id);
+ goto fail_cmd;
+ }
+ atomic_dec(&this_adm.copp_low_latency_cnt[index]);
+ } else {
+ if (!(atomic_read(&this_adm.copp_cnt[index]))) {
+ pr_err("%s: copp count for port[%#x]is 0\n", __func__,
+ port_id);
+ goto fail_cmd;
+ }
+ atomic_dec(&this_adm.copp_cnt[index]);
}
- atomic_dec(&this_adm.copp_cnt[index]);
- if (!(atomic_read(&this_adm.copp_cnt[index]))) {
+ if ((!perf_mode && !(atomic_read(&this_adm.copp_cnt[index]))) ||
+ (perf_mode &&
+ !(atomic_read(&this_adm.copp_low_latency_cnt[index])))) {
+ pr_debug("%s:Closing ADM: perf_mode: %d\n", __func__,
+ perf_mode);
close.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
close.pkt_size = sizeof(close);
@@ -1350,19 +1424,33 @@
close.src_port = port_id;
close.dest_svc = APR_SVC_ADM;
close.dest_domain = APR_DOMAIN_ADSP;
- close.dest_port = atomic_read(&this_adm.copp_id[index]);
+ if (perf_mode)
+ close.dest_port =
+ atomic_read(&this_adm.copp_low_latency_id[index]);
+ else
+ close.dest_port = atomic_read(&this_adm.copp_id[index]);
close.token = port_id;
close.opcode = ADM_CMD_DEVICE_CLOSE_V5;
- atomic_set(&this_adm.copp_id[index], RESET_COPP_ID);
atomic_set(&this_adm.copp_stat[index], 0);
-
- pr_debug("%s:coppid %d portid=%#x index=%d coppcnt=%d\n",
+ if (perf_mode) {
+ pr_debug("%s:coppid %d portid=%#x index=%d coppcnt=%d\n",
+ __func__,
+ atomic_read(&this_adm.copp_low_latency_id[index]),
+ port_id, index,
+ atomic_read(&this_adm.copp_low_latency_cnt[index]));
+ atomic_set(&this_adm.copp_low_latency_id[index],
+ RESET_COPP_ID);
+ } else {
+ pr_debug("%s:coppid %d portid=%#x index=%d coppcnt=%d\n",
__func__,
atomic_read(&this_adm.copp_id[index]),
port_id, index,
atomic_read(&this_adm.copp_cnt[index]));
+ atomic_set(&this_adm.copp_id[index],
+ RESET_COPP_ID);
+ }
ret = apr_send_pkt(this_adm.apr, (uint32_t *)&close);
if (ret < 0) {
@@ -1380,7 +1468,10 @@
ret = -EINVAL;
goto fail_cmd;
}
+ }
+ if (!perf_mode) {
+ pr_debug("%s: remove adm device from rtac\n", __func__);
rtac_remove_adm_device(port_id);
}
@@ -1393,11 +1484,15 @@
int i = 0;
this_adm.apr = NULL;
this_adm.set_custom_topology = 1;
+ this_adm.ec_ref_rx = -1;
for (i = 0; i < AFE_MAX_PORTS; i++) {
atomic_set(&this_adm.copp_id[i], RESET_COPP_ID);
+ atomic_set(&this_adm.copp_low_latency_id[i], RESET_COPP_ID);
atomic_set(&this_adm.copp_cnt[i], 0);
+ atomic_set(&this_adm.copp_low_latency_cnt[i], 0);
atomic_set(&this_adm.copp_stat[i], 0);
+ atomic_set(&this_adm.copp_perf_mode[i], 0);
init_waitqueue_head(&this_adm.wait[i]);
}
return 0;
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index 2b0d155..9882ec7 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -50,12 +50,16 @@
};
static atomic_t afe_ports_mad_type[SLIMBUS_PORT_LAST - SLIMBUS_0_RX];
+static unsigned long afe_configured_cmd;
static struct afe_ctl this_afe;
#define TIMEOUT_MS 1000
#define Q6AFE_MAX_VOLUME 0x3FFF
+#define Q6AFE_MSM_SPKR_PROCESSING 0
+#define Q6AFE_MSM_SPKR_CALIBRATION 1
+
static int pcm_afe_instance[2];
static int proxy_afe_instance[2];
bool afe_close_done[2] = {true, true};
@@ -217,7 +221,7 @@
switch (port_id) {
case PRIMARY_I2S_RX:
- case PCM_RX:
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
case SECONDARY_I2S_RX:
case MI2S_RX:
case HDMI_RX:
@@ -241,7 +245,7 @@
break;
case PRIMARY_I2S_TX:
- case PCM_TX:
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
case SECONDARY_I2S_TX:
case MI2S_TX:
case DIGI_MIC_TX:
@@ -307,8 +311,8 @@
case RT_PROXY_PORT_001_TX:
ret_size = SIZEOF_CFG_CMD(afe_param_id_rt_proxy_port_cfg);
break;
- case PCM_RX:
- case PCM_TX:
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
case AFE_PORT_ID_SECONDARY_PCM_RX:
case AFE_PORT_ID_SECONDARY_PCM_TX:
default:
@@ -389,11 +393,23 @@
if ((this_afe.afe_cal_addr[path].cal_paddr != cal_block.cal_paddr) ||
(cal_block.cal_size > this_afe.afe_cal_addr[path].cal_size)) {
atomic_set(&this_afe.mem_map_cal_index, path);
- if (this_afe.afe_cal_addr[path].cal_paddr != 0)
- afe_cmd_memory_unmap(
+ if (this_afe.afe_cal_addr[path].cal_paddr != 0) {
+ result = afe_cmd_memory_unmap(
this_afe.afe_cal_addr[path].cal_paddr);
+ if (result) {
+ pr_err("%s: AFE memory unmap failed\n",
+ __func__);
+ atomic_set(&this_afe.mem_map_cal_index, -1);
+ goto done;
+ }
+ }
- afe_cmd_memory_map(cal_block.cal_paddr, size);
+ result = afe_cmd_memory_map(cal_block.cal_paddr, size);
+ if (result) {
+ pr_err("%s: AFE memory map failed\n", __func__);
+ atomic_set(&this_afe.mem_map_cal_index, -1);
+ goto done;
+ }
atomic_set(&this_afe.mem_map_cal_index, -1);
this_afe.afe_cal_addr[path].cal_paddr = cal_block.cal_paddr;
this_afe.afe_cal_addr[path].cal_size = size;
@@ -515,25 +531,31 @@
/*Get spkr protection cfg data*/
get_spk_protection_cfg(&prot_cfg);
- if ((!prot_cfg.mode || prot_cfg.mode == 1) &&
+ if ((prot_cfg.mode != MSM_SPKR_PROT_DISABLED) &&
(this_afe.vi_tx_port == port_id)) {
afe_spk_config.mode_rx_cfg.minor_version = 1;
- afe_spk_config.mode_rx_cfg.mode =
- (uint32_t)prot_cfg.mode;
+ if (prot_cfg.mode == MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS)
+ afe_spk_config.mode_rx_cfg.mode =
+ Q6AFE_MSM_SPKR_CALIBRATION;
+ else
+ afe_spk_config.mode_rx_cfg.mode =
+ Q6AFE_MSM_SPKR_PROCESSING;
if (afe_spk_prot_prepare(port_id,
AFE_PARAM_ID_MODE_VI_PROC_CFG,
&afe_spk_config))
pr_err("%s TX VI_PROC_CFG failed\n", __func__);
- afe_spk_config.vi_proc_cfg.minor_version = 1;
- afe_spk_config.vi_proc_cfg.r0_cali_q24 =
- (uint32_t) prot_cfg.r0;
- afe_spk_config.vi_proc_cfg.t0_cali_q6 =
- (uint32_t) prot_cfg.t0;
- if (afe_spk_prot_prepare(port_id,
- AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG,
- &afe_spk_config))
- pr_err("%s SPKR_CALIB_VI_PROC_CFG failed\n",
- __func__);
+ if (prot_cfg.mode != MSM_SPKR_PROT_NOT_CALIBRATED) {
+ afe_spk_config.vi_proc_cfg.minor_version = 1;
+ afe_spk_config.vi_proc_cfg.r0_cali_q24 =
+ (uint32_t) prot_cfg.r0;
+ afe_spk_config.vi_proc_cfg.t0_cali_q6 =
+ (uint32_t) prot_cfg.t0;
+ if (afe_spk_prot_prepare(port_id,
+ AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG,
+ &afe_spk_config))
+ pr_err("%s SPKR_CALIB_VI_PROC_CFG failed\n",
+ __func__);
+ }
}
}
@@ -545,9 +567,13 @@
/*Get spkr protection cfg data*/
get_spk_protection_cfg(&prot_cfg);
- if (!prot_cfg.mode || prot_cfg.mode == 1) {
- afe_spk_config.mode_rx_cfg.mode =
- (uint32_t)prot_cfg.mode;
+ if (prot_cfg.mode != MSM_SPKR_PROT_DISABLED) {
+ if (prot_cfg.mode == MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS)
+ afe_spk_config.mode_rx_cfg.mode =
+ Q6AFE_MSM_SPKR_CALIBRATION;
+ else
+ afe_spk_config.mode_rx_cfg.mode =
+ Q6AFE_MSM_SPKR_PROCESSING;
afe_spk_config.mode_rx_cfg.minor_version = 1;
if (afe_spk_prot_prepare(port_id,
AFE_PARAM_ID_FBSP_MODE_RX_CFG,
@@ -899,6 +925,44 @@
return ret;
}
+static int afe_send_bank_selection_clip(
+ struct afe_param_id_clip_bank_sel *param)
+{
+ int ret;
+ struct afe_svc_cmd_set_clip_bank_selection config;
+ if (!param) {
+ pr_err("%s: Invalid params", __func__);
+ return -EINVAL;
+ }
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ config.hdr.pkt_size = sizeof(config);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = IDX_GLOBAL_CFG;
+ config.hdr.opcode = AFE_SVC_CMD_SET_PARAM;
+
+ config.param.payload_size = sizeof(struct afe_port_param_data_v2) +
+ sizeof(struct afe_param_id_clip_bank_sel);
+ config.param.payload_address_lsw = 0x00;
+ config.param.payload_address_msw = 0x00;
+ config.param.mem_map_handle = 0x00;
+
+ config.pdata.module_id = AFE_MODULE_CDC_DEV_CFG;
+ config.pdata.param_id = AFE_PARAM_ID_CLIP_BANK_SEL_CFG;
+ config.pdata.param_size =
+ sizeof(struct afe_param_id_clip_bank_sel);
+ config.bank_sel = *param;
+ ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]);
+ if (ret) {
+ pr_err("%s: AFE_PARAM_ID_CLIP_BANK_SEL_CFG failed %d\n",
+ __func__, ret);
+ } else if (atomic_read(&this_afe.status) != 0) {
+ pr_err("%s: config cmd failed\n", __func__);
+ ret = -EAGAIN;
+ }
+ return ret;
+}
int afe_send_aanc_version(
struct afe_param_id_cdc_aanc_version *version_cfg)
{
@@ -991,15 +1055,39 @@
case AFE_AANC_VERSION:
ret = afe_send_aanc_version(config_data);
break;
+ case AFE_CLIP_BANK_SEL:
+ ret = afe_send_bank_selection_clip(config_data);
+ break;
+ case AFE_CDC_CLIP_REGISTERS_CONFIG:
+ ret = afe_send_codec_reg_config(config_data);
+ break;
default:
pr_err("%s: unknown configuration type", __func__);
ret = -EINVAL;
}
+ if (!ret)
+ set_bit(config_type, &afe_configured_cmd);
+
pr_debug("%s: leave ret %d\n", __func__, ret);
return ret;
}
+/*
+ * afe_clear_config - If SSR happens ADSP loses AFE configs, let AFE driver know
+ * about the state so client driver can wait until AFE is
+ * reconfigured.
+ */
+void afe_clear_config(enum afe_config_type config)
+{
+ clear_bit(config, &afe_configured_cmd);
+}
+
+bool afe_has_config(enum afe_config_type config)
+{
+ return !!test_bit(config, &afe_configured_cmd);
+}
+
static int afe_send_cmd_port_start(u16 port_id)
{
struct afe_port_cmd_device_start start;
@@ -1149,21 +1237,20 @@
config.hdr.token = index;
switch (port_id) {
- case PRIMARY_I2S_RX:
- case PRIMARY_I2S_TX:
- cfg_type = AFE_PARAM_ID_PCM_CONFIG;
- break;
- case PCM_RX:
- case PCM_TX:
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
case AFE_PORT_ID_SECONDARY_PCM_RX:
case AFE_PORT_ID_SECONDARY_PCM_TX:
cfg_type = AFE_PARAM_ID_PCM_CONFIG;
break;
+ case PRIMARY_I2S_RX:
+ case PRIMARY_I2S_TX:
case SECONDARY_I2S_RX:
case SECONDARY_I2S_TX:
case MI2S_RX:
case MI2S_TX:
case AFE_PORT_ID_PRIMARY_MI2S_RX:
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
case AFE_PORT_ID_SECONDARY_MI2S_RX:
case AFE_PORT_ID_SECONDARY_MI2S_TX:
case AFE_PORT_ID_TERTIARY_MI2S_RX:
@@ -1250,8 +1337,10 @@
switch (port_id) {
case PRIMARY_I2S_RX: return IDX_PRIMARY_I2S_RX;
case PRIMARY_I2S_TX: return IDX_PRIMARY_I2S_TX;
- case PCM_RX: return IDX_PCM_RX;
- case PCM_TX: return IDX_PCM_TX;
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ return IDX_AFE_PORT_ID_PRIMARY_PCM_RX;
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ return IDX_AFE_PORT_ID_PRIMARY_PCM_TX;
case AFE_PORT_ID_SECONDARY_PCM_RX:
return IDX_AFE_PORT_ID_SECONDARY_PCM_RX;
case AFE_PORT_ID_SECONDARY_PCM_TX:
@@ -1286,6 +1375,8 @@
case SLIMBUS_5_TX: return IDX_SLIMBUS_5_TX;
case AFE_PORT_ID_PRIMARY_MI2S_RX:
return IDX_AFE_PORT_ID_PRIMARY_MI2S_RX;
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_PRIMARY_MI2S_TX;
case AFE_PORT_ID_QUATERNARY_MI2S_RX:
return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX;
case AFE_PORT_ID_QUATERNARY_MI2S_TX:
@@ -1294,6 +1385,10 @@
return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX;
case AFE_PORT_ID_SECONDARY_MI2S_TX:
return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX;
default: return -EINVAL;
}
@@ -1349,8 +1444,8 @@
case PRIMARY_I2S_TX:
cfg_type = AFE_PARAM_ID_I2S_CONFIG;
break;
- case PCM_RX:
- case PCM_TX:
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
case AFE_PORT_ID_SECONDARY_PCM_RX:
case AFE_PORT_ID_SECONDARY_PCM_TX:
cfg_type = AFE_PARAM_ID_PCM_CONFIG;
@@ -1866,6 +1961,7 @@
pr_debug("%s: dma_addr_p 0x%x , size %d\n", __func__,
dma_addr_p, dma_buf_sz);
atomic_set(&this_afe.state, 1);
+ atomic_set(&this_afe.status, 0);
this_afe.mmap_handle = 0;
ret = apr_send_pkt(this_afe.apr, (uint32_t *) mmap_region_cmd);
if (ret < 0) {
@@ -1883,7 +1979,14 @@
ret = -EINVAL;
goto fail_cmd;
}
+ if (atomic_read(&this_afe.status) != 0) {
+ pr_err("%s: Memory map cmd failed\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
pr_debug("%s: mmap handle 0x%x\n", __func__, this_afe.mmap_handle);
+ kfree(mmap_region_cmd);
return 0;
fail_cmd:
kfree(mmap_region_cmd);
@@ -1949,6 +2052,7 @@
if (ret)
pr_err("%s: AFE memory map cmd failed %d\n",
__func__, ret);
+ kfree(mmap_region_cmd);
return ret;
}
int q6afe_audio_client_buf_free_contiguous(unsigned int dir,
@@ -2035,10 +2139,16 @@
/* Todo */
index = mregion.hdr.token = IDX_RSVD_2;
+ atomic_set(&this_afe.status, 0);
ret = afe_apr_send_pkt(&mregion, &this_afe.wait[index]);
if (ret)
pr_err("%s: AFE memory unmap cmd failed %d\n",
__func__, ret);
+ if (atomic_read(&this_afe.status) != 0) {
+ pr_err("%s: Memory unmap cmd failed\n", __func__);
+ ret = -EINVAL;
+ }
+
return ret;
}
@@ -2473,7 +2583,7 @@
ret = wait_event_timeout(this_afe.wait[index],
(atomic_read(&this_afe.state) == 0),
msecs_to_jiffies(TIMEOUT_MS));
- if (ret < 0) {
+ if (!ret) {
pr_err("%s: wait_event timeout\n", __func__);
ret = -EINVAL;
goto fail_cmd;
@@ -2538,8 +2648,8 @@
switch (port_id) {
case PRIMARY_I2S_RX:
case PRIMARY_I2S_TX:
- case PCM_RX:
- case PCM_TX:
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
case AFE_PORT_ID_SECONDARY_PCM_RX:
case AFE_PORT_ID_SECONDARY_PCM_TX:
case SECONDARY_I2S_RX:
@@ -2965,12 +3075,18 @@
}
int afe_spk_prot_feed_back_cfg(int src_port, int dst_port,
- int l_ch, int r_ch)
+ int l_ch, int r_ch, u32 enable)
{
int ret = -EINVAL;
union afe_spkr_prot_config prot_config;
int index = 0;
+ if (!enable) {
+ pr_debug("%s Disable Feedback tx path", __func__);
+ this_afe.vi_tx_port = -1;
+ return 0;
+ }
+
if ((q6audio_validate_port(src_port) < 0) ||
(q6audio_validate_port(dst_port) < 0)) {
pr_err("%s invalid ports src %d dst %d",
@@ -3011,6 +3127,7 @@
this_afe.apr = NULL;
this_afe.dtmf_gen_rx_portid = -1;
this_afe.mmap_handle = 0;
+ this_afe.vi_tx_port = -1;
for (i = 0; i < AFE_MAX_PORTS; i++)
init_waitqueue_head(&this_afe.wait[i]);
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 59d4de2..46d208b 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -45,17 +45,6 @@
#define TRUE 0x01
#define FALSE 0x00
-#define READDONE_IDX_STATUS 0
-#define READDONE_IDX_BUFADD_LSW 1
-#define READDONE_IDX_BUFADD_MSW 2
-#define READDONE_IDX_MEMMAP_HDL 3
-#define READDONE_IDX_SIZE 4
-#define READDONE_IDX_OFFSET 5
-#define READDONE_IDX_LSW_TS 6
-#define READDONE_IDX_MSW_TS 7
-#define READDONE_IDX_FLAGS 8
-#define READDONE_IDX_NUMFRAMES 9
-#define READDONE_IDX_SEQ_ID 10
/* TODO, combine them together */
static DEFINE_MUTEX(session_lock);
@@ -342,6 +331,7 @@
mutex_unlock(&session_lock);
ac->session = 0;
ac->perf_mode = 0;
+ ac->fptr_cache_ops = NULL;
return;
}
@@ -418,7 +408,7 @@
result = wait_event_timeout(ac->cmd_wait,
(atomic_read(&ac->cmd_state) == 0), 5*HZ);
- if (result < 0) {
+ if (!result) {
pr_err("%s: Set topologies failed payload = 0x%x\n",
__func__, cal_block.cal_paddr);
goto done;
@@ -518,16 +508,17 @@
int q6asm_mmap_apr_dereg(void)
{
- if (atomic_read(&this_mmap.ref_cnt) <= 0) {
- pr_err("%s: APR Common Port Already Closed\n", __func__);
- goto done;
- }
- atomic_dec(&this_mmap.ref_cnt);
- if (atomic_read(&this_mmap.ref_cnt) == 0) {
+ int c;
+
+ c = atomic_sub_return(1, &this_mmap.ref_cnt);
+ if (c == 0) {
apr_deregister(this_mmap.apr);
- pr_debug("%s:APR De-Register common port\n", __func__);
+ pr_debug("%s: APR De-Register common port\n", __func__);
+ } else if (c < 0) {
+ pr_err("%s: APR Common Port Already Closed\n", __func__);
+ atomic_set(&this_mmap.ref_cnt, 0);
}
-done:
+
return 0;
}
@@ -588,7 +579,8 @@
void *q6asm_mmap_apr_reg(void)
{
- if (atomic_read(&this_mmap.ref_cnt) == 0) {
+ if ((atomic_read(&this_mmap.ref_cnt) == 0) ||
+ (this_mmap.apr == NULL)) {
this_mmap.apr = apr_register("ADSP", "ASM", \
(apr_fn)q6asm_mmapcallback,\
0x0FFFFFFFF, &this_mmap);
@@ -621,6 +613,7 @@
ac->priv = priv;
ac->io_mode = SYNC_IO_MODE;
ac->perf_mode = false;
+ ac->fptr_cache_ops = NULL;
ac->apr = apr_register("ADSP", "ASM", \
(apr_fn)q6asm_callback,\
((ac->session) << 8 | 0x0001),\
@@ -1724,12 +1717,14 @@
run.time_lsw = lsw_ts;
run.time_msw = msw_ts;
+ /* have to increase first avoid race */
+ atomic_inc(&ac->nowait_cmd_cnt);
rc = apr_send_pkt(ac->apr, (uint32_t *) &run);
if (rc < 0) {
+ atomic_dec(&ac->nowait_cmd_cnt);
pr_err("%s:Commmand run failed[%d]", __func__, rc);
return -EINVAL;
}
- atomic_inc(&ac->nowait_cmd_cnt);
return 0;
}
@@ -3371,6 +3366,8 @@
struct list_head *ptr, *next;
u32 lbuf_addr_lsw;
u32 liomode;
+ u32 io_compressed;
+ int dir = 0;
if (!ac || ac->apr == NULL) {
pr_err("%s: APR handle NULL\n", __func__);
@@ -3387,13 +3384,22 @@
read.buf_size = param->len;
read.seq_id = param->uid;
liomode = (NT_MODE | ASYNC_IO_MODE);
- if (ac->io_mode == liomode)
+ io_compressed = (ASYNC_IO_MODE | COMPRESSED_IO);
+ if (ac->io_mode == liomode) {
lbuf_addr_lsw = (read.buf_addr_lsw - 32);
- else
+ /*legacy wma driver case*/
+ dir = IN;
+ } else if (ac->io_mode == io_compressed) {
+ lbuf_addr_lsw = (read.buf_addr_lsw - 64);
+ dir = OUT;
+ } else {
lbuf_addr_lsw = read.buf_addr_lsw;
+ dir = OUT;
+ }
- list_for_each_safe(ptr, next, &ac->port[IN].mem_map_handle) {
- buf_node = list_entry(ptr, struct asm_buffer_node, list);
+ list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) {
+ buf_node = list_entry(ptr, struct asm_buffer_node,
+ list);
if (buf_node->buf_addr_lsw == lbuf_addr_lsw) {
read.mem_map_handle = buf_node->mmap_hdl;
break;
@@ -3689,12 +3695,14 @@
pr_debug("%s:session[%d]opcode[0x%x] ", __func__,
ac->session,
hdr.opcode);
+ /* have to increase first avoid race */
+ atomic_inc(&ac->nowait_cmd_cnt);
rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr);
if (rc < 0) {
+ atomic_dec(&ac->nowait_cmd_cnt);
pr_err("%s:Commmand 0x%x failed\n", __func__, hdr.opcode);
goto fail_cmd;
}
- atomic_inc(&ac->nowait_cmd_cnt);
return 0;
fail_cmd:
return -EINVAL;
diff --git a/sound/soc/msm/qdsp6v2/q6audio-v2.c b/sound/soc/msm/qdsp6v2/q6audio-v2.c
index faf5f35..bc7ad4d 100644
--- a/sound/soc/msm/qdsp6v2/q6audio-v2.c
+++ b/sound/soc/msm/qdsp6v2/q6audio-v2.c
@@ -24,8 +24,10 @@
switch (port_id) {
case PRIMARY_I2S_RX: return IDX_PRIMARY_I2S_RX;
case PRIMARY_I2S_TX: return IDX_PRIMARY_I2S_TX;
- case PCM_RX: return IDX_PCM_RX;
- case PCM_TX: return IDX_PCM_TX;
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ return IDX_AFE_PORT_ID_PRIMARY_PCM_RX;
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ return IDX_AFE_PORT_ID_PRIMARY_PCM_TX;
case AFE_PORT_ID_SECONDARY_PCM_RX:
return IDX_AFE_PORT_ID_SECONDARY_PCM_RX;
case AFE_PORT_ID_SECONDARY_PCM_TX:
@@ -58,6 +60,8 @@
case RT_PROXY_PORT_001_TX: return IDX_RT_PROXY_PORT_001_TX;
case AFE_PORT_ID_PRIMARY_MI2S_RX:
return IDX_AFE_PORT_ID_PRIMARY_MI2S_RX;
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_PRIMARY_MI2S_TX;
case AFE_PORT_ID_QUATERNARY_MI2S_RX:
return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX;
case AFE_PORT_ID_QUATERNARY_MI2S_TX:
@@ -66,6 +70,10 @@
return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX;
case AFE_PORT_ID_SECONDARY_MI2S_TX:
return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX;
default: return -EINVAL;
}
@@ -74,10 +82,12 @@
int q6audio_get_port_id(u16 port_id)
{
switch (port_id) {
- case PRIMARY_I2S_RX: return AFE_PORT_ID_PRIMARY_MI2S_RX;
- case PRIMARY_I2S_TX: return AFE_PORT_ID_PRIMARY_MI2S_TX;
- case PCM_RX: return AFE_PORT_ID_PRIMARY_PCM_RX;
- case PCM_TX: return AFE_PORT_ID_PRIMARY_PCM_TX;
+ case PRIMARY_I2S_RX: return PRIMARY_I2S_RX;
+ case PRIMARY_I2S_TX: return PRIMARY_I2S_TX;
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ return AFE_PORT_ID_PRIMARY_PCM_RX;
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ return AFE_PORT_ID_PRIMARY_PCM_TX;
case AFE_PORT_ID_SECONDARY_PCM_RX:
return AFE_PORT_ID_SECONDARY_PCM_RX;
case AFE_PORT_ID_SECONDARY_PCM_TX:
@@ -110,6 +120,8 @@
case RT_PROXY_PORT_001_TX: return AFE_PORT_ID_RT_PROXY_PORT_001_TX;
case AFE_PORT_ID_PRIMARY_MI2S_RX:
return AFE_PORT_ID_PRIMARY_MI2S_RX;
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
+ return AFE_PORT_ID_PRIMARY_MI2S_TX;
case AFE_PORT_ID_QUATERNARY_MI2S_RX:
return AFE_PORT_ID_QUATERNARY_MI2S_RX;
case AFE_PORT_ID_QUATERNARY_MI2S_TX:
@@ -118,7 +130,10 @@
return AFE_PORT_ID_SECONDARY_MI2S_RX;
case AFE_PORT_ID_SECONDARY_MI2S_TX:
return AFE_PORT_ID_SECONDARY_MI2S_TX;
-
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ return AFE_PORT_ID_TERTIARY_MI2S_RX;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ return AFE_PORT_ID_TERTIARY_MI2S_TX;
default:
pr_warn("%s: Invalid port_id %d\n", __func__, port_id);
return -EINVAL;
@@ -152,8 +167,8 @@
switch (port_id) {
case PRIMARY_I2S_RX:
case PRIMARY_I2S_TX:
- case PCM_RX:
- case PCM_TX:
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
case AFE_PORT_ID_SECONDARY_PCM_RX:
case AFE_PORT_ID_SECONDARY_PCM_TX:
case SECONDARY_I2S_RX:
@@ -164,6 +179,10 @@
case AFE_PORT_ID_TERTIARY_MI2S_RX:
case AFE_PORT_ID_QUATERNARY_MI2S_RX:
case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+ case AFE_PORT_ID_PRIMARY_MI2S_RX:
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX:
break;
default:
ret = -EINVAL;
@@ -179,8 +198,8 @@
switch (port_id) {
case PRIMARY_I2S_RX:
case PRIMARY_I2S_TX:
- case PCM_RX:
- case PCM_TX:
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
case AFE_PORT_ID_SECONDARY_PCM_RX:
case AFE_PORT_ID_SECONDARY_PCM_TX:
case SECONDARY_I2S_RX:
@@ -210,6 +229,7 @@
case RT_PROXY_PORT_001_RX:
case RT_PROXY_PORT_001_TX:
case AFE_PORT_ID_PRIMARY_MI2S_RX:
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
case AFE_PORT_ID_QUATERNARY_MI2S_RX:
case AFE_PORT_ID_QUATERNARY_MI2S_TX:
case AFE_PORT_ID_SECONDARY_MI2S_RX:
diff --git a/sound/soc/msm/qdsp6v2/q6core.c b/sound/soc/msm/qdsp6v2/q6core.c
index 42cbcd1..e9352df 100644
--- a/sound/soc/msm/qdsp6v2/q6core.c
+++ b/sound/soc/msm/qdsp6v2/q6core.c
@@ -29,7 +29,8 @@
struct apr_svc *core_handle_q;
wait_queue_head_t bus_bw_req_wait;
u32 bus_bw_resp_received;
- struct avcs_cmd_rsp_get_low_power_segments_info_t *lp_ocm_payload;
+ struct avcs_cmd_rsp_get_low_power_segments_info_t lp_ocm_payload;
+ u32 param;
};
static struct q6core_str q6core_lcl;
@@ -74,19 +75,19 @@
pr_info("%s: cmd = AVCS_CMDRSP_GET_LOW_POWER_SEGMENTS_INFO num_segments = 0x%x\n",
__func__, payload1[0]);
nseg = payload1[0];
- q6core_lcl.lp_ocm_payload->num_segments = nseg;
- q6core_lcl.lp_ocm_payload->bandwidth = payload1[1];
+ q6core_lcl.lp_ocm_payload.num_segments = nseg;
+ q6core_lcl.lp_ocm_payload.bandwidth = payload1[1];
for (i = 0, j = 2; i < nseg; i++) {
- q6core_lcl.lp_ocm_payload->mem_segment[i].type =
+ q6core_lcl.lp_ocm_payload.mem_segment[i].type =
(payload1[j] & 0xffff);
- q6core_lcl.lp_ocm_payload->mem_segment[i].category =
+ q6core_lcl.lp_ocm_payload.mem_segment[i].category =
((payload1[j++] >> 16) & 0xffff);
- q6core_lcl.lp_ocm_payload->mem_segment[i].size =
+ q6core_lcl.lp_ocm_payload.mem_segment[i].size =
payload1[j++];
- q6core_lcl.lp_ocm_payload->
+ q6core_lcl.lp_ocm_payload.
mem_segment[i].start_address_lsw =
payload1[j++];
- q6core_lcl.lp_ocm_payload->
+ q6core_lcl.lp_ocm_payload.
mem_segment[i].start_address_msw =
payload1[j++];
}
@@ -102,6 +103,17 @@
break;
}
+ case AVCS_CMDRSP_ADSP_EVENT_GET_STATE:
+ payload1 = data->payload;
+ q6core_lcl.param = payload1[0];
+ pr_debug("%s: Received ADSP get state response 0x%x\n",
+ __func__, q6core_lcl.param);
+ /* ensure .param is updated prior to .bus_bw_resp_received */
+ wmb();
+ q6core_lcl.bus_bw_resp_received = 1;
+ wake_up(&q6core_lcl.bus_bw_req_wait);
+ break;
+
default:
pr_err("Message id from adsp core svc: %d\n", data->opcode);
break;
@@ -152,7 +164,6 @@
struct avcs_cmd_rsp_get_low_power_segments_info_t **lp_memseg)
{
struct avcs_cmd_get_low_power_segments_info lp_ocm_cmd;
- u8 *cptr = NULL;
int ret = 0;
pr_debug("%s: ", __func__);
@@ -163,16 +174,6 @@
return -ENODEV;
}
- cptr = kzalloc(
- sizeof(struct avcs_cmd_rsp_get_low_power_segments_info_t),
- GFP_KERNEL);
- if (!cptr) {
- pr_err("%s: Failed to allocate memory for low power segment struct\n",
- __func__);
- return -ENOMEM;
- }
- q6core_lcl.lp_ocm_payload =
- (struct avcs_cmd_rsp_get_low_power_segments_info_t *) cptr;
lp_ocm_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
@@ -201,13 +202,46 @@
goto fail_cmd;
}
- *lp_memseg = q6core_lcl.lp_ocm_payload;
+ *lp_memseg = &q6core_lcl.lp_ocm_payload;
return 0;
fail_cmd:
return ret;
}
+bool q6core_is_adsp_ready(void)
+{
+ int rc;
+ bool ret = false;
+ struct apr_hdr hdr;
+
+ pr_debug("%s: enter\n", __func__);
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, 0);
+ hdr.opcode = AVCS_CMD_ADSP_EVENT_GET_STATE;
+
+ ocm_core_open();
+ q6core_lcl.bus_bw_resp_received = 0;
+ rc = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)&hdr);
+ if (rc < 0) {
+ pr_err("%s: Get ADSP state APR packet send event\n", __func__);
+ goto bail;
+ }
+
+ rc = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
+ (q6core_lcl.bus_bw_resp_received == 1),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (rc > 0 && q6core_lcl.bus_bw_resp_received) {
+ /* ensure to read updated param by callback thread */
+ rmb();
+ ret = !!q6core_lcl.param;
+ }
+bail:
+ pr_debug("%s: leave, rc %d, adsp ready %d\n", __func__, rc, ret);
+ return ret;
+}
static int __init core_init(void)
{
@@ -215,14 +249,6 @@
q6core_lcl.bus_bw_resp_received = 0;
q6core_lcl.core_handle_q = NULL;
- q6core_lcl.lp_ocm_payload = kzalloc(
- sizeof(struct avcs_cmd_rsp_get_low_power_segments_info_t), GFP_KERNEL);
-
- if (!q6core_lcl.lp_ocm_payload) {
- pr_err("%s: Failed to allocate memory for low power segment struct\n",
- __func__);
- return -ENOMEM;
- }
return 0;
}
@@ -230,7 +256,7 @@
static void __exit core_exit(void)
{
- kfree(q6core_lcl.lp_ocm_payload);
+
}
module_exit(core_exit);
MODULE_DESCRIPTION("ADSP core driver");
diff --git a/sound/soc/msm/qdsp6v2/q6core.h b/sound/soc/msm/qdsp6v2/q6core.h
index 39bf4ab..e5a59bc 100644
--- a/sound/soc/msm/qdsp6v2/q6core.h
+++ b/sound/soc/msm/qdsp6v2/q6core.h
@@ -25,6 +25,9 @@
#define AVCS_CMDRSP_GET_LOW_POWER_SEGMENTS_INFO 0x00012904
+#define AVCS_CMD_ADSP_EVENT_GET_STATE 0x0001290C
+#define AVCS_CMDRSP_ADSP_EVENT_GET_STATE 0x0001290D
+
/* @brief AVCS_CMDRSP_GET_LOW_POWER_SEGMENTS_INFO payload
* structure. Payload for this event comprises one instance of
* avcs_cmd_rsp_get_low_power_segments_info_t, followed
@@ -89,6 +92,7 @@
int core_get_low_power_segments(
struct avcs_cmd_rsp_get_low_power_segments_info_t **);
+bool q6core_is_adsp_ready(void);
#define ADSP_CMD_SET_DOLBY_MANUFACTURER_ID 0x00012918
diff --git a/sound/soc/msm/qdsp6v2/q6lsm.c b/sound/soc/msm/qdsp6v2/q6lsm.c
index 49e5ede..c5483ee 100644
--- a/sound/soc/msm/qdsp6v2/q6lsm.c
+++ b/sound/soc/msm/qdsp6v2/q6lsm.c
@@ -31,6 +31,7 @@
#include <mach/memory.h>
#include <mach/debug_mm.h>
#include "audio_acdb.h"
+#include "q6core.h"
#define APR_TIMEOUT (5 * HZ)
#define LSM_CAL_SIZE 4096
@@ -70,7 +71,6 @@
static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv);
static int q6lsm_send_cal(struct lsm_client *client);
-static int q6lsm_snd_model_buf_free(struct lsm_client *client);
static int q6lsm_callback(struct apr_client_data *data, void *priv)
{
@@ -83,6 +83,13 @@
return -EINVAL;
}
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("%s: SSR event received 0x%x, event 0x%x, proc 0x%x\n",
+ __func__, data->opcode, data->reset_event,
+ data->reset_proc);
+ return 0;
+ }
+
payload = data->payload;
pr_debug("%s: Session %d opcode 0x%x token 0x%x payload size %d\n",
__func__, client->session,
@@ -311,6 +318,13 @@
int rc;
struct lsm_stream_cmd_open_tx open;
+ if (!afe_has_config(AFE_CDC_REGISTERS_CONFIG) ||
+ !afe_has_config(AFE_SLIMBUS_SLAVE_CONFIG)) {
+ pr_err("%s: AFE isn't configured yet\n", __func__);
+ rc = -EAGAIN;
+ goto exit;
+ }
+
memset(&open, 0, sizeof(open));
q6lsm_add_hdr(client, &open.hdr, sizeof(open), true);
@@ -323,6 +337,7 @@
pr_err("%s: Open failed opcode 0x%x, rc %d\n",
__func__, open.hdr.opcode, rc);
+exit:
pr_debug("%s: leave %d\n", __func__, rc);
return rc;
}
@@ -461,9 +476,10 @@
cmd.hdr.opcode, rc);
} else {
pr_debug("%s: Deregister sound model succeeded\n", __func__);
- q6lsm_snd_model_buf_free(client);
}
+ q6lsm_snd_model_buf_free(client);
+
return rc;
}
@@ -493,8 +509,8 @@
int rc;
int cmd_size = 0;
- pr_debug("%s: dma_addr_p 0x%x, dma_buf_sz %d, session %d\n",
- __func__, dma_addr_p, dma_buf_sz, client->session);
+ pr_debug("%s: dma_addr_p 0x%x, dma_buf_sz %d, mmap_p 0x%p, session %d\n",
+ __func__, dma_addr_p, dma_buf_sz, mmap_p, client->session);
cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions) +
sizeof(struct avs_shared_map_region_payload);
@@ -530,6 +546,29 @@
return rc;
}
+static int q6lsm_memory_unmap_regions(struct lsm_client *client,
+ uint32_t handle)
+{
+ struct avs_cmd_shared_mem_unmap_regions unmap;
+ int rc = 0;
+ int cmd_size = 0;
+
+ cmd_size = sizeof(struct avs_cmd_shared_mem_unmap_regions);
+ q6lsm_add_mmaphdr(client, &unmap.hdr, cmd_size,
+ true, (client->session << 8));
+ unmap.hdr.opcode = LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS;
+ unmap.mem_map_handle = handle;
+
+ pr_debug("%s: unmap handle 0x%x\n", __func__, unmap.mem_map_handle);
+ rc = q6lsm_apr_send_pkt(client, client->mmap_apr, &unmap, true,
+ NULL);
+ if (rc)
+ pr_err("%s: Failed mmap_regions opcode 0x%x rc %d\n",
+ __func__, unmap.hdr.opcode, rc);
+
+ return rc;
+}
+
static int q6lsm_send_cal(struct lsm_client *client)
{
int rc;
@@ -550,15 +589,20 @@
/* Cache mmap address, only map once or if new addr */
if ((lsm_common.lsm_cal_addr != lsm_cal.cal_paddr) ||
(lsm_cal.cal_size > lsm_common.lsm_cal_size)) {
- if (lsm_common.lsm_cal_addr != 0)
- afe_cmd_memory_unmap(lsm_cal.cal_paddr);
+ if (lsm_common.lsm_cal_addr != 0) {
+ rc = q6lsm_memory_unmap_regions(client,
+ lsm_common.mmap_handle_for_cal);
+ if (rc)
+ pr_warn("%s: Unmapping %x failed, %d\n",
+ __func__, lsm_common.lsm_cal_addr, rc);
+ }
rc = q6lsm_memory_map_regions(client, lsm_cal.cal_paddr,
LSM_CAL_SIZE,
&lsm_common.mmap_handle_for_cal);
if (rc < 0) {
- pr_err("%s: Calibration data memory map failed\n",
- __func__);
+ pr_err("%s: Calibration data memory map failed, %d\n",
+ __func__, rc);
goto bail;
}
lsm_common.lsm_cal_addr = lsm_cal.cal_paddr;
@@ -582,38 +626,18 @@
return rc;
}
-static int q6lsm_memory_unmap_regions(struct lsm_client *client)
-{
- struct avs_cmd_shared_mem_unmap_regions unmap;
- int rc = 0;
- int cmd_size = 0;
-
- cmd_size = sizeof(struct avs_cmd_shared_mem_unmap_regions);
- q6lsm_add_mmaphdr(client, &unmap.hdr, cmd_size,
- true, (client->session << 8));
- unmap.hdr.opcode = LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS;
- unmap.mem_map_handle = client->sound_model.mem_map_handle;
-
- pr_debug("%s: unmap handle 0x%x\n", __func__, unmap.mem_map_handle);
- rc = q6lsm_apr_send_pkt(client, client->mmap_apr, &unmap, true,
- NULL);
- if (rc)
- pr_err("%s: Failed mmap_regions opcode 0x%x rc %d\n",
- __func__, unmap.hdr.opcode, rc);
-
- return rc;
-}
-
-static int q6lsm_snd_model_buf_free(struct lsm_client *client)
+int q6lsm_snd_model_buf_free(struct lsm_client *client)
{
int rc;
pr_debug("%s: Session id %d\n", __func__, client->session);
mutex_lock(&client->cmd_lock);
- rc = q6lsm_memory_unmap_regions(client);
- if (rc < 0) {
+ rc = q6lsm_memory_unmap_regions(client,
+ client->sound_model.mem_map_handle);
+ if (rc < 0)
pr_err("%s CMD Memory_unmap_regions failed\n", __func__);
- } else if (client->sound_model.data) {
+
+ if (client->sound_model.data) {
ion_unmap_kernel(client->sound_model.client,
client->sound_model.handle);
ion_free(client->sound_model.client,
@@ -649,12 +673,22 @@
static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv)
{
unsigned long flags;
- uint32_t sid = 0;
+ uint32_t command;
+ uint32_t retcode;
+ uint32_t sid;
const uint32_t *payload = data->payload;
- const uint32_t command = payload[0];
- const uint32_t retcode = payload[1];
struct lsm_client *client = NULL;
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("%s: SSR event received 0x%x, event 0x%x, proc 0x%x\n",
+ __func__, data->opcode, data->reset_event,
+ data->reset_proc);
+ lsm_common.lsm_cal_addr = 0;
+ return 0;
+ }
+
+ command = payload[0];
+ retcode = payload[1];
pr_debug("%s: opcode 0x%x command 0x%x return code 0x%x\n", __func__,
data->opcode, command, retcode);
@@ -710,6 +744,7 @@
if (IS_ERR_OR_NULL(client->sound_model.client)) {
pr_err("%s: ION create client for AUDIO failed\n",
__func__);
+ mutex_unlock(&client->cmd_lock);
goto fail;
}
client->sound_model.handle =
@@ -718,6 +753,7 @@
if (IS_ERR_OR_NULL(client->sound_model.handle)) {
pr_err("%s: ION memory allocation for AUDIO failed\n",
__func__);
+ mutex_unlock(&client->cmd_lock);
goto fail;
}
@@ -728,6 +764,7 @@
if (rc) {
pr_err("%s: ION get physical mem failed, rc%d\n",
__func__, rc);
+ mutex_unlock(&client->cmd_lock);
goto fail;
}
@@ -736,6 +773,7 @@
client->sound_model.handle);
if (IS_ERR_OR_NULL(client->sound_model.data)) {
pr_err("%s: ION memory mapping failed\n", __func__);
+ mutex_unlock(&client->cmd_lock);
goto fail;
}
memset(client->sound_model.data, 0, len);
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
index 80bc4f9..2fabca1 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.c
+++ b/sound/soc/msm/qdsp6v2/q6voice.c
@@ -56,12 +56,12 @@
static int voice_send_disable_vocproc_cmd(struct voice_data *v);
static int voice_send_vol_index_cmd(struct voice_data *v);
static int voice_send_mvm_unmap_memory_physical_cmd(struct voice_data *v,
- unsigned int bufcnt);
+ uint32_t mem_handle);
static int voice_send_mvm_cal_network_cmd(struct voice_data *v);
static int voice_send_mvm_media_type_cmd(struct voice_data *v);
static int voice_send_cvs_data_exchange_mode_cmd(struct voice_data *v);
static int voice_send_cvs_packet_exchange_config_cmd(struct voice_data *v);
-static int voice_set_packet_exchange_mode_and_config(uint16_t session_id,
+static int voice_set_packet_exchange_mode_and_config(uint32_t session_id,
uint32_t mode);
static int voice_send_cvs_register_cal_cmd(struct voice_data *v);
@@ -82,10 +82,12 @@
static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv);
static int32_t qdsp_cvp_callback(struct apr_client_data *data, void *priv);
-static int voice_send_set_widevoice_enable_cmd(struct voice_data *v);
static int voice_send_set_pp_enable_cmd(struct voice_data *v,
uint32_t module_id, int enable);
+static struct voice_data *voice_get_session_by_idx(int idx);
+static int voice_get_idx_for_session(u32 session_id);
+
static u16 voice_get_mvm_handle(struct voice_data *v)
{
if (v == NULL) {
@@ -155,7 +157,7 @@
v->cvp_handle = cvp_handle;
}
-char *voc_get_session_name(u16 session_id)
+char *voc_get_session_name(u32 session_id)
{
char *session_name = NULL;
@@ -170,9 +172,9 @@
return session_name;
}
-uint16_t voc_get_session_id(char *name)
+uint32_t voc_get_session_id(char *name)
{
- u16 session_id = 0;
+ u32 session_id = 0;
if (name != NULL) {
if (!strncmp(name, "Voice session", 13))
@@ -193,41 +195,132 @@
return session_id;
}
-static struct voice_data *voice_get_session(u16 session_id)
+static struct voice_data *voice_get_session(u32 session_id)
{
struct voice_data *v = NULL;
- if ((session_id >= SESSION_ID_BASE) &&
- (session_id < SESSION_ID_BASE + MAX_VOC_SESSIONS)) {
- v = &common.voice[session_id - SESSION_ID_BASE];
+ switch (session_id) {
+ case VOICE_SESSION_VSID:
+ v = &common.voice[VOC_PATH_PASSIVE];
+ break;
+
+ case VOICE2_SESSION_VSID:
+ v = &common.voice[VOC_PATH_VOICE2_PASSIVE];
+ break;
+
+ case VOLTE_SESSION_VSID:
+ v = &common.voice[VOC_PATH_VOLTE_PASSIVE];
+ break;
+
+ case VOIP_SESSION_VSID:
+ v = &common.voice[VOC_PATH_FULL];
+ break;
+
+ case ALL_SESSION_VSID:
+ break;
+
+ default:
+ pr_err("%s: Invalid session_id : %x\n", __func__, session_id);
+
+ break;
}
- pr_debug("%s: session_id 0x%x session handle 0x%x\n",
- __func__, session_id, (unsigned int)v);
+ pr_debug("%s:session_id 0x%x session handle 0x%x\n",
+ __func__, session_id, (unsigned int)v);
return v;
}
-static bool is_voice_session(u16 session_id)
+static int voice_get_idx_for_session(u32 session_id)
+{
+ int idx = 0;
+
+ switch (session_id) {
+ case VOICE_SESSION_VSID:
+ idx = VOC_PATH_PASSIVE;
+ break;
+
+ case VOICE2_SESSION_VSID:
+ idx = VOC_PATH_VOICE2_PASSIVE;
+ break;
+
+ case VOLTE_SESSION_VSID:
+ idx = VOC_PATH_VOLTE_PASSIVE;
+ break;
+
+ case VOIP_SESSION_VSID:
+ idx = VOC_PATH_FULL;
+ break;
+
+ case ALL_SESSION_VSID:
+ idx = MAX_VOC_SESSIONS - 1;
+ break;
+
+ default:
+ pr_err("%s: Invalid session_id : %x\n", __func__, session_id);
+
+ break;
+ }
+
+ return idx;
+}
+
+static struct voice_data *voice_get_session_by_idx(int idx)
+{
+ return ((idx < 0 || idx >= MAX_VOC_SESSIONS) ?
+ NULL : &common.voice[idx]);
+}
+
+static bool is_voice_session(u32 session_id)
{
return (session_id == common.voice[VOC_PATH_PASSIVE].session_id);
}
-static bool is_voip_session(u16 session_id)
+static bool is_voip_session(u32 session_id)
{
return (session_id == common.voice[VOC_PATH_FULL].session_id);
}
-static bool is_volte_session(u16 session_id)
+static bool is_volte_session(u32 session_id)
{
return (session_id == common.voice[VOC_PATH_VOLTE_PASSIVE].session_id);
}
-static bool is_voice2_session(u16 session_id)
+static bool is_voice2_session(u32 session_id)
{
return (session_id == common.voice[VOC_PATH_VOICE2_PASSIVE].session_id);
}
+static bool is_other_session_active(u32 session_id)
+{
+ int i;
+ bool ret = false;
+
+ /* Check if there is other active session except the input one */
+ for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+ if (common.voice[i].session_id == session_id)
+ continue;
+
+ if ((common.voice[i].voc_state == VOC_RUN) ||
+ (common.voice[i].voc_state == VOC_CHANGE) ||
+ (common.voice[i].voc_state == VOC_STANDBY)) {
+ ret = true;
+ break;
+ }
+ }
+ pr_debug("%s: ret %d\n", __func__, ret);
+
+ return ret;
+}
+
+static void init_session_id(void)
+{
+ common.voice[VOC_PATH_PASSIVE].session_id = VOICE_SESSION_VSID;
+ common.voice[VOC_PATH_VOLTE_PASSIVE].session_id = VOLTE_SESSION_VSID;
+ common.voice[VOC_PATH_VOICE2_PASSIVE].session_id = VOICE2_SESSION_VSID;
+ common.voice[VOC_PATH_FULL].session_id = VOIP_SESSION_VSID;
+}
+
static int voice_apr_register(void)
{
void *modem_mvm, *modem_cvs, *modem_cvp;
@@ -365,7 +458,8 @@
APR_HDR_SIZE);
pr_debug("%s: send mvm Voice Ctl pkt size = %d\n",
__func__, mvm_voice_ctl_cmd.hdr.pkt_size);
- mvm_voice_ctl_cmd.hdr.src_port = v->session_id;
+ mvm_voice_ctl_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
mvm_voice_ctl_cmd.hdr.dest_port = mvm_handle;
mvm_voice_ctl_cmd.hdr.token = 0;
mvm_voice_ctl_cmd.hdr.opcode =
@@ -439,7 +533,8 @@
APR_HDR_SIZE);
pr_debug("%s: send mvm create session pkt size = %d\n",
__func__, mvm_session_cmd.hdr.pkt_size);
- mvm_session_cmd.hdr.src_port = v->session_id;
+ mvm_session_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
mvm_session_cmd.hdr.dest_port = 0;
mvm_session_cmd.hdr.token = 0;
mvm_session_cmd.hdr.opcode =
@@ -450,7 +545,7 @@
sizeof(mvm_session_cmd.mvm_session.name));
} else if (is_voice2_session(v->session_id)) {
strlcpy(mvm_session_cmd.mvm_session.name,
- VOICE2_SESSION_VSID,
+ VOICE2_SESSION_VSID_STR,
sizeof(mvm_session_cmd.mvm_session.name));
} else {
strlcpy(mvm_session_cmd.mvm_session.name,
@@ -483,7 +578,8 @@
APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(mvm_session_cmd) -
APR_HDR_SIZE);
- mvm_session_cmd.hdr.src_port = v->session_id;
+ mvm_session_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
mvm_session_cmd.hdr.dest_port = 0;
mvm_session_cmd.hdr.token = 0;
mvm_session_cmd.hdr.opcode =
@@ -527,7 +623,8 @@
APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_session_cmd) -
APR_HDR_SIZE);
- cvs_session_cmd.hdr.src_port = v->session_id;
+ cvs_session_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_session_cmd.hdr.dest_port = 0;
cvs_session_cmd.hdr.token = 0;
cvs_session_cmd.hdr.opcode =
@@ -538,7 +635,7 @@
sizeof(cvs_session_cmd.cvs_session.name));
} else if (is_voice2_session(v->session_id)) {
strlcpy(cvs_session_cmd.cvs_session.name,
- VOICE2_SESSION_VSID,
+ VOICE2_SESSION_VSID_STR,
sizeof(cvs_session_cmd.cvs_session.name));
} else {
strlcpy(cvs_session_cmd.cvs_session.name,
@@ -576,7 +673,8 @@
sizeof(cvs_full_ctl_cmd) -
APR_HDR_SIZE);
- cvs_full_ctl_cmd.hdr.src_port = v->session_id;
+ cvs_full_ctl_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_full_ctl_cmd.hdr.dest_port = 0;
cvs_full_ctl_cmd.hdr.token = 0;
cvs_full_ctl_cmd.hdr.opcode =
@@ -623,7 +721,8 @@
APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(attach_stream_cmd) -
APR_HDR_SIZE);
- attach_stream_cmd.hdr.src_port = v->session_id;
+ attach_stream_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
attach_stream_cmd.hdr.dest_port = mvm_handle;
attach_stream_cmd.hdr.token = 0;
attach_stream_cmd.hdr.opcode =
@@ -677,7 +776,7 @@
cvs_handle = voice_get_cvs_handle(v);
/* MVM, CVS sessions are destroyed only for Full control sessions. */
- if (is_voip_session(v->session_id) || v->voc_state == VOC_ERROR) {
+ if (is_voip_session(v->session_id)) {
pr_debug("%s: MVM detach stream, VOC_STATE: %d\n", __func__,
v->voc_state);
@@ -688,7 +787,8 @@
APR_PKT_VER);
detach_stream.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(detach_stream) - APR_HDR_SIZE);
- detach_stream.hdr.src_port = v->session_id;
+ detach_stream.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
detach_stream.hdr.dest_port = mvm_handle;
detach_stream.hdr.token = 0;
detach_stream.hdr.opcode = VSS_IMVM_CMD_DETACH_STREAM;
@@ -699,6 +799,7 @@
if (ret < 0) {
pr_err("%s: Error %d sending DETACH_STREAM\n",
__func__, ret);
+
goto fail;
}
ret = wait_event_timeout(v->mvm_wait,
@@ -706,9 +807,25 @@
msecs_to_jiffies(TIMEOUT_MS));
if (!ret) {
pr_err("%s: wait event timeout\n", __func__);
+
goto fail;
}
+ /* Unmap memory */
+ if (v->shmem_info.mem_handle != 0) {
+ ret = voice_send_mvm_unmap_memory_physical_cmd(v,
+ v->shmem_info.mem_handle);
+ if (ret < 0) {
+ pr_err("%s Memory_unmap for voip failed %d\n",
+ __func__, ret);
+
+ goto fail;
+ }
+ v->shmem_info.mem_handle = 0;
+ }
+ }
+
+ if (is_voip_session(v->session_id) || v->voc_state == VOC_ERROR) {
/* Destroy CVS. */
pr_debug("%s: CVS destroy session\n", __func__);
@@ -717,7 +834,8 @@
APR_PKT_VER);
cvs_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_destroy) - APR_HDR_SIZE);
- cvs_destroy.src_port = v->session_id;
+ cvs_destroy.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_destroy.dest_port = cvs_handle;
cvs_destroy.token = 0;
cvs_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION;
@@ -727,6 +845,7 @@
if (ret < 0) {
pr_err("%s: Error %d sending CVS DESTROY\n",
__func__, ret);
+
goto fail;
}
ret = wait_event_timeout(v->cvs_wait,
@@ -740,11 +859,21 @@
cvs_handle = 0;
voice_set_cvs_handle(v, cvs_handle);
- ret = voice_send_mvm_unmap_memory_physical_cmd(v,
- NUM_OF_BUFFERS);
- if (ret < 0) {
- pr_err("%s CMD Memory_unmap_regions failed %d\n",
- __func__, ret);
+ /* Unmap physical memory for calibration */
+ pr_debug("%s: cal_mem_handle %d\n", __func__,
+ common.cal_mem_handle);
+
+ if (!is_other_session_active(v->session_id) &&
+ (common.cal_mem_handle != 0)) {
+ ret = voice_send_mvm_unmap_memory_physical_cmd(v,
+ common.cal_mem_handle);
+ if (ret < 0) {
+ pr_err("%s Fail at cal mem unmap %d\n",
+ __func__, ret);
+
+ goto fail;
+ }
+ common.cal_mem_handle = 0;
}
/* Destroy MVM. */
@@ -755,7 +884,8 @@
APR_PKT_VER);
mvm_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(mvm_destroy) - APR_HDR_SIZE);
- mvm_destroy.src_port = v->session_id;
+ mvm_destroy.src_port =
+ voice_get_idx_for_session(v->session_id);
mvm_destroy.dest_port = mvm_handle;
mvm_destroy.token = 0;
mvm_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION;
@@ -814,7 +944,8 @@
APR_HDR_SIZE);
pr_debug("%s: pkt size = %d\n",
__func__, mvm_tty_mode_cmd.hdr.pkt_size);
- mvm_tty_mode_cmd.hdr.src_port = v->session_id;
+ mvm_tty_mode_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
mvm_tty_mode_cmd.hdr.dest_port = mvm_handle;
mvm_tty_mode_cmd.hdr.token = 0;
mvm_tty_mode_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_TTY_MODE;
@@ -841,56 +972,6 @@
return -EINVAL;
}
-static int voice_send_set_widevoice_enable_cmd(struct voice_data *v)
-{
- struct mvm_set_widevoice_enable_cmd mvm_set_wv_cmd;
- int ret = 0;
- void *apr_mvm;
- u16 mvm_handle;
-
- if (v == NULL) {
- pr_err("%s: v is NULL\n", __func__);
- return -EINVAL;
- }
- apr_mvm = common.apr_q6_mvm;
-
- if (!apr_mvm) {
- pr_err("%s: apr_mvm is NULL.\n", __func__);
- return -EINVAL;
- }
- mvm_handle = voice_get_mvm_handle(v);
-
- mvm_set_wv_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
- APR_HDR_LEN(APR_HDR_SIZE),
- APR_PKT_VER);
- mvm_set_wv_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
- sizeof(mvm_set_wv_cmd) -
- APR_HDR_SIZE);
- mvm_set_wv_cmd.hdr.src_port = v->session_id;
- mvm_set_wv_cmd.hdr.dest_port = mvm_handle;
- mvm_set_wv_cmd.hdr.token = 0;
- mvm_set_wv_cmd.hdr.opcode = VSS_IWIDEVOICE_CMD_SET_WIDEVOICE;
-
- mvm_set_wv_cmd.vss_set_wv.enable = v->wv_enable;
-
- v->mvm_state = CMD_STATUS_FAIL;
- ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_wv_cmd);
- if (ret < 0) {
- pr_err("Fail: sending mvm set widevoice enable,\n");
- goto fail;
- }
- ret = wait_event_timeout(v->mvm_wait,
- (v->mvm_state == CMD_STATUS_SUCCESS),
- msecs_to_jiffies(TIMEOUT_MS));
- if (!ret) {
- pr_err("%s: wait_event timeout\n", __func__);
- goto fail;
- }
- return 0;
-fail:
- return -EINVAL;
-}
-
static int voice_send_set_pp_enable_cmd(struct voice_data *v,
uint32_t module_id, int enable)
{
@@ -917,7 +998,7 @@
cvs_set_pp_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_set_pp_cmd) -
APR_HDR_SIZE);
- cvs_set_pp_cmd.hdr.src_port = v->session_id;
+ cvs_set_pp_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id);
cvs_set_pp_cmd.hdr.dest_port = cvs_handle;
cvs_set_pp_cmd.hdr.token = 0;
cvs_set_pp_cmd.hdr.opcode = VSS_ICOMMON_CMD_SET_UI_PROPERTY;
@@ -975,7 +1056,8 @@
APR_PKT_VER);
cvs_set_dtx.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_set_dtx) - APR_HDR_SIZE);
- cvs_set_dtx.hdr.src_port = v->session_id;
+ cvs_set_dtx.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_set_dtx.hdr.dest_port = cvs_handle;
cvs_set_dtx.hdr.token = 0;
cvs_set_dtx.hdr.opcode = VSS_ISTREAM_CMD_SET_ENC_DTX_MODE;
@@ -1028,7 +1110,8 @@
mvm_set_cal_media_type.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(mvm_set_cal_media_type) -
APR_HDR_SIZE);
- mvm_set_cal_media_type.hdr.src_port = v->session_id;
+ mvm_set_cal_media_type.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
mvm_set_cal_media_type.hdr.dest_port = mvm_handle;
mvm_set_cal_media_type.hdr.token = 0;
mvm_set_cal_media_type.hdr.opcode = VSS_IMVM_CMD_SET_CAL_MEDIA_TYPE;
@@ -1084,7 +1167,8 @@
cvs_dtmf_rx_detection.hdr.pkt_size =
APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_dtmf_rx_detection) - APR_HDR_SIZE);
- cvs_dtmf_rx_detection.hdr.src_port = v->session_id;
+ cvs_dtmf_rx_detection.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_dtmf_rx_detection.hdr.dest_port = cvs_handle;
cvs_dtmf_rx_detection.hdr.token = 0;
cvs_dtmf_rx_detection.hdr.opcode =
@@ -1130,7 +1214,7 @@
}
}
-int voc_enable_dtmf_rx_detection(uint16_t session_id, uint32_t enable)
+int voc_enable_dtmf_rx_detection(uint32_t session_id, uint32_t enable)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -1181,7 +1265,8 @@
cvs_set_media_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_set_media_cmd) -
APR_HDR_SIZE);
- cvs_set_media_cmd.hdr.src_port = v->session_id;
+ cvs_set_media_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_set_media_cmd.hdr.dest_port = cvs_handle;
cvs_set_media_cmd.hdr.token = 0;
cvs_set_media_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_MEDIA_TYPE;
@@ -1207,7 +1292,9 @@
}
/* Set encoder properties. */
switch (common.mvs_info.media_type) {
- case VSS_MEDIA_ID_EVRC_MODEM: {
+ case VSS_MEDIA_ID_EVRC_MODEM:
+ case VSS_MEDIA_ID_4GV_NB_MODEM:
+ case VSS_MEDIA_ID_4GV_WB_MODEM: {
struct cvs_set_cdma_enc_minmax_rate_cmd cvs_set_cdma_rate;
pr_debug("Setting EVRC min-max rate\n");
@@ -1218,7 +1305,8 @@
APR_PKT_VER);
cvs_set_cdma_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_set_cdma_rate) - APR_HDR_SIZE);
- cvs_set_cdma_rate.hdr.src_port = v->session_id;
+ cvs_set_cdma_rate.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_set_cdma_rate.hdr.dest_port = cvs_handle;
cvs_set_cdma_rate.hdr.token = 0;
cvs_set_cdma_rate.hdr.opcode =
@@ -1255,7 +1343,8 @@
APR_PKT_VER);
cvs_set_amr_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_set_amr_rate) - APR_HDR_SIZE);
- cvs_set_amr_rate.hdr.src_port = v->session_id;
+ cvs_set_amr_rate.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_set_amr_rate.hdr.dest_port = cvs_handle;
cvs_set_amr_rate.hdr.token = 0;
cvs_set_amr_rate.hdr.opcode =
@@ -1296,7 +1385,8 @@
cvs_set_amrwb_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_set_amrwb_rate) -
APR_HDR_SIZE);
- cvs_set_amrwb_rate.hdr.src_port = v->session_id;
+ cvs_set_amrwb_rate.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_set_amrwb_rate.hdr.dest_port = cvs_handle;
cvs_set_amrwb_rate.hdr.token = 0;
cvs_set_amrwb_rate.hdr.opcode =
@@ -1368,7 +1458,8 @@
sizeof(mvm_start_voice_cmd) - APR_HDR_SIZE);
pr_debug("send mvm_start_voice_cmd pkt size = %d\n",
mvm_start_voice_cmd.pkt_size);
- mvm_start_voice_cmd.src_port = v->session_id;
+ mvm_start_voice_cmd.src_port =
+ voice_get_idx_for_session(v->session_id);
mvm_start_voice_cmd.dest_port = mvm_handle;
mvm_start_voice_cmd.token = 0;
mvm_start_voice_cmd.opcode = VSS_IMVM_CMD_START_VOICE;
@@ -1418,7 +1509,8 @@
sizeof(cvp_disable_cmd) - APR_HDR_SIZE);
pr_debug("cvp_disable_cmd pkt size = %d, cvp_handle=%d\n",
cvp_disable_cmd.pkt_size, cvp_handle);
- cvp_disable_cmd.src_port = v->session_id;
+ cvp_disable_cmd.src_port =
+ voice_get_idx_for_session(v->session_id);
cvp_disable_cmd.dest_port = cvp_handle;
cvp_disable_cmd.token = 0;
cvp_disable_cmd.opcode = VSS_IVOCPROC_CMD_DISABLE;
@@ -1442,6 +1534,34 @@
return -EINVAL;
}
+static void voc_get_tx_rx_topology(struct voice_data *v,
+ uint32_t *tx_topology_id,
+ uint32_t *rx_topology_id)
+{
+ uint32_t tx_id = 0;
+ uint32_t rx_id = 0;
+
+ if (v->lch_mode == VOICE_LCH_START) {
+ pr_debug("%s: Setting TX and RX topology to NONE for LCH\n",
+ __func__);
+
+ tx_id = VSS_IVOCPROC_TOPOLOGY_ID_NONE;
+ rx_id = VSS_IVOCPROC_TOPOLOGY_ID_NONE;
+ } else {
+ /* Use default topology if invalid value in ACDB */
+ tx_id = get_voice_tx_topology();
+ if (tx_id == 0)
+ tx_id = VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS;
+
+ rx_id = get_voice_rx_topology();
+ if (rx_id == 0)
+ rx_id = VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT;
+ }
+
+ *tx_topology_id = tx_id;
+ *rx_topology_id = rx_id;
+}
+
static int voice_send_set_device_cmd(struct voice_data *v)
{
struct cvp_set_device_cmd cvp_setdev_cmd;
@@ -1469,29 +1589,26 @@
sizeof(cvp_setdev_cmd) - APR_HDR_SIZE);
pr_debug(" send create cvp setdev, pkt size = %d\n",
cvp_setdev_cmd.hdr.pkt_size);
- cvp_setdev_cmd.hdr.src_port = v->session_id;
+ cvp_setdev_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvp_setdev_cmd.hdr.dest_port = cvp_handle;
cvp_setdev_cmd.hdr.token = 0;
- cvp_setdev_cmd.hdr.opcode = VSS_IVOCPROC_CMD_SET_DEVICE;
+ cvp_setdev_cmd.hdr.opcode = VSS_IVOCPROC_CMD_SET_DEVICE_V2;
- /* Use default topology if invalid value in ACDB */
- cvp_setdev_cmd.cvp_set_device.tx_topology_id =
- get_voice_tx_topology();
- if (cvp_setdev_cmd.cvp_set_device.tx_topology_id == 0)
- cvp_setdev_cmd.cvp_set_device.tx_topology_id =
- VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS;
+ voc_get_tx_rx_topology(v,
+ &cvp_setdev_cmd.cvp_set_device_v2.tx_topology_id,
+ &cvp_setdev_cmd.cvp_set_device_v2.rx_topology_id);
- cvp_setdev_cmd.cvp_set_device.rx_topology_id =
- get_voice_rx_topology();
- if (cvp_setdev_cmd.cvp_set_device.rx_topology_id == 0)
- cvp_setdev_cmd.cvp_set_device.rx_topology_id =
- VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT;
- cvp_setdev_cmd.cvp_set_device.tx_port_id = v->dev_tx.port_id;
- cvp_setdev_cmd.cvp_set_device.rx_port_id = v->dev_rx.port_id;
+ cvp_setdev_cmd.cvp_set_device_v2.tx_port_id = v->dev_tx.port_id;
+ cvp_setdev_cmd.cvp_set_device_v2.rx_port_id = v->dev_rx.port_id;
+ cvp_setdev_cmd.cvp_set_device_v2.vocproc_mode =
+ VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING;
+ cvp_setdev_cmd.cvp_set_device_v2.ec_ref_port_id =
+ VSS_IVOCPROC_PORT_ID_NONE;
pr_debug("topology=%d , tx_port_id=%d, rx_port_id=%d\n",
- cvp_setdev_cmd.cvp_set_device.tx_topology_id,
- cvp_setdev_cmd.cvp_set_device.tx_port_id,
- cvp_setdev_cmd.cvp_set_device.rx_port_id);
+ cvp_setdev_cmd.cvp_set_device_v2.tx_topology_id,
+ cvp_setdev_cmd.cvp_set_device_v2.tx_port_id,
+ cvp_setdev_cmd.cvp_set_device_v2.rx_port_id);
v->cvp_state = CMD_STATUS_FAIL;
ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_setdev_cmd);
@@ -1539,7 +1656,8 @@
sizeof(mvm_stop_voice_cmd) - APR_HDR_SIZE);
pr_debug("send mvm_stop_voice_cmd pkt size = %d\n",
mvm_stop_voice_cmd.pkt_size);
- mvm_stop_voice_cmd.src_port = v->session_id;
+ mvm_stop_voice_cmd.src_port =
+ voice_get_idx_for_session(v->session_id);
mvm_stop_voice_cmd.dest_port = mvm_handle;
mvm_stop_voice_cmd.token = 0;
mvm_stop_voice_cmd.opcode = VSS_IMVM_CMD_STOP_VOICE;
@@ -1599,7 +1717,8 @@
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
cvs_reg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_reg_cal_cmd) - APR_HDR_SIZE);
- cvs_reg_cal_cmd.hdr.src_port = v->session_id;
+ cvs_reg_cal_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_reg_cal_cmd.hdr.dest_port = voice_get_cvs_handle(v);
cvs_reg_cal_cmd.hdr.token = 0;
cvs_reg_cal_cmd.hdr.opcode =
@@ -1664,7 +1783,8 @@
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
cvs_dereg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_dereg_cal_cmd) - APR_HDR_SIZE);
- cvs_dereg_cal_cmd.hdr.src_port = v->session_id;
+ cvs_dereg_cal_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_dereg_cal_cmd.hdr.dest_port = voice_get_cvs_handle(v);
cvs_dereg_cal_cmd.hdr.token = 0;
cvs_dereg_cal_cmd.hdr.opcode =
@@ -1729,7 +1849,8 @@
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
cvp_reg_dev_cfg_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvp_reg_dev_cfg_cmd) - APR_HDR_SIZE);
- cvp_reg_dev_cfg_cmd.hdr.src_port = v->session_id;
+ cvp_reg_dev_cfg_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvp_reg_dev_cfg_cmd.hdr.dest_port = voice_get_cvp_handle(v);
cvp_reg_dev_cfg_cmd.hdr.token = 0;
cvp_reg_dev_cfg_cmd.hdr.opcode =
@@ -1791,7 +1912,8 @@
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
cvp_dereg_dev_cfg_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvp_dereg_dev_cfg_cmd) - APR_HDR_SIZE);
- cvp_dereg_dev_cfg_cmd.hdr.src_port = v->session_id;
+ cvp_dereg_dev_cfg_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvp_dereg_dev_cfg_cmd.hdr.dest_port = voice_get_cvp_handle(v);
cvp_dereg_dev_cfg_cmd.hdr.token = 0;
cvp_dereg_dev_cfg_cmd.hdr.opcode =
@@ -1857,7 +1979,8 @@
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
cvp_reg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvp_reg_cal_cmd) - APR_HDR_SIZE);
- cvp_reg_cal_cmd.hdr.src_port = v->session_id;
+ cvp_reg_cal_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvp_reg_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
cvp_reg_cal_cmd.hdr.token = 0;
cvp_reg_cal_cmd.hdr.opcode =
@@ -1922,7 +2045,8 @@
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
cvp_dereg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvp_dereg_cal_cmd) - APR_HDR_SIZE);
- cvp_dereg_cal_cmd.hdr.src_port = v->session_id;
+ cvp_dereg_cal_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvp_dereg_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
cvp_dereg_cal_cmd.hdr.token = 0;
cvp_dereg_cal_cmd.hdr.opcode =
@@ -1986,7 +2110,8 @@
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
cvp_reg_vol_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvp_reg_vol_cal_cmd) - APR_HDR_SIZE);
- cvp_reg_vol_cal_cmd.hdr.src_port = v->session_id;
+ cvp_reg_vol_cal_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvp_reg_vol_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
cvp_reg_vol_cal_cmd.hdr.token = 0;
cvp_reg_vol_cal_cmd.hdr.opcode =
@@ -2055,7 +2180,8 @@
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
cvp_dereg_vol_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvp_dereg_vol_cal_cmd) - APR_HDR_SIZE);
- cvp_dereg_vol_cal_cmd.hdr.src_port = v->session_id;
+ cvp_dereg_vol_cal_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvp_dereg_vol_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
cvp_dereg_vol_cal_cmd.hdr.token = 0;
cvp_dereg_vol_cal_cmd.hdr.opcode =
@@ -2136,7 +2262,8 @@
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
mvm_map_phys_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(mvm_map_phys_cmd) - APR_HDR_SIZE);
- mvm_map_phys_cmd.hdr.src_port = v->session_id;
+ mvm_map_phys_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
mvm_map_phys_cmd.hdr.dest_port = voice_get_mvm_handle(v);
mvm_map_phys_cmd.hdr.token = token;
mvm_map_phys_cmd.hdr.opcode = VSS_IMEMORY_CMD_MAP_PHYSICAL;
@@ -2241,24 +2368,16 @@
sizeof(cvp_session_cmd) - APR_HDR_SIZE);
pr_debug(" send create cvp session, pkt size = %d\n",
cvp_session_cmd.hdr.pkt_size);
- cvp_session_cmd.hdr.src_port = v->session_id;
+ cvp_session_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvp_session_cmd.hdr.dest_port = 0;
cvp_session_cmd.hdr.token = 0;
cvp_session_cmd.hdr.opcode =
VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V2;
- /* Use default topology if invalid value in ACDB */
- cvp_session_cmd.cvp_session.tx_topology_id =
- get_voice_tx_topology();
- if (cvp_session_cmd.cvp_session.tx_topology_id == 0)
- cvp_session_cmd.cvp_session.tx_topology_id =
- VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS;
-
- cvp_session_cmd.cvp_session.rx_topology_id =
- get_voice_rx_topology();
- if (cvp_session_cmd.cvp_session.rx_topology_id == 0)
- cvp_session_cmd.cvp_session.rx_topology_id =
- VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT;
+ voc_get_tx_rx_topology(v,
+ &cvp_session_cmd.cvp_session.tx_topology_id,
+ &cvp_session_cmd.cvp_session.rx_topology_id);
cvp_session_cmd.cvp_session.direction = 2; /*tx and rx*/
cvp_session_cmd.cvp_session.tx_port_id = v->dev_tx.port_id;
@@ -2327,20 +2446,11 @@
voice_send_netid_timing_cmd(v);
}
- /* enable widevoice if wv_enable is set */
- if (v->wv_enable)
- voice_send_set_widevoice_enable_cmd(v);
-
/* enable slowtalk if st_enable is set */
if (v->st_enable)
voice_send_set_pp_enable_cmd(v,
MODULE_ID_VOICE_MODULE_ST,
v->st_enable);
-
- voice_send_set_pp_enable_cmd(v,
- MODULE_ID_VOICE_MODULE_FENS,
- v->fens_enable);
-
/* Start in-call music delivery if this feature is enabled */
if (v->music_info.play_enable)
voice_cvs_start_playback(v);
@@ -2390,7 +2500,8 @@
sizeof(cvp_enable_cmd) - APR_HDR_SIZE);
pr_debug("cvp_enable_cmd pkt size = %d, cvp_handle=%d\n",
cvp_enable_cmd.pkt_size, cvp_handle);
- cvp_enable_cmd.src_port = v->session_id;
+ cvp_enable_cmd.src_port =
+ voice_get_idx_for_session(v->session_id);
cvp_enable_cmd.dest_port = cvp_handle;
cvp_enable_cmd.token = 0;
cvp_enable_cmd.opcode = VSS_IVOCPROC_CMD_ENABLE;
@@ -2438,7 +2549,8 @@
APR_PKT_VER);
mvm_set_cal_network.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(mvm_set_cal_network) - APR_HDR_SIZE);
- mvm_set_cal_network.hdr.src_port = v->session_id;
+ mvm_set_cal_network.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
mvm_set_cal_network.hdr.dest_port = mvm_handle;
mvm_set_cal_network.hdr.token = 0;
mvm_set_cal_network.hdr.opcode = VSS_IMVM_CMD_SET_CAL_NETWORK;
@@ -2497,7 +2609,8 @@
APR_PKT_VER);
mvm_set_network.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(mvm_set_network) - APR_HDR_SIZE);
- mvm_set_network.hdr.src_port = v->session_id;
+ mvm_set_network.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
mvm_set_network.hdr.dest_port = mvm_handle;
mvm_set_network.hdr.token = 0;
mvm_set_network.hdr.opcode = VSS_ICOMMON_CMD_SET_NETWORK;
@@ -2527,7 +2640,8 @@
mvm_set_voice_timing.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(mvm_set_voice_timing) -
APR_HDR_SIZE);
- mvm_set_voice_timing.hdr.src_port = v->session_id;
+ mvm_set_voice_timing.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
mvm_set_voice_timing.hdr.dest_port = mvm_handle;
mvm_set_voice_timing.hdr.token = 0;
mvm_set_voice_timing.hdr.opcode = VSS_ICOMMON_CMD_SET_VOICE_TIMING;
@@ -2585,7 +2699,8 @@
sizeof(mvm_a_vocproc_cmd) - APR_HDR_SIZE);
pr_debug("send mvm_a_vocproc_cmd pkt size = %d\n",
mvm_a_vocproc_cmd.hdr.pkt_size);
- mvm_a_vocproc_cmd.hdr.src_port = v->session_id;
+ mvm_a_vocproc_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
mvm_a_vocproc_cmd.hdr.dest_port = mvm_handle;
mvm_a_vocproc_cmd.hdr.token = 0;
mvm_a_vocproc_cmd.hdr.opcode = VSS_IMVM_CMD_ATTACH_VOCPROC;
@@ -2643,6 +2758,9 @@
if (v->dtmf_rx_detect_en)
voice_send_dtmf_rx_detection_cmd(v, 0);
+ /* reset LCH mode */
+ v->lch_mode = 0;
+
/* detach VOCPROC and wait for response from mvm */
mvm_d_vocproc_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE),
@@ -2651,7 +2769,8 @@
sizeof(mvm_d_vocproc_cmd) - APR_HDR_SIZE);
pr_debug("mvm_d_vocproc_cmd pkt size = %d\n",
mvm_d_vocproc_cmd.hdr.pkt_size);
- mvm_d_vocproc_cmd.hdr.src_port = v->session_id;
+ mvm_d_vocproc_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
mvm_d_vocproc_cmd.hdr.dest_port = mvm_handle;
mvm_d_vocproc_cmd.hdr.token = 0;
mvm_d_vocproc_cmd.hdr.opcode = VSS_IMVM_CMD_DETACH_VOCPROC;
@@ -2685,7 +2804,8 @@
sizeof(cvp_destroy_session_cmd) - APR_HDR_SIZE);
pr_debug("cvp_destroy_session_cmd pkt size = %d\n",
cvp_destroy_session_cmd.pkt_size);
- cvp_destroy_session_cmd.src_port = v->session_id;
+ cvp_destroy_session_cmd.src_port =
+ voice_get_idx_for_session(v->session_id);
cvp_destroy_session_cmd.dest_port = cvp_handle;
cvp_destroy_session_cmd.token = 0;
cvp_destroy_session_cmd.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION;
@@ -2713,7 +2833,7 @@
}
static int voice_send_mvm_unmap_memory_physical_cmd(struct voice_data *v,
- unsigned int bufcnt)
+ uint32_t mem_handle)
{
struct vss_imemory_cmd_unmap_t mem_unmap;
int ret = 0;
@@ -2737,11 +2857,12 @@
APR_PKT_VER);
mem_unmap.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(mem_unmap) - APR_HDR_SIZE);
- mem_unmap.hdr.src_port = v->session_id;
+ mem_unmap.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
mem_unmap.hdr.dest_port = mvm_handle;
mem_unmap.hdr.token = 0;
mem_unmap.hdr.opcode = VSS_IMEMORY_CMD_UNMAP;
- mem_unmap.mem_handle = v->shmem_info.mem_handle;
+ mem_unmap.mem_handle = mem_handle;
pr_debug("%s: mem_handle: ox%x\n", __func__, mem_unmap.mem_handle);
@@ -2798,7 +2919,8 @@
packet_exchange_config_pkt.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(packet_exchange_config_pkt) -
APR_HDR_SIZE);
- packet_exchange_config_pkt.hdr.src_port = v->session_id;
+ packet_exchange_config_pkt.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
packet_exchange_config_pkt.hdr.dest_port = cvs_handle;
packet_exchange_config_pkt.hdr.token = 0;
packet_exchange_config_pkt.hdr.opcode =
@@ -2858,7 +2980,8 @@
APR_PKT_VER);
data_exchange_pkt.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(data_exchange_pkt) - APR_HDR_SIZE);
- data_exchange_pkt.hdr.src_port = v->session_id;
+ data_exchange_pkt.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
data_exchange_pkt.hdr.dest_port = cvs_handle;
data_exchange_pkt.hdr.token = 0;
data_exchange_pkt.hdr.opcode = VSS_ISTREAM_CMD_SET_PACKET_EXCHANGE_MODE;
@@ -2882,7 +3005,7 @@
return -EINVAL;
}
-static int voice_send_mute_cmd(struct voice_data *v)
+static int voice_send_stream_mute_cmd(struct voice_data *v)
{
struct cvs_set_mute_cmd cvs_mute_cmd;
int ret = 0;
@@ -2905,12 +3028,13 @@
APR_PKT_VER);
cvs_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_mute_cmd) - APR_HDR_SIZE);
- cvs_mute_cmd.hdr.src_port = v->session_id;
+ cvs_mute_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_mute_cmd.hdr.dest_port = voice_get_cvs_handle(v);
cvs_mute_cmd.hdr.token = 0;
cvs_mute_cmd.hdr.opcode = VSS_IVOLUME_CMD_MUTE_V2;
cvs_mute_cmd.cvs_set_mute.direction = VSS_IVOLUME_DIRECTION_TX;
- cvs_mute_cmd.cvs_set_mute.mute_flag = v->dev_tx.mute;
+ cvs_mute_cmd.cvs_set_mute.mute_flag = v->stream_tx.stream_mute;
cvs_mute_cmd.cvs_set_mute.ramp_duration_ms = DEFAULT_MUTE_RAMP_DURATION;
v->cvs_state = CMD_STATUS_FAIL;
@@ -2935,7 +3059,8 @@
return -EINVAL;
}
-static int voice_send_rx_device_mute_cmd(struct voice_data *v)
+static int voice_send_device_mute_cmd(struct voice_data *v, uint16_t direction,
+ uint16_t mute_flag)
{
struct cvp_set_mute_cmd cvp_mute_cmd;
int ret = 0;
@@ -2957,12 +3082,14 @@
APR_PKT_VER);
cvp_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvp_mute_cmd) - APR_HDR_SIZE);
- cvp_mute_cmd.hdr.src_port = v->session_id;
+ cvp_mute_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvp_mute_cmd.hdr.dest_port = voice_get_cvp_handle(v);
cvp_mute_cmd.hdr.token = 0;
cvp_mute_cmd.hdr.opcode = VSS_IVOLUME_CMD_MUTE_V2;
- cvp_mute_cmd.cvp_set_mute.direction = VSS_IVOLUME_DIRECTION_RX;
- cvp_mute_cmd.cvp_set_mute.mute_flag = v->dev_rx.mute;
+ cvp_mute_cmd.cvp_set_mute.direction = direction;
+ cvp_mute_cmd.cvp_set_mute.mute_flag = mute_flag;
+ cvp_mute_cmd.cvp_set_mute.ramp_duration_ms = DEFAULT_MUTE_RAMP_DURATION;
v->cvp_state = CMD_STATUS_FAIL;
ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_mute_cmd);
@@ -3010,7 +3137,8 @@
APR_PKT_VER);
cvp_vol_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvp_vol_cmd) - APR_HDR_SIZE);
- cvp_vol_cmd.hdr.src_port = v->session_id;
+ cvp_vol_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvp_vol_cmd.hdr.dest_port = cvp_handle;
cvp_vol_cmd.hdr.token = 0;
cvp_vol_cmd.hdr.opcode = VSS_IVOCPROC_CMD_SET_RX_VOLUME_INDEX;
@@ -3059,7 +3187,8 @@
APR_PKT_VER);
cvs_start_record.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_start_record) - APR_HDR_SIZE);
- cvs_start_record.hdr.src_port = v->session_id;
+ cvs_start_record.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_start_record.hdr.dest_port = cvs_handle;
cvs_start_record.hdr.token = 0;
cvs_start_record.hdr.opcode = VSS_IRECORD_CMD_START;
@@ -3144,7 +3273,8 @@
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
cvs_stop_record.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_stop_record) - APR_HDR_SIZE);
- cvs_stop_record.src_port = v->session_id;
+ cvs_stop_record.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_stop_record.dest_port = cvs_handle;
cvs_stop_record.token = 0;
cvs_stop_record.opcode = VSS_IRECORD_CMD_STOP;
@@ -3315,7 +3445,8 @@
APR_PKT_VER);
cvs_start_playback.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_start_playback) - APR_HDR_SIZE);
- cvs_start_playback.hdr.src_port = v->session_id;
+ cvs_start_playback.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_start_playback.hdr.dest_port = cvs_handle;
cvs_start_playback.hdr.token = 0;
cvs_start_playback.hdr.opcode = VSS_IPLAYBACK_CMD_START;
@@ -3379,7 +3510,8 @@
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
cvs_stop_playback.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_stop_playback) - APR_HDR_SIZE);
- cvs_stop_playback.src_port = v->session_id;
+ cvs_stop_playback.src_port =
+ voice_get_idx_for_session(v->session_id);
cvs_stop_playback.dest_port = cvs_handle;
cvs_stop_playback.token = 0;
@@ -3450,7 +3582,7 @@
return ret;
}
-int voc_disable_cvp(uint16_t session_id)
+int voc_disable_cvp(uint32_t session_id)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -3484,13 +3616,13 @@
return ret;
}
-int voc_enable_cvp(uint16_t session_id)
+int voc_enable_cvp(uint32_t session_id)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
if (v == NULL) {
- pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+ pr_err("%s: Invalid session_id 0x%x\n", __func__, session_id);
return -EINVAL;
}
@@ -3500,7 +3632,7 @@
if (v->voc_state == VOC_CHANGE) {
ret = voice_send_set_device_cmd(v);
if (ret < 0) {
- pr_err("%s: set device failed\n", __func__);
+ pr_err("%s: Set device failed\n", __func__);
goto fail;
}
@@ -3508,31 +3640,45 @@
voice_send_cvp_register_cal_cmd(v);
voice_send_cvp_register_vol_cal_cmd(v);
+ if (v->lch_mode == VOICE_LCH_START) {
+ pr_debug("%s: TX and RX mute ON\n", __func__);
+
+ voice_send_device_mute_cmd(v,
+ VSS_IVOLUME_DIRECTION_TX,
+ VSS_IVOLUME_MUTE_ON);
+ voice_send_device_mute_cmd(v,
+ VSS_IVOLUME_DIRECTION_RX,
+ VSS_IVOLUME_MUTE_ON);
+ } else if (v->lch_mode == VOICE_LCH_STOP) {
+ pr_debug("%s: TX and RX mute OFF\n", __func__);
+
+ voice_send_device_mute_cmd(v,
+ VSS_IVOLUME_DIRECTION_TX,
+ VSS_IVOLUME_MUTE_OFF);
+ voice_send_device_mute_cmd(v,
+ VSS_IVOLUME_DIRECTION_RX,
+ VSS_IVOLUME_MUTE_OFF);
+ /* Reset lch mode when VOICE_LCH_STOP is recieved */
+ v->lch_mode = 0;
+ } else {
+ pr_debug("%s: Mute commands not sent for lch_mode=%d\n",
+ __func__, v->lch_mode);
+ }
+
ret = voice_send_enable_vocproc_cmd(v);
if (ret < 0) {
- pr_err("%s: enable vocproc failed %d\n", __func__, ret);
+ pr_err("%s: Enable vocproc failed %d\n", __func__, ret);
+
goto fail;
}
/* Send tty mode if tty device is used */
voice_send_tty_mode_cmd(v);
-
- /* enable widevoice if wv_enable is set */
- if (v->wv_enable)
- voice_send_set_widevoice_enable_cmd(v);
-
/* enable slowtalk */
if (v->st_enable)
voice_send_set_pp_enable_cmd(v,
MODULE_ID_VOICE_MODULE_ST,
v->st_enable);
-
- /* enable FENS */
- if (v->fens_enable)
- voice_send_set_pp_enable_cmd(v,
- MODULE_ID_VOICE_MODULE_FENS,
- v->fens_enable);
-
rtac_add_voice(voice_get_cvs_handle(v),
voice_get_cvp_handle(v),
v->dev_rx.port_id, v->dev_tx.port_id,
@@ -3545,7 +3691,7 @@
return ret;
}
-static int voice_set_packet_exchange_mode_and_config(uint16_t session_id,
+static int voice_set_packet_exchange_mode_and_config(uint32_t session_id,
uint32_t mode)
{
struct voice_data *v = voice_get_session(session_id);
@@ -3577,7 +3723,7 @@
return -EINVAL;
}
-int voc_set_tx_mute(uint16_t session_id, uint32_t dir, uint32_t mute)
+int voc_set_tx_mute(uint32_t session_id, uint32_t dir, uint32_t mute)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -3590,19 +3736,19 @@
mutex_lock(&v->lock);
- v->dev_tx.mute = mute;
+ v->stream_tx.stream_mute = mute;
if ((v->voc_state == VOC_RUN) ||
(v->voc_state == VOC_CHANGE) ||
(v->voc_state == VOC_STANDBY))
- ret = voice_send_mute_cmd(v);
+ ret = voice_send_stream_mute_cmd(v);
mutex_unlock(&v->lock);
return ret;
}
-int voc_set_rx_device_mute(uint16_t session_id, uint32_t mute)
+int voc_set_rx_device_mute(uint32_t session_id, uint32_t mute)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -3615,17 +3761,19 @@
mutex_lock(&v->lock);
- v->dev_rx.mute = mute;
+ v->dev_rx.dev_mute = mute;
if (v->voc_state == VOC_RUN)
- ret = voice_send_rx_device_mute_cmd(v);
+ ret = voice_send_device_mute_cmd(v,
+ VSS_IVOLUME_DIRECTION_RX,
+ v->dev_rx.dev_mute);
mutex_unlock(&v->lock);
return ret;
}
-int voc_get_rx_device_mute(uint16_t session_id)
+int voc_get_rx_device_mute(uint32_t session_id)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -3638,14 +3786,14 @@
mutex_lock(&v->lock);
- ret = v->dev_rx.mute;
+ ret = v->dev_rx.dev_mute;
mutex_unlock(&v->lock);
return ret;
}
-int voc_set_tty_mode(uint16_t session_id, uint8_t tty_mode)
+int voc_set_tty_mode(uint32_t session_id, uint8_t tty_mode)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -3665,7 +3813,7 @@
return ret;
}
-uint8_t voc_get_tty_mode(uint16_t session_id)
+uint8_t voc_get_tty_mode(uint32_t session_id)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -3685,52 +3833,7 @@
return ret;
}
-int voc_set_widevoice_enable(uint16_t session_id, uint32_t wv_enable)
-{
- struct voice_data *v = voice_get_session(session_id);
- u16 mvm_handle;
- int ret = 0;
-
- if (v == NULL) {
- pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
-
- return -EINVAL;
- }
-
- mutex_lock(&v->lock);
-
- v->wv_enable = wv_enable;
-
- mvm_handle = voice_get_mvm_handle(v);
- if (mvm_handle != 0)
- voice_send_set_widevoice_enable_cmd(v);
-
- mutex_unlock(&v->lock);
-
- return ret;
-}
-
-uint32_t voc_get_widevoice_enable(uint16_t session_id)
-{
- struct voice_data *v = voice_get_session(session_id);
- int ret = 0;
-
- if (v == NULL) {
- pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
-
- return -EINVAL;
- }
-
- mutex_lock(&v->lock);
-
- ret = v->wv_enable;
-
- mutex_unlock(&v->lock);
-
- return ret;
-}
-
-int voc_set_pp_enable(uint16_t session_id, uint32_t module_id, uint32_t enable)
+int voc_set_pp_enable(uint32_t session_id, uint32_t module_id, uint32_t enable)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -3744,18 +3847,12 @@
mutex_lock(&v->lock);
if (module_id == MODULE_ID_VOICE_MODULE_ST)
v->st_enable = enable;
- else if (module_id == MODULE_ID_VOICE_MODULE_FENS)
- v->fens_enable = enable;
if (v->voc_state == VOC_RUN) {
if (module_id == MODULE_ID_VOICE_MODULE_ST)
ret = voice_send_set_pp_enable_cmd(v,
MODULE_ID_VOICE_MODULE_ST,
enable);
- else if (module_id == MODULE_ID_VOICE_MODULE_FENS)
- ret = voice_send_set_pp_enable_cmd(v,
- MODULE_ID_VOICE_MODULE_FENS,
- enable);
}
mutex_unlock(&v->lock);
@@ -3763,7 +3860,7 @@
return ret;
}
-int voc_get_pp_enable(uint16_t session_id, uint32_t module_id)
+int voc_get_pp_enable(uint32_t session_id, uint32_t module_id)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -3777,15 +3874,12 @@
mutex_lock(&v->lock);
if (module_id == MODULE_ID_VOICE_MODULE_ST)
ret = v->st_enable;
- else if (module_id == MODULE_ID_VOICE_MODULE_FENS)
- ret = v->fens_enable;
-
mutex_unlock(&v->lock);
return ret;
}
-int voc_set_rx_vol_index(uint16_t session_id, uint32_t dir, uint32_t vol_idx)
+int voc_set_rx_vol_index(uint32_t session_id, uint32_t dir, uint32_t vol_idx)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -3810,7 +3904,7 @@
return ret;
}
-int voc_set_rxtx_port(uint16_t session_id, uint32_t port_id, uint32_t dev_type)
+int voc_set_rxtx_port(uint32_t session_id, uint32_t port_id, uint32_t dev_type)
{
struct voice_data *v = voice_get_session(session_id);
@@ -3834,7 +3928,7 @@
return 0;
}
-int voc_set_route_flag(uint16_t session_id, uint8_t path_dir, uint8_t set)
+int voc_set_route_flag(uint32_t session_id, uint8_t path_dir, uint8_t set)
{
struct voice_data *v = voice_get_session(session_id);
@@ -3858,7 +3952,7 @@
return 0;
}
-uint8_t voc_get_route_flag(uint16_t session_id, uint8_t path_dir)
+uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -3881,7 +3975,7 @@
return ret;
}
-int voc_end_voice_call(uint16_t session_id)
+int voc_end_voice_call(uint32_t session_id)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -3910,7 +4004,7 @@
return ret;
}
-int voc_standby_voice_call(uint16_t session_id)
+int voc_standby_voice_call(uint32_t session_id)
{
struct voice_data *v = voice_get_session(session_id);
struct apr_hdr mvm_standby_voice_cmd;
@@ -3939,7 +4033,8 @@
sizeof(mvm_standby_voice_cmd) - APR_HDR_SIZE);
pr_debug("send mvm_standby_voice_cmd pkt size = %d\n",
mvm_standby_voice_cmd.pkt_size);
- mvm_standby_voice_cmd.src_port = v->session_id;
+ mvm_standby_voice_cmd.src_port =
+ voice_get_idx_for_session(v->session_id);
mvm_standby_voice_cmd.dest_port = mvm_handle;
mvm_standby_voice_cmd.token = 0;
mvm_standby_voice_cmd.opcode = VSS_IMVM_CMD_STANDBY_VOICE;
@@ -3957,7 +4052,50 @@
return ret;
}
-int voc_resume_voice_call(uint16_t session_id)
+int voc_set_lch(uint32_t session_id, enum voice_lch_mode lch_mode)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: Invalid session_id 0x%x\n", __func__, session_id);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&v->lock);
+ if (v->lch_mode == lch_mode) {
+ pr_debug("%s: Session %d already in LCH mode %d\n",
+ __func__, session_id, lch_mode);
+
+ mutex_unlock(&v->lock);
+ goto done;
+ }
+
+ v->lch_mode = lch_mode;
+ mutex_unlock(&v->lock);
+
+ ret = voc_disable_cvp(session_id);
+ if (ret < 0) {
+ pr_err("%s: voc_disable_cvp failed ret=%d\n", __func__, ret);
+
+ goto done;
+ }
+
+ /* Mute and topology_none will be set as part of voc_enable_cvp() */
+ ret = voc_enable_cvp(session_id);
+ if (ret < 0) {
+ pr_err("%s: voc_enable_cvp failed ret=%d\n", __func__, ret);
+
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+int voc_resume_voice_call(uint32_t session_id)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -3973,7 +4111,7 @@
return -EINVAL;
}
-int voc_start_voice_call(uint16_t session_id)
+int voc_start_voice_call(uint32_t session_id)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -4094,7 +4232,7 @@
struct common_data *c = NULL;
struct voice_data *v = NULL;
int i = 0;
- uint16_t session_id = 0;
+ uint32_t session_id = 0;
if ((data == NULL) || (priv == NULL)) {
pr_err("%s: data or priv is NULL\n", __func__);
@@ -4140,7 +4278,7 @@
pr_debug("%s: session_id 0x%x\n", __func__, data->dest_port);
- v = voice_get_session(data->dest_port);
+ v = voice_get_session_by_idx(data->dest_port);
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
@@ -4178,7 +4316,6 @@
case VSS_IMVM_CMD_DETACH_STREAM:
case VSS_ICOMMON_CMD_SET_NETWORK:
case VSS_ICOMMON_CMD_SET_VOICE_TIMING:
- case VSS_IWIDEVOICE_CMD_SET_WIDEVOICE:
case VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL:
case VSS_IMVM_CMD_SET_CAL_NETWORK:
case VSS_IMVM_CMD_SET_CAL_MEDIA_TYPE:
@@ -4233,7 +4370,7 @@
struct common_data *c = NULL;
struct voice_data *v = NULL;
int i = 0;
- uint16_t session_id = 0;
+ uint32_t session_id = 0;
if ((data == NULL) || (priv == NULL)) {
pr_err("%s: data or priv is NULL\n", __func__);
@@ -4278,7 +4415,7 @@
return 0;
}
- v = voice_get_session(data->dest_port);
+ v = voice_get_session_by_idx(data->dest_port);
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
@@ -4380,7 +4517,8 @@
APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(send_enc_buf_consumed_cmd) - APR_HDR_SIZE);
- send_enc_buf_consumed_cmd.hdr.src_port = v->session_id;
+ send_enc_buf_consumed_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
send_enc_buf_consumed_cmd.hdr.dest_port = cvs_handle;
send_enc_buf_consumed_cmd.hdr.token = 0;
send_enc_buf_consumed_cmd.hdr.opcode =
@@ -4426,7 +4564,8 @@
send_dec_buf.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(send_dec_buf) - APR_HDR_SIZE);
- send_dec_buf.hdr.src_port = v->session_id;
+ send_dec_buf.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
send_dec_buf.hdr.dest_port = cvs_handle;
send_dec_buf.hdr.token = 0;
send_dec_buf.hdr.opcode =
@@ -4507,7 +4646,7 @@
struct common_data *c = NULL;
struct voice_data *v = NULL;
int i = 0;
- uint16_t session_id = 0;
+ uint32_t session_id = 0;
if ((data == NULL) || (priv == NULL)) {
pr_err("%s: data or priv is NULL\n", __func__);
@@ -4548,7 +4687,7 @@
return 0;
}
- v = voice_get_session(data->dest_port);
+ v = voice_get_session_by_idx(data->dest_port);
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
@@ -4577,7 +4716,7 @@
v->cvp_state = CMD_STATUS_SUCCESS;
wake_up(&v->cvp_wait);
break;
- case VSS_IVOCPROC_CMD_SET_DEVICE:
+ case VSS_IVOCPROC_CMD_SET_DEVICE_V2:
case VSS_IVOCPROC_CMD_SET_RX_VOLUME_INDEX:
case VSS_IVOCPROC_CMD_ENABLE:
case VSS_IVOCPROC_CMD_DISABLE:
@@ -4761,18 +4900,23 @@
mutex_init(&common.common_lock);
+ /* Initialize session id with vsid */
+ init_session_id();
+
for (i = 0; i < MAX_VOC_SESSIONS; i++) {
- common.voice[i].session_id = SESSION_ID_BASE + i;
/* initialize dev_rx and dev_tx */
common.voice[i].dev_rx.volume = common.default_vol_val;
- common.voice[i].dev_rx.mute = 0;
- common.voice[i].dev_tx.mute = common.default_mute_val;
+ common.voice[i].dev_rx.dev_mute = 0;
+ common.voice[i].dev_tx.dev_mute = 0;
+ common.voice[i].stream_rx.stream_mute = common.default_mute_val;
+ common.voice[i].stream_tx.stream_mute = common.default_mute_val;
common.voice[i].dev_tx.port_id = 0x100B;
common.voice[i].dev_rx.port_id = 0x100A;
common.voice[i].sidetone_gain = 0x512;
common.voice[i].dtmf_rx_detect_en = 0;
+ common.voice[i].lch_mode = 0;
common.voice[i].voc_state = VOC_INIT;
@@ -4800,4 +4944,4 @@
return rc;
}
-device_initcall(voice_init);
+late_initcall(voice_init);
diff --git a/sound/soc/msm/qdsp6v2/q6voice.h b/sound/soc/msm/qdsp6v2/q6voice.h
index ef5c6e3..5a16115 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.h
+++ b/sound/soc/msm/qdsp6v2/q6voice.h
@@ -14,6 +14,7 @@
#include <mach/qdsp6v2/apr.h>
#include <linux/msm_ion.h>
+#include <sound/voice_params.h>
#define MAX_VOC_PKT_SIZE 642
#define SESSION_NAME_LEN 20
@@ -41,11 +42,15 @@
void *cb_handle;
};
-/* Device information payload structure */
+/* Stream information payload structure */
+struct stream_data {
+ uint32_t stream_mute;
+};
+/* Device information payload structure */
struct device_data {
uint32_t volume; /* in index */
- uint32_t mute;
+ uint32_t dev_mute;
uint32_t sample;
uint32_t enabled;
uint32_t dev_id;
@@ -194,9 +199,6 @@
#define VSS_ICOMMON_CMD_SET_VOICE_TIMING 0x000111E0
/* Set the voice timing parameters. */
-#define VSS_IWIDEVOICE_CMD_SET_WIDEVOICE 0x00011243
-/* Enable/disable WideVoice */
-
#define VSS_IMEMORY_CMD_MAP_PHYSICAL 0x00011334
#define VSS_IMEMORY_RSP_MAP 0x00011336
#define VSS_IMEMORY_CMD_UNMAP 0x00011337
@@ -291,14 +293,6 @@
/* Set to TRUE to enable modem state machine control */
} __packed;
-struct vss_iwidevoice_cmd_set_widevoice_t {
- uint32_t enable;
- /* WideVoice enable/disable; possible values:
- * - 0 -- WideVoice disabled
- * - 1 -- WideVoice enabled
- */
-} __packed;
-
struct mvm_attach_vocproc_cmd {
struct apr_hdr hdr;
struct vss_istream_cmd_attach_vocproc_t mvm_attach_cvp_handle;
@@ -344,11 +338,6 @@
struct vss_icommon_cmd_set_voice_timing_t timing;
} __packed;
-struct mvm_set_widevoice_enable_cmd {
- struct apr_hdr hdr;
- struct vss_iwidevoice_cmd_set_widevoice_t vss_set_wv;
-} __packed;
-
struct vss_imemory_table_descriptor_t {
uint64_t mem_address;
/*
@@ -484,7 +473,6 @@
#define VSS_ISTREAM_CMD_SET_ENC_DTX_MODE 0x0001101D
/* Set encoder DTX mode. */
-#define MODULE_ID_VOICE_MODULE_FENS 0x00010EEB
#define MODULE_ID_VOICE_MODULE_ST 0x00010EE3
#define VOICE_PARAM_MOD_ENABLE 0x00010E00
#define MOD_ENABLE_PARAM_LEN 4
@@ -904,7 +892,7 @@
#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C
-#define VSS_IVOCPROC_CMD_SET_DEVICE 0x000100C4
+#define VSS_IVOCPROC_CMD_SET_DEVICE_V2 0x000112C6
#define VSS_IVOCPROC_CMD_SET_VP3_DATA 0x000110EB
@@ -1042,8 +1030,8 @@
*/
} __packed;
-struct vss_ivocproc_cmd_set_device_t {
- uint32_t tx_port_id;
+struct vss_ivocproc_cmd_set_device_v2_t {
+ uint16_t tx_port_id;
/*
* TX device port ID which vocproc will connect to.
* VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port.
@@ -1054,7 +1042,7 @@
* VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any
* pre/post-processing blocks and is pass-through.
*/
- int32_t rx_port_id;
+ uint16_t rx_port_id;
/*
* RX device port ID which vocproc will connect to.
* VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port.
@@ -1065,6 +1053,15 @@
* VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any
* pre/post-processing blocks and is pass-through.
*/
+ uint32_t vocproc_mode;
+ /* Vocproc mode. The supported values:
+ * VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING - 0x00010F7C
+ * VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING - 0x00010F7D
+ */
+ uint16_t ec_ref_port_id;
+ /* Port ID to which the vocproc connects for receiving
+ * echo
+ */
} __packed;
struct vss_ivocproc_cmd_register_device_config_t {
@@ -1128,7 +1125,7 @@
struct cvp_set_device_cmd {
struct apr_hdr hdr;
- struct vss_ivocproc_cmd_set_device_t cvp_set_device;
+ struct vss_ivocproc_cmd_set_device_v2_t cvp_set_device_v2;
} __packed;
struct cvp_set_vp3_data_cmd {
@@ -1230,10 +1227,14 @@
wait_queue_head_t cvs_wait;
wait_queue_head_t cvp_wait;
- /* cache the values related to Rx and Tx */
+ /* Cache the values related to Rx and Tx devices */
struct device_data dev_rx;
struct device_data dev_tx;
+ /* Cache the values related to Rx and Tx streams */
+ struct stream_data stream_rx;
+ struct stream_data stream_tx;
+
u32 mvm_state;
u32 cvs_state;
u32 cvp_state;
@@ -1249,18 +1250,15 @@
uint16_t sidetone_gain;
uint8_t tty_mode;
- /* widevoice enable value */
- uint8_t wv_enable;
/* slowtalk enable value */
uint32_t st_enable;
- /* FENC enable value */
- uint32_t fens_enable;
-
uint32_t dtmf_rx_detect_en;
+ /* Local Call Hold mode */
+ uint8_t lch_mode;
struct voice_dev_route_state voc_route_state;
- u16 session_id;
+ u32 session_id;
struct incall_rec_info rec_info;
@@ -1276,7 +1274,6 @@
};
#define MAX_VOC_SESSIONS 4
-#define SESSION_ID_BASE 0xFFF0
struct common_data {
/* these default values are for all devices */
@@ -1339,35 +1336,39 @@
#define VOLTE_SESSION_NAME "VoLTE session"
#define VOICE2_SESSION_NAME "Voice2 session"
-#define VOICE2_SESSION_VSID "10DC1000"
+#define VOICE2_SESSION_VSID_STR "10DC1000"
+#define VOICE_SESSION_VSID 0x10C01000
+#define VOICE2_SESSION_VSID 0x10DC1000
+#define VOLTE_SESSION_VSID 0x10C02000
+#define VOIP_SESSION_VSID 0x10004000
+#define ALL_SESSION_VSID 0xFFFFFFFF
/* called by alsa driver */
-int voc_set_pp_enable(uint16_t session_id, uint32_t module_id,
+int voc_set_pp_enable(uint32_t session_id, uint32_t module_id,
uint32_t enable);
-int voc_get_pp_enable(uint16_t session_id, uint32_t module_id);
-int voc_set_widevoice_enable(uint16_t session_id, uint32_t wv_enable);
-uint32_t voc_get_widevoice_enable(uint16_t session_id);
-uint8_t voc_get_tty_mode(uint16_t session_id);
-int voc_set_tty_mode(uint16_t session_id, uint8_t tty_mode);
-int voc_start_voice_call(uint16_t session_id);
-int voc_end_voice_call(uint16_t session_id);
-int voc_standby_voice_call(uint16_t session_id);
-int voc_resume_voice_call(uint16_t session_id);
-int voc_set_rxtx_port(uint16_t session_id,
+int voc_get_pp_enable(uint32_t session_id, uint32_t module_id);
+uint8_t voc_get_tty_mode(uint32_t session_id);
+int voc_set_tty_mode(uint32_t session_id, uint8_t tty_mode);
+int voc_start_voice_call(uint32_t session_id);
+int voc_end_voice_call(uint32_t session_id);
+int voc_standby_voice_call(uint32_t session_id);
+int voc_resume_voice_call(uint32_t session_id);
+int voc_set_lch(uint32_t session_id, enum voice_lch_mode lch_mode);
+int voc_set_rxtx_port(uint32_t session_id,
uint32_t dev_port_id,
uint32_t dev_type);
-int voc_set_rx_vol_index(uint16_t session_id, uint32_t dir, uint32_t voc_idx);
-int voc_set_tx_mute(uint16_t session_id, uint32_t dir, uint32_t mute);
-int voc_set_rx_device_mute(uint16_t session_id, uint32_t mute);
-int voc_get_rx_device_mute(uint16_t session_id);
-int voc_disable_cvp(uint16_t session_id);
-int voc_enable_cvp(uint16_t session_id);
-int voc_set_route_flag(uint16_t session_id, uint8_t path_dir, uint8_t set);
-uint8_t voc_get_route_flag(uint16_t session_id, uint8_t path_dir);
-int voc_enable_dtmf_rx_detection(uint16_t session_id, uint32_t enable);
+int voc_set_rx_vol_index(uint32_t session_id, uint32_t dir, uint32_t voc_idx);
+int voc_set_tx_mute(uint32_t session_id, uint32_t dir, uint32_t mute);
+int voc_set_rx_device_mute(uint32_t session_id, uint32_t mute);
+int voc_get_rx_device_mute(uint32_t session_id);
+int voc_disable_cvp(uint32_t session_id);
+int voc_enable_cvp(uint32_t session_id);
+int voc_set_route_flag(uint32_t session_id, uint8_t path_dir, uint8_t set);
+uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir);
+int voc_enable_dtmf_rx_detection(uint32_t session_id, uint32_t enable);
void voc_disable_dtmf_det_on_active_sessions(void);
-uint16_t voc_get_session_id(char *name);
+uint32_t voc_get_session_id(char *name);
int voc_start_playback(uint32_t set);
int voc_start_record(uint32_t port_id, uint32_t set);