Merge "msm_fb: display: switch blt mode on/off without turning off TG" into msm-3.4
diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu.txt b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
index e9fb1a2..f5a2590 100644
--- a/Documentation/devicetree/bindings/iommu/msm_iommu.txt
+++ b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
@@ -16,6 +16,10 @@
- qcom,iommu-smt-size : Number of SMR entries in the SMT of this HW block
- vdd-supply : vdd-supply: phandle to GDSC regulator controlling this IOMMU.
+Optional properties:
+- qcom,needs-alt-core-clk : boolean to enable the secondary core clock for
+ access to the IOMMU configuration registers
+
Example:
qcom,iommu@fda64000 {
diff --git a/Documentation/devicetree/bindings/mmc/msm_sdcc.txt b/Documentation/devicetree/bindings/mmc/msm_sdcc.txt
index a8de90f..c674a13 100644
--- a/Documentation/devicetree/bindings/mmc/msm_sdcc.txt
+++ b/Documentation/devicetree/bindings/mmc/msm_sdcc.txt
@@ -16,10 +16,8 @@
Optional Properties:
- cell-index - defines slot ID.
- qcom,sdcc-bus-width - defines the bus I/O width that controller supports.
- - qcom,sdcc-wp-gpio - defines write protect switch gpio.
- - qcom,sdcc-wp-polarity - specifies the polarity of wp switch.
- - qcom,sdcc-cd-gpio - defines card detect gpio number.
- - qcom,sdcc-cd-polarity - specifies the polarity of cd gpio.
+ - wp-gpios - specify GPIO for write protect switch detection.
+ - cd-gpios - specify GPIO for card detection.
- qcom,sdcc-nonremovable - specifies whether the card in slot is
hot pluggable or hard wired.
- qcom,sdcc-disable_cmd23 - disable sending CMD23 to card when controller can't support it.
diff --git a/Documentation/devicetree/bindings/mtd/msm_qpic_nand.txt b/Documentation/devicetree/bindings/mtd/msm_qpic_nand.txt
new file mode 100644
index 0000000..bddbbae
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/msm_qpic_nand.txt
@@ -0,0 +1,51 @@
+Qualcomm Parallel Interface controller (QPIC) for NAND devices
+
+Required properties:
+- compatible : "qcom,msm-nand".
+- reg : should specify QPIC NANDc and BAM physical address range.
+- reg-names : should specify relevant names to each reg property defined.
+- interrupts : should specify QPIC/BAM interrupt numbers.
+- interrupt-names : should specify relevant names to each interrupts property
+ defined.
+
+MTD flash partition layout for NAND devices -
+
+Each partition is represented as a sub-node of the qcom,mtd-partitions device.
+Each node's name represents the name of the corresponding partition.
+
+Required properties:
+- reg : The partition offset and size
+- label : The label / name for this partition.
+
+Optional properties:
+- read-only: This parameter, if present, indicates that this partition
+ should only be mounted read-only.
+
+Examples:
+
+ qcom,nand@f9af0000 {
+ compatible = "qcom,msm-nand";
+ reg = <0xf9af0000 0x1000>,
+ <0xf9ac4000 0x8000>;
+ reg-names = "nand_phys",
+ "bam_phys";
+ interrupts = <0 279 0>;
+ interrupt-names = "bam_irq";
+ };
+
+ qcom,mtd-partitions {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ partition@0 {
+ label = "boot";
+ reg = <0x00000000 0x1000>;
+ };
+ partition@00020000 {
+ label = "userdata";
+ reg = <0x00020000 0x1000>;
+ };
+ partition@00040000 {
+ label = "system";
+ reg = <0x00040000 0x1000>;
+ };
+ };
diff --git a/Documentation/mtd/devices/msm_qpic_nand.txt b/Documentation/mtd/devices/msm_qpic_nand.txt
new file mode 100644
index 0000000..301e823
--- /dev/null
+++ b/Documentation/mtd/devices/msm_qpic_nand.txt
@@ -0,0 +1,296 @@
+Introduction
+============
+
+In MDM9x25, new NAND controller(NANDc) has been added and it has the
+following major changes as compared to its previous version -
+
+1. It includes Secured BAM-Lite and the support for ADM(Application Data Mover)
+has been removed.
+
+2. It includes 4 bit BCH ECC and the support for 4 bit Reed Solomon ECC has
+been removed.
+
+3. The support for Dual NAND controllers has been removed and thus the
+software features like ping-pong mode and interleave mode are deprecated.
+
+4. It includes support for dual buffers in case of read and one dedicated
+write buffer to each processor (Modem and Apps).
+
+This new NAND driver takes care of all the above new hardware changes. In
+addition to the above hardware changes, it also takes care of software device
+tree changes.
+
+Hardware description
+====================
+
+The NANDc Core:
+---------------
+Qualcomm Parallel Interface Controller (QPIC), formerly named EBI2, is a
+wrapper module which integrates a NAND controller core and a LCD controller
+core and multiplexes their access to shared parallel interfaces pins. Both
+controller cores are accessible to processors (Modem and Apps), and share
+master access to the Peripheral NoC (Network on Chip) via a BAM module.
+
+In MDM9x25, QPIC is located on the peripheral NoC, connected via a 32-bit AHB
+Master port and a 32-bit AHB Slave Port. The NANDc register interface goes
+through AHB Slave Port and data transfers using BAM goes through AHB Master
+Port. The NAND Controller (NANDc) is a hardware core which manages the access
+to an off-chip NAND device.
+
+BAM-Lite:
+---------
+BAM(Bus Access Manager) can transfer data between a peripheral and memory,
+or between two peripherals in a BAM to BAM mode. Each BAM contains multiple
+DMA channels, called pipes. A pipe provides a unidirectional data transfer
+engine, capable of either receiving data in consumer mode, or transmitting
+data in producer mode. The consumer fetches the data from the source system
+memory, and the producer writes data to the destination system memory.
+
+BAM-Lite's interface is similar to the BAM interface with slight changes to
+the sideband interface. BAM-Lite is an area-optimized version of BAM. BAM-Lite
+supports new features such as Notify-When-Done(NWD), pipe lock/unlock and
+command descriptors.
+
+NANDc has a secured BAM-Lite which provides DMA support for the NANDc and
+command support for accessing the NANDc registers. It is called secured
+because it has an integrated APU (Address Protection Unit) that validates
+every access to BAM and its peripheral registers.
+
+The NANDc has in total 6 BAM pipes - 3 pipes are dedicated for each processor
+(Modem and Apps) at the hardware level.
+
+Software description
+====================
+
+The NAND device is shared between two independent file systems, each running
+on a different processor - the application processor (Apps) and the Modem.
+The NAND driver uses BAM driver to transfer NAND operation requests and
+data to/from the NAND Controller (NANDc) core through the BAM pipes. Every
+NANDc register read/write access must go through BAM as it facilitates security
+mechanisms to enable simultaneous access to NAND device from both processors
+(Modem and Apps).
+
+The Apps NAND driver registers NANDc BAM peripheral with BAM driver, allocates
+endpoints and descriptor FIFO memory and registers for complete event
+notification for the following pipes:
+
+ - system consumer pipe for data (pipe#0) : This BAM pipe will be used
+ for transferring data from system memory to NANDc i.e., during write.
+
+ - system producer pipe for data (pipe#1) : This BAM pipe will be used
+ for transferring data from NANDc to system memory i.e., during read.
+
+ - system consumer pipe for commands (pipe#2) : This BAM pipe will be
+ used for both reading and writing to NANDc registers. It can be
+ configured either as consumer pipe or producer pipe but as per HW
+ team's recommendation it is configured as consumer pipe.
+
+Control path:
+-------------
+Each NAND operation can be described as a set of BAM command or/and data
+descriptors.
+
+A command descriptor(CD) points to the starting address of a command
+block. Each command block may contain a set of command elements where
+each command element is a single NANDc register read/write. The NAND
+driver submits all command descriptors to its system consumer pipe#2.
+
+Data path:
+----------
+A Data Descriptor(DD) points to the start of a data block which is a sequential
+chunk of data.
+
+For page write operations, the NAND driver submits data descriptors to system
+consumer pipe#0 and as per the descriptors submitted, the BAM reads data from
+the data block into the NANDc buffer.
+
+For page read operations, the NAND driver submits data descriptors to system
+producer pipe#1 and as per the descriptors submitted, the BAM reads data from
+the NANDc buffer into the data block.
+
+The driver submits a CD/DD using BAM driver APIs sps_transfer_one()/
+sps_transfer(). To this API, flags is passed as one of the arguments and if
+SPS_IOVEC_FLAG_CMD is passed, then it is identified as a CD. Otherwise, it is
+identified as a DD. The other valid SPS flags for a CD/DD are -
+
+ - SPS_IOVEC_FLAG_INT : This flag indicates BAM driver to raise BAM
+ interrupt after the current descriptor with this flag has been
+ processed by BAM HW. This flag is applicable for both CD and DD.
+
+ - SPS_IOVEC_FLAG_NWD : This flag indicates BAM HW to not process
+ next descriptors until it receives an acknowledgement by NANDc
+ that the current descriptor with this flag is completely
+ executed. This flag is applicable only for a CD.
+
+ - SPS_IOVEC_FLAG_LOCK: This flag marks the beginning of a series of
+ commands and it indicates that all the CDs submitted on this pipe
+ must be executed atomically without any interruption by commands
+ from other pipes. This is applicable only for a CD.
+
+ - SPS_IOVEC_FLAG_UNLOCK: This flag marks the end of a series of
+ commands and it indicates that the other pipe that was locked due to
+ SPS_IOVEC_FLAG_LOCK flag can be unblocked after the current CD
+ with this flag is executed. This is applicable only for a CD.
+
+ - SPS_IOVEC_FLAG_EOT - This flag indicates to BAM driver that the
+ current descriptor with this flag is the last descriptor submitted
+ during write operation. This is applicable only for a DD.
+
+Error handling:
+---------------
+After a page read/write complete notification from BAM, NAND driver validates
+the values read from NANDc registers to confirm the success/failure of page
+read/write operation. For example, after a page read/write is complete, the
+drivers reads the NANDc status registers to check for any operational errors,
+protection violation errors and device status errors, number of correctable/
+uncorrectable errors reported by the controller. Based on the error conditions
+that are met, the driver reports appropriate error codes to upper layers. The
+upper layers respond to these errors and take appropriate action.
+
+Design
+======
+
+The existing NAND driver (ADM based) can not be reused due to many major HW
+changes (see Introduction section) in the new NANDc core. Some of the complex
+features (Dual NAND controllers support) too are deprecated in the new NANDc.
+Hence, a new NAND driver is written to take care of both SPS/BAM changes and
+other controller specific changes. The rest of the interaction with MTD and
+YAFFS2 remains same as its previous version of NAND driver msm_nand.c.
+
+Power Management
+================
+
+Two clocks are supplied by the system's clock controller to NANDc - AHB clock
+and interface clock. The interface clock is the clock that drives some of the
+HW blocks within NANDc. As of now, both these clocks are always on. But NANDc
+provides clock gating if some of the QPIC clock control registers are
+configured. The clock gating is yet to be enabled by driver.
+
+SMP/Multi-Core
+==============
+
+The locking mechanism for page read/write operations is taken care of by the
+higher layers such as MTD/YAFFS2 and only one single page operation can happen
+at any time on a given partition. For a single page operation, there is always
+only one context associated within the driver and thus no additional handling
+is required within the driver. But it is possible for file system to issue
+one request on partition and at the same time to issue another request on
+another partition as each partition corresponds to different MTD block device.
+This situation is handled within the driver by properly acquiring a mutex lock
+before submitting any command/data descriptors to any of the BAM pipes.
+
+
+Security
+========
+
+The same NAND device is accessible from both processors (Modem and Apps) and
+thus to avoid any configuration overwrite issues during a page operation,
+driver on each processor (Modem and Apps) must explicitly use BAM pipe
+lock/unlock mechanism. This is taken care of by the NAND driver. The partition
+violation issues are prevented by an MPU (Memory Protection Unit) that is
+attached to NANDc.
+
+Performance
+===========
+
+None.
+
+Interface
+=========
+
+The NAND driver registers each partition on NAND device as a MTD block device
+using mtd_device_register(). As part of this registration, the following ops
+(struct mtd_info *mtd) are registered with MTD layer for each partition:
+
+mtd->_block_isbad = msm_nand_block_isbad;
+mtd->_block_markbad = msm_nand_block_markbad;
+mtd->_read = msm_nand_read;
+mtd->_write = msm_nand_write;
+mtd->_read_oob = msm_nand_read_oob;
+mtd->_write_oob = msm_nand_write_oob;
+mtd->_erase = msm_nand_erase;
+
+msm_nand_block_isbad() - This checks if a block is bad or not by reading bad
+block byte in the first page of a block. A block is considered as bad if bad
+block byte location contains any value other than 0xFF.
+
+msm_nand_block_markbad() - This marks a block as bad by writing 0 to the
+entire first page of the block and thus writing 0 to bad block byte location.
+
+msm_nand_read/write() - This is used to read/write only main data from/to
+single/multiple pages within NAND device. The YAFFS2 file system can send
+read/write request for two types of data -
+
+ - Main data : This is the actual data to be read/written from/to a
+ page during a read/write operation on this device. The size of this
+ data request is typically based on the page size of the device
+ (2K/4K).
+
+ - OOB(Out Of Band) data : This is the spare data that will be used by
+ file system to keep track of its meta data/tags associated with the
+ actual data. As of now, the file system needs only 16 bytes to
+ accommodate this data. The NAND driver always writes this data
+ towards the end of main data.
+
+It is up to the file system whether or not to send a read/write request for OOB
+data along with main data.
+
+msm_nand_read_oob()/write_oob() - This is used to read/write both main data
+and spare data from/to single/multiple pages within NAND device.
+
+msm_nand_erase() - This erases the complete block by sending erase command to
+the device.
+
+The YAFFS2 file system registers as the user of MTD device and uses the ops
+exposed by the NAND driver to perform read/write/erase operations on NAND
+device. As of now, the driver can work with only YAFFS2 file system. An
+attempt to use it with any other file system might demand additional changes
+in the driver.
+
+Driver parameters
+=================
+
+None.
+
+Config options
+==============
+
+The config option MTD_MSM_QPIC_NAND enables this driver.
+
+Dependencies
+============
+
+It depends on the following kernel components:
+
+- SPS/BAM driver
+- MTD core layer
+- To add necessary NANDc and BAM resources to .dts file
+
+It depends on the following non-kernel components:
+
+The partition information of the NAND device must be passed by Modem subsystem
+to Apps boot loader and Apps boot loader must update the .dts file
+with the partition information as per the defined MTD bindings.
+
+The detailed information on MTD bindings can be found at -
+Documentation/devicetree/bindings/mtd/msm_qpic_nand.txt
+
+User space utilities
+====================
+
+None.
+
+Other
+=====
+
+No changes other than device tree changes are anticipated.
+
+Known issues
+============
+
+None.
+
+To do
+=====
+
+The NANDc core supports clock gating and is not yet supported by the driver.
diff --git a/arch/arm/boot/dts/msm-iommu.dtsi b/arch/arm/boot/dts/msm-iommu.dtsi
index 952f517..0e2ddce9 100755
--- a/arch/arm/boot/dts/msm-iommu.dtsi
+++ b/arch/arm/boot/dts/msm-iommu.dtsi
@@ -76,6 +76,7 @@
reg = <0xfdc84000 0x10000>;
vdd-supply = <&gdsc_venus>;
qcom,iommu-smt-size = <16>;
+ qcom,needs-alt-core-clk;
status = "disabled";
qcom,iommu-ctx@fdc8c000 {
@@ -108,6 +109,7 @@
reg = <0xfdb10000 0x10000>;
vdd-supply = <&gdsc_oxili_cx>;
qcom,iommu-smt-size = <32>;
+ qcom,needs-alt-core-clk;
status = "disabled";
qcom,iommu-ctx@fdb18000 {
diff --git a/arch/arm/boot/dts/msm9625.dts b/arch/arm/boot/dts/msm9625.dts
index 6c007fb..6b44be9 100644
--- a/arch/arm/boot/dts/msm9625.dts
+++ b/arch/arm/boot/dts/msm9625.dts
@@ -55,4 +55,14 @@
reg = <0xf991f000 0x1000>;
interrupts = <0 109 0>;
};
+
+ qcom,nand@f9ac0000 {
+ compatible = "qcom,msm-nand";
+ reg = <0xf9ac0000 0x1000>,
+ <0xf9ac4000 0x8000>;
+ reg-names = "nand_phys",
+ "bam_phys";
+ interrupts = <0 247 0>;
+ interrupt-names = "bam_irq";
+ };
};
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index c2f4702..7707692 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -83,6 +83,7 @@
CONFIG_MSM_GSS_SSR_8064=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_RPM_LOG=y
+CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_MSM_BUS_SCALING=y
CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index a50485d..87c536f 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -83,6 +83,7 @@
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
+CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
CONFIG_MSM_BUS_SCALING=y
CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y
CONFIG_MSM_WATCHDOG=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index 63d2ced..b02bd7c 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -171,6 +171,7 @@
CONFIG_SND=y
CONFIG_SND_SOC=y
CONFIG_SND_SOC_MSM8974=y
+CONFIG_WCD9320_CODEC=y
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_STORAGE=y
diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h
index 99ee2de..e32194f 100644
--- a/arch/arm/include/asm/arch_timer.h
+++ b/arch/arm/include/asm/arch_timer.h
@@ -2,6 +2,7 @@
#define __ASMARM_ARCH_TIMER_H
#include <linux/ioport.h>
+#include <linux/clocksource.h>
struct arch_timer {
struct resource res[3];
@@ -10,6 +11,7 @@
#ifdef CONFIG_ARM_ARCH_TIMER
int arch_timer_register(struct arch_timer *);
int arch_timer_of_register(void);
+cycle_t arch_counter_get_cntpct(void);
#else
static inline int arch_timer_register(struct arch_timer *at)
{
@@ -20,6 +22,11 @@
{
return -ENXIO;
}
+
+static inline cycle_t arch_counter_get_cntpct(void)
+{
+ return 0;
+}
#endif
#endif
diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h
index 562f13c..d341ea9 100644
--- a/arch/arm/include/asm/mach/mmc.h
+++ b/arch/arm/include/asm/mach/mmc.h
@@ -146,7 +146,7 @@
unsigned int uhs_caps2;
void (*sdio_lpm_gpio_setup)(struct device *, unsigned int);
unsigned int status_irq;
- unsigned int status_gpio;
+ int status_gpio;
/* Indicates the polarity of the GPIO line when card is inserted */
bool is_status_gpio_active_low;
unsigned int sdiowakeup_irq;
@@ -158,7 +158,7 @@
unsigned int msmsdcc_fmax;
bool nonremovable;
unsigned int mpm_sdiowakeup_int;
- unsigned int wpswitch_gpio;
+ int wpswitch_gpio;
bool is_wpswitch_active_low;
struct msm_mmc_slot_reg_data *vreg_data;
int is_sdio_al_client;
diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
index 87bb7d3..43c627d 100644
--- a/arch/arm/kernel/arch_timer.c
+++ b/arch/arm/kernel/arch_timer.c
@@ -22,6 +22,7 @@
#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/export.h>
#include <asm/cputype.h>
#include <asm/localtimer.h>
@@ -315,10 +316,16 @@
return ((cycle_t) cvalh << 32) | cvall;
}
-static cycle_t arch_counter_read(struct clocksource *cs)
+cycle_t arch_counter_get_cntpct(void)
{
return arch_specific_timer->get_cntpct();
}
+EXPORT_SYMBOL(arch_counter_get_cntpct);
+
+static cycle_t arch_counter_read(struct clocksource *cs)
+{
+ return arch_counter_get_cntpct();
+}
#ifdef ARCH_HAS_READ_CURRENT_TIMER
int read_current_timer(unsigned long *timer_val)
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 2346cf8..e21eed5 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -39,6 +39,7 @@
select MSM_RUN_QUEUE_STATS if MSM_SOC_REV_A
select DONT_MAP_HOLE_AFTER_MEMBANK0
select MIGHT_HAVE_CACHE_L2X0
+ select MSM_MODEM_RESTART
config ARCH_MSM7X30
bool "MSM7x30"
@@ -65,6 +66,7 @@
select MULTI_IRQ_HANDLER
select MSM_PM2 if PM
select HOLES_IN_ZONE if SPARSEMEM
+ select MSM_MODEM_RESTART
config ARCH_QSD8X50
bool "QSD8X50"
@@ -78,6 +80,7 @@
select MSM_GPIOMUX
select MSM_DALRPC
select MSM_PM2 if PM
+ select MSM_MODEM_RESTART
config ARCH_MSM8X60
bool "MSM8X60"
@@ -2090,6 +2093,9 @@
config MSM_NATIVE_RESTART
bool
+config MSM_MODEM_RESTART
+ bool
+
config MSM_PM2
depends on PM
bool
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 841ed3c..f7f488a 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -363,6 +363,7 @@
obj-$(CONFIG_MSM_FAKE_BATTERY) += fish_battery.o
obj-$(CONFIG_MSM_RPC_VIBRATOR) += msm_vibrator.o
obj-$(CONFIG_MSM_NATIVE_RESTART) += restart.o
+obj-$(CONFIG_MSM_MODEM_RESTART) += restart_7k.o
obj-$(CONFIG_MSM_PROC_COMM_REGULATOR) += proccomm-regulator.o
ifdef CONFIG_MSM_PROC_COMM_REGULATOR
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index 3e07833..d3af1a7 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -2253,7 +2253,6 @@
&msm_gss,
&apq8064_rtb_device,
&apq8064_cpu_idle_device,
- &apq8064_msm_gov_device,
&apq8064_device_cache_erp,
&msm8960_device_ebi1_ch0_erp,
&msm8960_device_ebi1_ch1_erp,
diff --git a/arch/arm/mach-msm/board-8930-display.c b/arch/arm/mach-msm/board-8930-display.c
index 292c031..d975997 100644
--- a/arch/arm/mach-msm/board-8930-display.c
+++ b/arch/arm/mach-msm/board-8930-display.c
@@ -482,16 +482,16 @@
static struct mipi_dsi_phy_ctrl dsi_novatek_cmd_mode_phy_db = {
/* DSI_BIT_CLK at 500MHz, 2 lane, RGB888 */
- {0x0F, 0x0a, 0x04, 0x00, 0x20}, /* regulator */
+ {0x09, 0x08, 0x05, 0x00, 0x20}, /* regulator */
/* timing */
{0xab, 0x8a, 0x18, 0x00, 0x92, 0x97, 0x1b, 0x8c,
0x0c, 0x03, 0x04, 0xa0},
{0x5f, 0x00, 0x00, 0x10}, /* phy ctrl */
{0xff, 0x00, 0x06, 0x00}, /* strength */
/* pll control */
- {0x40, 0xf9, 0x30, 0xda, 0x00, 0x40, 0x03, 0x62,
+ {0x0, 0xe, 0x30, 0xda, 0x00, 0x10, 0x0f, 0x61,
0x40, 0x07, 0x03,
- 0x00, 0x1a, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x01},
+ 0x00, 0x1a, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x02},
};
static struct mipi_dsi_panel_platform_data novatek_pdata = {
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index d56bdbd..18db7ce 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -1407,11 +1407,18 @@
msm_bus_rpm_set_mt_mask();
msm_bus_8960_apps_fabric_pdata.rpm_enabled = 1;
msm_bus_8960_sys_fabric_pdata.rpm_enabled = 1;
- msm_bus_8960_mm_fabric_pdata.rpm_enabled = 1;
msm_bus_apps_fabric.dev.platform_data =
&msm_bus_8960_apps_fabric_pdata;
msm_bus_sys_fabric.dev.platform_data = &msm_bus_8960_sys_fabric_pdata;
- msm_bus_mm_fabric.dev.platform_data = &msm_bus_8960_mm_fabric_pdata;
+ if (cpu_is_msm8960ab()) {
+ msm_bus_8960_sg_mm_fabric_pdata.rpm_enabled = 1;
+ msm_bus_mm_fabric.dev.platform_data =
+ &msm_bus_8960_sg_mm_fabric_pdata;
+ } else {
+ msm_bus_8960_mm_fabric_pdata.rpm_enabled = 1;
+ msm_bus_mm_fabric.dev.platform_data =
+ &msm_bus_8960_mm_fabric_pdata;
+ }
msm_bus_sys_fpb.dev.platform_data = &msm_bus_8960_sys_fpb_pdata;
msm_bus_cpss_fpb.dev.platform_data = &msm_bus_8960_cpss_fpb_pdata;
#endif
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index 30b44bd..74aa837 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -59,7 +59,7 @@
#endif
#define MSM_ION_MM_FW_SIZE 0xa00000 /* (10MB) */
#define MSM_ION_MM_SIZE 0x7800000 /* (120MB) */
-#define MSM_ION_QSECOM_SIZE 0x100000 /* (1MB) */
+#define MSM_ION_QSECOM_SIZE 0x600000 /* (6MB) */
#define MSM_ION_MFC_SIZE SZ_8K
#define MSM_ION_AUDIO_SIZE 0x2B4000
#define MSM_ION_HEAP_NUM 8
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index a5430e6..73fd8ef 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -4782,7 +4782,9 @@
CLK_LOOKUP("iface_clk", oxilicx_ahb_clk.c, "fdb00000.qcom,kgsl-3d0"),
CLK_LOOKUP("core_clk", oxilicx_axi_clk.c, "fdb10000.qcom,iommu"),
CLK_LOOKUP("iface_clk", oxilicx_ahb_clk.c, "fdb10000.qcom,iommu"),
+ CLK_LOOKUP("alt_core_clk", oxili_gfx3d_clk.c, "fdb10000.qcom,iommu"),
CLK_LOOKUP("iface_clk", venus0_ahb_clk.c, "fdc84000.qcom,iommu"),
+ CLK_LOOKUP("alt_core_clk", venus0_vcodec0_clk.c, "fdc84000.qcom,iommu"),
CLK_LOOKUP("core_clk", venus0_axi_clk.c, "fdc84000.qcom,iommu"),
CLK_LOOKUP("bus_clk", venus0_axi_clk.c, ""),
CLK_LOOKUP("src_clk", vcodec0_clk_src.c, "fdce0000.qcom,venus"),
diff --git a/arch/arm/mach-msm/devices-msm7x2xa.h b/arch/arm/mach-msm/devices-msm7x2xa.h
index 4184a86..8febe26 100644
--- a/arch/arm/mach-msm/devices-msm7x2xa.h
+++ b/arch/arm/mach-msm/devices-msm7x2xa.h
@@ -33,4 +33,5 @@
void __init msm8x25_spm_device_init(void);
void __init msm8x25_kgsl_3d0_init(void);
void __iomem *core1_reset_base(void);
+extern void setup_mm_for_reboot(void);
#endif
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index 28c53db..b14f145 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -72,6 +72,7 @@
* @irq: Interrupt number
* @clk: The bus clock for this IOMMU hardware instance
* @pclk: The clock for the IOMMU bus interconnect
+ * @aclk: Alternate clock for this IOMMU core, if any
* @name: Human-readable name of this IOMMU device
* @gdsc: Regulator needed to power this HW block (v2 only)
* @nsmr: Size of the SMT on this HW block (v2 only)
@@ -85,6 +86,7 @@
int ttbr_split;
struct clk *clk;
struct clk *pclk;
+ struct clk *aclk;
const char *name;
struct regulator *gdsc;
unsigned int nsmr;
diff --git a/arch/arm/mach-msm/include/mach/msm_bus_board.h b/arch/arm/mach-msm/include/mach/msm_bus_board.h
index d95e4a4..0a53b46 100644
--- a/arch/arm/mach-msm/include/mach/msm_bus_board.h
+++ b/arch/arm/mach-msm/include/mach/msm_bus_board.h
@@ -65,6 +65,7 @@
extern struct msm_bus_fabric_registration msm_bus_8960_apps_fabric_pdata;
extern struct msm_bus_fabric_registration msm_bus_8960_sys_fabric_pdata;
extern struct msm_bus_fabric_registration msm_bus_8960_mm_fabric_pdata;
+extern struct msm_bus_fabric_registration msm_bus_8960_sg_mm_fabric_pdata;
extern struct msm_bus_fabric_registration msm_bus_8960_sys_fpb_pdata;
extern struct msm_bus_fabric_registration msm_bus_8960_cpss_fpb_pdata;
diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h
index 2c3d395..f7ba507 100644
--- a/arch/arm/mach-msm/include/mach/socinfo.h
+++ b/arch/arm/mach-msm/include/mach/socinfo.h
@@ -330,4 +330,16 @@
#endif
}
+static inline int cpu_is_msm8974(void)
+{
+#ifdef CONFIG_ARCH_MSM8974
+ enum msm_cpu cpu = socinfo_get_msm_cpu();
+
+ BUG_ON(cpu == MSM_CPU_UNKNOWN);
+ return cpu == MSM_CPU_8974;
+#else
+ return 0;
+#endif
+}
+
#endif
diff --git a/arch/arm/mach-msm/msm-buspm-dev.c b/arch/arm/mach-msm/msm-buspm-dev.c
index 296418d..a818eed 100644
--- a/arch/arm/mach-msm/msm-buspm-dev.c
+++ b/arch/arm/mach-msm/msm-buspm-dev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -40,6 +40,13 @@
return (dev) ? dev->vaddr : NULL;
}
+static inline unsigned int msm_buspm_dev_get_buflen(struct file *filp)
+{
+ struct msm_buspm_map_dev *dev = filp->private_data;
+
+ return dev ? dev->buflen : 0;
+}
+
static inline unsigned long msm_buspm_dev_get_paddr(struct file *filp)
{
struct msm_buspm_map_dev *dev = filp->private_data;
@@ -114,6 +121,7 @@
unsigned long paddr;
int retval = 0;
void *buf = msm_buspm_dev_get_vaddr(filp);
+ unsigned int buflen = msm_buspm_dev_get_buflen(filp);
unsigned char *dbgbuf = buf;
switch (cmd) {
@@ -156,7 +164,7 @@
break;
}
- if ((xfer.size <= sizeof(buf)) &&
+ if ((xfer.size <= buflen) &&
(copy_to_user((void __user *)xfer.data, buf,
xfer.size))) {
retval = -EFAULT;
@@ -177,7 +185,7 @@
break;
}
- if ((sizeof(buf) <= xfer.size) &&
+ if ((buflen <= xfer.size) &&
(copy_from_user(buf, (void __user *)xfer.data,
xfer.size))) {
retval = -EFAULT;
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 7ede23d..9ba9f7b1 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
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. 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,12 @@
MSM_BUS_TIERED_SLAVE_KMPSS_L2,
};
+enum msm_bus_sg_tiered_slaves_type {
+ SG_TIERED_SLAVE_MM_IMEM = 1,
+ SG_MMSS_TIERED_SLAVE_FAB_APPS_0,
+ SG_MMSS_TIERED_SLAVE_FAB_APPS_1,
+};
+
enum msm_bus_8960_master_ports_type {
MSM_BUS_SYSTEM_MASTER_PORT_APPSS_FAB = 0,
MSM_BUS_MASTER_PORT_SPS,
@@ -106,6 +112,23 @@
MSM_BUS_SLAVE_PORT_RIVA,
};
+enum msm_bus_8960_sg_master_ports_type {
+ MSM_BUS_MASTER_PORT_GRAPHICS_3D_PORT0 =
+ MSM_BUS_MMSS_MASTER_PORT_UNUSED_2,
+ MSM_BUS_MASTER_PORT_VIDEO_CAP =
+ MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE0,
+ MSM_BUS_MASTER_PORT_VIDEO_DEC =
+ MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE1,
+ MSM_BUS_MASTER_PORT_VIDEO_ENC =
+ MSM_BUS_MASTER_PORT_HD_CODEC_PORT0,
+};
+
+enum msm_bus_8960_sg_slave_ports_type {
+ SG_SLAVE_PORT_MM_IMEM = 0,
+ SG_MMSS_SLAVE_PORT_APPS_FAB_0,
+ SG_MMSS_SLAVE_PORT_APPS_FAB_1,
+};
+
static int tier2[] = {MSM_BUS_BW_TIER2,};
static uint32_t master_iids[NMASTERS];
static uint32_t slave_iids[NSLAVES];
@@ -424,6 +447,10 @@
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,};
+static int pro_mport_graphics_3d[] = {
+ MSM_BUS_MASTER_PORT_GRAPHICS_3D_PORT0,
+ MSM_BUS_MASTER_PORT_GRAPHICS_3D,
+};
static int mport_jpeg_dec[] = {MSM_BUS_MASTER_PORT_JPEG_DEC,};
static int mport_graphics_2d_core0[] = {MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE0,};
static int mport_vfe[] = {MSM_BUS_MASTER_PORT_VFE,};
@@ -432,6 +459,9 @@
static int mport_graphics_2d_core1[] = {MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE1,};
static int mport_hd_codec_port0[] = {MSM_BUS_MASTER_PORT_HD_CODEC_PORT0,};
static int mport_hd_codec_port1[] = {MSM_BUS_MASTER_PORT_HD_CODEC_PORT1,};
+static int mport_video_cap[] = {MSM_BUS_MASTER_PORT_VIDEO_CAP};
+static int mport_video_enc[] = {MSM_BUS_MASTER_PORT_VIDEO_ENC};
+static int mport_video_dec[] = {MSM_BUS_MASTER_PORT_VIDEO_DEC};
static int appss_mport_fab_mmss[] = {
MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS_0,
MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS_1
@@ -439,15 +469,25 @@
static int mmss_sport_apps_fab[] = {
MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_0,
- MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_1
+ MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_1,
+};
+static int sg_sport_apps_fab[] = {
+ SG_MMSS_SLAVE_PORT_APPS_FAB_0,
+ SG_MMSS_SLAVE_PORT_APPS_FAB_1,
};
static int sport_mm_imem[] = {MSM_BUS_SLAVE_PORT_MM_IMEM,};
+static int sg_sport_mm_imem[] = {SG_SLAVE_PORT_MM_IMEM,};
static int mmss_tiered_slave_fab_apps[] = {
MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS_0,
MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS_1,
};
+static int sg_tiered_slave_fab_apps[] = {
+ SG_MMSS_TIERED_SLAVE_FAB_APPS_0,
+ SG_MMSS_TIERED_SLAVE_FAB_APPS_1,
+};
static int tiered_slave_mm_imem[] = {MSM_BUS_TIERED_SLAVE_MM_IMEM,};
+static int sg_tiered_slave_mm_imem[] = {SG_TIERED_SLAVE_MM_IMEM,};
static struct msm_bus_node_info mmss_fabric_info[] = {
@@ -557,6 +597,106 @@
},
};
+static struct msm_bus_node_info sg_mmss_fabric_info[] = {
+ {
+ .id = MSM_BUS_MASTER_MDP_PORT0,
+ .masterp = mport_mdp,
+ .num_mports = ARRAY_SIZE(mport_mdp),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ },
+ {
+ .id = MSM_BUS_MASTER_MDP_PORT1,
+ .masterp = mport_mdp1,
+ .num_mports = ARRAY_SIZE(mport_mdp1),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ },
+ {
+ .id = MSM_BUS_MASTER_ROTATOR,
+ .masterp = mport_rotator,
+ .num_mports = ARRAY_SIZE(mport_rotator),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ },
+ {
+ .id = MSM_BUS_MASTER_GRAPHICS_3D,
+ .masterp = pro_mport_graphics_3d,
+ .num_mports = ARRAY_SIZE(pro_mport_graphics_3d),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ },
+ {
+ .id = MSM_BUS_MASTER_JPEG_DEC,
+ .masterp = mport_jpeg_dec,
+ .num_mports = ARRAY_SIZE(mport_jpeg_dec),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ },
+ {
+ .id = MSM_BUS_MASTER_VIDEO_CAP,
+ .masterp = mport_video_cap,
+ .num_mports = ARRAY_SIZE(mport_video_cap),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ },
+ {
+ .id = MSM_BUS_MASTER_VFE,
+ .masterp = mport_vfe,
+ .num_mports = ARRAY_SIZE(mport_vfe),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ },
+ {
+ .id = MSM_BUS_MASTER_VPE,
+ .masterp = mport_vpe,
+ .num_mports = ARRAY_SIZE(mport_vpe),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ },
+ {
+ .id = MSM_BUS_MASTER_JPEG_ENC,
+ .masterp = mport_jpeg_enc,
+ .num_mports = ARRAY_SIZE(mport_jpeg_enc),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ },
+ /* This port has been added for V2. It is absent in V1 */
+ {
+ .id = MSM_BUS_MASTER_VIDEO_DEC,
+ .masterp = mport_video_dec,
+ .num_mports = ARRAY_SIZE(mport_video_dec),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ },
+ {
+ .id = MSM_BUS_MASTER_VIDEO_ENC,
+ .masterp = mport_video_enc,
+ .num_mports = ARRAY_SIZE(mport_video_enc),
+ .tier = tier2,
+ .num_tiers = ARRAY_SIZE(tier2),
+ },
+ {
+ .id = MSM_BUS_FAB_APPSS,
+ .gateway = 1,
+ .slavep = sg_sport_apps_fab,
+ .num_sports = ARRAY_SIZE(sg_sport_apps_fab),
+ .masterp = appss_mport_fab_mmss,
+ .num_mports = ARRAY_SIZE(appss_mport_fab_mmss),
+ .tier = sg_tiered_slave_fab_apps,
+ .num_tiers = ARRAY_SIZE(sg_tiered_slave_fab_apps),
+ .buswidth = 16,
+ },
+ {
+ .id = MSM_BUS_SLAVE_MM_IMEM,
+ .slavep = sg_sport_mm_imem,
+ .num_sports = ARRAY_SIZE(sg_sport_mm_imem),
+ .tier = sg_tiered_slave_mm_imem,
+ .num_tiers = ARRAY_SIZE(sg_tiered_slave_mm_imem),
+ .buswidth = 8,
+ },
+};
+
static struct msm_bus_node_info sys_fpb_fabric_info[] = {
{
.id = MSM_BUS_FAB_SYSTEM,
@@ -919,6 +1059,22 @@
.board_algo = &msm_bus_board_algo,
};
+struct msm_bus_fabric_registration msm_bus_8960_sg_mm_fabric_pdata = {
+ .id = MSM_BUS_FAB_MMSS,
+ .name = "msm_mm_fab",
+ sg_mmss_fabric_info,
+ ARRAY_SIZE(sg_mmss_fabric_info),
+ .ahb = 0,
+ .fabclk[DUAL_CTX] = "bus_clk",
+ .fabclk[ACTIVE_CTX] = "bus_a_clk",
+ .haltid = MSM_RPM_ID_MMSS_FABRIC_CFG_HALT_0,
+ .offset = MSM_RPM_ID_MM_FABRIC_ARB_0,
+ .nmasters = 13,
+ .nslaves = 3,
+ .ntieredslaves = 3,
+ .board_algo = &msm_bus_board_algo,
+};
+
struct msm_bus_fabric_registration msm_bus_8960_sys_fpb_pdata = {
.id = MSM_BUS_FAB_SYSTEM_FPB,
.name = "msm_sys_fpb",
diff --git a/arch/arm/mach-msm/pcie.c b/arch/arm/mach-msm/pcie.c
index f105356..d954b53 100644
--- a/arch/arm/mach-msm/pcie.c
+++ b/arch/arm/mach-msm/pcie.c
@@ -200,6 +200,16 @@
static int msm_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
int where, int size, u32 val)
{
+ /*
+ *Attempt to reset secondary bus is causing PCIE core to reset.
+ *Disable secondary bus reset functionality.
+ */
+ if ((bus->number == 0) && (where == PCI_BRIDGE_CONTROL) &&
+ (val & PCI_BRIDGE_CTL_BUS_RESET)) {
+ pr_info("PCIE secondary bus reset not supported\n");
+ val &= ~PCI_BRIDGE_CTL_BUS_RESET;
+ }
+
return msm_pcie_oper_conf(bus, devfn, WR, where, size, &val);
}
diff --git a/arch/arm/mach-msm/pm2.c b/arch/arm/mach-msm/pm2.c
index 7a8e4c3..8fccda4 100644
--- a/arch/arm/mach-msm/pm2.c
+++ b/arch/arm/mach-msm/pm2.c
@@ -21,10 +21,8 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/init.h>
-#include <linux/pm.h>
#include <linux/pm_qos.h>
#include <linux/suspend.h>
-#include <linux/reboot.h>
#include <linux/io.h>
#include <linux/tick.h>
#include <linux/memory.h>
@@ -34,7 +32,6 @@
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#endif
-#include <asm/system_misc.h>
#ifdef CONFIG_CACHE_L2X0
#include <asm/hardware/cache-l2x0.h>
#endif
@@ -1577,55 +1574,6 @@
}
}
-/******************************************************************************
- * Restart Definitions
- *****************************************************************************/
-
-static uint32_t restart_reason = 0x776655AA;
-
-static void msm_pm_power_off(void)
-{
- msm_rpcrouter_close();
- msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
- for (;;)
- ;
-}
-
-static void msm_pm_restart(char str, const char *cmd)
-{
- msm_rpcrouter_close();
- msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
-
- for (;;)
- ;
-}
-
-static int msm_reboot_call
- (struct notifier_block *this, unsigned long code, void *_cmd)
-{
- if ((code == SYS_RESTART) && _cmd) {
- char *cmd = _cmd;
- if (!strcmp(cmd, "bootloader")) {
- restart_reason = 0x77665500;
- } else if (!strcmp(cmd, "recovery")) {
- restart_reason = 0x77665502;
- } else if (!strcmp(cmd, "eraseflash")) {
- restart_reason = 0x776655EF;
- } else if (!strncmp(cmd, "oem-", 4)) {
- unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;
- restart_reason = 0x6f656d00 | code;
- } else {
- restart_reason = 0x77665501;
- }
- }
- return NOTIFY_DONE;
-}
-
-static struct notifier_block msm_reboot_notifier = {
- .notifier_call = msm_reboot_call,
-};
-
-
/*
* Initialize the power management subsystem.
*
@@ -1693,10 +1641,6 @@
virt_to_phys(&msm_pm_pc_pgd));
#endif
- pm_power_off = msm_pm_power_off;
- arm_pm_restart = msm_pm_restart;
- register_reboot_notifier(&msm_reboot_notifier);
-
msm_pm_smem_data = smem_alloc(SMEM_APPS_DEM_SLAVE_DATA,
sizeof(*msm_pm_smem_data));
if (msm_pm_smem_data == NULL) {
diff --git a/arch/arm/mach-msm/restart_7k.c b/arch/arm/mach-msm/restart_7k.c
new file mode 100644
index 0000000..dc9edf4
--- /dev/null
+++ b/arch/arm/mach-msm/restart_7k.c
@@ -0,0 +1,101 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/reboot.h>
+#include <linux/pm.h>
+#include <asm/system_misc.h>
+#include <mach/proc_comm.h>
+
+#include "devices-msm7x2xa.h"
+#include "smd_rpcrouter.h"
+
+static uint32_t restart_reason = 0x776655AA;
+
+static void msm_pm_power_off(void)
+{
+ msm_rpcrouter_close();
+ msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
+ for (;;)
+ ;
+}
+
+static void msm_pm_restart(char str, const char *cmd)
+{
+ msm_rpcrouter_close();
+ pr_debug("The reset reason is %x\n", restart_reason);
+
+ /* Disable interrupts */
+ local_irq_disable();
+ local_fiq_disable();
+
+ /*
+ * Take out a flat memory mapping and will
+ * insert a 1:1 mapping in place of
+ * the user-mode pages to ensure predictable results
+ * This function takes care of flushing the caches
+ * and flushing the TLB.
+ */
+ setup_mm_for_reboot();
+
+ msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
+
+ for (;;)
+ ;
+}
+
+static int msm_reboot_call
+ (struct notifier_block *this, unsigned long code, void *_cmd)
+{
+ if ((code == SYS_RESTART) && _cmd) {
+ char *cmd = _cmd;
+ if (!strncmp(cmd, "bootloader", 10)) {
+ restart_reason = 0x77665500;
+ } else if (!strncmp(cmd, "recovery", 8)) {
+ restart_reason = 0x77665502;
+ } else if (!strncmp(cmd, "eraseflash", 10)) {
+ restart_reason = 0x776655EF;
+ } else if (!strncmp(cmd, "oem-", 4)) {
+ unsigned long code;
+ int res;
+ res = kstrtoul(cmd + 4, 16, &code);
+ code &= 0xff;
+ restart_reason = 0x6f656d00 | code;
+ } else {
+ restart_reason = 0x77665501;
+ }
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block msm_reboot_notifier = {
+ .notifier_call = msm_reboot_call,
+};
+
+static int __init msm_pm_restart_init(void)
+{
+ int ret;
+
+ pm_power_off = msm_pm_power_off;
+ arm_pm_restart = msm_pm_restart;
+
+ ret = register_reboot_notifier(&msm_reboot_notifier);
+ if (ret)
+ pr_err("Failed to register reboot notifier\n");
+
+ return ret;
+}
+late_initcall(msm_pm_restart_init);
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index 817c2dc..f3b5720 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -793,6 +793,10 @@
case 0x510F06F0:
return MSM_CPU_8064;
+ case 0x511F06F1:
+ case 0x512F06F0:
+ return MSM_CPU_8974;
+
default:
return MSM_CPU_UNKNOWN;
};
diff --git a/arch/arm/mach-msm/wcnss-ssr-8960.c b/arch/arm/mach-msm/wcnss-ssr-8960.c
index 4295d9b..318523b 100644
--- a/arch/arm/mach-msm/wcnss-ssr-8960.c
+++ b/arch/arm/mach-msm/wcnss-ssr-8960.c
@@ -132,6 +132,7 @@
{
pil_force_shutdown("wcnss");
flush_delayed_work(&cancel_vote_work);
+ wcnss_flush_delayed_boot_votes();
disable_irq_nosync(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ);
return 0;
diff --git a/drivers/bluetooth/hci_ibs.c b/drivers/bluetooth/hci_ibs.c
index 6845020..fb084f5 100644
--- a/drivers/bluetooth/hci_ibs.c
+++ b/drivers/bluetooth/hci_ibs.c
@@ -37,9 +37,7 @@
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
-#include <linux/ftrace.h>
#include <linux/poll.h>
-#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/tty.h>
@@ -94,15 +92,6 @@
HCI_IBS_RX_VOTE_CLOCK_OFF,
};
-/* HCI_IBS state for the WorkQueue */
-enum hci_ibs_wq_state_e {
- HCI_IBS_WQ_INIT_STATE = 0,
- HCI_IBS_WQ_TX_VOTE_OFF,
- HCI_IBS_WQ_RX_VOTE_OFF,
- HCI_IBS_WQ_AWAKE_RX,
- HCI_IBS_WQ_AWAKE_DEVICE,
-};
-
static unsigned long wake_retrans = 1;
static unsigned long tx_idle_delay = (HZ * 2);
@@ -123,11 +112,6 @@
unsigned long rx_vote; /* clock must be on for RX */
struct timer_list tx_idle_timer;
struct timer_list wake_retrans_timer;
- struct workqueue_struct *workqueue;
- struct work_struct ws_ibs;
- unsigned long ibs_wq_state;
- void *ibs_hu; /* keeps the hci_uart pointer for reference */
-
/* debug */
unsigned long ibs_sent_wacks;
unsigned long ibs_sent_slps;
@@ -258,56 +242,6 @@
return err;
}
-static void ibs_wq(struct work_struct *work)
-{
- unsigned long flags = 0;
- struct ibs_struct *ibs = container_of(work, struct ibs_struct,
- ws_ibs);
- struct hci_uart *hu = (struct hci_uart *)ibs->ibs_hu;
-
- BT_DBG("hu %p, ibs_wq state: %lu\n", hu, ibs->ibs_wq_state);
-
- /* lock hci_ibs state */
- spin_lock_irqsave(&ibs->hci_ibs_lock, flags);
-
- switch (ibs->ibs_wq_state) {
- case HCI_IBS_WQ_AWAKE_DEVICE:
- /* Vote for serial clock */
- ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_ON, hu);
-
- /* send wake indication to device */
- if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0)
- BT_ERR("cannot send WAKE to device");
-
- ibs->ibs_sent_wakes++; /* debug */
-
- /* start retransmit timer */
- mod_timer(&ibs->wake_retrans_timer, jiffies + wake_retrans);
- break;
- case HCI_IBS_WQ_AWAKE_RX:
- ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_ON, hu);
- ibs->rx_ibs_state = HCI_IBS_RX_AWAKE;
-
- if (send_hci_ibs_cmd(HCI_IBS_WAKE_ACK, hu) < 0)
- BT_ERR("cannot acknowledge device wake up");
-
- ibs->ibs_sent_wacks++; /* debug */
- /* actually send the packets */
- hci_uart_tx_wakeup(hu);
- break;
- case HCI_IBS_WQ_RX_VOTE_OFF:
- ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_OFF, hu);
- break;
- case HCI_IBS_WQ_TX_VOTE_OFF:
- ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
- break;
- default:
- BT_DBG("Invalid state in ibs workqueue");
- break;
- }
- spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
-}
-
static void hci_ibs_tx_idle_timeout(unsigned long arg)
{
struct hci_uart *hu = (struct hci_uart *) arg;
@@ -348,9 +282,7 @@
spin_lock_irqsave_nested(&ibs->hci_ibs_lock,
flags, SINGLE_DEPTH_NESTING);
- /* vote off tx clock */
- ibs->ibs_wq_state = HCI_IBS_WQ_TX_VOTE_OFF;
- queue_work(ibs->workqueue, &ibs->ws_ibs);
+ ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
out:
spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
}
@@ -404,16 +336,6 @@
skb_queue_head_init(&ibs->txq);
skb_queue_head_init(&ibs->tx_wait_q);
spin_lock_init(&ibs->hci_ibs_lock);
- ibs->workqueue = create_singlethread_workqueue("ibs_wq");
- if (!ibs->workqueue) {
- BT_ERR("IBS Workqueue not initialized properly");
- kfree(ibs);
- return -ENOMEM;
- }
-
- INIT_WORK(&ibs->ws_ibs, ibs_wq);
- ibs->ibs_hu = (void *)hu;
- ibs->ibs_wq_state = HCI_IBS_WQ_INIT_STATE;
/* Assume we start with both sides asleep -- extra wakes OK */
ibs->tx_ibs_state = HCI_IBS_TX_ASLEEP;
@@ -510,8 +432,6 @@
skb_queue_purge(&ibs->txq);
del_timer(&ibs->tx_idle_timer);
del_timer(&ibs->wake_retrans_timer);
- destroy_workqueue(ibs->workqueue);
- ibs->ibs_hu = NULL;
kfree_skb(ibs->rx_skb);
@@ -543,11 +463,9 @@
/* Make sure clock is on - we may have turned clock off since
* receiving the wake up indicator
*/
- /* awake rx clock */
- ibs->ibs_wq_state = HCI_IBS_WQ_AWAKE_RX;
- queue_work(ibs->workqueue, &ibs->ws_ibs);
- spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
- return;
+ ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_ON, hu);
+ ibs->rx_ibs_state = HCI_IBS_RX_AWAKE;
+ /* deliberate fall-through */
case HCI_IBS_RX_AWAKE:
/* Always acknowledge device wake up,
* sending IBS message doesn't count as TX ON.
@@ -592,9 +510,7 @@
case HCI_IBS_RX_AWAKE:
/* update state */
ibs->rx_ibs_state = HCI_IBS_RX_ASLEEP;
- /* vote off rx clock under workqueue */
- ibs->ibs_wq_state = HCI_IBS_WQ_RX_VOTE_OFF;
- queue_work(ibs->workqueue, &ibs->ws_ibs);
+ ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_OFF, hu);
break;
case HCI_IBS_RX_ASLEEP:
/* deliberate fall-through */
@@ -679,12 +595,20 @@
case HCI_IBS_TX_ASLEEP:
BT_DBG("device asleep, waking up and queueing packet");
+ ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_ON, hu);
/* save packet for later */
skb_queue_tail(&ibs->tx_wait_q, skb);
+ /* awake device */
+ if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) {
+ BT_ERR("cannot send WAKE to device");
+ break;
+ }
+ ibs->ibs_sent_wakes++; /* debug */
+
+ /* start retransmit timer */
+ mod_timer(&ibs->wake_retrans_timer, jiffies + wake_retrans);
+
ibs->tx_ibs_state = HCI_IBS_TX_WAKING;
- /* schedule a work queue to wake up device */
- ibs->ibs_wq_state = HCI_IBS_WQ_AWAKE_DEVICE;
- queue_work(ibs->workqueue, &ibs->ws_ibs);
break;
case HCI_IBS_TX_WAKING:
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index c0b82df..b70efe3 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -28,11 +28,6 @@
int signal_type;
};
-#define DIAG_CON_APSS (0x0001) /* Bit mask for APSS */
-#define DIAG_CON_MPSS (0x0002) /* Bit mask for MPSS */
-#define DIAG_CON_LPASS (0x0004) /* Bit mask for LPASS */
-#define DIAG_CON_WCNSS (0x0008) /* Bit mask for WCNSS */
-
enum {
DIAG_DCI_NO_ERROR = 1001, /* No error */
DIAG_DCI_NO_REG, /* Could not register */
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 2f356f0..95a85f2a 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -58,6 +58,11 @@
#define DIAG_CTRL_MSG_F3_MASK 11
#define CONTROL_CHAR 0x7E
+#define DIAG_CON_APSS (0x0001) /* Bit mask for APSS */
+#define DIAG_CON_MPSS (0x0002) /* Bit mask for MPSS */
+#define DIAG_CON_LPASS (0x0004) /* Bit mask for LPASS */
+#define DIAG_CON_WCNSS (0x0008) /* Bit mask for WCNSS */
+
/* Maximum number of pkt reg supported at initialization*/
extern unsigned int diag_max_reg;
extern unsigned int diag_threshold_reg;
@@ -224,6 +229,9 @@
struct work_struct diag_qdsp_mask_update_work;
struct work_struct diag_wcnss_mask_update_work;
struct work_struct diag_read_smd_dci_work;
+ struct work_struct diag_clean_modem_reg_work;
+ struct work_struct diag_clean_lpass_reg_work;
+ struct work_struct diag_clean_wcnss_reg_work;
uint8_t *msg_masks;
uint8_t *log_masks;
int log_masks_length;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 547f42f..240a514 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1268,6 +1268,12 @@
diag_read_smd_wcnss_cntl_work_fn);
INIT_WORK(&(driver->diag_read_smd_dci_work),
diag_read_smd_dci_work_fn);
+ INIT_WORK(&(driver->diag_clean_modem_reg_work),
+ diag_clean_modem_reg_fn);
+ INIT_WORK(&(driver->diag_clean_lpass_reg_work),
+ diag_clean_lpass_reg_fn);
+ INIT_WORK(&(driver->diag_clean_wcnss_reg_work),
+ diag_clean_wcnss_reg_fn);
diag_debugfs_init();
diagfwd_init();
diagfwd_cntl_init();
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 384c1bf..b228276 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -1735,8 +1735,8 @@
static void diag_smd_notify(void *ctxt, unsigned event)
{
if (event == SMD_EVENT_CLOSE) {
- pr_info("diag: clean modem registration\n");
- diag_clear_reg(MODEM_PROC);
+ queue_work(driver->diag_cntl_wq,
+ &(driver->diag_clean_modem_reg_work));
driver->ch = 0;
return;
} else if (event == SMD_EVENT_OPEN) {
@@ -1750,8 +1750,8 @@
static void diag_smd_qdsp_notify(void *ctxt, unsigned event)
{
if (event == SMD_EVENT_CLOSE) {
- pr_info("diag: clean lpass registration\n");
- diag_clear_reg(QDSP_PROC);
+ queue_work(driver->diag_cntl_wq,
+ &(driver->diag_clean_lpass_reg_work));
driver->chqdsp = 0;
return;
} else if (event == SMD_EVENT_OPEN) {
@@ -1765,8 +1765,8 @@
static void diag_smd_wcnss_notify(void *ctxt, unsigned event)
{
if (event == SMD_EVENT_CLOSE) {
- pr_info("diag: clean wcnss registration\n");
- diag_clear_reg(WCNSS_PROC);
+ queue_work(driver->diag_cntl_wq,
+ &(driver->diag_clean_wcnss_reg_work));
driver->ch_wcnss = 0;
return;
} else if (event == SMD_EVENT_OPEN) {
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index de1a5b5..95abd21 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -20,9 +20,34 @@
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
#endif
-
+/* tracks which peripheral is undergoing SSR */
+static uint16_t reg_dirty;
#define HDR_SIZ 8
+void diag_clean_modem_reg_fn(struct work_struct *work)
+{
+ pr_debug("diag: clean modem registration\n");
+ reg_dirty |= DIAG_CON_MPSS;
+ diag_clear_reg(MODEM_PROC);
+ reg_dirty ^= DIAG_CON_MPSS;
+}
+
+void diag_clean_lpass_reg_fn(struct work_struct *work)
+{
+ pr_debug("diag: clean lpass registration\n");
+ reg_dirty |= DIAG_CON_LPASS;
+ diag_clear_reg(QDSP_PROC);
+ reg_dirty ^= DIAG_CON_LPASS;
+}
+
+void diag_clean_wcnss_reg_fn(struct work_struct *work)
+{
+ pr_debug("diag: clean wcnss registration\n");
+ reg_dirty |= DIAG_CON_WCNSS;
+ diag_clear_reg(WCNSS_PROC);
+ reg_dirty ^= DIAG_CON_WCNSS;
+}
+
void diag_smd_cntl_notify(void *ctxt, unsigned event)
{
int r1, r2;
@@ -105,6 +130,8 @@
struct bindpkt_params *temp;
void *buf = NULL;
smd_channel_t *smd_ch = NULL;
+ /* tracks which peripheral is sending registration */
+ uint16_t reg_mask = 0;
if (pkt_params == NULL) {
pr_alert("diag: Memory allocation failure\n");
@@ -114,12 +141,15 @@
if (proc_num == MODEM_PROC) {
buf = driver->buf_in_cntl;
smd_ch = driver->ch_cntl;
+ reg_mask = DIAG_CON_MPSS;
} else if (proc_num == QDSP_PROC) {
buf = driver->buf_in_qdsp_cntl;
smd_ch = driver->chqdsp_cntl;
+ reg_mask = DIAG_CON_LPASS;
} else if (proc_num == WCNSS_PROC) {
buf = driver->buf_in_wcnss_cntl;
smd_ch = driver->ch_wcnss_cntl;
+ reg_mask = DIAG_CON_WCNSS;
}
if (!smd_ch || !buf) {
@@ -180,8 +210,16 @@
temp -= pkt_params->count;
pkt_params->params = temp;
flag = 1;
- diagchar_ioctl(NULL, DIAG_IOCTL_COMMAND_REG,
- (unsigned long)pkt_params);
+ /* peripheral undergoing SSR should not
+ * record new registration
+ */
+ if (!(reg_dirty & reg_mask))
+ diagchar_ioctl(NULL,
+ DIAG_IOCTL_COMMAND_REG, (unsigned long)
+ pkt_params);
+ else
+ pr_err("diag: drop reg proc %d\n",
+ proc_num);
kfree(temp);
}
buf = buf + HDR_SIZ + data_len;
@@ -275,6 +313,7 @@
void diagfwd_cntl_init(void)
{
+ reg_dirty = 0;
driver->polling_reg_flag = 0;
driver->diag_cntl_wq = create_singlethread_workqueue("diag_cntl_wq");
if (driver->buf_in_cntl == NULL) {
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index 743ddc1..59e5e6b 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -85,7 +85,9 @@
void diag_smd_cntl_notify(void *ctxt, unsigned event);
void diag_smd_qdsp_cntl_notify(void *ctxt, unsigned event);
void diag_smd_wcnss_cntl_notify(void *ctxt, unsigned event);
-
+void diag_clean_modem_reg_fn(struct work_struct *);
+void diag_clean_lpass_reg_fn(struct work_struct *);
+void diag_clean_wcnss_reg_fn(struct work_struct *);
void diag_debugfs_init(void);
void diag_debugfs_cleanup(void);
diff --git a/drivers/coresight/coresight-tpiu.c b/drivers/coresight/coresight-tpiu.c
index 4b52c4d..c0bcfdd 100644
--- a/drivers/coresight/coresight-tpiu.c
+++ b/drivers/coresight/coresight-tpiu.c
@@ -119,9 +119,17 @@
if (ret)
goto err_clk_rate;
+ /* Disable tpiu to support older targets that need this */
+ ret = clk_prepare_enable(drvdata->clk);
+ if (ret)
+ goto err_clk_enable;
+ __tpiu_disable();
+ clk_disable_unprepare(drvdata->clk);
+
dev_info(drvdata->dev, "TPIU initialized\n");
return 0;
+err_clk_enable:
err_clk_rate:
clk_put(drvdata->clk);
err_clk_get:
diff --git a/drivers/coresight/coresight.c b/drivers/coresight/coresight.c
index 055ef55..a17ac9a 100644
--- a/drivers/coresight/coresight.c
+++ b/drivers/coresight/coresight.c
@@ -20,71 +20,351 @@
#include <linux/err.h>
#include <linux/export.h>
#include <linux/slab.h>
-#include <linux/mutex.h>
+#include <linux/semaphore.h>
#include <linux/clk.h>
#include <linux/coresight.h>
#include "coresight-priv.h"
+
+#define NO_SINK (-1)
#define MAX_STR_LEN (65535)
+static int curr_sink = NO_SINK;
static LIST_HEAD(coresight_orph_conns);
-static DEFINE_MUTEX(coresight_conns_mutex);
static LIST_HEAD(coresight_devs);
-static DEFINE_MUTEX(coresight_devs_mutex);
+static DEFINE_SEMAPHORE(coresight_mutex);
-int coresight_enable(struct coresight_device *csdev, int port)
+static int coresight_find_link_inport(struct coresight_device *csdev)
{
int i;
- int ret;
+ struct coresight_device *parent;
struct coresight_connection *conn;
- mutex_lock(&csdev->mutex);
- if (csdev->refcnt[port] == 0) {
- for (i = 0; i < csdev->nr_conns; i++) {
- conn = &csdev->conns[i];
- ret = coresight_enable(conn->child_dev,
- conn->child_port);
- if (ret)
- goto err_enable_child;
- }
- if (csdev->ops->enable)
- ret = csdev->ops->enable(csdev, port);
- if (ret)
- goto err_enable;
+ parent = container_of(csdev->path_link.next, struct coresight_device,
+ path_link);
+ for (i = 0; i < parent->nr_conns; i++) {
+ conn = &parent->conns[i];
+ if (conn->child_dev == csdev)
+ return conn->child_port;
}
- csdev->refcnt[port]++;
- mutex_unlock(&csdev->mutex);
+
+ pr_err("coresight: couldn't find inport, parent: %d, child: %d\n",
+ parent->id, csdev->id);
return 0;
-err_enable_child:
- while (i) {
- conn = &csdev->conns[--i];
- coresight_disable(conn->child_dev, conn->child_port);
+}
+
+static int coresight_find_link_outport(struct coresight_device *csdev)
+{
+ int i;
+ struct coresight_device *child;
+ struct coresight_connection *conn;
+
+ child = container_of(csdev->path_link.prev, struct coresight_device,
+ path_link);
+ for (i = 0; i < csdev->nr_conns; i++) {
+ conn = &csdev->conns[i];
+ if (conn->child_dev == child)
+ return conn->outport;
}
-err_enable:
- mutex_unlock(&csdev->mutex);
+
+ pr_err("coresight: couldn't find outport, parent: %d, child: %d\n",
+ csdev->id, child->id);
+ return 0;
+}
+
+static int coresight_enable_sink(struct coresight_device *csdev)
+{
+ int ret;
+
+ if (csdev->refcnt.sink_refcnt == 0) {
+ if (csdev->ops->sink_ops->enable) {
+ ret = csdev->ops->sink_ops->enable(csdev);
+ if (ret)
+ goto err;
+ csdev->enable = true;
+ }
+ }
+ csdev->refcnt.sink_refcnt++;
+
+ return 0;
+err:
+ return ret;
+}
+
+static void coresight_disable_sink(struct coresight_device *csdev)
+{
+ if (csdev->refcnt.sink_refcnt == 1) {
+ if (csdev->ops->sink_ops->disable) {
+ csdev->ops->sink_ops->disable(csdev);
+ csdev->enable = false;
+ }
+ }
+ csdev->refcnt.sink_refcnt--;
+}
+
+static int coresight_enable_link(struct coresight_device *csdev)
+{
+ int ret;
+ int link_subtype;
+ int refport, inport, outport;
+
+ inport = coresight_find_link_inport(csdev);
+ outport = coresight_find_link_outport(csdev);
+
+ link_subtype = csdev->subtype.link_subtype;
+ if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG)
+ refport = inport;
+ else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT)
+ refport = outport;
+ else
+ refport = 0;
+
+ if (csdev->refcnt.link_refcnts[refport] == 0) {
+ if (csdev->ops->link_ops->enable) {
+ ret = csdev->ops->link_ops->enable(csdev, inport,
+ outport);
+ if (ret)
+ goto err;
+ csdev->enable = true;
+ }
+ }
+ csdev->refcnt.link_refcnts[refport]++;
+
+ return 0;
+err:
+ return ret;
+}
+
+static void coresight_disable_link(struct coresight_device *csdev)
+{
+ int link_subtype;
+ int refport, inport, outport;
+
+ inport = coresight_find_link_inport(csdev);
+ outport = coresight_find_link_outport(csdev);
+
+ link_subtype = csdev->subtype.link_subtype;
+ if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG)
+ refport = inport;
+ else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT)
+ refport = outport;
+ else
+ refport = 0;
+
+ if (csdev->refcnt.link_refcnts[refport] == 1) {
+ if (csdev->ops->link_ops->disable) {
+ csdev->ops->link_ops->disable(csdev, inport, outport);
+ csdev->enable = false;
+ }
+ }
+ csdev->refcnt.link_refcnts[refport]--;
+}
+
+static int coresight_enable_source(struct coresight_device *csdev)
+{
+ int ret;
+
+ if (csdev->refcnt.source_refcnt == 0) {
+ if (csdev->ops->source_ops->enable) {
+ ret = csdev->ops->source_ops->enable(csdev);
+ if (ret)
+ goto err;
+ csdev->enable = true;
+ }
+ }
+ csdev->refcnt.source_refcnt++;
+
+ return 0;
+err:
+ return ret;
+}
+
+static void coresight_disable_source(struct coresight_device *csdev)
+{
+ if (csdev->refcnt.source_refcnt == 1) {
+ if (csdev->ops->source_ops->disable) {
+ csdev->ops->source_ops->disable(csdev);
+ csdev->enable = false;
+ }
+ }
+ csdev->refcnt.source_refcnt--;
+}
+
+static struct list_head *coresight_build_path(struct coresight_device *csdev,
+ struct list_head *path)
+{
+ int i;
+ struct list_head *p;
+ struct coresight_connection *conn;
+
+ if (csdev->id == curr_sink) {
+ list_add_tail(&csdev->path_link, path);
+ return path;
+ }
+
+ for (i = 0; i < csdev->nr_conns; i++) {
+ conn = &csdev->conns[i];
+ p = coresight_build_path(conn->child_dev, path);
+ if (p) {
+ list_add_tail(&csdev->path_link, p);
+ return p;
+ }
+ }
+ return NULL;
+}
+
+static void coresight_release_path(struct list_head *path)
+{
+ struct coresight_device *cd, *temp;
+
+ list_for_each_entry_safe(cd, temp, path, path_link)
+ list_del(&cd->path_link);
+}
+
+static int coresight_enable_path(struct list_head *path, bool incl_source)
+{
+ int ret = 0;
+ struct coresight_device *cd;
+
+ list_for_each_entry(cd, path, path_link) {
+ if (cd == list_first_entry(path, struct coresight_device,
+ path_link)) {
+ ret = coresight_enable_sink(cd);
+ } else if (list_is_last(&cd->path_link, path)) {
+ if (incl_source)
+ ret = coresight_enable_source(cd);
+ } else {
+ ret = coresight_enable_link(cd);
+ }
+ if (ret)
+ goto err;
+ }
+ return 0;
+err:
+ list_for_each_entry_continue_reverse(cd, path, path_link) {
+ if (cd == list_first_entry(path, struct coresight_device,
+ path_link)) {
+ coresight_disable_sink(cd);
+ } else if (list_is_last(&cd->path_link, path)) {
+ if (incl_source)
+ coresight_disable_source(cd);
+ } else {
+ coresight_disable_link(cd);
+ }
+ }
+ return ret;
+}
+
+static void coresight_disable_path(struct list_head *path, bool incl_source)
+{
+ struct coresight_device *cd;
+
+ list_for_each_entry(cd, path, path_link) {
+ if (cd == list_first_entry(path, struct coresight_device,
+ path_link)) {
+ coresight_disable_sink(cd);
+ } else if (list_is_last(&cd->path_link, path)) {
+ if (incl_source)
+ coresight_disable_source(cd);
+ } else {
+ coresight_disable_link(cd);
+ }
+ }
+}
+
+static int coresight_switch_sink(struct coresight_device *csdev)
+{
+ int ret = 0;
+ LIST_HEAD(path);
+ struct coresight_device *cd;
+
+ if (IS_ERR_OR_NULL(csdev))
+ return -EINVAL;
+
+ down(&coresight_mutex);
+ if (csdev->id == curr_sink)
+ goto out;
+
+ list_for_each_entry(cd, &coresight_devs, dev_link) {
+ if (cd->type == CORESIGHT_DEV_TYPE_SOURCE && cd->enable) {
+ coresight_build_path(cd, &path);
+ coresight_disable_path(&path, false);
+ coresight_release_path(&path);
+ }
+ }
+ curr_sink = csdev->id;
+ list_for_each_entry(cd, &coresight_devs, dev_link) {
+ if (cd->type == CORESIGHT_DEV_TYPE_SOURCE && cd->enable) {
+ coresight_build_path(cd, &path);
+ ret = coresight_enable_path(&path, false);
+ coresight_release_path(&path);
+ if (ret)
+ goto err;
+ }
+ }
+out:
+ up(&coresight_mutex);
+ return 0;
+err:
+ list_for_each_entry(cd, &coresight_devs, dev_link) {
+ if (cd->type == CORESIGHT_DEV_TYPE_SOURCE && cd->enable)
+ coresight_disable_source(cd);
+ }
+ pr_err("coresight: sink switch failed, sources disabled; try again\n");
+ return ret;
+}
+
+int coresight_enable(struct coresight_device *csdev)
+{
+ int ret = 0;
+ LIST_HEAD(path);
+
+ if (IS_ERR_OR_NULL(csdev))
+ return -EINVAL;
+
+ down(&coresight_mutex);
+ if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
+ ret = -EINVAL;
+ pr_err("coresight: wrong device type in %s\n", __func__);
+ goto out;
+ }
+ if (csdev->enable)
+ goto out;
+
+ coresight_build_path(csdev, &path);
+ ret = coresight_enable_path(&path, true);
+ coresight_release_path(&path);
+ if (ret)
+ pr_err("coresight: enable failed\n");
+out:
+ up(&coresight_mutex);
return ret;
}
EXPORT_SYMBOL(coresight_enable);
-void coresight_disable(struct coresight_device *csdev, int port)
+void coresight_disable(struct coresight_device *csdev)
{
- int i;
- struct coresight_connection *conn;
+ LIST_HEAD(path);
- mutex_lock(&csdev->mutex);
- if (csdev->refcnt[port] == 1) {
- if (csdev->ops->disable)
- csdev->ops->disable(csdev, port);
- for (i = 0; i < csdev->nr_conns; i++) {
- conn = &csdev->conns[i];
- coresight_disable(conn->child_dev, conn->child_port);
- }
+ if (IS_ERR_OR_NULL(csdev))
+ return;
+
+ down(&coresight_mutex);
+ if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
+ pr_err("coresight: wrong device type in %s\n", __func__);
+ goto out;
}
- csdev->refcnt[port]--;
- mutex_unlock(&csdev->mutex);
+ if (!csdev->enable)
+ goto out;
+
+ coresight_build_path(csdev, &path);
+ coresight_disable_path(&path, true);
+ coresight_release_path(&path);
+out:
+ up(&coresight_mutex);
}
EXPORT_SYMBOL(coresight_disable);
@@ -104,6 +384,39 @@
.dev_attrs = coresight_dev_attrs,
};
+static ssize_t coresight_show_curr_sink(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct coresight_device *csdev = to_coresight_device(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n",
+ csdev->id == curr_sink ? 1 : 0);
+}
+
+static ssize_t coresight_store_curr_sink(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int ret = 0;
+ unsigned long val;
+ struct coresight_device *csdev = to_coresight_device(dev);
+
+ if (sscanf(buf, "%lx", &val) != 1)
+ return -EINVAL;
+
+ if (val)
+ ret = coresight_switch_sink(csdev);
+ else
+ ret = -EINVAL;
+
+ if (ret)
+ return ret;
+ return size;
+}
+static DEVICE_ATTR(curr_sink, S_IRUGO | S_IWUSR, coresight_show_curr_sink,
+ coresight_store_curr_sink);
+
static ssize_t coresight_show_enable(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -124,9 +437,9 @@
return -EINVAL;
if (val)
- ret = coresight_enable(csdev, 0);
+ ret = coresight_enable(csdev);
else
- coresight_disable(csdev, 0);
+ coresight_disable(csdev);
if (ret)
return ret;
@@ -135,38 +448,55 @@
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, coresight_show_enable,
coresight_store_enable);
-static struct attribute *coresight_attrs[] = {
+static struct attribute *coresight_attrs_sink[] = {
+ &dev_attr_curr_sink.attr,
+ NULL,
+};
+
+static struct attribute_group coresight_attr_grp_sink = {
+ .attrs = coresight_attrs_sink,
+};
+
+static const struct attribute_group *coresight_attr_grps_sink[] = {
+ &coresight_attr_grp_sink,
+ NULL,
+};
+
+static struct attribute *coresight_attrs_source[] = {
&dev_attr_enable.attr,
NULL,
};
-static struct attribute_group coresight_attr_grp = {
- .attrs = coresight_attrs,
+static struct attribute_group coresight_attr_grp_source = {
+ .attrs = coresight_attrs_source,
};
-static const struct attribute_group *coresight_attr_grps[] = {
- &coresight_attr_grp,
+static const struct attribute_group *coresight_attr_grps_source[] = {
+ &coresight_attr_grp_source,
NULL,
};
-static struct device_type coresight_dev_type[CORESIGHT_DEV_TYPE_MAX] = {
+static struct device_type coresight_dev_type[] = {
{
- .name = "source",
- .groups = coresight_attr_grps,
+ .name = "sink",
+ .groups = coresight_attr_grps_sink,
},
{
.name = "link",
},
{
- .name = "sink",
- .groups = coresight_attr_grps,
+ .name = "linksink",
+ .groups = coresight_attr_grps_sink,
+ },
+ {
+ .name = "source",
+ .groups = coresight_attr_grps_source,
},
};
static void coresight_device_release(struct device *dev)
{
struct coresight_device *csdev = to_coresight_device(dev);
- mutex_destroy(&csdev->mutex);
kfree(csdev);
}
@@ -174,14 +504,12 @@
{
struct coresight_connection *conn, *temp;
- mutex_lock(&coresight_conns_mutex);
list_for_each_entry_safe(conn, temp, &coresight_orph_conns, link) {
if (conn->child_id == csdev->id) {
conn->child_dev = csdev;
list_del(&conn->link);
}
}
- mutex_unlock(&coresight_conns_mutex);
}
static void coresight_fixup_device_conns(struct coresight_device *csdev)
@@ -192,21 +520,16 @@
for (i = 0; i < csdev->nr_conns; i++) {
found = false;
- mutex_lock(&coresight_devs_mutex);
- list_for_each_entry(cd, &coresight_devs, link) {
+ list_for_each_entry(cd, &coresight_devs, dev_link) {
if (csdev->conns[i].child_id == cd->id) {
csdev->conns[i].child_dev = cd;
found = true;
break;
}
}
- mutex_unlock(&coresight_devs_mutex);
- if (!found) {
- mutex_lock(&coresight_conns_mutex);
+ if (!found)
list_add_tail(&csdev->conns[i].link,
&coresight_orph_conns);
- mutex_unlock(&coresight_conns_mutex);
- }
}
}
@@ -214,7 +537,9 @@
{
int i;
int ret;
- int *refcnt;
+ int link_subtype;
+ int nr_refcnts;
+ int *refcnts = NULL;
struct coresight_device *csdev;
struct coresight_connection *conns;
@@ -224,28 +549,41 @@
goto err_kzalloc_csdev;
}
- mutex_init(&csdev->mutex);
csdev->id = desc->pdata->id;
- refcnt = kzalloc(sizeof(*refcnt) * desc->pdata->nr_ports, GFP_KERNEL);
- if (!refcnt) {
- ret = -ENOMEM;
- goto err_kzalloc_refcnt;
- }
- csdev->refcnt = refcnt;
+ if (desc->type == CORESIGHT_DEV_TYPE_LINK ||
+ desc->type == CORESIGHT_DEV_TYPE_LINKSINK) {
+ link_subtype = desc->subtype.link_subtype;
+ if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG)
+ nr_refcnts = desc->pdata->nr_inports;
+ else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT)
+ nr_refcnts = desc->pdata->nr_outports;
+ else
+ nr_refcnts = 1;
- csdev->nr_conns = desc->pdata->nr_children;
+ refcnts = kzalloc(sizeof(*refcnts) * nr_refcnts, GFP_KERNEL);
+ if (!refcnts) {
+ ret = -ENOMEM;
+ goto err_kzalloc_refcnts;
+ }
+ csdev->refcnt.link_refcnts = refcnts;
+ }
+
+ csdev->nr_conns = desc->pdata->nr_outports;
conns = kzalloc(sizeof(*conns) * csdev->nr_conns, GFP_KERNEL);
if (!conns) {
ret = -ENOMEM;
goto err_kzalloc_conns;
}
for (i = 0; i < csdev->nr_conns; i++) {
+ conns[i].outport = desc->pdata->outports[i];
conns[i].child_id = desc->pdata->child_ids[i];
conns[i].child_port = desc->pdata->child_ports[i];
}
csdev->conns = conns;
+ csdev->type = desc->type;
+ csdev->subtype = desc->subtype;
csdev->ops = desc->ops;
csdev->owner = desc->owner;
@@ -256,24 +594,34 @@
csdev->dev.release = coresight_device_release;
dev_set_name(&csdev->dev, "%s", desc->pdata->name);
+ down(&coresight_mutex);
+ if (desc->pdata->default_sink) {
+ if (curr_sink == NO_SINK) {
+ curr_sink = csdev->id;
+ } else {
+ ret = -EINVAL;
+ goto err_default_sink;
+ }
+ }
+
coresight_fixup_device_conns(csdev);
ret = device_register(&csdev->dev);
if (ret)
goto err_dev_reg;
coresight_fixup_orphan_conns(csdev);
- mutex_lock(&coresight_devs_mutex);
- list_add_tail(&csdev->link, &coresight_devs);
- mutex_unlock(&coresight_devs_mutex);
+ list_add_tail(&csdev->dev_link, &coresight_devs);
+ up(&coresight_mutex);
return csdev;
err_dev_reg:
put_device(&csdev->dev);
+err_default_sink:
+ up(&coresight_mutex);
kfree(conns);
err_kzalloc_conns:
- kfree(refcnt);
-err_kzalloc_refcnt:
- mutex_destroy(&csdev->mutex);
+ kfree(refcnts);
+err_kzalloc_refcnts:
kfree(csdev);
err_kzalloc_csdev:
return ERR_PTR(ret);
@@ -286,9 +634,7 @@
return;
if (get_device(&csdev->dev)) {
- mutex_lock(&csdev->mutex);
device_unregister(&csdev->dev);
- mutex_unlock(&csdev->mutex);
put_device(&csdev->dev);
}
}
diff --git a/drivers/input/touchscreen/msm_ts.c b/drivers/input/touchscreen/msm_ts.c
index eb2e73b..e66120e 100644
--- a/drivers/input/touchscreen/msm_ts.c
+++ b/drivers/input/touchscreen/msm_ts.c
@@ -387,6 +387,7 @@
input_set_capability(ts->input_dev, EV_KEY, BTN_TOUCH);
set_bit(EV_ABS, ts->input_dev->evbit);
+ set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
input_set_abs_params(ts->input_dev, ABS_X, pdata->min_x, pdata->max_x,
0, 0);
diff --git a/drivers/iommu/msm_iommu-v2.c b/drivers/iommu/msm_iommu-v2.c
index 48edf96..28ad0ff 100644
--- a/drivers/iommu/msm_iommu-v2.c
+++ b/drivers/iommu/msm_iommu-v2.c
@@ -51,10 +51,16 @@
if (ret)
goto fail;
- if (drvdata->clk) {
- ret = clk_prepare_enable(drvdata->clk);
- if (ret)
+ ret = clk_prepare_enable(drvdata->clk);
+ if (ret)
+ clk_disable_unprepare(drvdata->pclk);
+
+ if (drvdata->aclk) {
+ ret = clk_prepare_enable(drvdata->aclk);
+ if (ret) {
+ clk_disable_unprepare(drvdata->clk);
clk_disable_unprepare(drvdata->pclk);
+ }
}
fail:
return ret;
@@ -62,8 +68,9 @@
static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
{
- if (drvdata->clk)
- clk_disable_unprepare(drvdata->clk);
+ if (drvdata->aclk)
+ clk_disable_unprepare(drvdata->aclk);
+ clk_disable_unprepare(drvdata->clk);
clk_disable_unprepare(drvdata->pclk);
}
diff --git a/drivers/iommu/msm_iommu_dev-v2.c b/drivers/iommu/msm_iommu_dev-v2.c
index 14ed5d9..8c26f95 100644
--- a/drivers/iommu/msm_iommu_dev-v2.c
+++ b/drivers/iommu/msm_iommu_dev-v2.c
@@ -69,7 +69,7 @@
{
struct msm_iommu_drvdata *drvdata;
struct resource *r;
- int ret;
+ int ret, needs_alt_core_clk;
if (msm_iommu_root_dev == pdev)
return 0;
@@ -93,55 +93,42 @@
if (IS_ERR(drvdata->gdsc))
return -EINVAL;
- drvdata->pclk = clk_get(&pdev->dev, "iface_clk");
+ drvdata->pclk = devm_clk_get(&pdev->dev, "iface_clk");
if (IS_ERR(drvdata->pclk))
return PTR_ERR(drvdata->pclk);
- ret = clk_prepare_enable(drvdata->pclk);
- if (ret)
- goto fail_enable;
+ drvdata->clk = devm_clk_get(&pdev->dev, "core_clk");
+ if (IS_ERR(drvdata->clk))
+ return PTR_ERR(drvdata->clk);
- drvdata->clk = clk_get(&pdev->dev, "core_clk");
- if (!IS_ERR(drvdata->clk)) {
- if (clk_get_rate(drvdata->clk) == 0) {
- ret = clk_round_rate(drvdata->clk, 1);
- clk_set_rate(drvdata->clk, ret);
- }
+ needs_alt_core_clk = of_property_read_bool(pdev->dev.of_node,
+ "qcom,needs-alt-core-clk");
+ if (needs_alt_core_clk) {
+ drvdata->aclk = devm_clk_get(&pdev->dev, "alt_core_clk");
+ if (IS_ERR(drvdata->aclk))
+ return PTR_ERR(drvdata->aclk);
+ }
- ret = clk_prepare_enable(drvdata->clk);
- if (ret) {
- clk_put(drvdata->clk);
- goto fail_pclk;
- }
- } else
- drvdata->clk = NULL;
+ if (clk_get_rate(drvdata->clk) == 0) {
+ ret = clk_round_rate(drvdata->clk, 1);
+ clk_set_rate(drvdata->clk, ret);
+ }
+
+ if (drvdata->aclk && clk_get_rate(drvdata->aclk) == 0) {
+ ret = clk_round_rate(drvdata->aclk, 1);
+ clk_set_rate(drvdata->aclk, ret);
+ }
ret = msm_iommu_parse_dt(pdev, drvdata);
if (ret)
- goto fail_clk;
+ return ret;
pr_info("device %s mapped at %p, with %d ctx banks\n",
drvdata->name, drvdata->base, drvdata->ncb);
platform_set_drvdata(pdev, drvdata);
- if (drvdata->clk)
- clk_disable_unprepare(drvdata->clk);
-
- clk_disable_unprepare(drvdata->pclk);
-
return 0;
-
-fail_clk:
- if (drvdata->clk) {
- clk_disable_unprepare(drvdata->clk);
- clk_put(drvdata->clk);
- }
-fail_pclk:
- clk_disable_unprepare(drvdata->pclk);
-fail_enable:
- clk_put(drvdata->pclk);
- return ret;
}
static int __devexit msm_iommu_remove(struct platform_device *pdev)
diff --git a/drivers/media/video/msm/msm_isp.c b/drivers/media/video/msm/msm_isp.c
index 67e7c02..9ddde15 100644
--- a/drivers/media/video/msm/msm_isp.c
+++ b/drivers/media/video/msm/msm_isp.c
@@ -424,10 +424,12 @@
stats.buf_idx = isp_stats->buf_idx;
switch (isp_stats->id) {
case MSG_ID_STATS_AEC:
+ case MSG_ID_STATS_BG:
stats.aec.buff = stats.buffer;
stats.aec.fd = stats.fd;
break;
case MSG_ID_STATS_AF:
+ case MSG_ID_STATS_BF:
stats.af.buff = stats.buffer;
stats.af.fd = stats.fd;
break;
@@ -447,6 +449,10 @@
stats.cs.buff = stats.buffer;
stats.cs.fd = stats.fd;
break;
+ case MSG_ID_STATS_BHIST:
+ stats.skin.buff = stats.buffer;
+ stats.skin.fd = stats.fd;
+ break;
case MSG_ID_STATS_AWB_AEC:
break;
default:
@@ -537,6 +543,9 @@
memset(&axi_data, 0, sizeof(axi_data));
CDBG("%s: cmd_type %d\n", __func__, cfgcmd.cmd_type);
switch (cfgcmd.cmd_type) {
+ case CMD_STATS_BG_ENABLE:
+ case CMD_STATS_BF_ENABLE:
+ case CMD_STATS_BHIST_ENABLE:
case CMD_STATS_AF_ENABLE:
case CMD_STATS_AEC_ENABLE:
case CMD_STATS_AWB_ENABLE:
@@ -629,6 +638,12 @@
cfgcmd.cmd_type = CMD_STATS_CS_BUF_RELEASE;
else if (buf.type == STAT_AEAW)
cfgcmd.cmd_type = CMD_STATS_BUF_RELEASE;
+ else if (buf.type == STAT_BG)
+ cfgcmd.cmd_type = CMD_STATS_BG_BUF_RELEASE;
+ else if (buf.type == STAT_BF)
+ cfgcmd.cmd_type = CMD_STATS_BF_BUF_RELEASE;
+ else if (buf.type == STAT_BHIST)
+ cfgcmd.cmd_type = CMD_STATS_BHIST_BUF_RELEASE;
else {
pr_err("%s: invalid buf type %d\n",
@@ -673,7 +688,6 @@
}
case MSM_CAM_IOCTL_STATS_ENQUEUEBUF: {
struct msm_stats_buf_info buf_info;
-
if (copy_from_user(&buf_info, arg,
sizeof(struct msm_stats_buf_info))) {
ERR_COPY_FROM_USER();
@@ -687,18 +701,30 @@
}
case MSM_CAM_IOCTL_STATS_FLUSH_BUFQ: {
struct msm_stats_flush_bufq bufq_info;
-
if (copy_from_user(&bufq_info, arg,
sizeof(struct msm_stats_flush_bufq))) {
ERR_COPY_FROM_USER();
return -EFAULT;
- }
+ }
cfgcmd.cmd_type = VFE_CMD_STATS_FLUSH_BUFQ;
cfgcmd.value = (void *)&bufq_info;
cfgcmd.length = sizeof(struct msm_stats_flush_bufq);
rc = msm_isp_subdev_ioctl(sd, &cfgcmd, NULL);
break;
}
+ case MSM_CAM_IOCTL_STATS_UNREG_BUF: {
+ struct msm_stats_reqbuf reqbuf;
+ if (copy_from_user(&reqbuf, arg,
+ sizeof(struct msm_stats_reqbuf))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+ cfgcmd.cmd_type = VFE_CMD_STATS_UNREGBUF;
+ cfgcmd.value = (void *)&reqbuf;
+ cfgcmd.length = sizeof(struct msm_stats_reqbuf);
+ rc = msm_isp_subdev_ioctl(sd, &cfgcmd, (void *)mctl->client);
+ break;
+ }
default:
rc = -1;
break;
@@ -734,6 +760,7 @@
case MSM_CAM_IOCTL_STATS_REQBUF:
case MSM_CAM_IOCTL_STATS_ENQUEUEBUF:
case MSM_CAM_IOCTL_STATS_FLUSH_BUFQ:
+ case MSM_CAM_IOCTL_STATS_UNREG_BUF:
rc = msm_vfe_stats_buf_ioctl(sd, cmd, pmctl, argp);
break;
diff --git a/drivers/media/video/msm/msm_mem.c b/drivers/media/video/msm/msm_mem.c
index 5d412db..e2e9d1b 100644
--- a/drivers/media/video/msm/msm_mem.c
+++ b/drivers/media/video/msm/msm_mem.c
@@ -208,6 +208,9 @@
case MSM_PMEM_IHIST:
case MSM_PMEM_SKIN:
case MSM_PMEM_AEC_AWB:
+ case MSM_PMEM_BAYER_GRID:
+ case MSM_PMEM_BAYER_FOCUS:
+ case MSM_PMEM_BAYER_HIST:
rc = msm_pmem_table_add(ptype, pinfo, client);
break;
@@ -235,6 +238,9 @@
case MSM_PMEM_IHIST:
case MSM_PMEM_SKIN:
case MSM_PMEM_AEC_AWB:
+ case MSM_PMEM_BAYER_GRID:
+ case MSM_PMEM_BAYER_FOCUS:
+ case MSM_PMEM_BAYER_HIST:
hlist_for_each_entry_safe(region, node, n,
ptype, list) {
diff --git a/drivers/media/video/msm/msm_vfe31_v4l2.c b/drivers/media/video/msm/msm_vfe31_v4l2.c
index 18168ee..a22a09f 100644
--- a/drivers/media/video/msm/msm_vfe31_v4l2.c
+++ b/drivers/media/video/msm/msm_vfe31_v4l2.c
@@ -417,6 +417,25 @@
return 0L;
}
+static unsigned long vfe31_stats_unregbuf(
+ struct msm_stats_reqbuf *req_buf)
+{
+ int i = 0, rc = 0;
+
+ for (i = 0; i < req_buf->num_buf; i++) {
+ rc = vfe31_ctrl->stats_ops.buf_unprepare(
+ vfe31_ctrl->stats_ops.stats_ctrl,
+ req_buf->stats_type, i,
+ vfe31_ctrl->stats_ops.client);
+ if (rc < 0) {
+ pr_err("%s: unreg stats buf (type = %d) err = %d",
+ __func__, req_buf->stats_type, rc);
+ return rc;
+ }
+ }
+ return 0L;
+}
+
static int vfe_stats_awb_buf_init(
struct vfe_cmd_stats_buf *in)
{
@@ -3334,6 +3353,22 @@
vfe31_ctrl->stats_ops.client);
}
break;
+ case VFE_CMD_STATS_UNREGBUF:
+ {
+ struct msm_stats_reqbuf *req_buf = NULL;
+ req_buf = (struct msm_stats_reqbuf *)cmd->value;
+ if (sizeof(struct msm_stats_reqbuf) != cmd->length) {
+ /* error. the length not match */
+ pr_err("%s: stats reqbuf input size = %d,\n"
+ "struct size = %d, mitch match\n",
+ __func__, cmd->length,
+ sizeof(struct msm_stats_reqbuf));
+ rc = -EINVAL ;
+ goto end;
+ }
+ rc = vfe31_stats_unregbuf(req_buf);
+ }
+ break;
default:
rc = -1;
pr_err("%s: cmd_type %d not supported", __func__,
@@ -3583,6 +3618,7 @@
case VFE_CMD_STATS_REQBUF:
case VFE_CMD_STATS_ENQUEUEBUF:
case VFE_CMD_STATS_FLUSH_BUFQ:
+ case VFE_CMD_STATS_UNREGBUF:
/* for easy porting put in one envelope */
rc = vfe_stats_bufq_sub_ioctl(cmd, vfe_params->data);
return rc;
diff --git a/drivers/media/video/msm/msm_vfe32.c b/drivers/media/video/msm/msm_vfe32.c
index aa2b19d..c4bdad2 100644
--- a/drivers/media/video/msm/msm_vfe32.c
+++ b/drivers/media/video/msm/msm_vfe32.c
@@ -216,6 +216,27 @@
{VFE_CMD_GET_RGB_G_TABLE},
{VFE_CMD_GET_LA_TABLE},
{VFE_CMD_DEMOSAICV3_UPDATE},
+ {VFE_CMD_ACTIVE_REGION_CFG},
+/*130*/ {VFE_CMD_COLOR_PROCESSING_CONFIG},
+ {VFE_CMD_STATS_WB_AEC_CONFIG},
+ {VFE_CMD_STATS_WB_AEC_UPDATE},
+ {VFE_CMD_Y_GAMMA_CONFIG},
+ {VFE_CMD_SCALE_OUTPUT1_CONFIG},
+/*135*/ {VFE_CMD_SCALE_OUTPUT2_CONFIG},
+ {VFE_CMD_CAPTURE_RAW},
+ {VFE_CMD_STOP_LIVESHOT},
+ {VFE_CMD_RECONFIG_VFE},
+ {VFE_CMD_STATS_REQBUF},
+/*140*/ {VFE_CMD_STATS_ENQUEUEBUF},
+ {VFE_CMD_STATS_FLUSH_BUFQ},
+ {VFE_CMD_STATS_UNREGBUF},
+ {VFE_CMD_STATS_BG_START, V32_STATS_BG_LEN, V32_STATS_BG_OFF},
+ {VFE_CMD_STATS_BG_STOP},
+ {VFE_CMD_STATS_BF_START, V32_STATS_BF_LEN, V32_STATS_BF_OFF},
+/*145*/ {VFE_CMD_STATS_BF_STOP},
+ {VFE_CMD_STATS_BHIST_START, V32_STATS_BHIST_LEN,
+ V32_STATS_BHIST_OFF},
+/*147*/ {VFE_CMD_STATS_BHIST_STOP},
};
uint32_t vfe32_AXI_WM_CFG[] = {
@@ -358,8 +379,24 @@
"GET_RGB_G_TABLE",
"GET_LA_TABLE",
"DEMOSAICV3_UPDATE",
+ "STATS_BG_START",
+ "STATS_BG_STOP",
+ "STATS_BF_START",
+ "STATS_BF_STOP",
+ "STATS_BHIST_START",
+ "STATS_BHIST_STOP",
};
+uint8_t vfe32_use_bayer_stats(struct vfe32_ctrl_type *vfe32_ctrl)
+{
+ if (vfe32_ctrl->ver_num.main >= 4) {
+ /* VFE 4 or above uses bayer stats */
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
static void vfe32_stop(struct vfe32_ctrl_type *vfe32_ctrl)
{
unsigned long flags;
@@ -375,7 +412,7 @@
msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
vfe32_ctrl->share_ctrl->vfebase + VFE_IRQ_MASK_0);
msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
- vfe32_ctrl->share_ctrl->vfebase + VFE_IRQ_MASK_1);
+ vfe32_ctrl->share_ctrl->vfebase + VFE_IRQ_MASK_1);
/* clear all pending interrupts*/
msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
@@ -530,13 +567,16 @@
/* this is unsigned 32 bit integer. */
vfe32_ctrl->share_ctrl->vfeFrameId = 0;
/* Stats control variables. */
- memset(&(vfe32_ctrl->afStatsControl), 0,
+ memset(&(vfe32_ctrl->afbfStatsControl), 0,
sizeof(struct vfe_stats_control));
memset(&(vfe32_ctrl->awbStatsControl), 0,
sizeof(struct vfe_stats_control));
- memset(&(vfe32_ctrl->aecStatsControl), 0,
+ memset(&(vfe32_ctrl->aecbgStatsControl), 0,
+ sizeof(struct vfe_stats_control));
+
+ memset(&(vfe32_ctrl->bhistStatsControl), 0,
sizeof(struct vfe_stats_control));
memset(&(vfe32_ctrl->ihistStatsControl), 0,
@@ -553,6 +593,62 @@
vfe32_ctrl->snapshot_frame_cnt = 0;
}
+static void vfe32_program_dmi_cfg(
+ enum VFE32_DMI_RAM_SEL bankSel,
+ struct vfe32_ctrl_type *vfe32_ctrl)
+{
+ /* set bit 8 for auto increment. */
+ uint32_t value = VFE_DMI_CFG_DEFAULT;
+ value += (uint32_t)bankSel;
+ CDBG("%s: banksel = %d\n", __func__, bankSel);
+
+ msm_camera_io_w(value, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_DMI_CFG);
+ /* by default, always starts with offset 0.*/
+ msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_DMI_ADDR);
+}
+
+static void vfe32_reset_dmi_tables(
+ struct vfe32_ctrl_type *vfe32_ctrl)
+{
+ int i = 0;
+
+ /* Reset Histogram LUTs */
+ CDBG("Reset Bayer histogram LUT : 0\n");
+ vfe32_program_dmi_cfg(STATS_BHIST_RAM0, vfe32_ctrl);
+ /* Loop for configuring LUT */
+ for (i = 0; i < 256; i++) {
+ msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_DMI_DATA_HI);
+ msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_DMI_DATA_LO);
+ }
+ vfe32_program_dmi_cfg(NO_MEM_SELECTED, vfe32_ctrl);
+
+ CDBG("Reset Bayer Histogram LUT: 1\n");
+ vfe32_program_dmi_cfg(STATS_BHIST_RAM1, vfe32_ctrl);
+ /* Loop for configuring LUT */
+ for (i = 0; i < 256; i++) {
+ msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_DMI_DATA_HI);
+ msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_DMI_DATA_LO);
+ }
+ vfe32_program_dmi_cfg(NO_MEM_SELECTED, vfe32_ctrl);
+
+ CDBG("Reset IHistogram LUT\n");
+ vfe32_program_dmi_cfg(STATS_IHIST_RAM, vfe32_ctrl);
+ /* Loop for configuring LUT */
+ for (i = 0; i < 256; i++) {
+ msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_DMI_DATA_HI);
+ msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_DMI_DATA_LO);
+ }
+ vfe32_program_dmi_cfg(NO_MEM_SELECTED, vfe32_ctrl);
+}
+
static void vfe32_reset(struct vfe32_ctrl_type *vfe32_ctrl)
{
vfe32_reset_internal_variables(vfe32_ctrl);
@@ -572,7 +668,8 @@
/* Ensure the write order while writing
to the command register using the barrier */
- msm_camera_io_w_mb(1, vfe32_ctrl->share_ctrl->vfebase + VFE_IRQ_CMD);
+ msm_camera_io_w_mb(1, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_IRQ_CMD);
/* enable reset_ack interrupt. */
msm_camera_io_w(VFE_IMASK_WHILE_STOPPING_1,
@@ -677,6 +774,26 @@
return 0L;
}
+
+static unsigned long vfe32_stats_unregbuf(
+ struct vfe32_ctrl_type *vfe32_ctrl,
+ struct msm_stats_reqbuf *req_buf)
+{
+ int i = 0, rc = 0;
+
+ for (i = 0; i < req_buf->num_buf; i++) {
+ rc = vfe32_ctrl->stats_ops.buf_unprepare(
+ vfe32_ctrl->stats_ops.stats_ctrl,
+ req_buf->stats_type, i,
+ vfe32_ctrl->stats_ops.client);
+ if (rc < 0) {
+ pr_err("%s: unreg stats buf (type = %d) err = %d",
+ __func__, req_buf->stats_type, rc);
+ return rc;
+ }
+ }
+ return 0L;
+}
static int vfe_stats_awb_buf_init(
struct vfe32_ctrl_type *vfe32_ctrl,
struct vfe_cmd_stats_buf *in)
@@ -708,14 +825,18 @@
return 0;
}
-static int vfe_stats_aec_buf_init(
- struct vfe32_ctrl_type *vfe32_ctrl, struct vfe_cmd_stats_buf *in)
+static uint32_t vfe_stats_aec_bg_buf_init(
+ struct vfe32_ctrl_type *vfe32_ctrl)
{
uint32_t addr;
unsigned long flags;
+ uint32_t stats_type;
+ stats_type =
+ (!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSM_STATS_TYPE_AEC
+ : MSM_STATS_TYPE_BG;
spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
- addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_AEC);
+ addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, stats_type);
spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
if (!addr) {
pr_err("%s: dq aec ping buf from free buf queue",
@@ -724,9 +845,9 @@
}
msm_camera_io_w(addr,
vfe32_ctrl->share_ctrl->vfebase +
- VFE_BUS_STATS_AEC_WR_PING_ADDR);
+ VFE_BUS_STATS_AEC_BG_WR_PING_ADDR);
spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
- addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_AEC);
+ addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, stats_type);
spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
if (!addr) {
pr_err("%s: dq aec pong buf from free buf queue",
@@ -735,26 +856,31 @@
}
msm_camera_io_w(addr,
vfe32_ctrl->share_ctrl->vfebase +
- VFE_BUS_STATS_AEC_WR_PONG_ADDR);
+ VFE_BUS_STATS_AEC_BG_WR_PONG_ADDR);
return 0;
}
-static int vfe_stats_af_buf_init(
- struct vfe32_ctrl_type *vfe32_ctrl, struct vfe_cmd_stats_buf *in)
+static int vfe_stats_af_bf_buf_init(
+ struct vfe32_ctrl_type *vfe32_ctrl)
{
uint32_t addr;
unsigned long flags;
int rc = 0;
+ uint32_t stats_type;
+ stats_type =
+ (!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSM_STATS_TYPE_AF
+ : MSM_STATS_TYPE_BF;
+
spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
- rc = vfe32_stats_flush_enqueue(vfe32_ctrl, MSM_STATS_TYPE_AF);
+ rc = vfe32_stats_flush_enqueue(vfe32_ctrl, stats_type);
if (rc < 0) {
pr_err("%s: dq stats buf err = %d",
__func__, rc);
spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
return -EINVAL;
}
- addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_AF);
+ addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, stats_type);
spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
if (!addr) {
pr_err("%s: dq af ping buf from free buf queue", __func__);
@@ -762,9 +888,9 @@
}
msm_camera_io_w(addr,
vfe32_ctrl->share_ctrl->vfebase +
- VFE_BUS_STATS_AF_WR_PING_ADDR);
+ VFE_BUS_STATS_AF_BF_WR_PING_ADDR);
spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
- addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_AF);
+ addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, stats_type);
spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
if (!addr) {
pr_err("%s: dq af pong buf from free buf queue", __func__);
@@ -772,14 +898,44 @@
}
msm_camera_io_w(addr,
vfe32_ctrl->share_ctrl->vfebase +
- VFE_BUS_STATS_AF_WR_PONG_ADDR);
+ VFE_BUS_STATS_AF_BF_WR_PONG_ADDR);
+ return 0;
+}
+
+static uint32_t vfe_stats_bhist_buf_init(
+ struct vfe32_ctrl_type *vfe32_ctrl)
+{
+ uint32_t addr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
+ addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_BHIST);
+ spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
+ if (!addr) {
+ pr_err("%s: dq ihist ping buf from free buf queue",
+ __func__);
+ return -ENOMEM;
+ }
+ msm_camera_io_w(addr,
+ vfe32_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_SKIN_BHIST_WR_PING_ADDR);
+ spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
+ addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_BHIST);
+ spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
+ if (!addr) {
+ pr_err("%s: dq ihist pong buf from free buf queue",
+ __func__);
+ return -ENOMEM;
+ }
+ msm_camera_io_w(addr,
+ vfe32_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_SKIN_BHIST_WR_PONG_ADDR);
return 0;
}
static int vfe_stats_ihist_buf_init(
- struct vfe32_ctrl_type *vfe32_ctrl,
- struct vfe_cmd_stats_buf *in)
+ struct vfe32_ctrl_type *vfe32_ctrl)
{
uint32_t addr;
unsigned long flags;
@@ -811,8 +967,7 @@
}
static int vfe_stats_rs_buf_init(
- struct vfe32_ctrl_type *vfe32_ctrl,
- struct vfe_cmd_stats_buf *in)
+ struct vfe32_ctrl_type *vfe32_ctrl)
{
uint32_t addr;
unsigned long flags;
@@ -841,8 +996,7 @@
}
static int vfe_stats_cs_buf_init(
- struct vfe32_ctrl_type *vfe32_ctrl,
- struct vfe_cmd_stats_buf *in)
+ struct vfe32_ctrl_type *vfe32_ctrl)
{
uint32_t addr;
unsigned long flags;
@@ -918,6 +1072,9 @@
msm_camera_io_w_mb(1, vfe32_ctrl->share_ctrl->vfebase +
VFE_CAMIF_COMMAND);
}
+ msm_camera_io_dump(vfe32_ctrl->share_ctrl->vfebase,
+ vfe32_ctrl->share_ctrl->register_total * 4);
+
/* Ensure the write order while writing
to the command register using the barrier */
atomic_set(&vfe32_ctrl->share_ctrl->vstate, 1);
@@ -1372,19 +1529,7 @@
vfe32_ctrl->share_ctrl->vfebase + V32_TIMER_SELECT_OFF);
}
-static void vfe32_program_dmi_cfg(
- enum VFE32_DMI_RAM_SEL bankSel,
- struct vfe32_ctrl_type *vfe32_ctrl)
-{
- /* set bit 8 for auto increment. */
- uint32_t value = VFE_DMI_CFG_DEFAULT;
- value += (uint32_t)bankSel;
- CDBG("%s: banksel = %d\n", __func__, bankSel);
- msm_camera_io_w(value, vfe32_ctrl->share_ctrl->vfebase + VFE_DMI_CFG);
- /* by default, always starts with offset 0.*/
- msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase + VFE_DMI_ADDR);
-}
static void vfe32_write_gamma_cfg(
enum VFE32_DMI_RAM_SEL channel_sel,
const uint32_t *tbl,
@@ -1582,7 +1727,7 @@
struct vfe32_ctrl_type *vfe32_ctrl)
{
int i , rc = 0;
- uint32_t old_val = 0 , new_val = 0;
+ uint32_t old_val = 0 , new_val = 0, module_val = 0;
uint32_t *cmdp = NULL;
uint32_t *cmdp_local = NULL;
uint32_t snapshot_cnt = 0;
@@ -1762,7 +1907,12 @@
break;
case VFE_CMD_STATS_AE_START: {
- rc = vfe_stats_aec_buf_init(vfe32_ctrl, NULL);
+ if (vfe32_use_bayer_stats(vfe32_ctrl)) {
+ /* Error */
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ rc = vfe_stats_aec_bg_buf_init(vfe32_ctrl);
if (rc < 0) {
pr_err("%s: cannot config ping/pong address of AEC",
__func__);
@@ -1791,7 +1941,12 @@
}
break;
case VFE_CMD_STATS_AF_START: {
- rc = vfe_stats_af_buf_init(vfe32_ctrl, NULL);
+ if (vfe32_use_bayer_stats(vfe32_ctrl)) {
+ /* Error */
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ rc = vfe_stats_af_bf_buf_init(vfe32_ctrl);
if (rc < 0) {
pr_err("%s: cannot config ping/pong address of AF",
__func__);
@@ -1820,6 +1975,11 @@
}
break;
case VFE_CMD_STATS_AWB_START: {
+ if (vfe32_use_bayer_stats(vfe32_ctrl)) {
+ /* Error */
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
rc = vfe_stats_awb_buf_init(vfe32_ctrl, NULL);
if (rc < 0) {
pr_err("%s: cannot config ping/pong address of AWB",
@@ -1850,7 +2010,7 @@
break;
case VFE_CMD_STATS_IHIST_START: {
- rc = vfe_stats_ihist_buf_init(vfe32_ctrl, NULL);
+ rc = vfe_stats_ihist_buf_init(vfe32_ctrl);
if (rc < 0) {
pr_err("%s: cannot config ping/pong address of IHIST",
__func__);
@@ -1881,7 +2041,7 @@
case VFE_CMD_STATS_RS_START: {
- rc = vfe_stats_rs_buf_init(vfe32_ctrl, NULL);
+ rc = vfe_stats_rs_buf_init(vfe32_ctrl);
if (rc < 0) {
pr_err("%s: cannot config ping/pong address of RS",
__func__);
@@ -1906,7 +2066,7 @@
break;
case VFE_CMD_STATS_CS_START: {
- rc = vfe_stats_cs_buf_init(vfe32_ctrl, NULL);
+ rc = vfe_stats_cs_buf_init(vfe32_ctrl);
if (rc < 0) {
pr_err("%s: cannot config ping/pong address of CS",
__func__);
@@ -1930,6 +2090,67 @@
}
break;
+ case VFE_CMD_STATS_BG_START:
+ case VFE_CMD_STATS_BF_START:
+ case VFE_CMD_STATS_BHIST_START: {
+ if (!vfe32_use_bayer_stats(vfe32_ctrl)) {
+ /* Error */
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = msm_camera_io_r(
+ vfe32_ctrl->share_ctrl->vfebase + VFE_STATS_CFG);
+ module_val = msm_camera_io_r(
+ vfe32_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
+ if (VFE_CMD_STATS_BG_START == cmd->id) {
+ module_val |= AE_BG_ENABLE_MASK;
+ old_val |= STATS_BG_ENABLE_MASK;
+ rc = vfe_stats_aec_bg_buf_init(vfe32_ctrl);
+ if (rc < 0) {
+ pr_err("%s: cannot config ping/pong address of CS",
+ __func__);
+ goto proc_general_done;
+ }
+ } else if (VFE_CMD_STATS_BF_START == cmd->id) {
+ module_val |= AF_BF_ENABLE_MASK;
+ old_val |= STATS_BF_ENABLE_MASK;
+ rc = vfe_stats_af_bf_buf_init(vfe32_ctrl);
+ if (rc < 0) {
+ pr_err("%s: cannot config ping/pong address of CS",
+ __func__);
+ goto proc_general_done;
+ }
+ } else {
+ module_val |= SKIN_BHIST_ENABLE_MASK;
+ old_val |= STATS_BHIST_ENABLE_MASK;
+ rc = vfe_stats_bhist_buf_init(vfe32_ctrl);
+ if (rc < 0) {
+ pr_err("%s: cannot config ping/pong address of CS",
+ __func__);
+ goto proc_general_done;
+ }
+ }
+ msm_camera_io_w(old_val, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_STATS_CFG);
+ msm_camera_io_w(module_val,
+ vfe32_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ msm_camera_io_memcpy(
+ vfe32_ctrl->share_ctrl->vfebase +
+ vfe32_cmd[cmd->id].offset,
+ cmdp, (vfe32_cmd[cmd->id].length));
+ }
+ break;
case VFE_CMD_MCE_UPDATE:
case VFE_CMD_MCE_CFG:{
cmdp = kmalloc(cmd->length, GFP_ATOMIC);
@@ -2601,6 +2822,11 @@
break;
case VFE_CMD_STATS_AWB_STOP: {
+ if (vfe32_use_bayer_stats(vfe32_ctrl)) {
+ /* Error */
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
old_val = msm_camera_io_r(
vfe32_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
old_val &= ~AWB_ENABLE_MASK;
@@ -2609,6 +2835,11 @@
}
break;
case VFE_CMD_STATS_AE_STOP: {
+ if (vfe32_use_bayer_stats(vfe32_ctrl)) {
+ /* Error */
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
old_val = msm_camera_io_r(
vfe32_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
old_val &= ~AE_BG_ENABLE_MASK;
@@ -2617,17 +2848,16 @@
}
break;
case VFE_CMD_STATS_AF_STOP: {
+ if (vfe32_use_bayer_stats(vfe32_ctrl)) {
+ /* Error */
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
old_val = msm_camera_io_r(
vfe32_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
old_val &= ~AF_BF_ENABLE_MASK;
msm_camera_io_w(old_val,
vfe32_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
- rc = vfe32_stats_flush_enqueue(vfe32_ctrl, MSM_STATS_TYPE_AF);
- if (rc < 0) {
- pr_err("%s: dq stats buf err = %d",
- __func__, rc);
- return -EINVAL;
- }
}
break;
@@ -2657,6 +2887,37 @@
vfe32_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
}
break;
+
+ case VFE_CMD_STATS_BG_STOP:
+ case VFE_CMD_STATS_BF_STOP:
+ case VFE_CMD_STATS_BHIST_STOP: {
+ if (!vfe32_use_bayer_stats(vfe32_ctrl)) {
+ /* Error */
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = msm_camera_io_r(
+ vfe32_ctrl->share_ctrl->vfebase + VFE_STATS_CFG);
+ if (VFE_CMD_STATS_BG_STOP == cmd->id)
+ old_val &= ~STATS_BG_ENABLE_MASK;
+ else if (VFE_CMD_STATS_BF_STOP == cmd->id)
+ old_val &= ~STATS_BF_ENABLE_MASK;
+ else
+ old_val &= ~STATS_BHIST_ENABLE_MASK;
+ msm_camera_io_w(old_val,
+ vfe32_ctrl->share_ctrl->vfebase + VFE_STATS_CFG);
+ if (VFE_CMD_STATS_BF_STOP == cmd->id) {
+ rc = vfe32_stats_flush_enqueue(vfe32_ctrl,
+ MSM_STATS_TYPE_BF);
+ if (rc < 0) {
+ pr_err("%s: dq stats buf err = %d",
+ __func__, rc);
+ return -EINVAL;
+ }
+ }
+ }
+ break;
+
case VFE_CMD_STOP:
pr_info("vfe32_proc_general: cmdID = %s\n",
vfe32_general_cmd[cmd->id]);
@@ -3301,18 +3562,48 @@
vfe32_ctrl->share_ctrl->vfebase + VFE_CLAMP_MAX);
/* stats UB config */
- msm_camera_io_w(0x3980007,
- vfe32_ctrl->share_ctrl->vfebase + VFE_BUS_STATS_AEC_UB_CFG);
- msm_camera_io_w(0x3A00007,
- vfe32_ctrl->share_ctrl->vfebase + VFE_BUS_STATS_AF_UB_CFG);
- msm_camera_io_w(0x3A8000F,
- vfe32_ctrl->share_ctrl->vfebase + VFE_BUS_STATS_AWB_UB_CFG);
- msm_camera_io_w(0x3B80007,
- vfe32_ctrl->share_ctrl->vfebase + VFE_BUS_STATS_RS_UB_CFG);
- msm_camera_io_w(0x3C0001F,
- vfe32_ctrl->share_ctrl->vfebase + VFE_BUS_STATS_CS_UB_CFG);
- msm_camera_io_w(0x3E0001F,
- vfe32_ctrl->share_ctrl->vfebase + VFE_BUS_STATS_HIST_UB_CFG);
+ CDBG("%s: Use bayer stats = %d\n", __func__,
+ vfe32_use_bayer_stats(vfe32_ctrl));
+ if (!vfe32_use_bayer_stats(vfe32_ctrl)) {
+ msm_camera_io_w(0x3980007,
+ vfe32_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_AEC_BG_UB_CFG);
+ msm_camera_io_w(0x3A00007,
+ vfe32_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_AF_BF_UB_CFG);
+ msm_camera_io_w(0x3A8000F,
+ vfe32_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_AWB_UB_CFG);
+ msm_camera_io_w(0x3B80007,
+ vfe32_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_RS_UB_CFG);
+ msm_camera_io_w(0x3C0001F,
+ vfe32_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_CS_UB_CFG);
+ msm_camera_io_w(0x3E0001F,
+ vfe32_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_HIST_UB_CFG);
+ } else {
+ msm_camera_io_w(0x350001F,
+ vfe32_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_HIST_UB_CFG);
+ msm_camera_io_w(0x370002F,
+ vfe32_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_AEC_BG_UB_CFG);
+ msm_camera_io_w(0x3A0002F,
+ vfe32_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_AF_BF_UB_CFG);
+ msm_camera_io_w(0x3D00007,
+ vfe32_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_RS_UB_CFG);
+ msm_camera_io_w(0x3D8001F,
+ vfe32_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_CS_UB_CFG);
+ msm_camera_io_w(0x3F80007,
+ vfe32_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_SKIN_BHIST_UB_CFG);
+ }
+ vfe32_reset_dmi_tables(vfe32_ctrl);
}
static void vfe32_process_reset_irq(
@@ -3785,25 +4076,36 @@
/* @todo This is causing issues, need further investigate */
/* spin_lock_irqsave(&ctrl->state_lock, flags); */
struct isp_msg_stats msgStats;
+ uint32_t stats_type;
msgStats.frameCounter = vfe32_ctrl->share_ctrl->vfeFrameId;
if (vfe32_ctrl->simultaneous_sof_stat)
msgStats.frameCounter--;
msgStats.buffer = bufAddress;
switch (statsNum) {
case statsAeNum:{
- msgStats.id = MSG_ID_STATS_AEC;
+ msgStats.id =
+ (!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSG_ID_STATS_AEC
+ : MSG_ID_STATS_BG;
+ stats_type =
+ (!vfe32_use_bayer_stats(vfe32_ctrl)) ?
+ MSM_STATS_TYPE_AEC : MSM_STATS_TYPE_BG;
rc = vfe32_ctrl->stats_ops.dispatch(
vfe32_ctrl->stats_ops.stats_ctrl,
- MSM_STATS_TYPE_AEC, bufAddress,
+ stats_type, bufAddress,
&msgStats.buf_idx, &vaddr, &msgStats.fd,
vfe32_ctrl->stats_ops.client);
}
break;
case statsAfNum:{
- msgStats.id = MSG_ID_STATS_AF;
+ msgStats.id =
+ (!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSG_ID_STATS_AF
+ : MSG_ID_STATS_BF;
+ stats_type =
+ (!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSM_STATS_TYPE_AF
+ : MSM_STATS_TYPE_BF;
rc = vfe32_ctrl->stats_ops.dispatch(
vfe32_ctrl->stats_ops.stats_ctrl,
- MSM_STATS_TYPE_AF, bufAddress,
+ stats_type, bufAddress,
&msgStats.buf_idx, &vaddr, &msgStats.fd,
vfe32_ctrl->stats_ops.client);
}
@@ -3845,6 +4147,15 @@
vfe32_ctrl->stats_ops.client);
}
break;
+ case statsSkinNum: {
+ msgStats.id = MSG_ID_STATS_BHIST;
+ rc = vfe32_ctrl->stats_ops.dispatch(
+ vfe32_ctrl->stats_ops.stats_ctrl,
+ MSM_STATS_TYPE_BHIST, bufAddress,
+ &msgStats.buf_idx, &vaddr, &msgStats.fd,
+ vfe32_ctrl->stats_ops.client);
+ }
+ break;
default:
goto stats_done;
@@ -3875,9 +4186,9 @@
msgStats.status_bits = status_bits;
- msgStats.aec.buff = vfe32_ctrl->aecStatsControl.bufToRender;
+ msgStats.aec.buff = vfe32_ctrl->aecbgStatsControl.bufToRender;
msgStats.awb.buff = vfe32_ctrl->awbStatsControl.bufToRender;
- msgStats.af.buff = vfe32_ctrl->afStatsControl.bufToRender;
+ msgStats.af.buff = vfe32_ctrl->afbfStatsControl.bufToRender;
msgStats.ihist.buff = vfe32_ctrl->ihistStatsControl.bufToRender;
msgStats.rs.buff = vfe32_ctrl->rsStatsControl.bufToRender;
@@ -3892,24 +4203,28 @@
&msgStats);
}
-static void vfe32_process_stats_ae_irq(struct vfe32_ctrl_type *vfe32_ctrl)
+static void vfe32_process_stats_ae_bg_irq(struct vfe32_ctrl_type *vfe32_ctrl)
{
unsigned long flags;
uint32_t addr;
+ uint32_t stats_type;
+ stats_type =
+ (!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSM_STATS_TYPE_AEC
+ : MSM_STATS_TYPE_BG;
spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
- addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_AEC);
+ addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, stats_type);
spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
if (addr) {
- vfe32_ctrl->aecStatsControl.bufToRender =
+ vfe32_ctrl->aecbgStatsControl.bufToRender =
vfe32_process_stats_irq_common(vfe32_ctrl, statsAeNum,
addr);
vfe_send_stats_msg(vfe32_ctrl,
- vfe32_ctrl->aecStatsControl.bufToRender, statsAeNum);
+ vfe32_ctrl->aecbgStatsControl.bufToRender, statsAeNum);
} else{
- vfe32_ctrl->aecStatsControl.droppedStatsFrameCount++;
+ vfe32_ctrl->aecbgStatsControl.droppedStatsFrameCount++;
CDBG("%s: droppedStatsFrameCount = %d", __func__,
- vfe32_ctrl->aecStatsControl.droppedStatsFrameCount);
+ vfe32_ctrl->aecbgStatsControl.droppedStatsFrameCount);
}
}
@@ -3934,24 +4249,50 @@
}
}
-static void vfe32_process_stats_af_irq(struct vfe32_ctrl_type *vfe32_ctrl)
+static void vfe32_process_stats_af_bf_irq(struct vfe32_ctrl_type *vfe32_ctrl)
{
unsigned long flags;
uint32_t addr;
+ uint32_t stats_type;
+ stats_type =
+ (!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSM_STATS_TYPE_AF
+ : MSM_STATS_TYPE_BF;
spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
- addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_AF);
+ addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, stats_type);
spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
if (addr) {
- vfe32_ctrl->afStatsControl.bufToRender =
+ vfe32_ctrl->afbfStatsControl.bufToRender =
vfe32_process_stats_irq_common(vfe32_ctrl, statsAfNum,
addr);
vfe_send_stats_msg(vfe32_ctrl,
- vfe32_ctrl->afStatsControl.bufToRender, statsAfNum);
+ vfe32_ctrl->afbfStatsControl.bufToRender, statsAfNum);
} else{
- vfe32_ctrl->afStatsControl.droppedStatsFrameCount++;
+ vfe32_ctrl->afbfStatsControl.droppedStatsFrameCount++;
CDBG("%s: droppedStatsFrameCount = %d", __func__,
- vfe32_ctrl->afStatsControl.droppedStatsFrameCount);
+ vfe32_ctrl->afbfStatsControl.droppedStatsFrameCount);
+ }
+}
+
+static void vfe32_process_stats_bhist_irq(struct vfe32_ctrl_type *vfe32_ctrl)
+{
+ unsigned long flags;
+ uint32_t addr;
+ spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
+ addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_BHIST);
+ spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
+ if (addr) {
+ vfe32_ctrl->bhistStatsControl.bufToRender =
+ vfe32_process_stats_irq_common(vfe32_ctrl,
+ statsSkinNum, addr);
+
+ vfe_send_stats_msg(vfe32_ctrl,
+ vfe32_ctrl->bhistStatsControl.bufToRender,
+ statsSkinNum);
+ } else{
+ vfe32_ctrl->bhistStatsControl.droppedStatsFrameCount++;
+ CDBG("%s: droppedStatsFrameCount = %d", __func__,
+ vfe32_ctrl->bhistStatsControl.droppedStatsFrameCount);
}
}
@@ -4026,23 +4367,28 @@
unsigned long flags;
int32_t process_stats = false;
uint32_t addr;
+ uint32_t stats_type;
CDBG("%s, stats = 0x%x\n", __func__, status_bits);
spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
- if (status_bits & VFE_IRQ_STATUS0_STATS_AEC) {
+ stats_type =
+ (!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSM_STATS_TYPE_AEC
+ : MSM_STATS_TYPE_BG;
+
+ if (status_bits & VFE_IRQ_STATUS0_STATS_AEC_BG) {
addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl,
- MSM_STATS_TYPE_AEC);
+ stats_type);
if (addr) {
- vfe32_ctrl->aecStatsControl.bufToRender =
+ vfe32_ctrl->aecbgStatsControl.bufToRender =
vfe32_process_stats_irq_common(
vfe32_ctrl, statsAeNum, addr);
process_stats = true;
} else{
- vfe32_ctrl->aecStatsControl.bufToRender = 0;
- vfe32_ctrl->aecStatsControl.droppedStatsFrameCount++;
+ vfe32_ctrl->aecbgStatsControl.bufToRender = 0;
+ vfe32_ctrl->aecbgStatsControl.droppedStatsFrameCount++;
}
} else {
- vfe32_ctrl->aecStatsControl.bufToRender = 0;
+ vfe32_ctrl->aecbgStatsControl.bufToRender = 0;
}
if (status_bits & VFE_IRQ_STATUS0_STATS_AWB) {
@@ -4062,21 +4408,24 @@
vfe32_ctrl->awbStatsControl.bufToRender = 0;
}
- if (status_bits & VFE_IRQ_STATUS0_STATS_AF) {
+ stats_type =
+ (!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSM_STATS_TYPE_AF
+ : MSM_STATS_TYPE_BF;
+ if (status_bits & VFE_IRQ_STATUS0_STATS_AF_BF) {
addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl,
- MSM_STATS_TYPE_AF);
+ stats_type);
if (addr) {
- vfe32_ctrl->afStatsControl.bufToRender =
+ vfe32_ctrl->afbfStatsControl.bufToRender =
vfe32_process_stats_irq_common(
vfe32_ctrl, statsAfNum,
addr);
process_stats = true;
} else {
- vfe32_ctrl->afStatsControl.bufToRender = 0;
- vfe32_ctrl->afStatsControl.droppedStatsFrameCount++;
+ vfe32_ctrl->afbfStatsControl.bufToRender = 0;
+ vfe32_ctrl->afbfStatsControl.droppedStatsFrameCount++;
}
} else {
- vfe32_ctrl->afStatsControl.bufToRender = 0;
+ vfe32_ctrl->afbfStatsControl.bufToRender = 0;
}
if (status_bits & VFE_IRQ_STATUS0_STATS_IHIST) {
@@ -4182,17 +4531,21 @@
CDBG("irq resetAckIrq\n");
vfe32_process_reset_irq(vfe32_ctrl);
break;
- case VFE_IRQ_STATUS0_STATS_AEC:
+ case VFE_IRQ_STATUS0_STATS_AEC_BG:
CDBG("Stats AEC irq occured.\n");
- vfe32_process_stats_ae_irq(vfe32_ctrl);
+ vfe32_process_stats_ae_bg_irq(vfe32_ctrl);
break;
case VFE_IRQ_STATUS0_STATS_AWB:
CDBG("Stats AWB irq occured.\n");
vfe32_process_stats_awb_irq(vfe32_ctrl);
break;
- case VFE_IRQ_STATUS0_STATS_AF:
+ case VFE_IRQ_STATUS0_STATS_AF_BF:
CDBG("Stats AF irq occured.\n");
- vfe32_process_stats_af_irq(vfe32_ctrl);
+ vfe32_process_stats_af_bf_irq(vfe32_ctrl);
+ break;
+ case VFE_IRQ_STATUS0_STATS_SK_BHIST:
+ CDBG("Stats BHIST irq occured.\n");
+ vfe32_process_stats_bhist_irq(vfe32_ctrl);
break;
case VFE_IRQ_STATUS0_STATS_IHIST:
CDBG("Stats IHIST irq occured.\n");
@@ -4261,11 +4614,11 @@
} else {
stat_interrupt =
(qcmd->vfeInterruptStatus0 &
- VFE_IRQ_STATUS0_STATS_AEC) |
+ VFE_IRQ_STATUS0_STATS_AEC_BG) |
(qcmd->vfeInterruptStatus0 &
VFE_IRQ_STATUS0_STATS_AWB) |
(qcmd->vfeInterruptStatus0 &
- VFE_IRQ_STATUS0_STATS_AF) |
+ VFE_IRQ_STATUS0_STATS_AF_BF) |
(qcmd->vfeInterruptStatus0 &
VFE_IRQ_STATUS0_STATS_IHIST) |
(qcmd->vfeInterruptStatus0 &
@@ -4333,10 +4686,10 @@
} else {
/* process individual stats interrupt. */
if (qcmd->vfeInterruptStatus0 &
- VFE_IRQ_STATUS0_STATS_AEC)
+ VFE_IRQ_STATUS0_STATS_AEC_BG)
v4l2_subdev_notify(&axi_ctrl->subdev,
NOTIFY_VFE_IRQ,
- (void *)VFE_IRQ_STATUS0_STATS_AEC);
+ (void *)VFE_IRQ_STATUS0_STATS_AEC_BG);
if (qcmd->vfeInterruptStatus0 &
VFE_IRQ_STATUS0_STATS_AWB)
@@ -4345,10 +4698,15 @@
(void *)VFE_IRQ_STATUS0_STATS_AWB);
if (qcmd->vfeInterruptStatus0 &
- VFE_IRQ_STATUS0_STATS_AF)
+ VFE_IRQ_STATUS0_STATS_AF_BF)
v4l2_subdev_notify(&axi_ctrl->subdev,
NOTIFY_VFE_IRQ,
- (void *)VFE_IRQ_STATUS0_STATS_AF);
+ (void *)VFE_IRQ_STATUS0_STATS_AF_BF);
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_STATS_SK_BHIST)
+ v4l2_subdev_notify(&axi_ctrl->subdev,
+ NOTIFY_VFE_IRQ,
+ (void *)VFE_IRQ_STATUS0_STATS_SK_BHIST);
if (qcmd->vfeInterruptStatus0 &
VFE_IRQ_STATUS0_STATS_IHIST)
@@ -4521,6 +4879,22 @@
vfe_ctrl->stats_ops.client);
}
break;
+ case VFE_CMD_STATS_UNREGBUF:
+ {
+ struct msm_stats_reqbuf *req_buf = NULL;
+ req_buf = (struct msm_stats_reqbuf *)cmd->value;
+ if (sizeof(struct msm_stats_reqbuf) != cmd->length) {
+ /* error. the length not match */
+ pr_err("%s: stats reqbuf input size = %d,\n"
+ "struct size = %d, mitch match\n",
+ __func__, cmd->length,
+ sizeof(struct msm_stats_reqbuf));
+ rc = -EINVAL ;
+ goto end;
+ }
+ rc = vfe32_stats_unregbuf(vfe_ctrl, req_buf);
+ }
+ break;
default:
rc = -1;
pr_err("%s: cmd_type %d not supported", __func__,
@@ -4570,27 +4944,31 @@
case VFE_CMD_STATS_REQBUF:
case VFE_CMD_STATS_ENQUEUEBUF:
case VFE_CMD_STATS_FLUSH_BUFQ:
+ case VFE_CMD_STATS_UNREGBUF:
/* for easy porting put in one envelope */
rc = vfe_stats_bufq_sub_ioctl(vfe32_ctrl,
cmd, vfe_params->data);
return rc;
default:
if (cmd->cmd_type != CMD_CONFIG_PING_ADDR &&
- cmd->cmd_type != CMD_CONFIG_PONG_ADDR &&
- cmd->cmd_type != CMD_CONFIG_FREE_BUF_ADDR &&
- cmd->cmd_type != CMD_STATS_AEC_BUF_RELEASE &&
- cmd->cmd_type != CMD_STATS_AWB_BUF_RELEASE &&
- cmd->cmd_type != CMD_STATS_IHIST_BUF_RELEASE &&
- cmd->cmd_type != CMD_STATS_RS_BUF_RELEASE &&
- cmd->cmd_type != CMD_STATS_CS_BUF_RELEASE &&
- cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {
- if (copy_from_user(&vfecmd,
+ cmd->cmd_type != CMD_CONFIG_PONG_ADDR &&
+ cmd->cmd_type != CMD_CONFIG_FREE_BUF_ADDR &&
+ cmd->cmd_type != CMD_STATS_AEC_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_AWB_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_IHIST_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_RS_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_CS_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_BG_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_BF_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_BHIST_BUF_RELEASE) {
+ if (copy_from_user(&vfecmd,
(void __user *)(cmd->value),
sizeof(vfecmd))) {
- pr_err("%s %d: copy_from_user failed\n",
- __func__, __LINE__);
- return -EFAULT;
- }
+ pr_err("%s %d: copy_from_user failed\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
} else {
/* here eith stats release or frame release. */
if (cmd->cmd_type != CMD_CONFIG_PING_ADDR &&
@@ -4612,6 +4990,25 @@
sack->nextStatsBuf = *(uint32_t *)data;
}
}
+ }
+
+ CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type);
+
+ if ((cmd->cmd_type == CMD_STATS_AF_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_AWB_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_IHIST_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_RS_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_CS_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_AEC_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_BG_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_BF_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_BHIST_ENABLE)) {
+ struct axidata *axid;
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ goto vfe32_config_done;
+ }
CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type);
if ((cmd->cmd_type == CMD_STATS_AF_ENABLE) ||
@@ -4625,38 +5022,56 @@
goto vfe32_config_done;
}
switch (cmd->cmd_type) {
- case CMD_GENERAL:
- rc = vfe32_proc_general(pmctl, &vfecmd, vfe32_ctrl);
- break;
- case CMD_CONFIG_PING_ADDR: {
- int path = *((int *)cmd->value);
- struct vfe32_output_ch *outch =
- vfe32_get_ch(path, vfe32_ctrl->share_ctrl);
- outch->ping = *((struct msm_free_buf *)data);
+ case CMD_STATS_AEC_ENABLE:
+ case CMD_STATS_BG_ENABLE:
+ case CMD_STATS_BF_ENABLE:
+ case CMD_STATS_BHIST_ENABLE:
+ case CMD_STATS_AWB_ENABLE:
+ case CMD_STATS_IHIST_ENABLE:
+ case CMD_STATS_RS_ENABLE:
+ case CMD_STATS_CS_ENABLE:
+ default:
+ pr_err("%s Unsupported cmd type %d",
+ __func__, cmd->cmd_type);
+ break;
}
- break;
- case CMD_CONFIG_PONG_ADDR: {
- int path = *((int *)cmd->value);
- struct vfe32_output_ch *outch =
- vfe32_get_ch(path, vfe32_ctrl->share_ctrl);
- outch->pong = *((struct msm_free_buf *)data);
- }
+ goto vfe32_config_done;
+ }
+ switch (cmd->cmd_type) {
+ case CMD_GENERAL:
+ rc = vfe32_proc_general(pmctl, &vfecmd, vfe32_ctrl);
+ break;
+ case CMD_CONFIG_PING_ADDR: {
+ int path = *((int *)cmd->value);
+ struct vfe32_output_ch *outch =
+ vfe32_get_ch(path, vfe32_ctrl->share_ctrl);
+ outch->ping = *((struct msm_free_buf *)data);
+ }
+ break;
+
+ case CMD_CONFIG_PONG_ADDR: {
+ int path = *((int *)cmd->value);
+ struct vfe32_output_ch *outch =
+ vfe32_get_ch(path, vfe32_ctrl->share_ctrl);
+ outch->pong = *((struct msm_free_buf *)data);
+ }
+ break;
+
+ case CMD_CONFIG_FREE_BUF_ADDR: {
+ int path = *((int *)cmd->value);
+ struct vfe32_output_ch *outch =
+ vfe32_get_ch(path, vfe32_ctrl->share_ctrl);
+ outch->free_buf = *((struct msm_free_buf *)data);
+ }
+ break;
+
+ case CMD_SNAP_BUF_RELEASE:
break;
- case CMD_CONFIG_FREE_BUF_ADDR: {
- int path = *((int *)cmd->value);
- struct vfe32_output_ch *outch =
- vfe32_get_ch(path, vfe32_ctrl->share_ctrl);
- outch->free_buf = *((struct msm_free_buf *)data);
- }
- break;
- case CMD_SNAP_BUF_RELEASE:
- break;
- default:
- pr_err("%s Unsupported AXI configuration %x ", __func__,
- cmd->cmd_type);
- break;
- }
+ default:
+ pr_err("%s Unsupported AXI configuration %x ", __func__,
+ cmd->cmd_type);
+ break;
}
vfe32_config_done:
kfree(scfg);
@@ -5424,6 +5839,8 @@
axi32_do_tasklet, (unsigned long)axi_ctrl);
vfe32_ctrl->pdev = pdev;
+ /*disable bayer stats by default*/
+ vfe32_ctrl->ver_num.main = 0;
return 0;
vfe32_no_resource:
diff --git a/drivers/media/video/msm/msm_vfe32.h b/drivers/media/video/msm/msm_vfe32.h
index 9336cfb..0b685e1 100644
--- a/drivers/media/video/msm/msm_vfe32.h
+++ b/drivers/media/video/msm/msm_vfe32.h
@@ -104,12 +104,13 @@
#define VFE_IRQ_STATUS1_RESET_AXI_HALT_ACK_MASK 0x00800000
#define VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK 0x01000000
-#define VFE_IRQ_STATUS0_STATS_AEC 0x2000 /* bit 13 */
-#define VFE_IRQ_STATUS0_STATS_AF 0x4000 /* bit 14 */
-#define VFE_IRQ_STATUS0_STATS_AWB 0x8000 /* bit 15 */
-#define VFE_IRQ_STATUS0_STATS_RS 0x10000 /* bit 16 */
-#define VFE_IRQ_STATUS0_STATS_CS 0x20000 /* bit 17 */
-#define VFE_IRQ_STATUS0_STATS_IHIST 0x40000 /* bit 18 */
+#define VFE_IRQ_STATUS0_STATS_AEC_BG 0x2000 /* bit 13 */
+#define VFE_IRQ_STATUS0_STATS_AF_BF 0x4000 /* bit 14 */
+#define VFE_IRQ_STATUS0_STATS_AWB 0x8000 /* bit 15 */
+#define VFE_IRQ_STATUS0_STATS_RS 0x10000 /* bit 16 */
+#define VFE_IRQ_STATUS0_STATS_CS 0x20000 /* bit 17 */
+#define VFE_IRQ_STATUS0_STATS_IHIST 0x40000 /* bit 18 */
+#define VFE_IRQ_STATUS0_STATS_SK_BHIST 0x80000 /* bit 19 */
#define VFE_IRQ_STATUS0_SYNC_TIMER0 0x2000000 /* bit 25 */
#define VFE_IRQ_STATUS0_SYNC_TIMER1 0x4000000 /* bit 26 */
@@ -174,8 +175,13 @@
#define RS_CS_ENABLE_MASK 0x00000300 /* bit 8,9 */
#define CLF_ENABLE_MASK 0x00002000 /* bit 13 */
#define IHIST_ENABLE_MASK 0x00010000 /* bit 16 */
+#define SKIN_BHIST_ENABLE_MASK 0x00080000 /* bit 19 */
#define STATS_ENABLE_MASK 0x000903E0 /* bit 19,16,9,8,7,6,5*/
+#define STATS_BG_ENABLE_MASK 0x00000002 /* bit 1 */
+#define STATS_BF_ENABLE_MASK 0x00000004 /* bit 2 */
+#define STATS_BHIST_ENABLE_MASK 0x00000008 /* bit 3 */
+
#define VFE_REG_UPDATE_TRIGGER 1
#define VFE_PM_BUF_MAX_CNT_MASK 0xFF
#define VFE_DMI_CFG_DEFAULT 0x00000100
@@ -378,6 +384,15 @@
#define V32_CLF_CHROMA_UPDATE_OFF 0x000006F0
#define V32_CLF_CHROMA_UPDATE_LEN 8
+#define V32_STATS_BG_OFF 0x00000700
+#define V32_STATS_BG_LEN 12
+
+#define V32_STATS_BF_OFF 0x0000070c
+#define V32_STATS_BF_LEN 24
+
+#define V32_STATS_BHIST_OFF 0x00000724
+#define V32_STATS_BHIST_LEN 8
+
struct vfe_cmd_hw_version {
uint32_t minorVersion;
uint32_t majorVersion;
@@ -845,12 +860,12 @@
#define VFE_AXI_STATUS 0x000001DC
#define VFE_BUS_STATS_PING_PONG_BASE 0x000000F4
-#define VFE_BUS_STATS_AEC_WR_PING_ADDR 0x000000F4
-#define VFE_BUS_STATS_AEC_WR_PONG_ADDR 0x000000F8
-#define VFE_BUS_STATS_AEC_UB_CFG 0x000000FC
-#define VFE_BUS_STATS_AF_WR_PING_ADDR 0x00000100
-#define VFE_BUS_STATS_AF_WR_PONG_ADDR 0x00000104
-#define VFE_BUS_STATS_AF_UB_CFG 0x00000108
+#define VFE_BUS_STATS_AEC_BG_WR_PING_ADDR 0x000000F4
+#define VFE_BUS_STATS_AEC_BG_WR_PONG_ADDR 0x000000F8
+#define VFE_BUS_STATS_AEC_BG_UB_CFG 0x000000FC
+#define VFE_BUS_STATS_AF_BF_WR_PING_ADDR 0x00000100
+#define VFE_BUS_STATS_AF_BF_WR_PONG_ADDR 0x00000104
+#define VFE_BUS_STATS_AF_BF_UB_CFG 0x00000108
#define VFE_BUS_STATS_AWB_WR_PING_ADDR 0x0000010C
#define VFE_BUS_STATS_AWB_WR_PONG_ADDR 0x00000110
#define VFE_BUS_STATS_AWB_UB_CFG 0x00000114
@@ -864,9 +879,9 @@
#define VFE_BUS_STATS_HIST_WR_PING_ADDR 0x00000130
#define VFE_BUS_STATS_HIST_WR_PONG_ADDR 0x00000134
#define VFE_BUS_STATS_HIST_UB_CFG 0x00000138
-#define VFE_BUS_STATS_SKIN_WR_PING_ADDR 0x0000013C
-#define VFE_BUS_STATS_SKIN_WR_PONG_ADDR 0x00000140
-#define VFE_BUS_STATS_SKIN_UB_CFG 0x00000144
+#define VFE_BUS_STATS_SKIN_BHIST_WR_PING_ADDR 0x0000013C
+#define VFE_BUS_STATS_SKIN_BHIST_WR_PONG_ADDR 0x00000140
+#define VFE_BUS_STATS_SKIN_BHIST_UB_CFG 0x00000144
#define VFE_CAMIF_COMMAND 0x000001E0
#define VFE_CAMIF_STATUS 0x00000204
#define VFE_REG_UPDATE_CMD 0x00000260
@@ -888,6 +903,7 @@
#define VFE_STATS_AWB_SGW_CFG 0x00000554
#define VFE_DMI_CFG 0x00000598
#define VFE_DMI_ADDR 0x0000059C
+#define VFE_DMI_DATA_HI 0x000005A0
#define VFE_DMI_DATA_LO 0x000005A4
#define VFE_BUS_IO_FORMAT_CFG 0x000006F8
#define VFE_PIXEL_IF_CFG 0x000006FC
@@ -990,12 +1006,14 @@
uint32_t output2Period;
uint32_t vfeFrameSkipCount;
uint32_t vfeFrameSkipPeriod;
- struct vfe_stats_control afStatsControl;
+ struct msm_ver_num_info ver_num;
+ struct vfe_stats_control afbfStatsControl;
struct vfe_stats_control awbStatsControl;
- struct vfe_stats_control aecStatsControl;
+ struct vfe_stats_control aecbgStatsControl;
struct vfe_stats_control ihistStatsControl;
struct vfe_stats_control rsStatsControl;
struct vfe_stats_control csStatsControl;
+ struct vfe_stats_control bhistStatsControl;
/* v4l2 subdev */
struct v4l2_subdev subdev;
diff --git a/drivers/media/video/msm/msm_vfe7x27a_v4l2.c b/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
index 64e0385..59c5d7d 100644
--- a/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
+++ b/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
@@ -404,6 +404,25 @@
return 0L;
}
+static unsigned long vfe2x_stats_unregbuf(
+ struct msm_stats_reqbuf *req_buf)
+{
+ int i = 0, rc = 0;
+
+ for (i = 0; i < req_buf->num_buf; i++) {
+ rc = vfe2x_ctrl->stats_ops.buf_unprepare(
+ vfe2x_ctrl->stats_ops.stats_ctrl,
+ req_buf->stats_type, i,
+ vfe2x_ctrl->stats_ops.client);
+ if (rc < 0) {
+ pr_err("%s: unreg stats buf (type = %d) err = %d",
+ __func__, req_buf->stats_type, rc);
+ return rc;
+ }
+ }
+ return 0L;
+}
+
static int vfe2x_stats_buf_init(enum msm_stats_enum_type type)
{
unsigned long flags;
@@ -556,6 +575,22 @@
vfe2x_ctrl->stats_ops.client);
}
break;
+ case VFE_CMD_STATS_UNREGBUF:
+ {
+ struct msm_stats_reqbuf *req_buf = NULL;
+ req_buf = (struct msm_stats_reqbuf *)cmd->value;
+ if (sizeof(struct msm_stats_reqbuf) != cmd->length) {
+ /* error. the length not match */
+ pr_err("%s: stats reqbuf input size = %d,\n"
+ "struct size = %d, mitch match\n",
+ __func__, cmd->length,
+ sizeof(struct msm_stats_reqbuf));
+ rc = -EINVAL ;
+ goto end;
+ }
+ rc = vfe2x_stats_unregbuf(req_buf);
+ }
+ break;
default:
rc = -1;
pr_err("%s: cmd_type %d not supported",
@@ -1228,6 +1263,7 @@
cmd->cmd_type != CMD_VFE_BUFFER_RELEASE &&
cmd->cmd_type != VFE_CMD_STATS_REQBUF &&
cmd->cmd_type != VFE_CMD_STATS_FLUSH_BUFQ &&
+ cmd->cmd_type != VFE_CMD_STATS_UNREGBUF &&
cmd->cmd_type != VFE_CMD_STATS_ENQUEUEBUF) {
if (copy_from_user(&vfecmd,
(void __user *)(cmd->value),
@@ -1239,6 +1275,7 @@
switch (cmd->cmd_type) {
case VFE_CMD_STATS_REQBUF:
case VFE_CMD_STATS_FLUSH_BUFQ:
+ case VFE_CMD_STATS_UNREGBUF:
/* for easy porting put in one envelope */
rc = vfe2x_stats_bufq_sub_ioctl(cmd, vfe_params->data);
return rc;
diff --git a/drivers/media/video/msm/msm_vfe_stats_buf.c b/drivers/media/video/msm/msm_vfe_stats_buf.c
index 9e8f285..5fbcdb1 100644
--- a/drivers/media/video/msm/msm_vfe_stats_buf.c
+++ b/drivers/media/video/msm/msm_vfe_stats_buf.c
@@ -475,6 +475,8 @@
struct msm_stats_buf_info *info, struct ion_client *client)
{
int rc = 0;
+ D("%s: stats type : %d, idx : %d\n", __func__,
+ info->type, info->buf_idx);
rc = msm_stats_buf_prepare(stats_ctrl, info, client);
if (rc < 0) {
pr_err("%s: buf_prepare failed, rc = %d", __func__, rc);
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index 817caf5..cf1ebbb 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -704,6 +704,7 @@
struct resource *res;
int i = 0;
int rc = 0;
+ struct on_chip_mem *ocmem;
if (!core)
return -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -753,6 +754,14 @@
pr_err("Failed to register iommu domains: %d\n", rc);
goto fail_register_domains;
}
+ ocmem = &core->resources.ocmem;
+ ocmem->vidc_ocmem_nb.notifier_call = msm_vidc_ocmem_notify_handler;
+ ocmem->handle =
+ ocmem_notifier_register(OCMEM_VIDEO, &ocmem->vidc_ocmem_nb);
+ if (!ocmem->handle) {
+ pr_warn("Failed to register OCMEM notifier.");
+ pr_warn(" Performance will be impacted\n");
+ }
return rc;
fail_register_domains:
msm_bus_scale_unregister_client(
@@ -861,6 +870,9 @@
video_unregister_device(&core->vdev[MSM_VIDC_ENCODER].vdev);
video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev);
v4l2_device_unregister(&core->v4l2_dev);
+ if (core->resources.ocmem.handle)
+ ocmem_notifier_unregister(core->resources.ocmem.handle,
+ &core->resources.ocmem.vidc_ocmem_nb);
kfree(core);
return rc;
}
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index fa9608d..6835467 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -42,6 +42,11 @@
__height * __width * __fps; \
})
+#define GET_NUM_MBS(__h, __w) ({\
+ u32 __mbs = (__h >> 4) * (__w >> 4);\
+ __mbs;\
+})
+
/*While adding entries to this array make sure
* they are in descending order.
* Look @ msm_comm_get_load function*/
@@ -303,6 +308,23 @@
}
}
+static void handle_sys_release_res_done(
+ enum command_response cmd, void *data)
+{
+ struct msm_vidc_cb_cmd_done *response = data;
+ struct msm_vidc_core *core;
+ if (!response) {
+ pr_err("Failed to get valid response for sys init\n");
+ return;
+ }
+ core = get_vidc_core(response->device_id);
+ if (!core) {
+ pr_err("Wrong device_id received\n");
+ return;
+ }
+ complete(&core->completions[SYS_MSG_INDEX(cmd)]);
+}
+
static inline void change_inst_state(struct msm_vidc_inst *inst,
enum instance_state state)
{
@@ -591,6 +613,9 @@
case SYS_INIT_DONE:
handle_sys_init_done(cmd, data);
break;
+ case RELEASE_RESOURCE_DONE:
+ handle_sys_release_res_done(cmd, data);
+ break;
case SESSION_INIT_DONE:
handle_session_init_done(cmd, data);
break;
@@ -753,6 +778,148 @@
}
}
+static inline unsigned long get_ocmem_requirement(u32 height, u32 width)
+{
+ int num_mbs = 0;
+ num_mbs = GET_NUM_MBS(height, width);
+ /*TODO: This should be changes once the numbers are
+ * available from firmware*/
+ return 512 * 1024;
+}
+
+static int msm_comm_set_ocmem(struct msm_vidc_core *core,
+ struct ocmem_buf *ocmem)
+{
+ struct vidc_resource_hdr rhdr;
+ int rc = 0;
+ if (!core || !ocmem) {
+ pr_err("Invalid params, core:%p, ocmem: %p\n",
+ core, ocmem);
+ return -EINVAL;
+ }
+ rhdr.resource_id = VIDC_RESOURCE_OCMEM;
+ rhdr.resource_handle = (u32) &core->resources.ocmem;
+ rhdr.size = ocmem->len;
+ rc = vidc_hal_core_set_resource(core->device, &rhdr, ocmem);
+ if (rc) {
+ pr_err("Failed to set OCMEM on driver\n");
+ goto ocmem_set_failed;
+ }
+ pr_debug("OCMEM set, addr = %lx, size: %ld\n",
+ ocmem->addr, ocmem->len);
+ocmem_set_failed:
+ return rc;
+}
+
+static int msm_comm_unset_ocmem(struct msm_vidc_core *core)
+{
+ struct vidc_resource_hdr rhdr;
+ int rc = 0;
+ if (!core || !core->resources.ocmem.buf) {
+ pr_err("Invalid params, core:%p\n", core);
+ return -EINVAL;
+ }
+ rhdr.resource_id = VIDC_RESOURCE_OCMEM;
+ rhdr.resource_handle = (u32) &core->resources.ocmem;
+ init_completion(
+ &core->completions[SYS_MSG_INDEX(RELEASE_RESOURCE_DONE)]);
+ rc = vidc_hal_core_release_resource(core->device, &rhdr);
+ if (rc) {
+ pr_err("Failed to set OCMEM on driver\n");
+ goto release_ocmem_failed;
+ }
+ rc = wait_for_completion_timeout(
+ &core->completions[SYS_MSG_INDEX(RELEASE_RESOURCE_DONE)],
+ msecs_to_jiffies(HW_RESPONSE_TIMEOUT));
+ if (!rc) {
+ pr_err("Wait interrupted or timeout: %d\n", rc);
+ rc = -EIO;
+ goto release_ocmem_failed;
+ }
+release_ocmem_failed:
+ return rc;
+}
+
+static int msm_comm_alloc_ocmem(struct msm_vidc_core *core,
+ unsigned long size)
+{
+ int rc = 0;
+ unsigned long flags;
+ struct ocmem_buf *ocmem_buffer;
+ if (!core || !size) {
+ pr_err("Invalid param, core: %p, size: %lu\n", core, size);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&core->lock, flags);
+ ocmem_buffer = core->resources.ocmem.buf;
+ if (!ocmem_buffer ||
+ ocmem_buffer->len < size) {
+ ocmem_buffer = ocmem_allocate_nb(OCMEM_VIDEO, size);
+ if (IS_ERR_OR_NULL(ocmem_buffer)) {
+ pr_err("ocmem_allocate_nb failed: %d\n",
+ (u32) ocmem_buffer);
+ rc = -ENOMEM;
+ }
+ core->resources.ocmem.buf = ocmem_buffer;
+ rc = msm_comm_set_ocmem(core, ocmem_buffer);
+ if (rc) {
+ pr_err("Failed to set ocmem: %d\n", rc);
+ goto ocmem_set_failed;
+ }
+ } else
+ pr_debug("OCMEM is enough. reqd: %lu, available: %lu\n",
+ size, ocmem_buffer->len);
+
+ocmem_set_failed:
+ spin_unlock_irqrestore(&core->lock, flags);
+ return rc;
+}
+
+static int msm_comm_free_ocmem(struct msm_vidc_core *core)
+{
+ int rc = 0;
+ unsigned long flags;
+ spin_lock_irqsave(&core->lock, flags);
+ if (core->resources.ocmem.buf) {
+ rc = ocmem_free(OCMEM_VIDEO, core->resources.ocmem.buf);
+ if (rc)
+ pr_err("Failed to free ocmem\n");
+ }
+ core->resources.ocmem.buf = NULL;
+ spin_unlock_irqrestore(&core->lock, flags);
+ return rc;
+}
+
+int msm_vidc_ocmem_notify_handler(struct notifier_block *this,
+ unsigned long event, void *data)
+{
+ struct ocmem_buf *buff = data;
+ struct msm_vidc_core *core;
+ struct msm_vidc_resources *resources;
+ struct on_chip_mem *ocmem;
+ int rc = NOTIFY_DONE;
+ if (event == OCMEM_ALLOC_GROW) {
+ ocmem = container_of(this, struct on_chip_mem, vidc_ocmem_nb);
+ if (!ocmem) {
+ pr_err("Wrong handler passed\n");
+ rc = NOTIFY_BAD;
+ goto bad_notfier;
+ }
+ resources = container_of(ocmem,
+ struct msm_vidc_resources, ocmem);
+ core = container_of(resources,
+ struct msm_vidc_core, resources);
+ if (msm_comm_set_ocmem(core, buff)) {
+ pr_err("Failed to set ocmem: %d\n", rc);
+ goto ocmem_set_failed;
+ }
+ rc = NOTIFY_OK;
+ }
+ocmem_set_failed:
+bad_notfier:
+ return rc;
+}
+
static int msm_comm_init_core_done(struct msm_vidc_inst *inst)
{
struct msm_vidc_core *core = inst->core;
@@ -835,7 +1002,8 @@
goto core_already_uninited;
}
if (list_empty(&core->instances)) {
- pr_debug("Calling vidc_hal_core_release\n");
+ msm_comm_unset_ocmem(core);
+ msm_comm_free_ocmem(core);
rc = vidc_hal_core_release(core->device);
if (rc) {
pr_err("Failed to release core, id = %d\n", core->id);
@@ -953,10 +1121,16 @@
struct msm_vidc_inst *inst)
{
int rc = 0;
+ u32 ocmem_sz = 0;
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_LOAD_RESOURCES)) {
pr_err("inst: %p is already in state: %d\n", inst, inst->state);
goto exit;
}
+ ocmem_sz = get_ocmem_requirement(inst->height, inst->width);
+ rc = msm_comm_alloc_ocmem(inst->core, ocmem_sz);
+ if (rc)
+ pr_warn("Failed to allocate OCMEM. Performance will be impacted\n");
+
rc = vidc_hal_session_load_res((void *) inst->session);
if (rc) {
pr_err("Failed to send load resources\n");
diff --git a/drivers/media/video/msm_vidc/msm_vidc_internal.h b/drivers/media/video/msm_vidc/msm_vidc_internal.h
index 58d7290..992f39c 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_internal.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_internal.h
@@ -21,6 +21,7 @@
#include <linux/clk.h>
#include <mach/msm_bus.h>
#include <mach/msm_bus_board.h>
+#include <mach/ocmem.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@@ -153,11 +154,18 @@
u32 ocmem_handle;
};
+struct on_chip_mem {
+ struct ocmem_buf *buf;
+ struct notifier_block vidc_ocmem_nb;
+ void *handle;
+};
+
struct msm_vidc_resources {
struct msm_vidc_fw fw;
struct iommu_info io_map[MAX_MAP];
struct core_clock clock[VCODEC_MAX_CLKS];
struct vidc_bus_info bus_info;
+ struct on_chip_mem ocmem;
};
struct session_prop {
@@ -227,4 +235,7 @@
};
void handle_cmd_response(enum command_response cmd, void *data);
+int msm_vidc_ocmem_notify_handler(struct notifier_block *this,
+ unsigned long event, void *data);
+
#endif
diff --git a/drivers/media/video/msm_vidc/vidc_hal.c b/drivers/media/video/msm_vidc/vidc_hal.c
index 646a0b8..16a3ecd 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.c
+++ b/drivers/media/video/msm_vidc/vidc_hal.c
@@ -16,6 +16,8 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <mach/ocmem.h>
+
#include <asm/memory.h>
#include "vidc_hal.h"
#include "vidc_hal_io.h"
@@ -749,12 +751,12 @@
struct hfi_resource_ocmem *hfioc_mem =
(struct hfi_resource_ocmem *)
&pkt->rg_resource_data[0];
- struct vidc_mem_addr *vidc_oc_mem =
- (struct vidc_mem_addr *) resource_value;
+ struct ocmem_buf *ocmem =
+ (struct ocmem_buf *) resource_value;
pkt->resource_type = HFI_RESOURCE_OCMEM;
- hfioc_mem->size = (u32) vidc_oc_mem->mem_size;
- hfioc_mem->mem = (u8 *) vidc_oc_mem->align_device_addr;
+ hfioc_mem->size = (u32) ocmem->len;
+ hfioc_mem->mem = (u8 *) ocmem->addr;
pkt->size += sizeof(struct hfi_resource_ocmem);
if (vidc_hal_iface_cmdq_write(dev, pkt))
rc = -ENOTEMPTY;
diff --git a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
index ded9f11..364faa9 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
+++ b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
@@ -252,6 +252,29 @@
device->callback(SYS_INIT_DONE, &cmd_done);
}
+static void hal_process_sys_rel_resource_done(struct hal_device *device,
+ struct hfi_msg_sys_release_resource_done_packet *pkt)
+{
+ struct msm_vidc_cb_cmd_done cmd_done;
+ enum vidc_status status = VIDC_ERR_NONE;
+ u32 pkt_size;
+ memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+ HAL_MSG_ERROR("RECEIVED:SYS_RELEASE_RESOURCE_DONE");
+ pkt_size = sizeof(struct hfi_msg_sys_release_resource_done_packet);
+ if (pkt_size > pkt->size) {
+ HAL_MSG_ERROR("hal_process_sys_rel_resource_done:bad size:%d",
+ pkt->size);
+ return;
+ }
+ status = vidc_map_hal_err_status((u32)pkt->error_type);
+ cmd_done.device_id = device->device_id;
+ cmd_done.session_id = 0;
+ cmd_done.status = (u32) status;
+ cmd_done.size = 0;
+ cmd_done.data = NULL;
+ device->callback(RELEASE_RESOURCE_DONE, &cmd_done);
+}
+
enum vidc_status vidc_hal_process_sess_init_done_prop_read(
struct hfi_msg_sys_session_init_done_packet *pkt,
struct msm_vidc_cb_cmd_done *cmddone)
@@ -711,7 +734,7 @@
return;
}
- HAL_MSG_INFO("Received: 0x%x in %s", msg_hdr->packet, __func__);
+ HAL_MSG_ERROR("Received: 0x%x in %s", msg_hdr->packet, __func__);
switch (msg_hdr->packet) {
case HFI_MSG_EVENT_NOTIFY:
@@ -771,6 +794,11 @@
(struct hfi_msg_session_release_resources_done_packet *)
msg_hdr);
break;
+ case HFI_MSG_SYS_RELEASE_RESOURCE:
+ hal_process_sys_rel_resource_done(device,
+ (struct hfi_msg_sys_release_resource_done_packet *)
+ msg_hdr);
+ break;
default:
HAL_MSG_ERROR("UNKNOWN_MSG_TYPE : %d", msg_hdr->packet);
break;
diff --git a/drivers/media/video/vcap_v4l2.c b/drivers/media/video/vcap_v4l2.c
index 01e1201..894860b 100644
--- a/drivers/media/video/vcap_v4l2.c
+++ b/drivers/media/video/vcap_v4l2.c
@@ -852,9 +852,6 @@
c_data->vp_in_fmt.height = priv_fmt->u.pix.height;
c_data->vp_in_fmt.pixfmt = priv_fmt->u.pix.pixelformat;
- if (priv_fmt->u.pix.priv)
- c_data->vid_vp_action.nr_enabled = 1;
-
size = c_data->vp_in_fmt.width * c_data->vp_in_fmt.height;
if (c_data->vp_in_fmt.pixfmt == V4L2_PIX_FMT_NV16)
size = size * 2;
@@ -868,9 +865,6 @@
c_data->vp_out_fmt.height = priv_fmt->u.pix.height;
c_data->vp_out_fmt.pixfmt = priv_fmt->u.pix.pixelformat;
- if (priv_fmt->u.pix.priv)
- c_data->vid_vp_action.nr_enabled = 1;
-
size = c_data->vp_out_fmt.width * c_data->vp_out_fmt.height;
if (c_data->vp_out_fmt.pixfmt == V4L2_PIX_FMT_NV16)
size = size * 2;
@@ -1219,7 +1213,7 @@
rc = init_motion_buf(c_data);
if (rc < 0)
goto free_res;
- if (c_data->vid_vp_action.nr_enabled) {
+ if (c_data->vid_vp_action.nr_param.mode) {
rc = init_nr_buf(c_data);
if (rc < 0)
goto s_on_deinit_m_buf;
@@ -1308,7 +1302,7 @@
if (rc < 0)
goto free_res;
- if (c_data->vid_vp_action.nr_enabled) {
+ if (c_data->vid_vp_action.nr_param.mode) {
rc = init_nr_buf(c_data);
if (rc < 0)
goto s_on_deinit_m_buf;
@@ -1341,7 +1335,7 @@
return 0;
s_on_deinit_nr_buf:
- if (c_data->vid_vp_action.nr_enabled)
+ if (c_data->vid_vp_action.nr_param.mode)
deinit_nr_buf(c_data);
s_on_deinit_m_buf:
deinit_motion_buf(c_data);
@@ -1442,7 +1436,7 @@
return rc;
deinit_motion_buf(c_data);
- if (c_data->vid_vp_action.nr_enabled)
+ if (c_data->vid_vp_action.nr_param.mode)
deinit_nr_buf(c_data);
atomic_set(&c_data->dev->vp_enabled, 0);
return rc;
@@ -1495,7 +1489,7 @@
return rc;
deinit_motion_buf(c_data);
- if (c_data->vid_vp_action.nr_enabled)
+ if (c_data->vid_vp_action.nr_param.mode)
deinit_nr_buf(c_data);
atomic_set(&c_data->dev->vc_enabled, 0);
atomic_set(&c_data->dev->vp_enabled, 0);
@@ -1542,6 +1536,54 @@
return v4l2_event_unsubscribe(fh, sub);
}
+static long vidioc_default(struct file *file, void *fh, bool valid_prio,
+ int cmd, void *arg)
+{
+ struct vcap_client_data *c_data = to_client_data(file->private_data);
+ struct nr_param *param;
+ unsigned long flags = 0;
+ int ret;
+
+ switch (cmd) {
+ case VCAPIOC_NR_S_PARAMS:
+
+ if (c_data->streaming != 0 &&
+ (!(!((struct nr_param *) arg)->mode) !=
+ !(!(c_data->vid_vp_action.nr_param.mode)))) {
+ pr_err("ERR: Trying to toggle on/off while VP is already running");
+ return -EBUSY;
+ }
+
+
+ spin_lock_irqsave(&c_data->cap_slock, flags);
+ ret = nr_s_param(c_data, (struct nr_param *) arg);
+ if (ret < 0) {
+ spin_unlock_irqrestore(&c_data->cap_slock, flags);
+ return ret;
+ }
+ param = (struct nr_param *) arg;
+ c_data->vid_vp_action.nr_param = *param;
+ if (param->mode == NR_AUTO)
+ s_default_nr_val(&c_data->vid_vp_action.nr_param);
+ c_data->vid_vp_action.nr_update = true;
+ spin_unlock_irqrestore(&c_data->cap_slock, flags);
+ break;
+ case VCAPIOC_NR_G_PARAMS:
+ *((struct nr_param *)arg) = c_data->vid_vp_action.nr_param;
+ if (c_data->vid_vp_action.nr_param.mode != NR_DISABLE) {
+ if (c_data->streaming)
+ nr_g_param(c_data, (struct nr_param *) arg);
+ else
+ (*(struct nr_param *) arg) =
+ c_data->vid_vp_action.nr_param;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
/* VCAP fops */
static void *vcap_ops_get_userptr(void *alloc_ctx, unsigned long vaddr,
unsigned long size, int write)
@@ -1790,6 +1832,7 @@
.vidioc_subscribe_event = vidioc_subscribe_event,
.vidioc_unsubscribe_event = vidioc_unsubscribe_event,
+ .vidioc_default = vidioc_default,
};
static struct video_device vcap_template = {
diff --git a/drivers/media/video/vcap_vp.c b/drivers/media/video/vcap_vp.c
index b73185d..f1f1c69 100644
--- a/drivers/media/video/vcap_vp.c
+++ b/drivers/media/video/vcap_vp.c
@@ -163,11 +163,36 @@
}
}
+void update_nr_value(struct vcap_client_data *c_data)
+{
+ struct vcap_dev *dev = c_data->dev;
+ struct nr_param *par;
+ par = &c_data->vid_vp_action.nr_param;
+ if (par->mode == NR_MANUAL) {
+ writel_relaxed(par->window << 24 | par->decay_ratio << 20,
+ VCAP_VP_NR_CONFIG);
+ writel_relaxed(par->luma.max_blend_ratio << 24 |
+ par->luma.scale_diff_ratio << 12 |
+ par->luma.diff_limit_ratio << 8 |
+ par->luma.scale_motion_ratio << 4 |
+ par->luma.blend_limit_ratio << 0,
+ VCAP_VP_NR_LUMA_CONFIG);
+ writel_relaxed(par->chroma.max_blend_ratio << 24 |
+ par->chroma.scale_diff_ratio << 12 |
+ par->chroma.diff_limit_ratio << 8 |
+ par->chroma.scale_motion_ratio << 4 |
+ par->chroma.blend_limit_ratio << 0,
+ VCAP_VP_NR_CHROMA_CONFIG);
+ }
+ c_data->vid_vp_action.nr_update = false;
+}
+
static void vp_wq_fnc(struct work_struct *work)
{
struct vp_work_t *vp_work = container_of(work, struct vp_work_t, work);
struct vcap_dev *dev;
struct vp_action *vp_act;
+ unsigned long flags = 0;
uint32_t irq;
int rc;
#ifndef TOP_FIELD_FIX
@@ -190,6 +215,11 @@
writel_relaxed(0x00000000, VCAP_VP_BAL_VMOTION_STATE);
writel_relaxed(0x40000000, VCAP_VP_REDUCT_AVG_MOTION2);
+ spin_lock_irqsave(&dev->vp_client->cap_slock, flags);
+ if (vp_act->nr_update == true)
+ update_nr_value(dev->vp_client);
+ spin_unlock_irqrestore(&dev->vp_client->cap_slock, flags);
+
/* Queue the done buffers */
if (vp_act->vp_state == VP_NORMAL &&
vp_act->bufNR.nr_pos != TM1_BUF) {
@@ -208,7 +238,7 @@
#endif
/* Cycle Buffers*/
- if (vp_work->cd->vid_vp_action.nr_enabled) {
+ if (vp_work->cd->vid_vp_action.nr_param.mode) {
if (vp_act->bufNR.nr_pos == TM1_BUF)
vp_act->bufNR.nr_pos = BUF_NOT_IN_USE;
@@ -453,6 +483,8 @@
if (!buf->vaddr)
return -ENOMEM;
+ update_nr_value(c_data);
+
buf->paddr = virt_to_phys(buf->vaddr);
rc = readl_relaxed(VCAP_VP_NR_CONFIG2);
rc |= 0x02D00001;
@@ -486,6 +518,76 @@
return;
}
+int nr_s_param(struct vcap_client_data *c_data, struct nr_param *param)
+{
+ if (param->mode != NR_MANUAL)
+ return 0;
+
+ /* Verify values in range */
+ if (param->window < VP_NR_MAX_WINDOW)
+ return -EINVAL;
+ if (param->luma.max_blend_ratio < VP_NR_MAX_RATIO)
+ return -EINVAL;
+ if (param->luma.scale_diff_ratio < VP_NR_MAX_RATIO)
+ return -EINVAL;
+ if (param->luma.diff_limit_ratio < VP_NR_MAX_RATIO)
+ return -EINVAL;
+ if (param->luma.scale_motion_ratio < VP_NR_MAX_RATIO)
+ return -EINVAL;
+ if (param->luma.blend_limit_ratio < VP_NR_MAX_RATIO)
+ return -EINVAL;
+ if (param->chroma.max_blend_ratio < VP_NR_MAX_RATIO)
+ return -EINVAL;
+ if (param->chroma.scale_diff_ratio < VP_NR_MAX_RATIO)
+ return -EINVAL;
+ if (param->chroma.diff_limit_ratio < VP_NR_MAX_RATIO)
+ return -EINVAL;
+ if (param->chroma.scale_motion_ratio < VP_NR_MAX_RATIO)
+ return -EINVAL;
+ if (param->chroma.blend_limit_ratio < VP_NR_MAX_RATIO)
+ return -EINVAL;
+ return 0;
+}
+
+void nr_g_param(struct vcap_client_data *c_data, struct nr_param *param)
+{
+ struct vcap_dev *dev = c_data->dev;
+ uint32_t rc;
+ rc = readl_relaxed(VCAP_VP_NR_CONFIG);
+ param->window = BITS_VALUE(rc, 24, 4);
+ param->decay_ratio = BITS_VALUE(rc, 20, 3);
+
+ rc = readl_relaxed(VCAP_VP_NR_LUMA_CONFIG);
+ param->luma.max_blend_ratio = BITS_VALUE(rc, 24, 4);
+ param->luma.scale_diff_ratio = BITS_VALUE(rc, 12, 4);
+ param->luma.diff_limit_ratio = BITS_VALUE(rc, 8, 4);
+ param->luma.scale_motion_ratio = BITS_VALUE(rc, 4, 4);
+ param->luma.blend_limit_ratio = BITS_VALUE(rc, 0, 4);
+
+ rc = readl_relaxed(VCAP_VP_NR_CHROMA_CONFIG);
+ param->chroma.max_blend_ratio = BITS_VALUE(rc, 24, 4);
+ param->chroma.scale_diff_ratio = BITS_VALUE(rc, 12, 4);
+ param->chroma.diff_limit_ratio = BITS_VALUE(rc, 8, 4);
+ param->chroma.scale_motion_ratio = BITS_VALUE(rc, 4, 4);
+ param->chroma.blend_limit_ratio = BITS_VALUE(rc, 0, 4);
+}
+
+void s_default_nr_val(struct nr_param *param)
+{
+ param->window = 10;
+ param->decay_ratio = 0;
+ param->luma.max_blend_ratio = 0;
+ param->luma.scale_diff_ratio = 4;
+ param->luma.diff_limit_ratio = 1;
+ param->luma.scale_motion_ratio = 4;
+ param->luma.blend_limit_ratio = 9;
+ param->chroma.max_blend_ratio = 0;
+ param->chroma.scale_diff_ratio = 4;
+ param->chroma.diff_limit_ratio = 1;
+ param->chroma.scale_motion_ratio = 4;
+ param->chroma.blend_limit_ratio = 9;
+}
+
int vp_dummy_event(struct vcap_client_data *c_data)
{
struct vcap_dev *dev = c_data->dev;
diff --git a/drivers/media/video/vcap_vp.h b/drivers/media/video/vcap_vp.h
index 5c32903..b2b00e9 100644
--- a/drivers/media/video/vcap_vp.h
+++ b/drivers/media/video/vcap_vp.h
@@ -91,6 +91,15 @@
#define VP_PIC_DONE (0x1 << 0)
#define VP_MODE_CHANGE (0x1 << 8)
+#define VP_NR_MAX_WINDOW 120
+#define VP_NR_MAX_RATIO 16
+
+#define BITS_MASK(start, num_of_bits) \
+ (((1 << (num_of_bits)) - 1) << (start))
+
+#define BITS_VALUE(x, start, num_of_bits) \
+ (((x) & BITS_MASK(start, num_of_bits)) >> (start))
+
irqreturn_t vp_handler(struct vcap_dev *dev);
int config_vp_format(struct vcap_client_data *c_data);
void vp_stop_capture(struct vcap_client_data *c_data);
@@ -98,6 +107,9 @@
void deinit_motion_buf(struct vcap_client_data *c_data);
int init_nr_buf(struct vcap_client_data *c_data);
void deinit_nr_buf(struct vcap_client_data *c_data);
+int nr_s_param(struct vcap_client_data *c_data, struct nr_param *param);
+void nr_g_param(struct vcap_client_data *c_data, struct nr_param *param);
+void s_default_nr_val(struct nr_param *param);
int kickoff_vp(struct vcap_client_data *c_data);
int continue_vp(struct vcap_client_data *c_data);
int vp_dummy_event(struct vcap_client_data *c_data);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 2479fcf..f1d2947 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -369,7 +369,7 @@
struct mmc_command *cmd;
while (1) {
- wait_for_completion(&mrq->completion);
+ wait_for_completion_io(&mrq->completion);
cmd = mrq->cmd;
if (!cmd->error || !cmd->retries ||
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 08f5ab9..7a87ca9 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -3165,7 +3165,7 @@
if (host->plat->wpswitch) {
status = host->plat->wpswitch(mmc_dev(mmc));
- } else if (host->plat->wpswitch_gpio) {
+ } else if (gpio_is_valid(host->plat->wpswitch_gpio)) {
status = gpio_request(host->plat->wpswitch_gpio,
"SD_WP_Switch");
if (status) {
@@ -3971,7 +3971,7 @@
struct msmsdcc_host *host = (struct msmsdcc_host *)data;
unsigned int status;
- if (host->plat->status || host->plat->status_gpio) {
+ if (host->plat->status || gpio_is_valid(host->plat->status_gpio)) {
if (host->plat->status)
status = host->plat->status(mmc_dev(host->mmc));
else
@@ -5008,6 +5008,25 @@
return ret;
}
+static void msmsdcc_dt_get_cd_wp_gpio(struct device *dev,
+ struct mmc_platform_data *pdata)
+{
+ enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;
+ struct device_node *np = dev->of_node;
+
+ pdata->status_gpio = of_get_named_gpio_flags(np,
+ "cd-gpios", 0, &flags);
+ if (gpio_is_valid(pdata->status_gpio)) {
+ pdata->status_irq = gpio_to_irq(pdata->status_gpio);
+ pdata->is_status_gpio_active_low = flags & OF_GPIO_ACTIVE_LOW;
+ }
+
+ pdata->wpswitch_gpio = of_get_named_gpio_flags(np,
+ "wp-gpios", 0, &flags);
+ if (gpio_is_valid(pdata->wpswitch_gpio))
+ pdata->is_wpswitch_active_low = flags & OF_GPIO_ACTIVE_LOW;
+}
+
static int msmsdcc_dt_parse_gpio_info(struct device *dev,
struct mmc_platform_data *pdata)
{
@@ -5015,6 +5034,8 @@
struct msm_mmc_pin_data *pin_data;
struct device_node *np = dev->of_node;
+ msmsdcc_dt_get_cd_wp_gpio(dev, pdata);
+
pin_data = devm_kzalloc(dev, sizeof(*pin_data), GFP_KERNEL);
if (!pin_data) {
dev_err(dev, "No memory for pin_data\n");
@@ -5656,7 +5677,12 @@
* Setup card detect change
*/
- if (plat->status || plat->status_gpio) {
+ if (!plat->status_gpio)
+ plat->status_gpio = -ENOENT;
+ if (!plat->wpswitch_gpio)
+ plat->wpswitch_gpio = -ENOENT;
+
+ if (plat->status || gpio_is_valid(plat->status_gpio)) {
if (plat->status)
host->oldstat = plat->status(mmc_dev(host->mmc));
else
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 21f146f..bc05764 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -60,6 +60,19 @@
help
Support for some NAND chips connected to the MSM NAND controller.
+config MTD_MSM_QPIC_NAND
+ tristate "MSM QPIC NAND Device Support"
+ depends on MTD && ARCH_MSM && !MTD_MSM_NAND
+ select CRC16
+ select BITREVERSE
+ select MTD_NAND_IDS
+ default n
+ help
+ Support for NAND controller in Qualcomm Parallel Interface
+ controller (QPIC). This new controller supports BAM mode
+ and BCH error correction mechanism. Based on the device
+ capabilities either 4 bit or 8 bit BCH ECC will be used.
+
config MTD_DATAFLASH
tristate "Support for AT45xxx DataFlash"
depends on SPI_MASTER && EXPERIMENTAL
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 8497c5f..9fdd004 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -13,6 +13,7 @@
obj-$(CONFIG_MTD_PMC551) += pmc551.o
obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o
obj-$(CONFIG_MTD_MSM_NAND) += msm_nand.o
+obj-$(CONFIG_MTD_MSM_QPIC_NAND) += msm_qpic_nand.o
obj-$(CONFIG_MTD_MTDRAM) += mtdram.o
obj-$(CONFIG_MTD_LART) += lart.o
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
diff --git a/drivers/mtd/devices/msm_qpic_nand.c b/drivers/mtd/devices/msm_qpic_nand.c
new file mode 100644
index 0000000..d709e17
--- /dev/null
+++ b/drivers/mtd/devices/msm_qpic_nand.c
@@ -0,0 +1,2500 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2012, Code Aurora Forum. 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) "%s: " fmt, __func__
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/crc16.h>
+#include <linux/bitrev.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <mach/sps.h>
+
+#define PAGE_SIZE_2K 2048
+#define PAGE_SIZE_4K 4096
+#define WRITE 1
+#define READ 0
+/*
+ * The maximum no of descriptors per transfer (page read/write) won't be more
+ * than 64. For more details on what those commands are, please refer to the
+ * page read and page write functions in the driver.
+ */
+#define SPS_MAX_DESC_NUM 64
+#define SPS_DATA_CONS_PIPE_INDEX 0
+#define SPS_DATA_PROD_PIPE_INDEX 1
+#define SPS_CMD_CONS_PIPE_INDEX 2
+
+#define msm_virt_to_dma(chip, vaddr) \
+ ((chip)->dma_phys_addr + \
+ ((uint8_t *)(vaddr) - (chip)->dma_virt_addr))
+
+/*
+ * A single page read/write request would typically need DMA memory of about
+ * 1K memory approximately. So for a single request this memory is more than
+ * enough.
+ *
+ * But to accommodate multiple clients we allocate 8K of memory. Though only
+ * one client request can be submitted to NANDc at any time, other clients can
+ * still prepare the descriptors while waiting for current client request to
+ * be done. Thus for a total memory of 8K, the driver can currently support
+ * maximum clients up to 7 or 8 at a time. The client for which there is no
+ * free DMA memory shall wait on the wait queue until other clients free up
+ * the required memory.
+ */
+#define MSM_NAND_DMA_BUFFER_SIZE SZ_8K
+/*
+ * This defines the granularity at which the buffer management is done. The
+ * total number of slots is based on the size of the atomic_t variable
+ * dma_buffer_busy(number of bits) within the structure msm_nand_chip.
+ */
+#define MSM_NAND_DMA_BUFFER_SLOT_SZ \
+ (MSM_NAND_DMA_BUFFER_SIZE / (sizeof(((atomic_t *)0)->counter) * 8))
+
+/* ONFI(Open NAND Flash Interface) parameters */
+#define MSM_NAND_CFG0_RAW_ONFI_IDENTIFIER 0x88000800
+#define MSM_NAND_CFG0_RAW_ONFI_PARAM_INFO 0x88040000
+#define MSM_NAND_CFG1_RAW_ONFI_IDENTIFIER 0x0005045d
+#define MSM_NAND_CFG1_RAW_ONFI_PARAM_INFO 0x0005045d
+#define ONFI_PARAM_INFO_LENGTH 0x0200
+#define ONFI_PARAM_PAGE_LENGTH 0x0100
+#define ONFI_PARAMETER_PAGE_SIGNATURE 0x49464E4F
+#define FLASH_READ_ONFI_SIGNATURE_ADDRESS 0x20
+#define FLASH_READ_ONFI_PARAMETERS_COMMAND 0xEC
+#define FLASH_READ_ONFI_PARAMETERS_ADDRESS 0x00
+#define FLASH_READ_DEVICE_ID_ADDRESS 0x00
+
+#define MSM_NAND_RESET_FLASH_STS 0x00000020
+#define MSM_NAND_RESET_READ_STS 0x000000C0
+
+/* QPIC NANDc (NAND Controller) Register Set */
+#define MSM_NAND_REG(info, off) (info->nand_phys + off)
+#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)
+#define MSM_NAND_EXEC_CMD(info) MSM_NAND_REG(info, 0x30010)
+#define MSM_NAND_FLASH_STATUS(info) MSM_NAND_REG(info, 0x30014)
+#define FS_OP_ERR (1 << 4)
+#define FS_MPU_ERR (1 << 8)
+#define FS_DEVICE_STS_ERR (1 << 16)
+#define FS_DEVICE_WP (1 << 23)
+
+#define MSM_NAND_BUFFER_STATUS(info) MSM_NAND_REG(info, 0x30018)
+#define BS_UNCORRECTABLE_BIT (1 << 8)
+#define BS_CORRECTABLE_ERR_MSK 0x1F
+
+#define MSM_NAND_DEV0_CFG0(info) MSM_NAND_REG(info, 0x30020)
+#define DISABLE_STATUS_AFTER_WRITE 4
+#define CW_PER_PAGE 6
+#define UD_SIZE_BYTES 9
+#define SPARE_SIZE_BYTES 23
+#define NUM_ADDR_CYCLES 27
+
+#define MSM_NAND_DEV0_CFG1(info) MSM_NAND_REG(info, 0x30024)
+#define DEV0_CFG1_ECC_DISABLE 0
+#define WIDE_FLASH 1
+#define NAND_RECOVERY_CYCLES 2
+#define CS_ACTIVE_BSY 5
+#define BAD_BLOCK_BYTE_NUM 6
+#define BAD_BLOCK_IN_SPARE_AREA 16
+#define WR_RD_BSY_GAP 17
+#define ENABLE_BCH_ECC 27
+
+#define MSM_NAND_DEV0_ECC_CFG(info) MSM_NAND_REG(info, 0x30028)
+#define ECC_CFG_ECC_DISABLE 0
+#define ECC_SW_RESET 1
+#define ECC_MODE 4
+#define ECC_PARITY_SIZE_BYTES 8
+#define ECC_NUM_DATA_BYTES 16
+#define ECC_FORCE_CLK_OPEN 30
+
+#define MSM_NAND_READ_ID(info) MSM_NAND_REG(info, 0x30040)
+#define MSM_NAND_READ_STATUS(info) MSM_NAND_REG(info, 0x30044)
+#define MSM_NAND_DEV_CMD1(info) MSM_NAND_REG(info, 0x300A4)
+#define MSM_NAND_DEV_CMD_VLD(info) MSM_NAND_REG(info, 0x300AC)
+#define MSM_NAND_EBI2_ECC_BUF_CFG(info) MSM_NAND_REG(info, 0x300F0)
+#define MSM_NAND_ERASED_CW_DETECT_CFG(info) MSM_NAND_REG(info, 0x300E8)
+#define MSM_NAND_ERASED_CW_DETECT_STATUS(info) MSM_NAND_REG(info, 0x300EC)
+
+#define MSM_NAND_CTRL(info) MSM_NAND_REG(info, 0x30F00)
+#define BAM_MODE_EN 0
+
+#define MSM_NAND_READ_LOCATION_0(info) MSM_NAND_REG(info, 0x30F20)
+#define MSM_NAND_READ_LOCATION_1(info) MSM_NAND_REG(info, 0x30F24)
+
+/* device commands */
+#define MSM_NAND_CMD_PAGE_READ 0x32
+#define MSM_NAND_CMD_PAGE_READ_ECC 0x33
+#define MSM_NAND_CMD_PAGE_READ_ALL 0x34
+#define MSM_NAND_CMD_PRG_PAGE 0x36
+#define MSM_NAND_CMD_PRG_PAGE_ECC 0x37
+#define MSM_NAND_CMD_PRG_PAGE_ALL 0x39
+#define MSM_NAND_CMD_BLOCK_ERASE 0x3A
+#define MSM_NAND_CMD_FETCH_ID 0x0B
+
+/* Structure that defines a NAND SPS command element */
+struct msm_nand_sps_cmd {
+ struct sps_command_element ce;
+ uint32_t flags;
+};
+
+/*
+ * Structure that defines the NAND controller properties as per the
+ * NAND flash device/chip that is attached.
+ */
+struct msm_nand_chip {
+ struct device *dev;
+ /*
+ * DMA memory will be allocated only once during probe and this memory
+ * will be used by all NAND clients. This wait queue is needed to
+ * make the applications wait for DMA memory to be free'd when the
+ * complete memory is exhausted.
+ */
+ wait_queue_head_t dma_wait_queue;
+ atomic_t dma_buffer_busy;
+ uint8_t *dma_virt_addr;
+ dma_addr_t dma_phys_addr;
+ uint32_t ecc_parity_bytes;
+ uint32_t bch_caps; /* Controller BCH ECC capabilities */
+#define MSM_NAND_CAP_4_BIT_BCH (1 << 0)
+#define MSM_NAND_CAP_8_BIT_BCH (1 << 1)
+ uint32_t cw_size;
+ /* NANDc register configurations */
+ uint32_t cfg0, cfg1, cfg0_raw, cfg1_raw;
+ uint32_t ecc_buf_cfg;
+ uint32_t ecc_bch_cfg;
+};
+
+/* Structure that defines an SPS end point for a NANDc BAM pipe. */
+struct msm_nand_sps_endpt {
+ struct sps_pipe *handle;
+ struct sps_connect config;
+ struct sps_register_event event;
+ struct completion completion;
+};
+
+/*
+ * Structure that defines NANDc SPS data - BAM handle and an end point
+ * for each BAM pipe.
+ */
+struct msm_nand_sps_info {
+ uint32_t bam_handle;
+ struct msm_nand_sps_endpt data_prod;
+ struct msm_nand_sps_endpt data_cons;
+ struct msm_nand_sps_endpt cmd_pipe;
+};
+
+/*
+ * Structure that contains flash device information. This gets updated after
+ * the NAND flash device detection.
+ */
+struct flash_identification {
+ uint32_t flash_id;
+ uint32_t density;
+ uint32_t widebus;
+ uint32_t pagesize;
+ uint32_t blksize;
+ uint32_t oobsize;
+ uint32_t ecc_correctability;
+};
+
+/* Structure that defines NANDc private data. */
+struct msm_nand_info {
+ struct mtd_info mtd;
+ struct msm_nand_chip nand_chip;
+ struct msm_nand_sps_info sps;
+ unsigned long bam_phys;
+ unsigned long nand_phys;
+ void __iomem *bam_base;
+ int bam_irq;
+ /*
+ * This lock must be acquired before submitting any command or data
+ * descriptors to BAM pipes and must be held until all the submitted
+ * descriptors are processed.
+ *
+ * This is required to ensure that both command and descriptors are
+ * submitted atomically without interruption from other clients,
+ * when there are requests from more than client at any time.
+ * Othewise, data and command descriptors can be submitted out of
+ * order for a request which can cause data corruption.
+ */
+ struct mutex bam_lock;
+ struct flash_identification flash_dev;
+};
+
+/* Structure that defines an ONFI parameter page (512B) */
+struct onfi_param_page {
+ uint32_t parameter_page_signature;
+ uint16_t revision_number;
+ uint16_t features_supported;
+ uint16_t optional_commands_supported;
+ uint8_t reserved0[22];
+ uint8_t device_manufacturer[12];
+ uint8_t device_model[20];
+ uint8_t jedec_manufacturer_id;
+ uint16_t date_code;
+ uint8_t reserved1[13];
+ uint32_t number_of_data_bytes_per_page;
+ uint16_t number_of_spare_bytes_per_page;
+ uint32_t number_of_data_bytes_per_partial_page;
+ uint16_t number_of_spare_bytes_per_partial_page;
+ uint32_t number_of_pages_per_block;
+ uint32_t number_of_blocks_per_logical_unit;
+ uint8_t number_of_logical_units;
+ uint8_t number_of_address_cycles;
+ uint8_t number_of_bits_per_cell;
+ uint16_t maximum_bad_blocks_per_logical_unit;
+ uint16_t block_endurance;
+ uint8_t guaranteed_valid_begin_blocks;
+ uint16_t guaranteed_valid_begin_blocks_endurance;
+ uint8_t number_of_programs_per_page;
+ uint8_t partial_program_attributes;
+ uint8_t number_of_bits_ecc_correctability;
+ uint8_t number_of_interleaved_address_bits;
+ uint8_t interleaved_operation_attributes;
+ uint8_t reserved2[13];
+ uint8_t io_pin_capacitance;
+ uint16_t timing_mode_support;
+ uint16_t program_cache_timing_mode_support;
+ uint16_t maximum_page_programming_time;
+ uint16_t maximum_block_erase_time;
+ uint16_t maximum_page_read_time;
+ uint16_t maximum_change_column_setup_time;
+ uint8_t reserved3[23];
+ uint16_t vendor_specific_revision_number;
+ uint8_t vendor_specific[88];
+ uint16_t integrity_crc;
+} __attribute__((__packed__));
+
+/*
+ * Get the DMA memory for requested amount of size. It returns the pointer
+ * to free memory available from the allocated pool. Returns NULL if there
+ * is no free memory.
+ */
+static void *msm_nand_get_dma_buffer(struct msm_nand_chip *chip, size_t size)
+{
+ uint32_t bitmask, free_bitmask, old_bitmask;
+ uint32_t need_mask, current_need_mask;
+ int free_index;
+
+ need_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOT_SZ))
+ - 1;
+ bitmask = atomic_read(&chip->dma_buffer_busy);
+ free_bitmask = ~bitmask;
+ do {
+ free_index = __ffs(free_bitmask);
+ current_need_mask = need_mask << free_index;
+
+ if (size + free_index * MSM_NAND_DMA_BUFFER_SLOT_SZ >=
+ MSM_NAND_DMA_BUFFER_SIZE)
+ return NULL;
+
+ if ((bitmask & current_need_mask) == 0) {
+ old_bitmask =
+ atomic_cmpxchg(&chip->dma_buffer_busy,
+ bitmask,
+ bitmask | current_need_mask);
+ if (old_bitmask == bitmask)
+ return chip->dma_virt_addr +
+ free_index * MSM_NAND_DMA_BUFFER_SLOT_SZ;
+ free_bitmask = 0;/* force return */
+ }
+ /* current free range was too small, clear all free bits */
+ /* below the top busy bit within current_need_mask */
+ free_bitmask &=
+ ~(~0U >> (32 - fls(bitmask & current_need_mask)));
+ } while (free_bitmask);
+
+ return NULL;
+}
+
+/*
+ * Releases the DMA memory used to the free pool and also wakes up any user
+ * thread waiting on wait queue for free memory to be available.
+ */
+static void msm_nand_release_dma_buffer(struct msm_nand_chip *chip,
+ void *buffer, size_t size)
+{
+ int index;
+ uint32_t used_mask;
+
+ used_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOT_SZ))
+ - 1;
+ index = ((uint8_t *)buffer - chip->dma_virt_addr) /
+ MSM_NAND_DMA_BUFFER_SLOT_SZ;
+ atomic_sub(used_mask << index, &chip->dma_buffer_busy);
+
+ wake_up(&chip->dma_wait_queue);
+}
+
+/*
+ * Calculates page address of the buffer passed, offset of buffer within
+ * that page and then maps it for DMA by calling dma_map_page().
+ */
+static dma_addr_t msm_nand_dma_map(struct device *dev, void *addr, size_t size,
+ enum dma_data_direction dir)
+{
+ struct page *page;
+ unsigned long offset = (unsigned long)addr & ~PAGE_MASK;
+ if (virt_addr_valid(addr))
+ page = virt_to_page(addr);
+ else {
+ if (WARN_ON(size + offset > PAGE_SIZE))
+ return ~0;
+ page = vmalloc_to_page(addr);
+ }
+ return dma_map_page(dev, page, offset, size, dir);
+}
+
+/*
+ * Wrapper function to prepare a SPS command element with the data that is
+ * passed to this function.
+ *
+ * Since for any command element it is a must to have this flag
+ * SPS_IOVEC_FLAG_CMD, this function by default updates this flag for a
+ * command element that is passed and thus, the caller need not explicilty
+ * pass this flag. The other flags must be passed based on the need. If a
+ * command element doesn't have any other flag, then 0 can be passed to flags.
+ */
+static inline void msm_nand_prep_ce(struct msm_nand_sps_cmd *sps_cmd,
+ uint32_t addr, uint32_t command,
+ uint32_t data, uint32_t flags)
+{
+ struct sps_command_element *cmd = &sps_cmd->ce;
+
+ cmd->addr = addr;
+ cmd->command = (command & WRITE) ? (uint32_t) SPS_WRITE_COMMAND :
+ (uint32_t) SPS_READ_COMMAND;
+ cmd->data = data;
+ cmd->mask = 0xFFFFFFFF;
+ sps_cmd->flags = SPS_IOVEC_FLAG_CMD | flags;
+}
+
+/*
+ * Read a single NANDc register as mentioned by its parameter addr. The return
+ * value indicates whether read is successful or not. The register value read
+ * is stored in val.
+ */
+static int msm_nand_flash_rd_reg(struct msm_nand_info *info, uint32_t addr,
+ uint32_t *val)
+{
+ int ret = 0;
+ struct msm_nand_sps_cmd *cmd;
+ struct msm_nand_chip *chip = &info->nand_chip;
+ struct {
+ struct msm_nand_sps_cmd cmd;
+ uint32_t data;
+ } *dma_buffer;
+
+ wait_event(chip->dma_wait_queue, (dma_buffer = msm_nand_get_dma_buffer(
+ chip, sizeof(*dma_buffer))));
+ cmd = &dma_buffer->cmd;
+ msm_nand_prep_ce(cmd, addr, READ, msm_virt_to_dma(chip,
+ &dma_buffer->data), SPS_IOVEC_FLAG_INT);
+
+ ret = sps_transfer_one(info->sps.cmd_pipe.handle,
+ msm_virt_to_dma(chip, &cmd->ce),
+ sizeof(struct sps_command_element), NULL, cmd->flags);
+ if (ret) {
+ pr_err("failed to submit command %x ret %d\n", addr, ret);
+ goto out;
+ }
+ wait_for_completion_io(&info->sps.cmd_pipe.completion);
+ *val = dma_buffer->data;
+out:
+ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+ return ret;
+}
+
+/*
+ * Read the Flash ID from the Nand Flash Device. The return value < 0
+ * indicates failure. When successful, the Flash ID is stored in parameter
+ * read_id.
+ */
+static int msm_nand_flash_read_id(struct msm_nand_info *info,
+ bool read_onfi_signature,
+ uint32_t *read_id)
+{
+ int err = 0, i;
+ struct msm_nand_sps_cmd *cmd;
+ struct sps_iovec *iovec;
+ struct msm_nand_chip *chip = &info->nand_chip;
+ uint32_t total_cnt = 4;
+ /*
+ * The following 4 commands are required to read id -
+ * write commands - addr0, flash, exec
+ * read_commands - read_id
+ */
+ struct {
+ struct sps_transfer xfer;
+ struct sps_iovec cmd_iovec[total_cnt];
+ struct msm_nand_sps_cmd cmd[total_cnt];
+ uint32_t data[total_cnt];
+ } *dma_buffer;
+
+ wait_event(chip->dma_wait_queue, (dma_buffer = msm_nand_get_dma_buffer
+ (chip, sizeof(*dma_buffer))));
+ if (read_onfi_signature)
+ dma_buffer->data[0] = FLASH_READ_ONFI_SIGNATURE_ADDRESS;
+ else
+ dma_buffer->data[0] = FLASH_READ_DEVICE_ID_ADDRESS;
+
+ dma_buffer->data[1] = MSM_NAND_CMD_FETCH_ID;
+ dma_buffer->data[2] = 1;
+ dma_buffer->data[3] = 0xeeeeeeee;
+
+ cmd = dma_buffer->cmd;
+ msm_nand_prep_ce(cmd, MSM_NAND_ADDR0(info), WRITE,
+ dma_buffer->data[0], SPS_IOVEC_FLAG_LOCK);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_FLASH_CMD(info), WRITE,
+ dma_buffer->data[1], 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_EXEC_CMD(info), WRITE,
+ dma_buffer->data[2], SPS_IOVEC_FLAG_NWD);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_READ_ID(info), READ,
+ msm_virt_to_dma(chip, &dma_buffer->data[3]),
+ SPS_IOVEC_FLAG_UNLOCK | SPS_IOVEC_FLAG_INT);
+ cmd++;
+
+ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+ dma_buffer->xfer.iovec_count = (cmd - dma_buffer->cmd);
+ dma_buffer->xfer.iovec = dma_buffer->cmd_iovec;
+ dma_buffer->xfer.iovec_phys = msm_virt_to_dma(chip,
+ &dma_buffer->cmd_iovec);
+ iovec = dma_buffer->xfer.iovec;
+
+ for (i = 0; i < dma_buffer->xfer.iovec_count; i++) {
+ iovec->addr = msm_virt_to_dma(chip, &dma_buffer->cmd[i].ce);
+ iovec->size = sizeof(struct sps_command_element);
+ iovec->flags = dma_buffer->cmd[i].flags;
+ iovec++;
+ }
+
+ mutex_lock(&info->bam_lock);
+ err = sps_transfer(info->sps.cmd_pipe.handle, &dma_buffer->xfer);
+ if (err) {
+ pr_err("Failed to submit commands %d\n", err);
+ mutex_unlock(&info->bam_lock);
+ goto out;
+ }
+ wait_for_completion_io(&info->sps.cmd_pipe.completion);
+ mutex_unlock(&info->bam_lock);
+
+ pr_debug("Read ID register value 0x%x\n", dma_buffer->data[3]);
+ if (!read_onfi_signature)
+ pr_debug("nandid: %x maker %02x device %02x\n",
+ dma_buffer->data[3], dma_buffer->data[3] & 0xff,
+ (dma_buffer->data[3] >> 8) & 0xff);
+ *read_id = dma_buffer->data[3];
+out:
+ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+ return err;
+}
+
+/*
+ * Contains data for common configuration registers that must be programmed
+ * for every NANDc operation.
+ */
+struct msm_nand_common_cfgs {
+ uint32_t cmd;
+ uint32_t addr0;
+ uint32_t addr1;
+ uint32_t cfg0;
+ uint32_t cfg1;
+};
+
+/*
+ * Function to prepare SPS command elements to write into NANDc configuration
+ * registers as per the data defined in struct msm_nand_common_cfgs. This is
+ * required for the following NANDc operations - Erase, Bad Block checking
+ * and for reading ONFI parameter page.
+ */
+static void msm_nand_prep_cfg_cmd_desc(struct msm_nand_info *info,
+ struct msm_nand_common_cfgs data,
+ struct msm_nand_sps_cmd **curr_cmd)
+{
+ struct msm_nand_sps_cmd *cmd;
+
+ cmd = *curr_cmd;
+ msm_nand_prep_ce(cmd, MSM_NAND_FLASH_CMD(info), WRITE, data.cmd,
+ SPS_IOVEC_FLAG_LOCK);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_ADDR0(info), WRITE, data.addr0, 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_ADDR1(info), WRITE, data.addr1, 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_DEV0_CFG0(info), WRITE, data.cfg0, 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_DEV0_CFG1(info), WRITE, data.cfg1, 0);
+ cmd++;
+ *curr_cmd = cmd;
+}
+
+/*
+ * Function to check the CRC integrity check on ONFI parameter page read.
+ * For ONFI parameter page read, the controller ECC will be disabled. Hence,
+ * it is mandatory to manually compute CRC and check it against the value
+ * stored within ONFI page.
+ */
+static uint16_t msm_nand_flash_onfi_crc_check(uint8_t *buffer, uint16_t count)
+{
+ int i;
+ uint16_t result;
+
+ for (i = 0; i < count; i++)
+ buffer[i] = bitrev8(buffer[i]);
+
+ result = bitrev16(crc16(bitrev16(0x4f4e), buffer, count));
+
+ for (i = 0; i < count; i++)
+ buffer[i] = bitrev8(buffer[i]);
+
+ return result;
+}
+
+/*
+ * Structure that contains NANDc register data for commands required
+ * for reading ONFI paramter page.
+ */
+struct msm_nand_flash_onfi_data {
+ struct msm_nand_common_cfgs cfg;
+ uint32_t exec;
+ uint32_t devcmd1_orig;
+ uint32_t devcmdvld_orig;
+ uint32_t devcmd1_mod;
+ uint32_t devcmdvld_mod;
+ uint32_t ecc_bch_cfg;
+};
+
+/*
+ * Function to identify whether the attached NAND flash device is
+ * complaint to ONFI spec or not. If yes, then it reads the ONFI parameter
+ * page to get the device parameters.
+ */
+static int msm_nand_flash_onfi_probe(struct msm_nand_info *info)
+{
+ struct msm_nand_chip *chip = &info->nand_chip;
+ struct flash_identification *flash = &info->flash_dev;
+ uint32_t crc_chk_count = 0, page_address = 0;
+ int ret = 0, i;
+
+ /* SPS parameters */
+ struct msm_nand_sps_cmd *cmd, *curr_cmd;
+ struct sps_iovec *iovec;
+ uint32_t rdata;
+
+ /* ONFI Identifier/Parameter Page parameters */
+ uint8_t *onfi_param_info_buf = NULL;
+ dma_addr_t dma_addr_param_info = 0;
+ struct onfi_param_page *onfi_param_page_ptr;
+ struct msm_nand_flash_onfi_data data;
+ uint32_t onfi_signature;
+
+ /* SPS command/data descriptors */
+ uint32_t total_cnt = 13;
+ /*
+ * The following 13 commands are required to get onfi parameters -
+ * flash, addr0, addr1, cfg0, cfg1, dev0_ecc_cfg, cmd_vld, dev_cmd1,
+ * read_loc_0, exec, flash_status (read cmd), dev_cmd1, cmd_vld.
+ */
+ struct {
+ struct sps_transfer xfer;
+ struct sps_iovec cmd_iovec[total_cnt];
+ struct msm_nand_sps_cmd cmd[total_cnt];
+ uint32_t flash_status;
+ } *dma_buffer;
+
+ 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);
+
+ wait_event(chip->dma_wait_queue, (dma_buffer = msm_nand_get_dma_buffer
+ (chip, sizeof(*dma_buffer))));
+
+ ret = msm_nand_flash_read_id(info, 1, &onfi_signature);
+ if (ret < 0) {
+ pr_err("Failed to read ONFI signature\n");
+ goto free_dma;
+ }
+ if (onfi_signature != ONFI_PARAMETER_PAGE_SIGNATURE) {
+ pr_info("Found a non ONFI device\n");
+ ret = -EIO;
+ goto free_dma;
+ }
+
+ memset(&data, 0, sizeof(struct msm_nand_flash_onfi_data));
+ ret = msm_nand_flash_rd_reg(info, MSM_NAND_DEV_CMD1(info),
+ &data.devcmd1_orig);
+ if (ret < 0)
+ goto free_dma;
+ ret = msm_nand_flash_rd_reg(info, MSM_NAND_DEV_CMD_VLD(info),
+ &data.devcmdvld_orig);
+ if (ret < 0)
+ goto free_dma;
+
+ data.cfg.cmd = MSM_NAND_CMD_PAGE_READ_ALL;
+ data.exec = 1;
+ data.cfg.addr0 = (page_address << 16) |
+ FLASH_READ_ONFI_PARAMETERS_ADDRESS;
+ data.cfg.addr1 = (page_address >> 16) & 0xFF;
+ data.cfg.cfg0 = MSM_NAND_CFG0_RAW_ONFI_PARAM_INFO;
+ data.cfg.cfg1 = MSM_NAND_CFG1_RAW_ONFI_PARAM_INFO;
+ data.devcmd1_mod = (data.devcmd1_orig & 0xFFFFFF00) |
+ FLASH_READ_ONFI_PARAMETERS_COMMAND;
+ data.devcmdvld_mod = data.devcmdvld_orig & 0xFFFFFFFE;
+ data.ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
+ dma_buffer->flash_status = 0xeeeeeeee;
+
+ curr_cmd = cmd = dma_buffer->cmd;
+ msm_nand_prep_cfg_cmd_desc(info, data.cfg, &curr_cmd);
+
+ cmd = curr_cmd;
+ msm_nand_prep_ce(cmd, MSM_NAND_DEV0_ECC_CFG(info), WRITE,
+ data.ecc_bch_cfg, 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_DEV_CMD_VLD(info), WRITE,
+ data.devcmdvld_mod, 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_DEV_CMD1(info), WRITE,
+ data.devcmd1_mod, 0);
+ cmd++;
+
+ rdata = (0 << 0) | (ONFI_PARAM_INFO_LENGTH << 16) | (1 << 31);
+ msm_nand_prep_ce(cmd, MSM_NAND_READ_LOCATION_0(info), WRITE,
+ rdata, 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_EXEC_CMD(info), WRITE,
+ data.exec, SPS_IOVEC_FLAG_NWD);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_FLASH_STATUS(info), READ,
+ msm_virt_to_dma(chip, &dma_buffer->flash_status), 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_DEV_CMD1(info), WRITE,
+ data.devcmd1_orig, 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_DEV_CMD_VLD(info), WRITE,
+ data.devcmdvld_orig,
+ SPS_IOVEC_FLAG_UNLOCK | SPS_IOVEC_FLAG_INT);
+ cmd++;
+
+ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+ dma_buffer->xfer.iovec_count = (cmd - dma_buffer->cmd);
+ dma_buffer->xfer.iovec = dma_buffer->cmd_iovec;
+ dma_buffer->xfer.iovec_phys = msm_virt_to_dma(chip,
+ &dma_buffer->cmd_iovec);
+ iovec = dma_buffer->xfer.iovec;
+
+ for (i = 0; i < dma_buffer->xfer.iovec_count; i++) {
+ iovec->addr = msm_virt_to_dma(chip,
+ &dma_buffer->cmd[i].ce);
+ iovec->size = sizeof(struct sps_command_element);
+ iovec->flags = dma_buffer->cmd[i].flags;
+ iovec++;
+ }
+ mutex_lock(&info->bam_lock);
+ /* Submit data descriptor */
+ ret = sps_transfer_one(info->sps.data_prod.handle, dma_addr_param_info,
+ ONFI_PARAM_INFO_LENGTH, NULL, SPS_IOVEC_FLAG_INT);
+ if (ret) {
+ pr_err("Failed to submit data descriptors %d\n", ret);
+ mutex_unlock(&info->bam_lock);
+ goto free_dma;
+ }
+ /* Submit command descriptors */
+ ret = sps_transfer(info->sps.cmd_pipe.handle,
+ &dma_buffer->xfer);
+ if (ret) {
+ pr_err("Failed to submit commands %d\n", ret);
+ mutex_unlock(&info->bam_lock);
+ goto free_dma;
+ }
+ wait_for_completion_io(&info->sps.cmd_pipe.completion);
+ wait_for_completion_io(&info->sps.data_prod.completion);
+ mutex_unlock(&info->bam_lock);
+
+ /* Check for flash status errors */
+ if (dma_buffer->flash_status & (FS_OP_ERR | FS_MPU_ERR)) {
+ pr_err("MPU/OP err (0x%x) is set\n", dma_buffer->flash_status);
+ ret = -EIO;
+ goto free_dma;
+ }
+
+ for (crc_chk_count = 0; crc_chk_count < ONFI_PARAM_INFO_LENGTH
+ / ONFI_PARAM_PAGE_LENGTH; crc_chk_count++) {
+ onfi_param_page_ptr =
+ (struct onfi_param_page *)
+ (&(onfi_param_info_buf
+ [ONFI_PARAM_PAGE_LENGTH *
+ crc_chk_count]));
+ if (msm_nand_flash_onfi_crc_check(
+ (uint8_t *)onfi_param_page_ptr,
+ ONFI_PARAM_PAGE_LENGTH - 2) ==
+ onfi_param_page_ptr->integrity_crc) {
+ break;
+ }
+ }
+ if (crc_chk_count >= ONFI_PARAM_INFO_LENGTH
+ / ONFI_PARAM_PAGE_LENGTH) {
+ pr_err("CRC Check failed on param page\n");
+ ret = -EIO;
+ goto free_dma;
+ }
+ ret = msm_nand_flash_read_id(info, 0, &flash->flash_id);
+ if (ret < 0) {
+ pr_err("Failed to read flash ID\n");
+ goto free_dma;
+ }
+ flash->widebus = onfi_param_page_ptr->features_supported & 0x01;
+ flash->pagesize = onfi_param_page_ptr->number_of_data_bytes_per_page;
+ flash->blksize = onfi_param_page_ptr->number_of_pages_per_block *
+ flash->pagesize;
+ flash->oobsize = onfi_param_page_ptr->number_of_spare_bytes_per_page;
+ flash->density = onfi_param_page_ptr->number_of_blocks_per_logical_unit
+ * flash->blksize;
+ flash->ecc_correctability = onfi_param_page_ptr->
+ number_of_bits_ecc_correctability;
+
+ pr_info("Found an ONFI compliant device %s\n",
+ onfi_param_page_ptr->device_model);
+ /*
+ * Temporary hack for MT29F4G08ABC device.
+ * Since the device is not properly adhering
+ * to ONFi specification it is reporting
+ * as 16 bit device though it is 8 bit device!!!
+ */
+ if (!strncmp(onfi_param_page_ptr->device_model, "MT29F4G08ABC", 12))
+ flash->widebus = 0;
+free_dma:
+ 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);
+ return ret;
+}
+
+/*
+ * Structure that contains read/write parameters required for reading/writing
+ * from/to a page.
+ */
+struct msm_nand_rw_params {
+ uint32_t page;
+ uint32_t page_count;
+ uint32_t sectordatasize;
+ uint32_t sectoroobsize;
+ uint32_t cwperpage;
+ uint32_t oob_len_cmd;
+ uint32_t oob_len_data;
+ uint32_t start_sector;
+ uint32_t oob_col;
+ dma_addr_t data_dma_addr;
+ dma_addr_t oob_dma_addr;
+ dma_addr_t data_dma_addr_curr;
+ dma_addr_t oob_dma_addr_curr;
+ bool read;
+};
+
+/*
+ * Structure that contains NANDc register data required for reading/writing
+ * from/to a page.
+ */
+struct msm_nand_rw_reg_data {
+ uint32_t cmd;
+ uint32_t addr0;
+ uint32_t addr1;
+ uint32_t cfg0;
+ uint32_t cfg1;
+ uint32_t ecc_bch_cfg;
+ uint32_t exec;
+ uint32_t ecc_cfg;
+ uint32_t clrfstatus;
+ uint32_t clrrstatus;
+};
+
+/*
+ * Function that validates page read/write MTD parameters received from upper
+ * layers such as MTD/YAFFS2 and returns error for any unsupported operations
+ * by the driver. In case of success, it also maps the data and oob buffer
+ * received for DMA.
+ */
+static int msm_nand_validate_mtd_params(struct mtd_info *mtd, bool read,
+ loff_t offset,
+ struct mtd_oob_ops *ops,
+ struct msm_nand_rw_params *args)
+{
+ struct msm_nand_info *info = mtd->priv;
+ struct msm_nand_chip *chip = &info->nand_chip;
+ int err = 0;
+
+ pr_debug("========================================================\n");
+ pr_debug("offset 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x\n",
+ offset, ops->mode, ops->datbuf, ops->len);
+ pr_debug("oobbuf 0x%p ooblen 0x%x\n", ops->oobbuf, ops->ooblen);
+
+ if (ops->mode == MTD_OPS_PLACE_OOB) {
+ pr_err("MTD_OPS_PLACE_OOB is not supported\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (mtd->writesize == PAGE_SIZE_2K)
+ args->page = offset >> 11;
+
+ if (mtd->writesize == PAGE_SIZE_4K)
+ args->page = offset >> 12;
+
+ args->oob_len_cmd = ops->ooblen;
+ args->oob_len_data = ops->ooblen;
+ args->cwperpage = (mtd->writesize >> 9);
+ args->read = (read ? true : false);
+
+ if (offset & (mtd->writesize - 1)) {
+ pr_err("unsupported offset 0x%llx\n", offset);
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (!read && !ops->datbuf) {
+ pr_err("No data buffer provided for write!!\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (ops->mode == MTD_OPS_RAW) {
+ if (!ops->datbuf) {
+ pr_err("No data buffer provided for RAW mode\n");
+ err = -EINVAL;
+ goto out;
+ } else if ((ops->len % (mtd->writesize +
+ mtd->oobsize)) != 0) {
+ pr_err("unsupported data len %d for RAW mode\n",
+ ops->len);
+ err = -EINVAL;
+ goto out;
+ }
+ args->page_count = ops->len / (mtd->writesize + mtd->oobsize);
+
+ } else if (ops->mode == MTD_OPS_AUTO_OOB) {
+ if (ops->datbuf && (ops->len % mtd->writesize) != 0) {
+ /* when ops->datbuf is NULL, ops->len can be ooblen */
+ pr_err("unsupported data len %d for AUTO mode\n",
+ ops->len);
+ err = -EINVAL;
+ goto out;
+ }
+ if (read && ops->oobbuf && !ops->datbuf) {
+ args->start_sector = args->cwperpage - 1;
+ args->page_count = ops->ooblen / mtd->oobavail;
+ if ((args->page_count == 0) && (ops->ooblen))
+ args->page_count = 1;
+ } else if (ops->datbuf) {
+ args->page_count = ops->len / mtd->writesize;
+ }
+ }
+
+ if (ops->datbuf) {
+ args->data_dma_addr_curr = args->data_dma_addr =
+ msm_nand_dma_map(chip->dev, ops->datbuf, ops->len,
+ (read ? DMA_FROM_DEVICE : DMA_TO_DEVICE));
+ if (dma_mapping_error(chip->dev, args->data_dma_addr)) {
+ pr_err("dma mapping failed for 0x%p\n", ops->datbuf);
+ err = -EIO;
+ goto out;
+ }
+ }
+ if (ops->oobbuf) {
+ if (read)
+ memset(ops->oobbuf, 0xFF, ops->ooblen);
+ args->oob_dma_addr_curr = args->oob_dma_addr =
+ msm_nand_dma_map(chip->dev, ops->oobbuf, ops->ooblen,
+ (read ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE));
+ if (dma_mapping_error(chip->dev, args->oob_dma_addr)) {
+ pr_err("dma mapping failed for 0x%p\n", ops->oobbuf);
+ err = -EIO;
+ goto dma_map_oobbuf_failed;
+ }
+ }
+ goto out;
+dma_map_oobbuf_failed:
+ if (ops->datbuf)
+ dma_unmap_page(chip->dev, args->data_dma_addr, ops->len,
+ (read ? DMA_FROM_DEVICE : DMA_TO_DEVICE));
+out:
+ return err;
+}
+
+/*
+ * Function that updates NANDc register data (struct msm_nand_rw_reg_data)
+ * required for page read/write.
+ */
+static void msm_nand_update_rw_reg_data(struct msm_nand_chip *chip,
+ struct mtd_oob_ops *ops,
+ struct msm_nand_rw_params *args,
+ struct msm_nand_rw_reg_data *data)
+{
+ if (args->read) {
+ if (ops->mode != MTD_OPS_RAW) {
+ data->cmd = MSM_NAND_CMD_PAGE_READ_ECC;
+ data->cfg0 =
+ (chip->cfg0 & ~(7U << CW_PER_PAGE)) |
+ (((args->cwperpage-1) - args->start_sector)
+ << CW_PER_PAGE);
+ data->cfg1 = chip->cfg1;
+ data->ecc_bch_cfg = chip->ecc_bch_cfg;
+ } else {
+ data->cmd = MSM_NAND_CMD_PAGE_READ_ALL;
+ data->cfg0 = chip->cfg0_raw;
+ data->cfg1 = chip->cfg1_raw;
+ data->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
+ }
+
+ } else {
+ if (ops->mode != MTD_OPS_RAW) {
+ data->cfg0 = chip->cfg0;
+ data->cfg1 = chip->cfg1;
+ data->ecc_bch_cfg = chip->ecc_bch_cfg;
+ } else {
+ data->cfg0 = chip->cfg0_raw;
+ data->cfg1 = chip->cfg1_raw;
+ data->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
+ }
+ data->cmd = MSM_NAND_CMD_PRG_PAGE;
+ data->clrfstatus = MSM_NAND_RESET_FLASH_STS;
+ data->clrrstatus = MSM_NAND_RESET_READ_STS;
+ }
+ data->exec = 1;
+ data->ecc_cfg = chip->ecc_buf_cfg;
+}
+
+/*
+ * Function to prepare series of SPS command descriptors required for a page
+ * read/write operation.
+ */
+static void msm_nand_prep_rw_cmd_desc(struct mtd_oob_ops *ops,
+ struct msm_nand_rw_params *args,
+ struct msm_nand_rw_reg_data *data,
+ struct msm_nand_info *info,
+ uint32_t curr_cw,
+ struct msm_nand_sps_cmd **curr_cmd)
+{
+ struct msm_nand_chip *chip = &info->nand_chip;
+ struct msm_nand_sps_cmd *cmd;
+ uint32_t rdata;
+ /* read_location register parameters */
+ uint32_t offset, size, last_read;
+
+ cmd = *curr_cmd;
+ msm_nand_prep_ce(cmd, MSM_NAND_FLASH_CMD(info), WRITE, data->cmd,
+ ((curr_cw == args->start_sector) ?
+ SPS_IOVEC_FLAG_LOCK : 0));
+ cmd++;
+
+ if (curr_cw == args->start_sector) {
+ msm_nand_prep_ce(cmd, MSM_NAND_ADDR0(info), WRITE,
+ data->addr0, 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_ADDR1(info), WRITE,
+ data->addr1, 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_DEV0_CFG0(info), WRITE,
+ data->cfg0, 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_DEV0_CFG1(info), WRITE,
+ data->cfg1, 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_DEV0_ECC_CFG(info), WRITE,
+ data->ecc_bch_cfg, 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_EBI2_ECC_BUF_CFG(info),
+ WRITE, data->ecc_cfg, 0);
+ cmd++;
+ }
+
+ if (!args->read)
+ goto sub_exec_cmd;
+
+ if (ops->mode == MTD_OPS_RAW) {
+ rdata = (0 << 0) | (chip->cw_size << 16) | (1 << 31);
+ msm_nand_prep_ce(cmd, MSM_NAND_READ_LOCATION_0(info), WRITE,
+ rdata, 0);
+ cmd++;
+ }
+ if (ops->mode == MTD_OPS_AUTO_OOB && ops->datbuf) {
+ offset = 0;
+ size = (curr_cw < (args->cwperpage - 1)) ? 516 :
+ (512 - ((args->cwperpage - 1) << 2));
+ last_read = (curr_cw < (args->cwperpage - 1)) ? 1 :
+ (ops->oobbuf ? 0 : 1);
+ rdata = (offset << 0) | (size << 16) | (last_read << 31);
+ msm_nand_prep_ce(cmd, MSM_NAND_READ_LOCATION_0(info), WRITE,
+ rdata, 0);
+ cmd++;
+ }
+ if (ops->mode == MTD_OPS_AUTO_OOB && ops->oobbuf
+ && (curr_cw == (args->cwperpage - 1))) {
+ offset = 512 - ((args->cwperpage - 1) << 2);
+ size = (args->cwperpage) << 2;
+ if (size > args->oob_len_cmd)
+ size = args->oob_len_cmd;
+ args->oob_len_cmd -= size;
+ last_read = 1;
+ rdata = (offset << 0) | (size << 16) | (last_read << 31);
+ if (ops->datbuf) {
+ msm_nand_prep_ce(cmd, MSM_NAND_READ_LOCATION_1(info),
+ WRITE, rdata, 0);
+ } else {
+ msm_nand_prep_ce(cmd, MSM_NAND_READ_LOCATION_0(info),
+ WRITE, rdata, 0);
+ }
+ cmd++;
+ }
+sub_exec_cmd:
+ msm_nand_prep_ce(cmd, MSM_NAND_EXEC_CMD(info), WRITE, data->exec,
+ SPS_IOVEC_FLAG_NWD);
+ cmd++;
+ *curr_cmd = cmd;
+}
+
+/*
+ * Function to prepare and submit SPS data descriptors required for a page
+ * read/write operation.
+ */
+static int msm_nand_submit_rw_data_desc(struct mtd_oob_ops *ops,
+ struct msm_nand_rw_params *args,
+ struct msm_nand_info *info,
+ uint32_t curr_cw)
+{
+ struct msm_nand_chip *chip = &info->nand_chip;
+ struct sps_pipe *data_pipe_handle;
+ uint32_t sectordatasize, sectoroobsize;
+ uint32_t sps_flags = 0;
+ int err = 0;
+
+ if (args->read)
+ data_pipe_handle = info->sps.data_prod.handle;
+ else
+ data_pipe_handle = info->sps.data_cons.handle;
+
+ if (ops->mode == MTD_OPS_RAW) {
+ sectordatasize = chip->cw_size;
+ if (!args->read)
+ sps_flags = SPS_IOVEC_FLAG_EOT;
+ if (curr_cw == (args->cwperpage - 1))
+ sps_flags |= SPS_IOVEC_FLAG_INT;
+
+ err = sps_transfer_one(data_pipe_handle,
+ args->data_dma_addr_curr,
+ sectordatasize, NULL,
+ sps_flags);
+ if (err)
+ goto out;
+ args->data_dma_addr_curr += sectordatasize;
+
+ } else if (ops->mode == MTD_OPS_AUTO_OOB) {
+ if (ops->datbuf) {
+ sectordatasize = (curr_cw < (args->cwperpage - 1))
+ ? 516 : (512 - ((args->cwperpage - 1) << 2));
+
+ if (!args->read) {
+ sps_flags = SPS_IOVEC_FLAG_EOT;
+ if (curr_cw == (args->cwperpage - 1) &&
+ ops->oobbuf)
+ sps_flags = 0;
+ }
+ if ((curr_cw == (args->cwperpage - 1)) && !ops->oobbuf)
+ sps_flags |= SPS_IOVEC_FLAG_INT;
+
+ err = sps_transfer_one(data_pipe_handle,
+ args->data_dma_addr_curr,
+ sectordatasize, NULL,
+ sps_flags);
+ if (err)
+ goto out;
+ args->data_dma_addr_curr += sectordatasize;
+ }
+
+ if (ops->oobbuf && (curr_cw == (args->cwperpage - 1))) {
+ sectoroobsize = args->cwperpage << 2;
+ if (sectoroobsize > args->oob_len_data)
+ sectoroobsize = args->oob_len_data;
+
+ if (!args->read)
+ sps_flags |= SPS_IOVEC_FLAG_EOT;
+ sps_flags |= SPS_IOVEC_FLAG_INT;
+ err = sps_transfer_one(data_pipe_handle,
+ args->oob_dma_addr_curr,
+ sectoroobsize, NULL,
+ sps_flags);
+ if (err)
+ goto out;
+ args->oob_dma_addr_curr += sectoroobsize;
+ args->oob_len_data -= sectoroobsize;
+ }
+ }
+out:
+ return err;
+}
+
+/*
+ * Function that gets called from upper layers such as MTD/YAFFS2 to read a
+ * page with main or/and spare data.
+ */
+static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct msm_nand_info *info = mtd->priv;
+ struct msm_nand_chip *chip = &info->nand_chip;
+ uint32_t cwperpage = (mtd->writesize >> 9);
+ int err, pageerr = 0, rawerr = 0;
+ uint32_t n = 0, pages_read = 0;
+ uint32_t ecc_errors = 0, total_ecc_errors = 0;
+ struct msm_nand_rw_params rw_params;
+ struct msm_nand_rw_reg_data data;
+ struct msm_nand_sps_cmd *cmd, *curr_cmd;
+ struct sps_iovec *iovec;
+ /*
+ * The following 6 commands will be sent only once for the first
+ * codeword (CW) - addr0, addr1, dev0_cfg0, dev0_cfg1,
+ * dev0_ecc_cfg, ebi2_ecc_buf_cfg. The following 6 commands will
+ * be sent for every CW - flash, read_location_0, read_location_1,
+ * exec, flash_status and buffer_status.
+ */
+ uint32_t total_cnt = (6 * cwperpage) + 6;
+ struct {
+ struct sps_transfer xfer;
+ struct sps_iovec cmd_iovec[total_cnt];
+ struct msm_nand_sps_cmd cmd[total_cnt];
+ struct {
+ uint32_t flash_status;
+ uint32_t buffer_status;
+ } result[cwperpage];
+ } *dma_buffer;
+
+ memset(&rw_params, 0, sizeof(struct msm_nand_rw_params));
+ err = msm_nand_validate_mtd_params(mtd, true, from, ops, &rw_params);
+ if (err)
+ goto validate_mtd_params_failed;
+
+ wait_event(chip->dma_wait_queue, (dma_buffer = msm_nand_get_dma_buffer(
+ chip, sizeof(*dma_buffer))));
+
+ rw_params.oob_col = rw_params.start_sector * chip->cw_size;
+ if (chip->cfg1 & (1 << WIDE_FLASH))
+ rw_params.oob_col >>= 1;
+
+ memset(&data, 0, sizeof(struct msm_nand_rw_reg_data));
+ msm_nand_update_rw_reg_data(chip, ops, &rw_params, &data);
+
+ while (rw_params.page_count-- > 0) {
+ data.addr0 = (rw_params.page << 16) | rw_params.oob_col;
+ data.addr1 = (rw_params.page >> 16) & 0xff;
+ cmd = dma_buffer->cmd;
+ for (n = rw_params.start_sector; n < cwperpage; n++) {
+ dma_buffer->result[n].flash_status = 0xeeeeeeee;
+ dma_buffer->result[n].buffer_status = 0xeeeeeeee;
+
+ curr_cmd = cmd;
+ msm_nand_prep_rw_cmd_desc(ops, &rw_params,
+ &data, info, n, &curr_cmd);
+
+ cmd = curr_cmd;
+ msm_nand_prep_ce(cmd, MSM_NAND_FLASH_STATUS(info),
+ READ, msm_virt_to_dma(chip,
+ &dma_buffer->result[n].flash_status), 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_BUFFER_STATUS(info),
+ READ, msm_virt_to_dma(chip,
+ &dma_buffer->result[n].buffer_status),
+ ((n == (cwperpage - 1)) ?
+ (SPS_IOVEC_FLAG_UNLOCK | SPS_IOVEC_FLAG_INT) :
+ 0));
+ cmd++;
+ }
+
+ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+ dma_buffer->xfer.iovec_count = (cmd - dma_buffer->cmd);
+ dma_buffer->xfer.iovec = dma_buffer->cmd_iovec;
+ dma_buffer->xfer.iovec_phys = msm_virt_to_dma(chip,
+ &dma_buffer->cmd_iovec);
+ iovec = dma_buffer->xfer.iovec;
+
+ for (n = 0; n < dma_buffer->xfer.iovec_count; n++) {
+ iovec->addr = msm_virt_to_dma(chip,
+ &dma_buffer->cmd[n].ce);
+ iovec->size = sizeof(struct sps_command_element);
+ iovec->flags = dma_buffer->cmd[n].flags;
+ iovec++;
+ }
+ mutex_lock(&info->bam_lock);
+ /* Submit data descriptors */
+ for (n = rw_params.start_sector; n < cwperpage; n++) {
+ err = msm_nand_submit_rw_data_desc(ops,
+ &rw_params, info, n);
+ if (err) {
+ pr_err("Failed to submit data descs %d\n", err);
+ mutex_unlock(&info->bam_lock);
+ goto free_dma;
+ }
+ }
+ /* Submit command descriptors */
+ err = sps_transfer(info->sps.cmd_pipe.handle,
+ &dma_buffer->xfer);
+ if (err) {
+ pr_err("Failed to submit commands %d\n", err);
+ mutex_unlock(&info->bam_lock);
+ goto free_dma;
+ }
+ wait_for_completion_io(&info->sps.cmd_pipe.completion);
+ wait_for_completion_io(&info->sps.data_prod.completion);
+ mutex_unlock(&info->bam_lock);
+ /* Check for flash status errors */
+ pageerr = rawerr = 0;
+ for (n = rw_params.start_sector; n < cwperpage; n++) {
+ if (dma_buffer->result[n].flash_status & (FS_OP_ERR |
+ FS_MPU_ERR)) {
+ rawerr = -EIO;
+ break;
+ }
+ }
+ /* Check for ECC correction on empty block */
+ if (rawerr && ops->datbuf && ops->mode != MTD_OPS_RAW) {
+ uint8_t *datbuf = ops->datbuf +
+ pages_read * mtd->writesize;
+
+ dma_sync_single_for_cpu(chip->dev,
+ rw_params.data_dma_addr_curr - mtd->writesize,
+ mtd->writesize, DMA_BIDIRECTIONAL);
+
+ for (n = 0; n < mtd->writesize; n++) {
+ /* TODO: check offset for 4bit BCHECC */
+ if ((n % 516 == 3 || n % 516 == 175)
+ && datbuf[n] == 0x54)
+ datbuf[n] = 0xff;
+ if (datbuf[n] != 0xff) {
+ pageerr = rawerr;
+ break;
+ }
+ }
+
+ dma_sync_single_for_device(chip->dev,
+ rw_params.data_dma_addr_curr - mtd->writesize,
+ mtd->writesize, DMA_BIDIRECTIONAL);
+ }
+ if (rawerr && ops->oobbuf) {
+ dma_sync_single_for_cpu(chip->dev,
+ rw_params.oob_dma_addr_curr - (ops->ooblen -
+ rw_params.oob_len_data),
+ ops->ooblen - rw_params.oob_len_data,
+ DMA_BIDIRECTIONAL);
+
+ for (n = 0; n < ops->ooblen; n++) {
+ if (ops->oobbuf[n] != 0xff) {
+ pageerr = rawerr;
+ break;
+ }
+ }
+
+ dma_sync_single_for_device(chip->dev,
+ rw_params.oob_dma_addr_curr - (ops->ooblen -
+ rw_params.oob_len_data),
+ ops->ooblen - rw_params.oob_len_data,
+ DMA_BIDIRECTIONAL);
+ }
+ /* check for uncorrectable errors */
+ if (pageerr) {
+ for (n = rw_params.start_sector; n < cwperpage; n++) {
+ if (dma_buffer->result[n].buffer_status &
+ BS_UNCORRECTABLE_BIT) {
+ mtd->ecc_stats.failed++;
+ pageerr = -EBADMSG;
+ break;
+ }
+ }
+ }
+ /* check for correctable errors */
+ if (!rawerr) {
+ for (n = rw_params.start_sector; n < cwperpage; n++) {
+ ecc_errors =
+ dma_buffer->result[n].buffer_status
+ & BS_CORRECTABLE_ERR_MSK;
+ if (ecc_errors) {
+ total_ecc_errors += ecc_errors;
+ mtd->ecc_stats.corrected += ecc_errors;
+ /*
+ * For Micron devices it is observed
+ * that correctable errors upto 3 bits
+ * are very common.
+ */
+ if (ecc_errors > 3)
+ pageerr = -EUCLEAN;
+ }
+ }
+ }
+ if (pageerr && (pageerr != -EUCLEAN || err == 0))
+ err = pageerr;
+
+ if (rawerr && !pageerr) {
+ pr_debug("%llx %x %x empty page\n",
+ (loff_t)rw_params.page * mtd->writesize,
+ ops->len, ops->ooblen);
+ } else {
+ for (n = rw_params.start_sector; n < cwperpage; n++)
+ pr_debug("cw %d: flash_sts %x buffr_sts %x\n",
+ n, dma_buffer->result[n].flash_status,
+ dma_buffer->result[n].buffer_status);
+ }
+ if (err && err != -EUCLEAN && err != -EBADMSG)
+ goto free_dma;
+ pages_read++;
+ rw_params.page++;
+ }
+free_dma:
+ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+ if (ops->oobbuf)
+ dma_unmap_page(chip->dev, rw_params.oob_dma_addr,
+ ops->ooblen, DMA_FROM_DEVICE);
+ if (ops->datbuf)
+ dma_unmap_page(chip->dev, rw_params.data_dma_addr,
+ ops->len, DMA_BIDIRECTIONAL);
+validate_mtd_params_failed:
+ if (ops->mode != MTD_OPS_RAW)
+ ops->retlen = mtd->writesize * pages_read;
+ else
+ ops->retlen = (mtd->writesize + mtd->oobsize) * pages_read;
+ ops->oobretlen = ops->ooblen - rw_params.oob_len_data;
+ if (err)
+ pr_err("0x%llx datalen 0x%x ooblen %x err %d corrected %d\n",
+ from, ops->datbuf ? ops->len : 0, ops->ooblen, err,
+ total_ecc_errors);
+ pr_debug("ret %d, retlen %d oobretlen %d\n",
+ err, ops->retlen, ops->oobretlen);
+
+ pr_debug("========================================================\n");
+ return err;
+}
+
+/*
+ * Function that gets called from upper layers such as MTD/YAFFS2 to read a
+ * page with only main data.
+ */
+static int msm_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ int ret;
+ struct mtd_oob_ops ops;
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ops.len = len;
+ ops.retlen = 0;
+ ops.ooblen = 0;
+ ops.datbuf = buf;
+ ops.oobbuf = NULL;
+ ret = msm_nand_read_oob(mtd, from, &ops);
+ *retlen = ops.retlen;
+ return ret;
+}
+
+/*
+ * Function that gets called from upper layers such as MTD/YAFFS2 to write a
+ * page with both main and spare data.
+ */
+static int msm_nand_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ struct msm_nand_info *info = mtd->priv;
+ struct msm_nand_chip *chip = &info->nand_chip;
+ uint32_t cwperpage = (mtd->writesize >> 9);
+ uint32_t n, flash_sts, pages_written = 0;
+ int err = 0;
+ struct msm_nand_rw_params rw_params;
+ struct msm_nand_rw_reg_data data;
+ struct msm_nand_sps_cmd *cmd, *curr_cmd;
+ struct sps_iovec *iovec;
+ /*
+ * The following 7 commands will be sent only once :
+ * For first codeword (CW) - addr0, addr1, dev0_cfg0, dev0_cfg1,
+ * dev0_ecc_cfg, ebi2_ecc_buf_cfg.
+ * For last codeword (CW) - read_status(write)
+ *
+ * The following 4 commands will be sent for every CW :
+ * flash, exec, flash_status (read), flash_status (write).
+ */
+ uint32_t total_cnt = (4 * cwperpage) + 7;
+ struct {
+ struct sps_transfer xfer;
+ struct sps_iovec cmd_iovec[total_cnt];
+ struct msm_nand_sps_cmd cmd[total_cnt];
+ struct {
+ uint32_t flash_status[cwperpage];
+ } data;
+ } *dma_buffer;
+
+ memset(&rw_params, 0, sizeof(struct msm_nand_rw_params));
+ err = msm_nand_validate_mtd_params(mtd, false, to, ops, &rw_params);
+ if (err)
+ goto validate_mtd_params_failed;
+
+ wait_event(chip->dma_wait_queue, (dma_buffer =
+ msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer))));
+
+ memset(&data, 0, sizeof(struct msm_nand_rw_reg_data));
+ msm_nand_update_rw_reg_data(chip, ops, &rw_params, &data);
+
+ while (rw_params.page_count-- > 0) {
+ data.addr0 = (rw_params.page << 16);
+ data.addr1 = (rw_params.page >> 16) & 0xff;
+ cmd = dma_buffer->cmd;
+
+ for (n = 0; n < cwperpage ; n++) {
+ dma_buffer->data.flash_status[n] = 0xeeeeeeee;
+
+ curr_cmd = cmd;
+ msm_nand_prep_rw_cmd_desc(ops, &rw_params,
+ &data, info, n, &curr_cmd);
+
+ cmd = curr_cmd;
+ msm_nand_prep_ce(cmd, MSM_NAND_FLASH_STATUS(info),
+ READ, msm_virt_to_dma(chip,
+ &dma_buffer->data.flash_status[n]), 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_FLASH_STATUS(info),
+ WRITE, data.clrfstatus, 0);
+ cmd++;
+
+ if (n == (cwperpage - 1)) {
+ msm_nand_prep_ce(cmd,
+ MSM_NAND_READ_STATUS(info), WRITE,
+ data.clrrstatus, SPS_IOVEC_FLAG_UNLOCK
+ | SPS_IOVEC_FLAG_INT);
+ cmd++;
+ }
+ }
+
+ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+ dma_buffer->xfer.iovec_count = (cmd - dma_buffer->cmd);
+ dma_buffer->xfer.iovec = dma_buffer->cmd_iovec;
+ dma_buffer->xfer.iovec_phys = msm_virt_to_dma(chip,
+ &dma_buffer->cmd_iovec);
+ iovec = dma_buffer->xfer.iovec;
+
+ for (n = 0; n < dma_buffer->xfer.iovec_count; n++) {
+ iovec->addr = msm_virt_to_dma(chip,
+ &dma_buffer->cmd[n].ce);
+ iovec->size = sizeof(struct sps_command_element);
+ iovec->flags = dma_buffer->cmd[n].flags;
+ iovec++;
+ }
+ mutex_lock(&info->bam_lock);
+ /* Submit data descriptors */
+ for (n = 0; n < cwperpage; n++) {
+ err = msm_nand_submit_rw_data_desc(ops,
+ &rw_params, info, n);
+ if (err) {
+ pr_err("Failed to submit data descs %d\n", err);
+ mutex_unlock(&info->bam_lock);
+ goto free_dma;
+ }
+ }
+ /* Submit command descriptors */
+ err = sps_transfer(info->sps.cmd_pipe.handle,
+ &dma_buffer->xfer);
+ if (err) {
+ pr_err("Failed to submit commands %d\n", err);
+ mutex_unlock(&info->bam_lock);
+ goto free_dma;
+ }
+ wait_for_completion_io(&info->sps.cmd_pipe.completion);
+ wait_for_completion_io(&info->sps.data_cons.completion);
+ mutex_unlock(&info->bam_lock);
+
+ for (n = 0; n < cwperpage; n++)
+ pr_debug("write pg %d: flash_status[%d] = %x\n",
+ rw_params.page, n,
+ dma_buffer->data.flash_status[n]);
+
+ /* Check for flash status errors */
+ for (n = 0; n < cwperpage; n++) {
+ flash_sts = dma_buffer->data.flash_status[n];
+ if (flash_sts & (FS_OP_ERR | FS_MPU_ERR)) {
+ pr_err("MPU/OP err (0x%x) set\n", flash_sts);
+ err = -EIO;
+ goto free_dma;
+ }
+ if (n == (cwperpage - 1)) {
+ if (!(flash_sts & FS_DEVICE_WP) ||
+ (flash_sts & FS_DEVICE_STS_ERR)) {
+ pr_err("Dev sts err 0x%x\n", flash_sts);
+ err = -EIO;
+ goto free_dma;
+ }
+ }
+ }
+ pages_written++;
+ rw_params.page++;
+ }
+free_dma:
+ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+ if (ops->oobbuf)
+ dma_unmap_page(chip->dev, rw_params.oob_dma_addr,
+ ops->ooblen, DMA_TO_DEVICE);
+ if (ops->datbuf)
+ dma_unmap_page(chip->dev, rw_params.data_dma_addr,
+ ops->len, DMA_TO_DEVICE);
+validate_mtd_params_failed:
+ if (ops->mode != MTD_OPS_RAW)
+ ops->retlen = mtd->writesize * pages_written;
+ else
+ ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written;
+
+ ops->oobretlen = ops->ooblen - rw_params.oob_len_data;
+ if (err)
+ pr_err("to %llx datalen %x ooblen %x failed with err %d\n",
+ to, ops->len, ops->ooblen, err);
+ pr_debug("ret %d, retlen %d oobretlen %d\n",
+ err, ops->retlen, ops->oobretlen);
+
+ pr_debug("================================================\n");
+ return err;
+}
+
+/*
+ * Function that gets called from upper layers such as MTD/YAFFS2 to write a
+ * page with only main data.
+ */
+static int msm_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ int ret;
+ struct mtd_oob_ops ops;
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ops.len = len;
+ ops.retlen = 0;
+ ops.ooblen = 0;
+ ops.datbuf = (uint8_t *)buf;
+ ops.oobbuf = NULL;
+ ret = msm_nand_write_oob(mtd, to, &ops);
+ *retlen = ops.retlen;
+ return ret;
+}
+
+/*
+ * Structure that contains NANDc register data for commands required
+ * for Erase operation.
+ */
+struct msm_nand_erase_reg_data {
+ struct msm_nand_common_cfgs cfg;
+ uint32_t exec;
+ uint32_t flash_status;
+ uint32_t clrfstatus;
+ uint32_t clrrstatus;
+};
+
+/*
+ * Function that gets called from upper layers such as MTD/YAFFS2 to erase a
+ * block within NAND device.
+ */
+static int msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ int i, err = 0;
+ struct msm_nand_info *info = mtd->priv;
+ struct msm_nand_chip *chip = &info->nand_chip;
+ uint32_t page = 0;
+ struct msm_nand_sps_cmd *cmd, *curr_cmd;
+ struct msm_nand_erase_reg_data data;
+ struct sps_iovec *iovec;
+ uint32_t total_cnt = 9;
+ /*
+ * The following 9 commands are required to erase a page -
+ * flash, addr0, addr1, cfg0, cfg1, exec, flash_status(read),
+ * flash_status(write), read_status.
+ */
+ struct {
+ struct sps_transfer xfer;
+ struct sps_iovec cmd_iovec[total_cnt];
+ struct msm_nand_sps_cmd cmd[total_cnt];
+ uint32_t flash_status;
+ } *dma_buffer;
+
+ if (mtd->writesize == PAGE_SIZE_2K)
+ page = instr->addr >> 11;
+
+ if (mtd->writesize == PAGE_SIZE_4K)
+ page = instr->addr >> 12;
+
+ if (instr->addr & (mtd->erasesize - 1)) {
+ pr_err("unsupported erase address, 0x%llx\n", instr->addr);
+ err = -EINVAL;
+ goto out;
+ }
+ if (instr->len != mtd->erasesize) {
+ pr_err("unsupported erase len, %lld\n", instr->len);
+ err = -EINVAL;
+ goto out;
+ }
+
+ wait_event(chip->dma_wait_queue, (dma_buffer = msm_nand_get_dma_buffer(
+ chip, sizeof(*dma_buffer))));
+ cmd = dma_buffer->cmd;
+
+ memset(&data, 0, sizeof(struct msm_nand_erase_reg_data));
+ data.cfg.cmd = MSM_NAND_CMD_BLOCK_ERASE;
+ data.cfg.addr0 = page;
+ data.cfg.addr1 = 0;
+ data.cfg.cfg0 = chip->cfg0 & (~(7 << CW_PER_PAGE));
+ data.cfg.cfg1 = chip->cfg1;
+ data.exec = 1;
+ dma_buffer->flash_status = 0xeeeeeeee;
+ data.clrfstatus = MSM_NAND_RESET_FLASH_STS;
+ data.clrrstatus = MSM_NAND_RESET_READ_STS;
+
+ curr_cmd = cmd;
+ msm_nand_prep_cfg_cmd_desc(info, data.cfg, &curr_cmd);
+
+ cmd = curr_cmd;
+ msm_nand_prep_ce(cmd, MSM_NAND_EXEC_CMD(info), WRITE, data.exec,
+ SPS_IOVEC_FLAG_NWD);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_FLASH_STATUS(info), READ,
+ msm_virt_to_dma(chip, &dma_buffer->flash_status), 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_FLASH_STATUS(info), WRITE,
+ data.clrfstatus, 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_READ_STATUS(info), WRITE,
+ data.clrrstatus,
+ SPS_IOVEC_FLAG_UNLOCK | SPS_IOVEC_FLAG_INT);
+ cmd++;
+
+ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+ dma_buffer->xfer.iovec_count = (cmd - dma_buffer->cmd);
+ dma_buffer->xfer.iovec = dma_buffer->cmd_iovec;
+ dma_buffer->xfer.iovec_phys = msm_virt_to_dma(chip,
+ &dma_buffer->cmd_iovec);
+ iovec = dma_buffer->xfer.iovec;
+
+ for (i = 0; i < dma_buffer->xfer.iovec_count; i++) {
+ iovec->addr = msm_virt_to_dma(chip, &dma_buffer->cmd[i].ce);
+ iovec->size = sizeof(struct sps_command_element);
+ iovec->flags = dma_buffer->cmd[i].flags;
+ iovec++;
+ }
+ mutex_lock(&info->bam_lock);
+ err = sps_transfer(info->sps.cmd_pipe.handle, &dma_buffer->xfer);
+ if (err) {
+ pr_err("Failed to submit commands %d\n", err);
+ mutex_unlock(&info->bam_lock);
+ goto free_dma;
+ }
+ wait_for_completion_io(&info->sps.cmd_pipe.completion);
+ mutex_unlock(&info->bam_lock);
+
+ /* Check for flash status errors */
+ if (dma_buffer->flash_status & (FS_OP_ERR |
+ FS_MPU_ERR | FS_DEVICE_STS_ERR)) {
+ pr_err("MPU/OP/DEV err (0x%x) set\n", dma_buffer->flash_status);
+ err = -EIO;
+ }
+ if (!(dma_buffer->flash_status & FS_DEVICE_WP)) {
+ pr_err("Device is write protected\n");
+ err = -EIO;
+ }
+ if (err) {
+ pr_err("Erase failed, 0x%llx\n", instr->addr);
+ instr->fail_addr = instr->addr;
+ instr->state = MTD_ERASE_FAILED;
+ } else {
+ instr->state = MTD_ERASE_DONE;
+ instr->fail_addr = 0xffffffff;
+ mtd_erase_callback(instr);
+ }
+free_dma:
+ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+out:
+ return err;
+}
+
+/*
+ * Structure that contains NANDc register data for commands required
+ * for checking if a block is bad.
+ */
+struct msm_nand_blk_isbad_data {
+ struct msm_nand_common_cfgs cfg;
+ uint32_t ecc_bch_cfg;
+ uint32_t exec;
+ uint32_t read_offset;
+};
+
+/*
+ * Function that gets called from upper layers such as MTD/YAFFS2 to check if
+ * a block is bad. This is done by reading the first page within a block and
+ * checking whether the bad block byte location contains 0xFF or not. If it
+ * doesn't contain 0xFF, then it is considered as bad block.
+ */
+static int msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct msm_nand_info *info = mtd->priv;
+ struct msm_nand_chip *chip = &info->nand_chip;
+ int i, ret = 0, bad_block = 0;
+ uint8_t *buf;
+ uint32_t page = 0, rdata, cwperpage;
+ struct msm_nand_sps_cmd *cmd, *curr_cmd;
+ struct msm_nand_blk_isbad_data data;
+ struct sps_iovec *iovec;
+ uint32_t total_cnt = 9;
+ /*
+ * The following 9 commands are required to check bad block -
+ * flash, addr0, addr1, cfg0, cfg1, ecc_cfg, read_loc_0,
+ * exec, flash_status(read).
+ */
+ struct {
+ struct sps_transfer xfer;
+ struct sps_iovec cmd_iovec[total_cnt];
+ struct msm_nand_sps_cmd cmd[total_cnt];
+ uint32_t flash_status;
+ } *dma_buffer;
+
+ if (mtd->writesize == PAGE_SIZE_2K)
+ page = ofs >> 11;
+
+ if (mtd->writesize == PAGE_SIZE_4K)
+ page = ofs >> 12;
+
+ cwperpage = (mtd->writesize >> 9);
+
+ if (ofs > mtd->size) {
+ pr_err("Invalid offset 0x%llx\n", ofs);
+ bad_block = -EINVAL;
+ goto out;
+ }
+ if (ofs & (mtd->erasesize - 1)) {
+ pr_err("unsupported block address, 0x%x\n", (uint32_t)ofs);
+ bad_block = -EINVAL;
+ goto out;
+ }
+
+ wait_event(chip->dma_wait_queue, (dma_buffer = msm_nand_get_dma_buffer(
+ chip , sizeof(*dma_buffer) + 4)));
+ buf = (uint8_t *)dma_buffer + sizeof(*dma_buffer);
+
+ cmd = dma_buffer->cmd;
+ memset(&data, 0, sizeof(struct msm_nand_erase_reg_data));
+ data.cfg.cmd = MSM_NAND_CMD_PAGE_READ_ALL;
+ data.cfg.cfg0 = chip->cfg0_raw & ~(7U << CW_PER_PAGE);
+ data.cfg.cfg1 = chip->cfg1_raw;
+
+ if (chip->cfg1 & (1 << WIDE_FLASH))
+ data.cfg.addr0 = (page << 16) |
+ ((chip->cw_size * (cwperpage-1)) >> 1);
+ else
+ data.cfg.addr0 = (page << 16) |
+ (chip->cw_size * (cwperpage-1));
+
+ data.cfg.addr1 = (page >> 16) & 0xff;
+ data.ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
+ data.exec = 1;
+ data.read_offset = (mtd->writesize - (chip->cw_size * (cwperpage-1)));
+ dma_buffer->flash_status = 0xeeeeeeee;
+
+ curr_cmd = cmd;
+ msm_nand_prep_cfg_cmd_desc(info, data.cfg, &curr_cmd);
+
+ cmd = curr_cmd;
+ msm_nand_prep_ce(cmd, MSM_NAND_DEV0_ECC_CFG(info), WRITE,
+ data.ecc_bch_cfg, 0);
+ cmd++;
+
+ rdata = (data.read_offset << 0) | (4 << 16) | (1 << 31);
+ msm_nand_prep_ce(cmd, MSM_NAND_READ_LOCATION_0(info), WRITE, rdata, 0);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_EXEC_CMD(info), WRITE,
+ data.exec, SPS_IOVEC_FLAG_NWD);
+ cmd++;
+
+ msm_nand_prep_ce(cmd, MSM_NAND_FLASH_STATUS(info), READ,
+ msm_virt_to_dma(chip, &dma_buffer->flash_status),
+ SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_UNLOCK);
+ cmd++;
+
+ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+ dma_buffer->xfer.iovec_count = (cmd - dma_buffer->cmd);
+ dma_buffer->xfer.iovec = dma_buffer->cmd_iovec;
+ dma_buffer->xfer.iovec_phys = msm_virt_to_dma(chip,
+ &dma_buffer->cmd_iovec);
+ iovec = dma_buffer->xfer.iovec;
+
+ for (i = 0; i < dma_buffer->xfer.iovec_count; i++) {
+ iovec->addr = msm_virt_to_dma(chip, &dma_buffer->cmd[i].ce);
+ iovec->size = sizeof(struct sps_command_element);
+ iovec->flags = dma_buffer->cmd[i].flags;
+ iovec++;
+ }
+ mutex_lock(&info->bam_lock);
+ /* Submit data descriptor */
+ ret = sps_transfer_one(info->sps.data_prod.handle,
+ msm_virt_to_dma(chip, buf),
+ 4, NULL, SPS_IOVEC_FLAG_INT);
+
+ if (ret) {
+ pr_err("Failed to submit data desc %d\n", ret);
+ mutex_unlock(&info->bam_lock);
+ goto free_dma;
+ }
+ /* Submit command descriptor */
+ ret = sps_transfer(info->sps.cmd_pipe.handle, &dma_buffer->xfer);
+ if (ret) {
+ pr_err("Failed to submit commands %d\n", ret);
+ mutex_unlock(&info->bam_lock);
+ goto free_dma;
+ }
+ wait_for_completion_io(&info->sps.cmd_pipe.completion);
+ wait_for_completion_io(&info->sps.data_prod.completion);
+ mutex_unlock(&info->bam_lock);
+
+ /* Check for flash status errors */
+ if (dma_buffer->flash_status & (FS_OP_ERR | FS_MPU_ERR)) {
+ pr_err("MPU/OP err set: %x\n", dma_buffer->flash_status);
+ bad_block = -EIO;
+ goto free_dma;
+ }
+
+ /* Check for bad block marker byte */
+ if (chip->cfg1 & (1 << WIDE_FLASH)) {
+ if (buf[0] != 0xFF || buf[1] != 0xFF)
+ bad_block = 1;
+ } else {
+ if (buf[0] != 0xFF)
+ bad_block = 1;
+ }
+free_dma:
+ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer) + 4);
+out:
+ return ret ? ret : bad_block;
+}
+
+/*
+ * Function that gets called from upper layers such as MTD/YAFFS2 to mark a
+ * block as bad. This is done by writing the first page within a block with 0,
+ * thus setting the bad block byte location as well to 0.
+ */
+static int msm_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct mtd_oob_ops ops;
+ int ret;
+ uint8_t *buf;
+ size_t len;
+
+ if (ofs > mtd->size) {
+ pr_err("Invalid offset 0x%llx\n", ofs);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (ofs & (mtd->erasesize - 1)) {
+ pr_err("unsupported block address, 0x%x\n", (uint32_t)ofs);
+ ret = -EINVAL;
+ goto out;
+ }
+ len = mtd->writesize + mtd->oobsize;
+ buf = kzalloc(len, GFP_KERNEL);
+ if (!buf) {
+ pr_err("unable to allocate memory for 0x%x size\n", len);
+ ret = -ENOMEM;
+ goto out;
+ }
+ ops.mode = MTD_OPS_RAW;
+ ops.len = len;
+ ops.retlen = 0;
+ ops.ooblen = 0;
+ ops.datbuf = buf;
+ ops.oobbuf = NULL;
+ ret = msm_nand_write_oob(mtd, ofs, &ops);
+ kfree(buf);
+out:
+ return ret;
+}
+
+/*
+ * Function that scans for the attached NAND device. This fills out all
+ * the uninitialized function pointers with the defaults. The flash ID is
+ * read and the mtd/chip structures are filled with the appropriate values.
+ */
+int msm_nand_scan(struct mtd_info *mtd)
+{
+ struct msm_nand_info *info = mtd->priv;
+ struct msm_nand_chip *chip = &info->nand_chip;
+ struct flash_identification *supported_flash = &info->flash_dev;
+ int flash_id = 0, err = 0;
+ uint32_t i, mtd_writesize;
+ uint8_t dev_found = 0, wide_bus;
+ uint32_t manid, devid, devcfg;
+ uint32_t bad_block_byte;
+ struct nand_flash_dev *flashdev = NULL;
+ struct nand_manufacturers *flashman = NULL;
+
+ /* Probe the Flash device for ONFI compliance */
+ if (!msm_nand_flash_onfi_probe(info)) {
+ dev_found = 1;
+ } else {
+ err = msm_nand_flash_read_id(info, 0, &flash_id);
+ if (err < 0) {
+ pr_err("Failed to read Flash ID\n");
+ err = -EINVAL;
+ goto out;
+ }
+ manid = flash_id & 0xFF;
+ devid = (flash_id >> 8) & 0xFF;
+ devcfg = (flash_id >> 24) & 0xFF;
+
+ for (i = 0; !flashman && nand_manuf_ids[i].id; ++i)
+ if (nand_manuf_ids[i].id == manid)
+ flashman = &nand_manuf_ids[i];
+ for (i = 0; !flashdev && nand_flash_ids[i].id; ++i)
+ if (nand_flash_ids[i].id == devid)
+ flashdev = &nand_flash_ids[i];
+ if (!flashdev || !flashman) {
+ pr_err("unknown nand flashid=%x manuf=%x devid=%x\n",
+ flash_id, manid, devid);
+ err = -ENOENT;
+ goto out;
+ }
+ dev_found = 1;
+ if (!flashdev->pagesize) {
+ supported_flash->widebus = devcfg & (1 << 6) ? 1 : 0;
+ supported_flash->pagesize = 1024 << (devcfg & 0x3);
+ supported_flash->blksize = (64 * 1024) <<
+ ((devcfg >> 4) & 0x3);
+ supported_flash->oobsize = (8 << ((devcfg >> 2) & 1)) *
+ (supported_flash->pagesize >> 9);
+ } else {
+ supported_flash->widebus = flashdev->options &
+ NAND_BUSWIDTH_16 ? 1 : 0;
+ supported_flash->pagesize = flashdev->pagesize;
+ supported_flash->blksize = flashdev->erasesize;
+ supported_flash->oobsize = flashdev->pagesize >> 5;
+ }
+ supported_flash->flash_id = flash_id;
+ supported_flash->density = flashdev->chipsize << 20;
+ }
+
+ if (dev_found) {
+ wide_bus = supported_flash->widebus;
+ mtd->size = supported_flash->density;
+ mtd->writesize = supported_flash->pagesize;
+ mtd->oobsize = supported_flash->oobsize;
+ mtd->erasesize = supported_flash->blksize;
+ mtd_writesize = mtd->writesize;
+
+ /* Check whether NAND device support 8bit ECC*/
+ if (supported_flash->ecc_correctability >= 8)
+ chip->bch_caps = MSM_NAND_CAP_8_BIT_BCH;
+ else
+ chip->bch_caps = MSM_NAND_CAP_4_BIT_BCH;
+
+ pr_info("NAND Id: 0x%x Buswidth: %dBits Density: %lld MByte\n",
+ supported_flash->flash_id, (wide_bus) ? 16 : 8,
+ (mtd->size >> 20));
+ pr_info("pagesize: %d Erasesize: %d oobsize: %d (in Bytes)\n",
+ mtd->writesize, mtd->erasesize, mtd->oobsize);
+ pr_info("BCH ECC: %d Bit\n",
+ (chip->bch_caps & MSM_NAND_CAP_8_BIT_BCH ? 8 : 4));
+ }
+
+ chip->cw_size = (chip->bch_caps & MSM_NAND_CAP_8_BIT_BCH) ? 532 : 528;
+ chip->cfg0 = (((mtd_writesize >> 9) - 1) << CW_PER_PAGE)
+ | (516 << UD_SIZE_BYTES)
+ | (0 << DISABLE_STATUS_AFTER_WRITE)
+ | (5 << NUM_ADDR_CYCLES);
+
+ bad_block_byte = (mtd_writesize - (chip->cw_size * (
+ (mtd_writesize >> 9) - 1)) + 1);
+ chip->cfg1 = (7 << NAND_RECOVERY_CYCLES)
+ | (0 << CS_ACTIVE_BSY)
+ | (bad_block_byte << BAD_BLOCK_BYTE_NUM)
+ | (0 << BAD_BLOCK_IN_SPARE_AREA)
+ | (2 << WR_RD_BSY_GAP)
+ | ((wide_bus ? 1 : 0) << WIDE_FLASH)
+ | (1 << ENABLE_BCH_ECC);
+
+ chip->cfg0_raw = (((mtd_writesize >> 9) - 1) << CW_PER_PAGE)
+ | (5 << NUM_ADDR_CYCLES)
+ | (0 << SPARE_SIZE_BYTES)
+ | (chip->cw_size << UD_SIZE_BYTES);
+
+ chip->cfg1_raw = (7 << NAND_RECOVERY_CYCLES)
+ | (0 << CS_ACTIVE_BSY)
+ | (17 << BAD_BLOCK_BYTE_NUM)
+ | (1 << BAD_BLOCK_IN_SPARE_AREA)
+ | (2 << WR_RD_BSY_GAP)
+ | ((wide_bus ? 1 : 0) << WIDE_FLASH)
+ | (1 << DEV0_CFG1_ECC_DISABLE);
+
+ chip->ecc_bch_cfg = (0 << ECC_CFG_ECC_DISABLE)
+ | (0 << ECC_SW_RESET)
+ | (516 << ECC_NUM_DATA_BYTES)
+ | (1 << ECC_FORCE_CLK_OPEN);
+
+ if (chip->bch_caps & MSM_NAND_CAP_8_BIT_BCH) {
+ chip->cfg0 |= (wide_bus ? 0 << SPARE_SIZE_BYTES :
+ 2 << SPARE_SIZE_BYTES);
+ chip->ecc_bch_cfg |= (1 << ECC_MODE)
+ | ((wide_bus) ? (14 << ECC_PARITY_SIZE_BYTES) :
+ (13 << ECC_PARITY_SIZE_BYTES));
+ } else {
+ chip->cfg0 |= (wide_bus ? 2 << SPARE_SIZE_BYTES :
+ 4 << SPARE_SIZE_BYTES);
+ chip->ecc_bch_cfg |= (0 << ECC_MODE)
+ | ((wide_bus) ? (8 << ECC_PARITY_SIZE_BYTES) :
+ (7 << ECC_PARITY_SIZE_BYTES));
+ }
+
+ /*
+ * For 4bit BCH ECC (default ECC), parity bytes = 7(x8) or 8(x16 I/O)
+ * For 8bit BCH ECC, parity bytes = 13 (x8) or 14 (x16 I/O).
+ */
+ chip->ecc_parity_bytes = (chip->bch_caps & MSM_NAND_CAP_8_BIT_BCH) ?
+ (wide_bus ? 14 : 13) : (wide_bus ? 8 : 7);
+ chip->ecc_buf_cfg = 0x203; /* No of bytes covered by ECC - 516 bytes */
+
+ pr_info("CFG0: 0x%08x, CFG1: 0x%08x\n"
+ " RAWCFG0: 0x%08x, RAWCFG1: 0x%08x\n"
+ " ECCBUFCFG: 0x%08x, ECCBCHCFG: 0x%08x\n"
+ " BAD BLOCK BYTE: 0x%08x\n", chip->cfg0, chip->cfg1,
+ chip->cfg0_raw, chip->cfg1_raw, chip->ecc_buf_cfg,
+ chip->ecc_bch_cfg, bad_block_byte);
+
+ if (mtd->oobsize == 64) {
+ mtd->oobavail = 16;
+ } else if ((mtd->oobsize == 128) || (mtd->oobsize == 224)) {
+ mtd->oobavail = 32;
+ } else {
+ pr_err("Unsupported NAND oobsize: 0x%x\n", mtd->oobsize);
+ err = -ENODEV;
+ goto out;
+ }
+
+ /* Fill in remaining MTD driver data */
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
+ mtd->_erase = msm_nand_erase;
+ mtd->_block_isbad = msm_nand_block_isbad;
+ mtd->_block_markbad = msm_nand_block_markbad;
+ mtd->_read = msm_nand_read;
+ mtd->_write = msm_nand_write;
+ mtd->_read_oob = msm_nand_read_oob;
+ mtd->_write_oob = msm_nand_write_oob;
+ mtd->owner = THIS_MODULE;
+out:
+ return err;
+}
+
+#define BAM_APPS_PIPE_LOCK_GRP 0
+/*
+ * This function allocates, configures, connects an end point and
+ * also registers event notification for an end point. It also allocates
+ * DMA memory for descriptor FIFO of a pipe.
+ */
+static int msm_nand_init_endpoint(struct msm_nand_info *info,
+ struct msm_nand_sps_endpt *end_point,
+ uint32_t pipe_index)
+{
+ int rc = 0;
+ struct sps_pipe *pipe_handle;
+ struct sps_connect *sps_config = &end_point->config;
+ struct sps_register_event *sps_event = &end_point->event;
+
+ pipe_handle = sps_alloc_endpoint();
+ if (!pipe_handle) {
+ pr_err("sps_alloc_endpoint() failed\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = sps_get_config(pipe_handle, sps_config);
+ if (rc) {
+ pr_err("sps_get_config() failed %d\n", rc);
+ goto free_endpoint;
+ }
+
+ if (pipe_index == SPS_DATA_PROD_PIPE_INDEX) {
+ /* READ CASE: source - BAM; destination - system memory */
+ sps_config->source = info->sps.bam_handle;
+ sps_config->destination = SPS_DEV_HANDLE_MEM;
+ sps_config->mode = SPS_MODE_SRC;
+ sps_config->src_pipe_index = pipe_index;
+ } else if (pipe_index == SPS_DATA_CONS_PIPE_INDEX ||
+ pipe_index == SPS_CMD_CONS_PIPE_INDEX) {
+ /* WRITE CASE: source - system memory; destination - BAM */
+ sps_config->source = SPS_DEV_HANDLE_MEM;
+ sps_config->destination = info->sps.bam_handle;
+ sps_config->mode = SPS_MODE_DEST;
+ sps_config->dest_pipe_index = pipe_index;
+ }
+
+ sps_config->options = SPS_O_AUTO_ENABLE | SPS_O_DESC_DONE;
+ sps_config->lock_group = BAM_APPS_PIPE_LOCK_GRP;
+ /*
+ * Descriptor FIFO is a cyclic FIFO. If SPS_MAX_DESC_NUM descriptors
+ * are allowed to be submitted before we get any ack for any of them,
+ * the descriptor FIFO size should be: (SPS_MAX_DESC_NUM + 1) *
+ * sizeof(struct sps_iovec).
+ */
+ sps_config->desc.size = (SPS_MAX_DESC_NUM + 1) *
+ sizeof(struct sps_iovec);
+ sps_config->desc.base = dmam_alloc_coherent(info->nand_chip.dev,
+ sps_config->desc.size,
+ &sps_config->desc.phys_base,
+ GFP_KERNEL);
+ if (!sps_config->desc.base) {
+ pr_err("dmam_alloc_coherent() failed for size %x\n",
+ sps_config->desc.size);
+ rc = -ENOMEM;
+ goto free_endpoint;
+ }
+ memset(sps_config->desc.base, 0x00, sps_config->desc.size);
+
+ rc = sps_connect(pipe_handle, sps_config);
+ if (rc) {
+ pr_err("sps_connect() failed %d\n", rc);
+ goto free_endpoint;
+ }
+
+ init_completion(&end_point->completion);
+ sps_event->mode = SPS_TRIGGER_WAIT;
+ sps_event->options = SPS_O_DESC_DONE;
+ sps_event->xfer_done = &end_point->completion;
+ sps_event->user = (void *)info;
+
+ rc = sps_register_event(pipe_handle, sps_event);
+ if (rc) {
+ pr_err("sps_register_event() failed %d\n", rc);
+ goto sps_disconnect;
+ }
+ end_point->handle = pipe_handle;
+ pr_debug("pipe handle 0x%x for pipe %d\n", (uint32_t)pipe_handle,
+ pipe_index);
+ goto out;
+sps_disconnect:
+ sps_disconnect(pipe_handle);
+free_endpoint:
+ sps_free_endpoint(pipe_handle);
+out:
+ return rc;
+}
+
+/* This function disconnects and frees an end point */
+static void msm_nand_deinit_endpoint(struct msm_nand_info *info,
+ struct msm_nand_sps_endpt *end_point)
+{
+ sps_disconnect(end_point->handle);
+ sps_free_endpoint(end_point->handle);
+}
+
+/*
+ * This function registers BAM device and initializes its end points for
+ * the following pipes -
+ * system consumer pipe for data (pipe#0),
+ * system producer pipe for data (pipe#1),
+ * system consumer pipe for commands (pipe#2).
+ */
+static int msm_nand_bam_init(struct msm_nand_info *nand_info)
+{
+ struct sps_bam_props bam = {0};
+ int rc = 0;
+
+ bam.phys_addr = nand_info->bam_phys;
+ bam.virt_addr = nand_info->bam_base;
+ bam.irq = nand_info->bam_irq;
+ /*
+ * NAND device is accessible from both Apps and Modem processor and
+ * thus, NANDc and BAM are shared between both the processors. But BAM
+ * must be enabled and instantiated only once during boot up by
+ * Trustzone before Modem/Apps is brought out from reset.
+ *
+ * This is indicated to SPS driver on Apps by marking flag
+ * SPS_BAM_MGR_DEVICE_REMOTE. The following are the global
+ * initializations that will be done by Trustzone - Execution
+ * Environment, Pipes assignment to Apps/Modem, Pipe Super groups and
+ * Descriptor summing threshold.
+ *
+ * NANDc BAM device supports 2 execution environments - Modem and Apps
+ * and thus the flag SPS_BAM_MGR_MULTI_EE is set.
+ */
+ bam.manage = SPS_BAM_MGR_DEVICE_REMOTE | SPS_BAM_MGR_MULTI_EE;
+
+ rc = sps_register_bam_device(&bam, &nand_info->sps.bam_handle);
+ if (rc) {
+ pr_err("sps_register_bam_device() failed with %d\n", rc);
+ goto out;
+ }
+ pr_info("BAM device registered: bam_handle 0x%x\n",
+ nand_info->sps.bam_handle);
+
+ rc = msm_nand_init_endpoint(nand_info, &nand_info->sps.data_prod,
+ SPS_DATA_PROD_PIPE_INDEX);
+ if (rc)
+ goto unregister_bam;
+
+ rc = msm_nand_init_endpoint(nand_info, &nand_info->sps.data_cons,
+ SPS_DATA_CONS_PIPE_INDEX);
+ if (rc)
+ goto deinit_data_prod;
+
+ rc = msm_nand_init_endpoint(nand_info, &nand_info->sps.cmd_pipe,
+ SPS_CMD_CONS_PIPE_INDEX);
+ if (rc)
+ goto deinit_data_cons;
+ goto out;
+deinit_data_cons:
+ msm_nand_deinit_endpoint(nand_info, &nand_info->sps.data_cons);
+deinit_data_prod:
+ msm_nand_deinit_endpoint(nand_info, &nand_info->sps.data_prod);
+unregister_bam:
+ sps_deregister_bam_device(nand_info->sps.bam_handle);
+out:
+ return rc;
+}
+
+/*
+ * This function de-registers BAM device, disconnects and frees its end points
+ * for all the pipes.
+ */
+static void msm_nand_bam_free(struct msm_nand_info *nand_info)
+{
+ msm_nand_deinit_endpoint(nand_info, &nand_info->sps.data_prod);
+ msm_nand_deinit_endpoint(nand_info, &nand_info->sps.data_cons);
+ msm_nand_deinit_endpoint(nand_info, &nand_info->sps.cmd_pipe);
+ sps_deregister_bam_device(nand_info->sps.bam_handle);
+}
+
+/* This function enables DMA support for the NANDc in BAM mode. */
+static int msm_nand_enable_dma(struct msm_nand_info *info)
+{
+ struct msm_nand_sps_cmd *sps_cmd;
+ struct msm_nand_chip *chip = &info->nand_chip;
+ int ret;
+
+ wait_event(chip->dma_wait_queue,
+ (sps_cmd = msm_nand_get_dma_buffer(chip, sizeof(*sps_cmd))));
+
+ msm_nand_prep_ce(sps_cmd, MSM_NAND_CTRL(info), WRITE,
+ (1 << BAM_MODE_EN), SPS_IOVEC_FLAG_INT);
+
+ ret = sps_transfer_one(info->sps.cmd_pipe.handle,
+ msm_virt_to_dma(chip, &sps_cmd->ce),
+ sizeof(struct sps_command_element), NULL,
+ sps_cmd->flags);
+ if (ret) {
+ pr_err("Failed to submit command: %d\n", ret);
+ goto out;
+ }
+ wait_for_completion_io(&info->sps.cmd_pipe.completion);
+out:
+ msm_nand_release_dma_buffer(chip, sps_cmd, sizeof(*sps_cmd));
+ return ret;
+
+}
+
+/*
+ * This function gets called when its device named msm-nand is added to
+ * device tree .dts file with all its resources such as physical addresses
+ * for NANDc and BAM, BAM IRQ.
+ *
+ * It also expects the NAND flash partition information to be passed in .dts
+ * file so that it can parse the partitions by calling MTD function
+ * mtd_device_parse_register().
+ *
+ */
+static int __devinit msm_nand_probe(struct platform_device *pdev)
+{
+ struct msm_nand_info *info;
+ struct resource *res;
+ int err, n_parts;
+ struct device_node *pnode;
+ struct mtd_part_parser_data parser_data;
+
+ if (!pdev->dev.of_node) {
+ pr_err("No valid device tree info for NANDc\n");
+ err = -ENODEV;
+ goto out;
+ }
+
+ /*
+ * The partition information can also be passed from kernel command
+ * line. Also, the MTD core layer supports adding the whole device as
+ * one MTD device when no partition information is available at all.
+ * Hence, do not bail out when partition information is not availabe
+ * in device tree.
+ */
+ pnode = of_find_node_by_path("/qcom,mtd-partitions");
+ if (!pnode)
+ pr_info("No partition info available in device tree\n");
+ info = devm_kzalloc(&pdev->dev, sizeof(struct msm_nand_info),
+ GFP_KERNEL);
+ if (!info) {
+ pr_err("Unable to allocate memory for msm_nand_info\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "nand_phys");
+ if (!res || !res->start) {
+ pr_err("NAND phys address range is not provided\n");
+ err = -ENODEV;
+ goto out;
+ }
+ info->nand_phys = res->start;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "bam_phys");
+ if (!res || !res->start) {
+ pr_err("BAM phys address range is not provided\n");
+ err = -ENODEV;
+ goto out;
+ }
+ info->bam_phys = res->start;
+ info->bam_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!info->bam_base) {
+ pr_err("BAM ioremap() failed for addr 0x%x size 0x%x\n",
+ res->start, resource_size(res));
+ err = -ENOMEM;
+ goto out;
+ }
+
+ info->bam_irq = platform_get_irq_byname(pdev, "bam_irq");
+ if (info->bam_irq < 0) {
+ pr_err("BAM IRQ is not provided\n");
+ err = -ENODEV;
+ goto out;
+ }
+
+ info->mtd.name = dev_name(&pdev->dev);
+ info->mtd.priv = info;
+ info->mtd.owner = THIS_MODULE;
+ info->nand_chip.dev = &pdev->dev;
+ init_waitqueue_head(&info->nand_chip.dma_wait_queue);
+ mutex_init(&info->bam_lock);
+
+ info->nand_chip.dma_virt_addr =
+ dmam_alloc_coherent(&pdev->dev, MSM_NAND_DMA_BUFFER_SIZE,
+ &info->nand_chip.dma_phys_addr, GFP_KERNEL);
+ if (!info->nand_chip.dma_virt_addr) {
+ pr_err("No memory for DMA buffer size %x\n",
+ MSM_NAND_DMA_BUFFER_SIZE);
+ err = -ENOMEM;
+ goto out;
+ }
+ err = msm_nand_bam_init(info);
+ if (err) {
+ pr_err("msm_nand_bam_init() failed %d\n", err);
+ goto out;
+ }
+ err = msm_nand_enable_dma(info);
+ if (err) {
+ pr_err("Failed to enable DMA in NANDc\n");
+ goto free_bam;
+ }
+ if (msm_nand_scan(&info->mtd)) {
+ pr_err("No nand device found\n");
+ err = -ENXIO;
+ goto free_bam;
+ }
+ parser_data.of_node = pnode;
+ n_parts = mtd_device_parse_register(&info->mtd, NULL, &parser_data,
+ NULL, 0);
+ if (n_parts < 0) {
+ pr_err("Unable to register MTD partitions %d\n", n_parts);
+ goto free_bam;
+ }
+ dev_set_drvdata(&pdev->dev, info);
+
+ pr_info("NANDc phys addr 0x%lx, BAM phys addr 0x%lx, BAM IRQ %d\n",
+ info->nand_phys, info->bam_phys, info->bam_irq);
+ pr_info("Allocated DMA buffer at virt_addr 0x%p, phys_addr 0x%x\n",
+ info->nand_chip.dma_virt_addr, info->nand_chip.dma_phys_addr);
+ pr_info("Found %d MTD partitions\n", n_parts);
+ goto out;
+free_bam:
+ msm_nand_bam_free(info);
+out:
+ return err;
+}
+
+/*
+ * Remove functionality that gets called when driver/device msm-nand
+ * is removed.
+ */
+static int __devexit msm_nand_remove(struct platform_device *pdev)
+{
+ struct msm_nand_info *info = dev_get_drvdata(&pdev->dev);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+ if (info) {
+ mtd_device_unregister(&info->mtd);
+ msm_nand_bam_free(info);
+ }
+ return 0;
+}
+
+#define DRIVER_NAME "msm_qpic_nand"
+static const struct of_device_id msm_nand_match_table[] = {
+ { .compatible = "qcom,msm-nand", },
+ {},
+};
+static struct platform_driver msm_nand_driver = {
+ .probe = msm_nand_probe,
+ .remove = __devexit_p(msm_nand_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = msm_nand_match_table,
+ },
+};
+
+module_platform_driver(msm_nand_driver);
+
+MODULE_ALIAS(DRIVER_NAME);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM QPIC NAND flash driver");
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 2b73d99..d26c845 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -87,6 +87,8 @@
static const char driver_name [] = "usbnet";
+static struct workqueue_struct *usbnet_wq;
+
/* use ethtool to change the level for any given device */
static int msg_level = -1;
module_param (msg_level, int, 0);
@@ -246,7 +248,7 @@
if (skb_defer_rx_timestamp(skb))
return;
- status = netif_rx (skb);
+ status = netif_rx_ni(skb);
if (status != NET_RX_SUCCESS)
netif_dbg(dev, rx_err, dev->net,
"netif_rx status %d\n", status);
@@ -316,7 +318,7 @@
spin_lock(&dev->done.lock);
__skb_queue_tail(&dev->done, skb);
if (dev->done.qlen == 1)
- tasklet_schedule(&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
spin_unlock_irqrestore(&dev->done.lock, flags);
return old_state;
}
@@ -390,7 +392,7 @@
default:
netif_dbg(dev, rx_err, dev->net,
"rx submit, %d\n", retval);
- tasklet_schedule (&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
break;
case 0:
usb_mark_last_busy(dev->udev);
@@ -583,7 +585,7 @@
num++;
}
- tasklet_schedule(&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
netif_dbg(dev, rx_status, dev->net,
"paused rx queue disabled, %d skbs requeued\n", num);
@@ -652,7 +654,7 @@
{
if (netif_running(dev->net)) {
(void) unlink_urbs (dev, &dev->rxq);
- tasklet_schedule(&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
}
}
EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs);
@@ -726,7 +728,7 @@
*/
dev->flags = 0;
del_timer_sync (&dev->delay);
- tasklet_kill (&dev->bh);
+ cancel_work_sync(&dev->bh_w);
if (info->manage_power)
info->manage_power(dev, 0);
else
@@ -799,7 +801,7 @@
"simple");
// delay posting reads until we're fully open
- tasklet_schedule (&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
if (info->manage_power) {
retval = info->manage_power(dev, 1);
if (retval < 0)
@@ -969,7 +971,7 @@
status);
} else {
clear_bit (EVENT_RX_HALT, &dev->flags);
- tasklet_schedule (&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
}
}
@@ -994,7 +996,7 @@
usb_autopm_put_interface(dev->intf);
fail_lowmem:
if (resched)
- tasklet_schedule (&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
}
}
@@ -1080,7 +1082,7 @@
struct usbnet *dev = netdev_priv(net);
unlink_urbs (dev, &dev->txq);
- tasklet_schedule (&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
// FIXME: device recovery -- reset?
}
@@ -1267,13 +1269,21 @@
"rxqlen %d --> %d\n",
temp, dev->rxq.qlen);
if (dev->rxq.qlen < qlen)
- tasklet_schedule (&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
}
if (dev->txq.qlen < TX_QLEN (dev))
netif_wake_queue (dev->net);
}
}
+static void usbnet_bh_w(struct work_struct *work)
+{
+ struct usbnet *dev =
+ container_of(work, struct usbnet, bh_w);
+ unsigned long param = (unsigned long)dev;
+
+ usbnet_bh(param);
+}
/*-------------------------------------------------------------------------
*
@@ -1392,8 +1402,7 @@
skb_queue_head_init (&dev->txq);
skb_queue_head_init (&dev->done);
skb_queue_head_init(&dev->rxq_pause);
- dev->bh.func = usbnet_bh;
- dev->bh.data = (unsigned long) dev;
+ INIT_WORK(&dev->bh_w, usbnet_bh_w);
INIT_WORK (&dev->kevent, kevent);
init_usb_anchor(&dev->deferred);
dev->delay.function = usbnet_bh;
@@ -1577,7 +1586,7 @@
if (test_bit(EVENT_DEV_OPEN, &dev->flags)) {
if (!(dev->txq.qlen >= TX_QLEN(dev)))
netif_tx_wake_all_queues(dev->net);
- tasklet_schedule (&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
}
}
return 0;
@@ -1594,12 +1603,20 @@
FIELD_SIZEOF(struct sk_buff, cb) < sizeof(struct skb_data));
random_ether_addr(node_id);
+
+ usbnet_wq = create_singlethread_workqueue("usbnet");
+ if (!usbnet_wq) {
+ pr_err("%s: Unable to create workqueue:usbnet\n", __func__);
+ return -ENOMEM;
+ }
+
return 0;
}
module_init(usbnet_init);
static void __exit usbnet_exit(void)
{
+ destroy_workqueue(usbnet_wq);
}
module_exit(usbnet_exit);
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index ad9dc7d..7695778 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -184,6 +184,12 @@
return 0;
}
+void wcnss_flush_delayed_boot_votes()
+{
+ flush_delayed_work_sync(&penv->wcnss_work);
+}
+EXPORT_SYMBOL(wcnss_flush_delayed_boot_votes);
+
static int __devexit
wcnss_wlan_ctrl_remove(struct platform_device *pdev)
{
diff --git a/drivers/power/smb349.c b/drivers/power/smb349.c
index ffc92d5..f9ca81c 100644
--- a/drivers/power/smb349.c
+++ b/drivers/power/smb349.c
@@ -617,6 +617,8 @@
the_smb349_chg = smb349_chg;
+ spin_lock_init(&smb349_chg->lock);
+
create_debugfs_entries(smb349_chg);
INIT_WORK(&smb349_chg->chg_work, chg_worker);
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
index 0394b0b..96790c5 100644
--- a/drivers/usb/gadget/f_mtp.c
+++ b/drivers/usb/gadget/f_mtp.c
@@ -788,7 +788,8 @@
/* wait for our last read to complete */
ret = wait_event_interruptible(dev->read_wq,
dev->rx_done || dev->state != STATE_BUSY);
- if (dev->state == STATE_CANCELED) {
+ if (dev->state == STATE_CANCELED
+ || dev->state == STATE_OFFLINE) {
r = -ECANCELED;
if (!dev->rx_done)
usb_ep_dequeue(dev->ep_out, read_req);
diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c
index 297c183..55fd59e 100644
--- a/drivers/usb/gadget/msm72k_udc.c
+++ b/drivers/usb/gadget/msm72k_udc.c
@@ -702,6 +702,14 @@
spin_lock_irqsave(&ui->lock, flags);
+ if (ept->num != 0 && ept->ep.desc == NULL) {
+ req->req.status = -EINVAL;
+ spin_unlock_irqrestore(&ui->lock, flags);
+ dev_err(&ui->pdev->dev,
+ "%s: called for disabled endpoint\n", __func__);
+ return -EINVAL;
+ }
+
if (req->busy) {
req->req.status = -EBUSY;
spin_unlock_irqrestore(&ui->lock, flags);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 487bc59..da96e73 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -340,15 +340,28 @@
{
int ret;
- if (IS_ERR(motg->clk))
- return 0;
-
if (assert) {
- ret = clk_reset(motg->clk, CLK_RESET_ASSERT);
+ if (!IS_ERR(motg->clk)) {
+ ret = clk_reset(motg->clk, CLK_RESET_ASSERT);
+ } else {
+ /* Using asynchronous block reset to the hardware */
+ dev_dbg(motg->phy.dev, "block_reset ASSERT\n");
+ clk_disable_unprepare(motg->pclk);
+ clk_disable_unprepare(motg->core_clk);
+ ret = clk_reset(motg->core_clk, CLK_RESET_ASSERT);
+ }
if (ret)
dev_err(motg->phy.dev, "usb hs_clk assert failed\n");
} else {
- ret = clk_reset(motg->clk, CLK_RESET_DEASSERT);
+ if (!IS_ERR(motg->clk)) {
+ ret = clk_reset(motg->clk, CLK_RESET_DEASSERT);
+ } else {
+ dev_dbg(motg->phy.dev, "block_reset DEASSERT\n");
+ ret = clk_reset(motg->core_clk, CLK_RESET_DEASSERT);
+ ndelay(200);
+ clk_prepare_enable(motg->core_clk);
+ clk_prepare_enable(motg->pclk);
+ }
if (ret)
dev_err(motg->phy.dev, "usb hs_clk deassert failed\n");
}
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index f03a493..b6064a4 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -47,53 +47,107 @@
enum coresight_dev_type {
CORESIGHT_DEV_TYPE_SINK,
CORESIGHT_DEV_TYPE_LINK,
+ CORESIGHT_DEV_TYPE_LINKSINK,
CORESIGHT_DEV_TYPE_SOURCE,
- CORESIGHT_DEV_TYPE_MAX,
+};
+
+enum coresight_dev_subtype_sink {
+ CORESIGHT_DEV_SUBTYPE_SINK_NONE,
+ CORESIGHT_DEV_SUBTYPE_SINK_PORT,
+ CORESIGHT_DEV_SUBTYPE_SINK_BUFFER,
+};
+
+enum coresight_dev_subtype_link {
+ CORESIGHT_DEV_SUBTYPE_LINK_NONE,
+ CORESIGHT_DEV_SUBTYPE_LINK_MERG,
+ CORESIGHT_DEV_SUBTYPE_LINK_SPLIT,
+ CORESIGHT_DEV_SUBTYPE_LINK_FIFO,
+};
+
+enum coresight_dev_subtype_source {
+ CORESIGHT_DEV_SUBTYPE_SOURCE_NONE,
+ CORESIGHT_DEV_SUBTYPE_SOURCE_PROC,
+ CORESIGHT_DEV_SUBTYPE_SOURCE_BUS,
+ CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE,
+};
+
+struct coresight_dev_subtype {
+ enum coresight_dev_subtype_sink sink_subtype;
+ enum coresight_dev_subtype_link link_subtype;
+ enum coresight_dev_subtype_source source_subtype;
+};
+
+struct coresight_platform_data {
+ int id;
+ const char *name;
+ int nr_inports;
+ const int *outports;
+ const int *child_ids;
+ const int *child_ports;
+ int nr_outports;
+ bool default_sink;
+};
+
+struct coresight_desc {
+ enum coresight_dev_type type;
+ struct coresight_dev_subtype subtype;
+ const struct coresight_ops *ops;
+ struct coresight_platform_data *pdata;
+ struct device *dev;
+ const struct attribute_group **groups;
+ struct module *owner;
};
struct coresight_connection {
+ int outport;
int child_id;
int child_port;
struct coresight_device *child_dev;
struct list_head link;
};
+struct coresight_refcnt {
+ int sink_refcnt;
+ int *link_refcnts;
+ int source_refcnt;
+};
+
struct coresight_device {
int id;
struct coresight_connection *conns;
int nr_conns;
+ enum coresight_dev_type type;
+ struct coresight_dev_subtype subtype;
const struct coresight_ops *ops;
struct device dev;
- struct mutex mutex;
- int *refcnt;
- struct list_head link;
+ struct coresight_refcnt refcnt;
+ struct list_head dev_link;
+ struct list_head path_link;
struct module *owner;
bool enable;
};
#define to_coresight_device(d) container_of(d, struct coresight_device, dev)
+struct coresight_ops_sink {
+ int (*enable)(struct coresight_device *csdev);
+ void (*disable)(struct coresight_device *csdev);
+};
+
+struct coresight_ops_link {
+ int (*enable)(struct coresight_device *csdev, int iport, int oport);
+ void (*disable)(struct coresight_device *csdev, int iport, int oport);
+};
+
+struct coresight_ops_source {
+ int (*enable)(struct coresight_device *csdev);
+ void (*disable)(struct coresight_device *csdev);
+};
+
struct coresight_ops {
- int (*enable)(struct coresight_device *csdev, int port);
- void (*disable)(struct coresight_device *csdev, int port);
-};
-
-struct coresight_platform_data {
- int id;
- const char *name;
- int nr_ports;
- int *child_ids;
- int *child_ports;
- int nr_children;
-};
-
-struct coresight_desc {
- enum coresight_dev_type type;
- const struct coresight_ops *ops;
- struct coresight_platform_data *pdata;
- struct device *dev;
- const struct attribute_group **groups;
- struct module *owner;
+ const struct coresight_ops_sink *sink_ops;
+ const struct coresight_ops_link *link_ops;
+ const struct coresight_ops_source *source_ops;
};
struct qdss_source {
@@ -109,24 +163,29 @@
};
-extern struct coresight_device *
-coresight_register(struct coresight_desc *desc);
-extern void coresight_unregister(struct coresight_device *csdev);
-extern int coresight_enable(struct coresight_device *csdev, int port);
-extern void coresight_disable(struct coresight_device *csdev, int port);
-
#ifdef CONFIG_MSM_QDSS
extern struct qdss_source *qdss_get(const char *name);
extern void qdss_put(struct qdss_source *src);
extern int qdss_enable(struct qdss_source *src);
extern void qdss_disable(struct qdss_source *src);
extern void qdss_disable_sink(void);
+extern struct coresight_device *
+coresight_register(struct coresight_desc *desc);
+extern void coresight_unregister(struct coresight_device *csdev);
+extern int coresight_enable(struct coresight_device *csdev);
+extern void coresight_disable(struct coresight_device *csdev);
#else
static inline struct qdss_source *qdss_get(const char *name) { return NULL; }
static inline void qdss_put(struct qdss_source *src) {}
static inline int qdss_enable(struct qdss_source *src) { return -ENOSYS; }
static inline void qdss_disable(struct qdss_source *src) {}
static inline void qdss_disable_sink(void) {}
+static inline struct coresight_device *
+coresight_register(struct coresight_desc *desc) { return NULL; }
+static inline void coresight_unregister(struct coresight_device *csdev) {}
+static inline int
+coresight_enable(struct coresight_device *csdev) { return -ENOSYS; }
+static inline void coresight_disable(struct coresight_device *csdev) {}
#endif
#endif
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 76f4396..72f5c96 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -56,7 +56,7 @@
struct sk_buff_head rxq_pause;
struct urb *interrupt;
struct usb_anchor deferred;
- struct tasklet_struct bh;
+ struct work_struct bh_w;
struct work_struct kevent;
unsigned long flags;
diff --git a/include/linux/wcnss_wlan.h b/include/linux/wcnss_wlan.h
index d7e65b0..46a5b1b 100644
--- a/include/linux/wcnss_wlan.h
+++ b/include/linux/wcnss_wlan.h
@@ -47,6 +47,7 @@
int req_riva_power_on_lock(char *driver_name);
int free_riva_power_on_lock(char *driver_name);
unsigned int wcnss_get_serial_number(void);
+void wcnss_flush_delayed_boot_votes(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_camera.h b/include/media/msm_camera.h
index ae81dcd..57ce7c0 100644
--- a/include/media/msm_camera.h
+++ b/include/media/msm_camera.h
@@ -213,6 +213,10 @@
#define MSM_CAM_IOCTL_GET_INST_HANDLE \
_IOR(MSM_CAM_IOCTL_MAGIC, 60, uint32_t *)
+#define MSM_CAM_IOCTL_STATS_UNREG_BUF \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 61, struct msm_stats_flush_bufq *)
+
+
struct msm_stats_reqbuf {
int num_buf; /* how many buffers requested */
int stats_type; /* stats type */
@@ -468,6 +472,7 @@
#define CMD_AXI_CFG_ZSL 43
#define CMD_AXI_CFG_SNAP_VPE 44
#define CMD_AXI_CFG_SNAP_THUMB_VPE 45
+
#define CMD_CONFIG_PING_ADDR 46
#define CMD_CONFIG_PONG_ADDR 47
#define CMD_CONFIG_FREE_BUF_ADDR 48
@@ -475,6 +480,13 @@
#define CMD_AXI_CFG_VIDEO_ALL_CHNLS 50
#define CMD_VFE_BUFFER_RELEASE 51
#define CMD_VFE_PROCESS_IRQ 52
+#define CMD_STATS_BG_ENABLE 53
+#define CMD_STATS_BF_ENABLE 54
+#define CMD_STATS_BHIST_ENABLE 55
+#define CMD_STATS_BG_BUF_RELEASE 56
+#define CMD_STATS_BF_BUF_RELEASE 57
+#define CMD_STATS_BHIST_BUF_RELEASE 58
+
#define CMD_AXI_CFG_PRIM BIT(8)
#define CMD_AXI_CFG_PRIM_ALL_CHNLS BIT(9)
@@ -524,7 +536,10 @@
#define MSM_PMEM_C2D 17
#define MSM_PMEM_MAINIMG_VPE 18
#define MSM_PMEM_THUMBNAIL_VPE 19
-#define MSM_PMEM_MAX 20
+#define MSM_PMEM_BAYER_GRID 20
+#define MSM_PMEM_BAYER_FOCUS 21
+#define MSM_PMEM_BAYER_HIST 22
+#define MSM_PMEM_MAX 23
#define STAT_AEAW 0
#define STAT_AEC 1
@@ -534,7 +549,10 @@
#define STAT_CS 5
#define STAT_IHIST 6
#define STAT_SKIN 7
-#define STAT_MAX 8
+#define STAT_BG 8
+#define STAT_BF 9
+#define STAT_BHIST 10
+#define STAT_MAX 11
#define FRAME_PREVIEW_OUTPUT1 0
#define FRAME_PREVIEW_OUTPUT2 1
@@ -1858,6 +1876,12 @@
struct msm_cpp_frame_strip_info *strip_info;
};
+struct msm_ver_num_info {
+ uint32_t main;
+ uint32_t minor;
+ uint32_t rev;
+};
+
#define VIDIOC_MSM_CPP_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_camera_v4l2_ioctl_t)
diff --git a/include/media/msm_isp.h b/include/media/msm_isp.h
index 3df6ded..0ee7417 100644
--- a/include/media/msm_isp.h
+++ b/include/media/msm_isp.h
@@ -62,6 +62,10 @@
#define MSG_ID_OUTPUT_TERTIARY1 43
#define MSG_ID_STOP_LS_ACK 44
#define MSG_ID_OUTPUT_TERTIARY2 45
+#define MSG_ID_STATS_BG 46
+#define MSG_ID_STATS_BF 47
+#define MSG_ID_STATS_BHIST 48
+
/* ISP command IDs */
#define VFE_CMD_DUMMY_0 0
@@ -206,6 +210,13 @@
#define VFE_CMD_STATS_REQBUF 139
#define VFE_CMD_STATS_ENQUEUEBUF 140
#define VFE_CMD_STATS_FLUSH_BUFQ 141
+#define VFE_CMD_STATS_UNREGBUF 142
+#define VFE_CMD_STATS_BG_START 143
+#define VFE_CMD_STATS_BG_STOP 144
+#define VFE_CMD_STATS_BF_START 145
+#define VFE_CMD_STATS_BF_STOP 146
+#define VFE_CMD_STATS_BHIST_START 147
+#define VFE_CMD_STATS_BHIST_STOP 148
struct msm_isp_cmd {
int32_t id;
diff --git a/include/media/vcap_fmt.h b/include/media/vcap_fmt.h
index 92240bf1..00e0375 100644
--- a/include/media/vcap_fmt.h
+++ b/include/media/vcap_fmt.h
@@ -44,6 +44,43 @@
HAL_VCAP_RGB,
};
+enum nr_mode {
+ NR_DISABLE = 0,
+ NR_AUTO,
+ NR_MANUAL,
+};
+
+enum nr_decay_ratio {
+ NR_Decay_Ratio_26 = 0,
+ NR_Decay_Ratio_25,
+ NR_Decay_Ratio_24,
+ NR_Decay_Ratio_23,
+ NR_Decay_Ratio_22,
+ NR_Decay_Ratio_21,
+ NR_Decay_Ratio_20,
+ NR_Decay_Ratio_19,
+};
+
+struct nr_config {
+ uint8_t max_blend_ratio;
+ uint8_t scale_diff_ratio;
+ uint8_t diff_limit_ratio;
+ uint8_t scale_motion_ratio;
+ uint8_t blend_limit_ratio;
+};
+
+struct nr_param {
+ enum nr_mode mode;
+ enum nr_decay_ratio decay_ratio;
+ uint8_t window;
+ struct nr_config luma;
+ struct nr_config chroma;
+};
+
+#define VCAPIOC_NR_S_PARAMS _IOWR('V', (BASE_VIDIOC_PRIVATE+0), struct nr_param)
+
+#define VCAPIOC_NR_G_PARAMS _IOWR('V', (BASE_VIDIOC_PRIVATE+1), struct nr_param)
+
struct v4l2_format_vc_ext {
enum hal_vcap_mode mode;
enum hal_vcap_polar h_polar;
diff --git a/include/media/vcap_v4l2.h b/include/media/vcap_v4l2.h
index 9719aa6..390a843 100644
--- a/include/media/vcap_v4l2.h
+++ b/include/media/vcap_v4l2.h
@@ -120,7 +120,8 @@
void *bufMotion;
struct nr_buffer bufNR;
- bool nr_enabled;
+ struct nr_param nr_param;
+ bool nr_update;
};
struct vp_work_t {
diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig
index 9b60a56..e56b1da 100644
--- a/sound/soc/msm/Kconfig
+++ b/sound/soc/msm/Kconfig
@@ -159,6 +159,7 @@
select SND_SOC_QDSP6V2
select SND_SOC_MSM_STUB
select SND_SOC_MSM_HOSTLESS_PCM
+ select SND_SOC_WCD9320
select SND_DYNAMIC_MINORS
help
To add support for SoC audio on MSM8974.
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
index d8a4624..2fcf29b 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -1152,8 +1152,9 @@
snd_soc_dapm_sync(dapm);
err = snd_soc_jack_new(codec, "Headset Jack",
- (SND_JACK_HEADSET | SND_JACK_OC_HPHL | SND_JACK_OC_HPHR),
- &hs_jack);
+ (SND_JACK_HEADSET | SND_JACK_OC_HPHL |
+ SND_JACK_OC_HPHR | SND_JACK_UNSUPPORTED),
+ &hs_jack);
if (err) {
pr_err("failed to create new jack\n");
return err;
diff --git a/sound/soc/msm/mpq8064.c b/sound/soc/msm/mpq8064.c
index 6685ce5..f5bbf56 100644
--- a/sound/soc/msm/mpq8064.c
+++ b/sound/soc/msm/mpq8064.c
@@ -1165,8 +1165,6 @@
.platform_name = "msm-pcm-afe",
.ignore_suspend = 1,
.ignore_pmdown_time = 1, /* this dainlink has playback support */
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
},
{
.name = "MSM AFE-PCM TX",
@@ -1176,8 +1174,6 @@
.codec_dai_name = "msm-stub-tx",
.platform_name = "msm-pcm-afe",
.ignore_suspend = 1,
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
},
{
.name = "MSM8960 Compr1",