Merge "platform-drivers: msm: sps: migrate to new clock API" into msm-3.0
diff --git a/Documentation/arm/msm/tspp.txt b/Documentation/arm/msm/tspp.txt
new file mode 100644
index 0000000..a56f014
--- /dev/null
+++ b/Documentation/arm/msm/tspp.txt
@@ -0,0 +1,250 @@
+Introduction
+============
+The TSPP (Transport stream packet processor) is a hardware accelerator
+designed to process MPEG2 TS (transport stream) data. It is mainly used for
+broadcast terrestrial services to off-load the host CPU from real-time
+sensitive TS processing for high bandwidth streams (~20Mbps). Data is received
+either via TSIF (Transport stream interface) or system memory.
+The TSPP driver manages the TSIF 12Seg HW core, which consists of a TSPP, a
+BAM (Bus access manager, used for DMA) and two TSIF inputs.
+It is applicable to the TSIF 12Seg core found on select Qualcomm MSM chips.
+
+For more information on the TSIF interface, please refer to TSIF documentation
+(Documentation/arm/msm/tsif.txt).
+For more information on the BAM interface, please refer to SPS documentation
+(Documentation/dma/sps/sps_architecture.txt).
+
+Hardware description
+====================
+The TSPP unit expands the capabilities of the TSIF interface by adding MPEG2
+stream processing such as:
+ - Elementary Stream PID filtering and de-multiplexing
+ - Several TSP processing operation modes:
+ - TSP Raw processing (with or w/o suffix)
+ - TSP PES assembly
+ - Error handling
+ - Multi2 decryption
+
+ +-------------+ +------+
+ +>|ARM subsystem| |Memory|
+ | +-------------+ +------+
+ | | |
+ I| | |
+ R| ========================== System bus
+ Q| |
+ s| | TSIF 12Seg
+ | +-----------------------+
+ | | +---+ |
+ | | |BAM| |
+ | | +---+ | GPIOs
+ | | | +------+--|-------- TSIF 0 clk
+ | | +----+ |TSIF 0|--|-------- TSIF 0 data
+ +-| | |-----+------+--|-------- TSIF 0 en
+ | |TSPP| |
+ | | |-----+------+--|-------- TSIF 1 clk
+ | +----+ |TSIF 1|--|-------- TSIF 1 data
+ | +------+--|-------- TSIF 1 en
+ +-----------------------+
+
+ The TSPP unit receives an MPEG2 transport stream either via the two TSIF
+ interfaces, or via system memory.
+ It uses the BAM interface to transfer data to and from system memory.
+ The ARM subsystem receives interrupts on various error conditions from TSPP
+ and TSIF units, and on data transfer events from the BAM.
+
+Software Description
+====================
+The TSPP driver is responsible for:
+ - TSPP/TSIF hardware configuration (using SPS driver to configure BAM
+ hardware)
+ - TSIF GPIO/Clocks configuration
+ - Memory resource management
+ - Handling TSIF/TSPP interrupts and BAM events
+ - TSPP Power management
+
+TSPP Terminology
+----------------
+Device - the TSPP hardware instance
+ - the device contains many streams
+Stream: All data that is received from a particular TS packet source
+ - For example, MPEG2 Transport Stream from TSIF0
+ - A stream can consist of many channels
+Channel: A channel routes part of a stream to a specified memory location
+ - For example, video and audio can be routed to different memory locations
+ using different channels
+ - Channel contents are defined by filters
+Filter: Enables TS packet filtering and routing according to PID (packet ID)
+ - The decision regarding which PIDs in the stream will be routed to a
+ channel is done via filters
+ - Several filters can be registered to the same channel
+ - Filters can pass TS packets as-is (Raw mode) or assemble them into PES
+ packets (PES mode)
+ - Groups of contiguous PIDs can be filtered together (i.e. PSI/SI 0x0-0x2F,
+ 1Seg PMTs 0x1FC8-0x1FCF)
+ - Filters can be used to discard packets (e.g. eliminate processing of
+ unwanted channels)
+
+Init flow:
+----------
+Driver registers BAM (via SPS driver) and initializes TSIF/TSPP hardware.
+
+Control path:
+-------------
+1. Client opens a TSPP stream and channel
+2. Client notifies the driver of the source stream (TSIF0/TSIF1/Memory)
+ - TSPP driver allocates memory for the channel (circular buffer)
+ - As the amount of memory varies according to stream bandwidth (which the
+ driver doesn't know), a client can hint to the driver about the required
+ memory or stream bandwidth
+ - TSPP driver configures TSIF/BAM hardware according to selected stream
+3. Client notifies the driver to filter a set of PIDs for the selected channel
+ - TSPP driver configures TSPP hardware filters accordingly
+4. Client can now read data received from the selected channel
+
+Optional: Scrambling keys can be configured for a filter to decrypt Multi2
+encrypted streams. The scrambling keys are received encrypted in-stream every
+~2 seconds. The client uses a smart card and master key to decrypt the
+received scrambling keys. The master key remains inside the smart card and is
+never revealed to software.
+
+Conceptual flow:
+Client TSPP Driver SPS Driver Hardware
+ | | | |
+ | | Init TSIF/TSPP | |
+ | |---------------------------|------------------>|
+ | | Register BAM | |
+ | |-------------------------->| |
+ | | | |
+ | open(tspp.5) | | |
+ |-------------------------->| | |
+ | ioctl(tspp.5,SOURCE,TSIF0)| | |
+ |-------------------------->| | |
+ | | buff[0..N] = alloc() | |
+ | |----------> | |
+ | | Connect(TSPP,MEM) | |
+ | |-------------------------->| |
+ | | Transfer(buff[0..N]) | |
+ | |-------------------------->| |
+ | | | |
+ | | Configure TSIF0 | |
+ | |---------------------------|------------------>|
+ | | | |
+ | ioctl(tspp.5,FILTER,pid) | | |
+ |-------------------------->| | |
+ | | Configure TSPP filters | |
+ | |---------------------------|------------------>|
+ | | | |
+ | | | INT |
+ | | |<------------------|
+ | | notify(EOT,buff[x]) | |
+ | |<--------------------------| |
+ | | Transfer(buff[y]) | |
+ | |-------------------------->| |
+ | | | |
+ | read(tspp.5) | | |
+ |-------------------------->| | |
+ | | | |
+
+
+Data path:
+----------
+The TSPP driver is not involved in data transfer, other than managing the
+circular buffer pointers when a transfer is complete. Data loss can occur if
+a client cannot keep up with stream bandwidth.
+The driver does not notify the application if there is data loss. It is
+assumed that the application will read data when the data is ready, and when
+the application is able.
+
+API
+===
+int tspp_open_stream(tspp_device *dev, void *stream, void *channel, tspp_mode
+ mode);
+int tspp_close_stream(tspp_device *dev, void *stream);
+int tspp_open_channel(tspp_device *dev, int dest, int bufsize, void *channel);
+int tspp_close_channel(tspp_device *dev, void *channel);
+int tspp_register_filter(tspp_device *dev, void *channel, tspp_filter *filter);
+int tspp_unregister_filter(tspp_device *dev, void *channel, int pid);
+
+Refer to chrdev implementation in kernel/drivers/misc/tspp.c for an example of
+how to use this api.
+
+Each stream is represented by a chrdev device
+16 available devices: /dev/tspp00 - /dev/tspp15
+Each device implements these functions:
+open()
+close()
+read()
+ioctl()
+
+ioctl() contains several sub-functions:
+0: select source
+1: add filter
+2: remove filter
+3: set decryption keys
+4: set initial cbc values (IV)
+5: set system keys
+6: set buffer size
+
+You can refer to include/linux/tspp.h for the details on the structures
+associated with these IOCTL calls.
+
+Design
+======
+Design is based on the existing TSIF driver, with added control functionality
+for the extra hardware features.
+
+Power Management
+===============
+TSPP driver prevents MSM from sleeping while TSPP hardware is active.
+To achieve this, the driver holds the wake lock. When no channels are
+configured to receive data, the driver stops the clocks to save power.
+It will register for suspend/resume in the future.
+
+SMP/multi-core
+==============
+Driver is fully SMP aware.
+
+Performance
+===========
+Control flows are rare.
+Data path only involves maintaining the circular buffer pointers.
+
+Interface
+=========
+Driver exposes a char device interface to user-space for each channel.
+Control transactions are performed via ioctl on the channel. Data is read as
+any regular char device (blocking and non-blocking). Please see sequence
+under software description for more info.
+
+Debugfs may be used for debug purposes. Through debugfs, all of the TSIF
+registers can be accessed under /sys/kernel/debug/tsif0 and tsif1. The
+TSPP registers are found under /sys/kernel/debug/tspp0. If you cannot
+see these devices, then either the driver has been built without debugfs
+support (check the define TSPP_USE_DEBUGFS in tspp.c) or the debugfs has
+not been mounted correctly (or mounted in a different location).
+
+Driver Parameters
+=================
+TSPP driver has target-specific parameters:
+- Memory base addresses for TSIF/TSPP/BAM
+- TSIF/TSPP/BAM IRQ numbers
+- TSIF GPIO numbers
+
+Config Options
+==============
+To enable the driver, set CONFIG_TSPP (=y or =m) in the appropriate
+config file for the platform.
+
+Dependencies
+============
+The TSPP driver depends on the SPS driver to configure BAM hardware.
+
+User space utilities
+====================
+The TSPP test suite can be found in:
+vendor/qcom/proprietary/kernel-tests/tspp
+
+Known Issues
+============
+Currently PES processing mode cannot be configured for streams in which the PES
+length is 0. This is a HW limitation.
diff --git a/arch/arm/configs/msm7627a-perf_defconfig b/arch/arm/configs/msm7627a-perf_defconfig
index 00ff580..fc1d81a 100644
--- a/arch/arm/configs/msm7627a-perf_defconfig
+++ b/arch/arm/configs/msm7627a-perf_defconfig
@@ -295,6 +295,7 @@
CONFIG_MMC_MSM_CARD_HW_DETECTION=y
CONFIG_MMC_MSM_SDC3_SUPPORT=y
CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT=y
+CONFIG_LEDS_GPIO=y
CONFIG_LEDS_MSM_PDM=y
CONFIG_SWITCH=y
CONFIG_SWITCH_GPIO=y
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index da8b9df..a872cdc 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -254,6 +254,7 @@
CONFIG_MSM_RMNET_BAM=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_EVBUG=m
+CONFIG_KEYBOARD_GPIO=y
CONFIG_KEYBOARD_PMIC8XXX=y
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
@@ -291,6 +292,7 @@
CONFIG_THERMAL=y
CONFIG_THERMAL_TSENS8960=y
CONFIG_THERMAL_PM8XXX=y
+CONFIG_THERMAL_MONITOR=y
CONFIG_MFD_PM8921_CORE=y
CONFIG_MFD_PM8821_CORE=y
CONFIG_MFD_PM8038_CORE=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index 10b55dc..3337d06 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -255,6 +255,7 @@
CONFIG_MSM_RMNET_BAM=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_EVBUG=m
+CONFIG_KEYBOARD_GPIO=y
CONFIG_KEYBOARD_PMIC8XXX=y
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
@@ -292,6 +293,7 @@
CONFIG_THERMAL=y
CONFIG_THERMAL_TSENS8960=y
CONFIG_THERMAL_PM8XXX=y
+CONFIG_THERMAL_MONITOR=y
CONFIG_MFD_PM8921_CORE=y
CONFIG_MFD_PM8821_CORE=y
CONFIG_MFD_PM8038_CORE=y
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 69675e1..a3ed7ab 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -242,7 +242,7 @@
obj-$(CONFIG_ARCH_APQ8064) += devices-8960.o devices-8064.o
board-8960-all-objs += board-8960.o board-8960-camera.o board-8960-display.o board-8960-pmic.o board-8960-storage.o board-8960-gpiomux.o
board-8930-all-objs += board-8930.o board-8930-camera.o board-8930-display.o board-8930-pmic.o board-8930-storage.o board-8930-gpiomux.o devices-8930.o
-board-8064-all-objs += board-8064.o board-8064-pmic.o board-8064-storage.o board-8064-gpiomux.o board-8064-camera.o
+board-8064-all-objs += board-8064.o board-8064-pmic.o board-8064-storage.o board-8064-gpiomux.o board-8064-camera.o board-8064-display.o
obj-$(CONFIG_MACH_MSM8960_SIM) += board-8960-all.o board-8960-regulator.o
obj-$(CONFIG_MACH_MSM8960_RUMI3) += board-8960-all.o board-8960-regulator.o
obj-$(CONFIG_MACH_MSM8960_CDP) += board-8960-all.o board-8960-regulator.o
diff --git a/arch/arm/mach-msm/board-8064-display.c b/arch/arm/mach-msm/board-8064-display.c
new file mode 100644
index 0000000..73758d0
--- /dev/null
+++ b/arch/arm/mach-msm/board-8064-display.c
@@ -0,0 +1,144 @@
+/* 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/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/bootmem.h>
+#include <asm/mach-types.h>
+#include <mach/msm_memtypes.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <mach/gpiomux.h>
+#include <linux/ion.h>
+#include <mach/ion.h>
+
+#include "devices.h"
+#include "board-8064.h"
+
+#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER
+/* prim = 1366 x 768 x 3(bpp) x 3(pages) */
+#define MSM_FB_PRIM_BUF_SIZE roundup(1366 * 768 * 3 * 3, 0x10000)
+#else
+/* prim = 1366 x 768 x 3(bpp) x 2(pages) */
+#define MSM_FB_PRIM_BUF_SIZE roundup(1366 * 768 * 3 * 2, 0x10000)
+#endif
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL
+/* hdmi = 1920 x 1088 x 2(bpp) x 1(page) */
+#define MSM_FB_EXT_BUF_SIZE 0x3FC000
+#elif defined(CONFIG_FB_MSM_TVOUT)
+/* tvout = 720 x 576 x 2(bpp) x 2(pages) */
+#define MSM_FB_EXT_BUF_SIZE 0x195000
+#else /* CONFIG_FB_MSM_HDMI_MSM_PANEL */
+#define MSM_FB_EXT_BUF_SIZE 0
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL */
+
+#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE + MSM_FB_EXT_BUF_SIZE, 4096)
+
+#ifdef CONFIG_FB_MSM_OVERLAY0_WRITEBACK
+#define MSM_FB_OVERLAY0_WRITEBACK_SIZE roundup((1376 * 768 * 3 * 2), 4096)
+#else
+#define MSM_FB_OVERLAY0_WRITEBACK_SIZE (0)
+#endif /* CONFIG_FB_MSM_OVERLAY0_WRITEBACK */
+
+#ifdef CONFIG_FB_MSM_OVERLAY1_WRITEBACK
+#define MSM_FB_OVERLAY1_WRITEBACK_SIZE roundup((1920 * 1088 * 3 * 2), 4096)
+#else
+#define MSM_FB_OVERLAY1_WRITEBACK_SIZE (0)
+#endif /* CONFIG_FB_MSM_OVERLAY1_WRITEBACK */
+
+static struct resource msm_fb_resources[] = {
+ {
+ .flags = IORESOURCE_DMA,
+ }
+};
+
+#define SIMULATOR_PANAL_NAME "mipi_video_simulator"
+#define SIMULATOR_PANAL_NAME_LEN 20
+#define LVDS_CHIMEI_PANEL_NAME "lvds_chimei_wxga"
+#define LVDS_CHIMEI_PANEL_NAME_LEN 16
+
+static int msm_fb_detect_panel(const char *name)
+{
+ if (!strncmp(name, LVDS_CHIMEI_PANEL_NAME,
+ LVDS_CHIMEI_PANEL_NAME_LEN))
+ return 0;
+ return -ENODEV;
+}
+
+static struct msm_fb_platform_data msm_fb_pdata = {
+ .detect_client = msm_fb_detect_panel,
+};
+
+static struct platform_device msm_fb_device = {
+ .name = "msm_fb",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(msm_fb_resources),
+ .resource = msm_fb_resources,
+ .dev.platform_data = &msm_fb_pdata,
+};
+
+void __init apq8064_allocate_fb_region(void)
+{
+ void *addr;
+ unsigned long size;
+
+ size = MSM_FB_SIZE;
+ addr = alloc_bootmem_align(size, 0x1000);
+ msm_fb_resources[0].start = __pa(addr);
+ msm_fb_resources[0].end = msm_fb_resources[0].start + size - 1;
+ pr_info("allocating %lu bytes at %p (%lx physical) for fb\n",
+ size, addr, __pa(addr));
+}
+
+#define MDP_VSYNC_GPIO 0
+
+static int mdp_core_clk_rate_table[] = {
+ 266667000,
+ 266667000,
+ 266667000,
+ 266667000,
+};
+
+static struct msm_panel_common_pdata mdp_pdata = {
+ .gpio = MDP_VSYNC_GPIO,
+ .mdp_core_clk_rate = 266667000,
+ .mdp_core_clk_table = mdp_core_clk_rate_table,
+ .num_mdp_clk = ARRAY_SIZE(mdp_core_clk_rate_table),
+ .mdp_rev = MDP_REV_44,
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+ .mem_hid = ION_CP_MM_HEAP_ID,
+#else
+ .mem_hid = MEMTYPE_EBI1,
+#endif
+};
+
+void __init apq8064_mdp_writeback(struct memtype_reserve* reserve_table)
+{
+ mdp_pdata.ov0_wb_size = MSM_FB_OVERLAY0_WRITEBACK_SIZE;
+ mdp_pdata.ov1_wb_size = MSM_FB_OVERLAY1_WRITEBACK_SIZE;
+#if defined(CONFIG_ANDROID_PMEM) && !defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
+ reserve_table[mdp_pdata.mem_hid].size +=
+ mdp_pdata.ov0_wb_size;
+ reserve_table[mdp_pdata.mem_hid].size +=
+ mdp_pdata.ov1_wb_size;
+#endif
+}
+
+void __init apq8064_init_fb(void)
+{
+ platform_device_register(&msm_fb_device);
+ msm_fb_register_device("mdp", &mdp_pdata);
+ msm_fb_register_device("lvds", NULL);
+}
diff --git a/arch/arm/mach-msm/board-8064-regulator.c b/arch/arm/mach-msm/board-8064-regulator.c
index 0808a54..6e3612a 100644
--- a/arch/arm/mach-msm/board-8064-regulator.c
+++ b/arch/arm/mach-msm/board-8064-regulator.c
@@ -27,6 +27,8 @@
};
VREG_CONSUMERS(L2) = {
REGULATOR_SUPPLY("8921_l2", NULL),
+ REGULATOR_SUPPLY("mipi_csi_vdd", "4-001a"),
+ REGULATOR_SUPPLY("mipi_csi_vdd", "4-006c"),
};
VREG_CONSUMERS(L3) = {
REGULATOR_SUPPLY("8921_l3", NULL),
@@ -55,6 +57,8 @@
};
VREG_CONSUMERS(L8) = {
REGULATOR_SUPPLY("8921_l8", NULL),
+ REGULATOR_SUPPLY("cam_vana", "4-001a"),
+ REGULATOR_SUPPLY("cam_vana", "4-006c"),
};
VREG_CONSUMERS(L9) = {
REGULATOR_SUPPLY("8921_l9", NULL),
@@ -67,6 +71,8 @@
REGULATOR_SUPPLY("8921_l11", NULL),
};
VREG_CONSUMERS(L12) = {
+ REGULATOR_SUPPLY("cam_vdig", "4-001a"),
+ REGULATOR_SUPPLY("cam_vdig", "4-006c"),
REGULATOR_SUPPLY("8921_l12", NULL),
};
VREG_CONSUMERS(L14) = {
@@ -77,6 +83,8 @@
};
VREG_CONSUMERS(L16) = {
REGULATOR_SUPPLY("8921_l16", NULL),
+ REGULATOR_SUPPLY("cam_vaf", "4-001a"),
+ REGULATOR_SUPPLY("cam_vaf", "4-006c"),
};
VREG_CONSUMERS(L17) = {
REGULATOR_SUPPLY("8921_l17", NULL),
@@ -158,6 +166,8 @@
};
VREG_CONSUMERS(LVS5) = {
REGULATOR_SUPPLY("8921_lvs5", NULL),
+ REGULATOR_SUPPLY("cam_vio", "4-001a"),
+ REGULATOR_SUPPLY("cam_vio", "4-006c"),
};
VREG_CONSUMERS(LVS6) = {
REGULATOR_SUPPLY("8921_lvs6", NULL),
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index 924a67d..ec2b879 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -70,7 +70,7 @@
#endif
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
-#define MSM_PMEM_KERNEL_EBI1_SIZE 0xB0C000
+#define MSM_PMEM_KERNEL_EBI1_SIZE 0x280000
#define MSM_ION_SF_SIZE MSM_PMEM_SIZE
#define MSM_ION_MM_FW_SIZE 0x200000 /* (2MB) */
#define MSM_ION_MM_SIZE MSM_PMEM_ADSP_SIZE
@@ -314,11 +314,17 @@
#endif
}
+static void __init reserve_mdp_memory(void)
+{
+ apq8064_mdp_writeback(apq8064_reserve_table);
+}
+
static void __init apq8064_calculate_reserve_sizes(void)
{
size_pmem_devices();
reserve_pmem_memory();
reserve_ion_memory();
+ reserve_mdp_memory();
}
static struct reserve_info apq8064_reserve_info __initdata = {
@@ -1080,6 +1086,9 @@
static struct platform_device *rumi3_devices[] __initdata = {
&apq8064_device_uart_gsbi1,
&msm_device_sps_apq8064,
+#ifdef CONFIG_MSM_ROTATOR
+ &msm_rotator_device,
+#endif
};
static struct platform_device *cdp_devices[] __initdata = {
@@ -1185,6 +1194,11 @@
BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
}
+static void __init apq8064_allocate_memory_regions(void)
+{
+ apq8064_allocate_fb_region();
+}
+
static void __init apq8064_sim_init(void)
{
struct msm_watchdog_pdata *wdog_pdata = (struct msm_watchdog_pdata *)
@@ -1201,6 +1215,7 @@
ethernet_init();
platform_add_devices(rumi3_devices, ARRAY_SIZE(rumi3_devices));
spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
+ apq8064_init_fb();
}
static void __init apq8064_cdp_init(void)
@@ -1227,6 +1242,7 @@
.handle_irq = gic_handle_irq,
.timer = &msm_timer,
.init_machine = apq8064_rumi3_init,
+ .init_early = apq8064_allocate_memory_regions,
MACHINE_END
MACHINE_START(APQ8064_CDP, "QCT APQ8064 CDP")
diff --git a/arch/arm/mach-msm/board-8064.h b/arch/arm/mach-msm/board-8064.h
index 1634b05..916e569 100644
--- a/arch/arm/mach-msm/board-8064.h
+++ b/arch/arm/mach-msm/board-8064.h
@@ -15,7 +15,7 @@
#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/mfd/pm8xxx/pm8821.h>
-
+#include <mach/msm_memtypes.h>
/* Macros assume PMIC GPIOs and MPPs start at 1 */
#define PM8921_GPIO_BASE NR_GPIO_IRQS
#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE)
@@ -47,4 +47,8 @@
extern struct msm_camera_board_info apq8064_camera_board_info;
void apq8064_init_cam(void);
#define APQ_8064_GSBI4_QUP_I2C_BUS_ID 4
+
+void apq8064_init_fb(void);
+void apq8064_allocate_fb_region(void);
+void apq8064_mdp_writeback(struct memtype_reserve *reserve_table);
#endif
diff --git a/arch/arm/mach-msm/board-8930-display.c b/arch/arm/mach-msm/board-8930-display.c
index b7400d1..f55125c 100644
--- a/arch/arm/mach-msm/board-8930-display.c
+++ b/arch/arm/mach-msm/board-8930-display.c
@@ -148,7 +148,7 @@
static struct regulator *reg_l8, *reg_l23, *reg_l2;
int rc;
- pr_info("%s: state : %d\n", __func__, on);
+ pr_debug("%s: state : %d\n", __func__, on);
if (!dsi_power_on) {
@@ -269,7 +269,7 @@
static int mipi_dsi_panel_power(int on)
{
- pr_info("%s: on=%d\n", __func__, on);
+ pr_debug("%s: on=%d\n", __func__, on);
return mipi_dsi_cdp_panel_power(on);
}
diff --git a/arch/arm/mach-msm/board-8930-regulator.c b/arch/arm/mach-msm/board-8930-regulator.c
index 989fde3..c652238 100644
--- a/arch/arm/mach-msm/board-8930-regulator.c
+++ b/arch/arm/mach-msm/board-8930-regulator.c
@@ -110,6 +110,9 @@
REGULATOR_SUPPLY("dsi_vddio", "mipi_dsi.1"),
REGULATOR_SUPPLY("hdmi_avdd", "hdmi_msm.0"),
REGULATOR_SUPPLY("hdmi_vcc", "hdmi_msm.0"),
+ REGULATOR_SUPPLY("pll_vdd", "pil_riva"),
+ REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.1"),
+ REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.2"),
};
VREG_CONSUMERS(L24) = {
REGULATOR_SUPPLY("8038_l24", NULL),
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index 75d0fb2..fc91337 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -126,7 +126,7 @@
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
-#define MSM_PMEM_KERNEL_EBI1_SIZE 0xB0C000
+#define MSM_PMEM_KERNEL_EBI1_SIZE 0x280000
#define MSM_ION_SF_SIZE MSM_PMEM_SIZE
#define MSM_ION_MM_FW_SIZE 0x200000 /* (2MB) */
#define MSM_ION_MM_SIZE MSM_PMEM_ADSP_SIZE
@@ -1554,9 +1554,20 @@
return;
}
+static struct mxt_config_info mxt_config_array[] = {
+ {
+ .config = mxt_config_data_8930,
+ .config_length = ARRAY_SIZE(mxt_config_data_8930),
+ .family_id = 0x81,
+ .variant_id = 0x01,
+ .version = 0x10,
+ .build = 0xAA,
+ },
+};
+
static struct mxt_platform_data mxt_platform_data_8930 = {
- .config = mxt_config_data_8930,
- .config_length = ARRAY_SIZE(mxt_config_data_8930),
+ .config_array = mxt_config_array,
+ .config_array_size = ARRAY_SIZE(mxt_config_array),
.x_size = 1067,
.y_size = 566,
.irqflags = IRQF_TRIGGER_FALLING,
@@ -1701,14 +1712,6 @@
.tsens_num_sensor = 5,
};
-static struct platform_device msm_tsens_device = {
- .name = "tsens8960-tm",
- .id = -1,
- .dev = {
- .platform_data = &msm_tsens_pdata,
- },
-};
-
#ifdef CONFIG_MSM_FAKE_BATTERY
static struct platform_device fish_battery_device = {
.name = "fish_battery",
@@ -1891,7 +1894,6 @@
&msm_bus_mm_fabric,
&msm_bus_sys_fpb,
&msm_bus_cpss_fpb,
- &msm_tsens_device,
};
static void __init msm8930_i2c_init(void)
@@ -2213,6 +2215,7 @@
if (meminfo_init(SYS_MEMORY, SZ_256M) < 0)
pr_err("meminfo_init() failed!\n");
+ msm_tsens_early_init(&msm_tsens_pdata);
BUG_ON(msm_rpm_init(&msm8930_rpm_data));
BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data));
diff --git a/arch/arm/mach-msm/board-8960-display.c b/arch/arm/mach-msm/board-8960-display.c
index 3c7878d..bc45cdf 100644
--- a/arch/arm/mach-msm/board-8960-display.c
+++ b/arch/arm/mach-msm/board-8960-display.c
@@ -15,13 +15,13 @@
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/bootmem.h>
+#include <linux/ion.h>
#include <asm/mach-types.h>
#include <mach/msm_bus_board.h>
#include <mach/msm_memtypes.h>
#include <mach/board.h>
#include <mach/gpio.h>
#include <mach/gpiomux.h>
-#include <linux/ion.h>
#include <mach/ion.h>
#include "devices.h"
@@ -156,7 +156,7 @@
static int gpio21, gpio24, gpio43;
int rc;
- pr_info("%s: on=%d\n", __func__, on);
+ pr_debug("%s: on=%d\n", __func__, on);
gpio21 = PM8921_GPIO_PM_TO_SYS(21); /* disp power enable_n */
gpio43 = PM8921_GPIO_PM_TO_SYS(43); /* Displays Enable (rst_n)*/
@@ -266,7 +266,7 @@
static int gpio43;
int rc;
- pr_info("%s: state : %d\n", __func__, on);
+ pr_debug("%s: state : %d\n", __func__, on);
if (!dsi_power_on) {
@@ -386,7 +386,7 @@
{
int ret;
- pr_info("%s: on=%d\n", __func__, on);
+ pr_debug("%s: on=%d\n", __func__, on);
if (machine_is_msm8960_liquid())
ret = mipi_dsi_liquid_panel_power(on);
diff --git a/arch/arm/mach-msm/board-8960-gpiomux.c b/arch/arm/mach-msm/board-8960-gpiomux.c
index 0dfa1c6..5b632bd 100644
--- a/arch/arm/mach-msm/board-8960-gpiomux.c
+++ b/arch/arm/mach-msm/board-8960-gpiomux.c
@@ -210,7 +210,7 @@
static struct gpiomux_setting ap2mdm_kpdpwr_n_cfg = {
.func = GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_NONE,
+ .pull = GPIOMUX_PULL_DOWN,
};
static struct gpiomux_setting mdp_vsync_suspend_cfg = {
diff --git a/arch/arm/mach-msm/board-8960-regulator.c b/arch/arm/mach-msm/board-8960-regulator.c
index f36c3a1..5520bf9 100644
--- a/arch/arm/mach-msm/board-8960-regulator.c
+++ b/arch/arm/mach-msm/board-8960-regulator.c
@@ -110,6 +110,9 @@
REGULATOR_SUPPLY("8921_l23", NULL),
REGULATOR_SUPPLY("dsi_vddio", "mipi_dsi.1"),
REGULATOR_SUPPLY("hdmi_avdd", "hdmi_msm.0"),
+ REGULATOR_SUPPLY("pll_vdd", "pil_riva"),
+ REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.1"),
+ REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.2"),
};
VREG_CONSUMERS(L24) = {
REGULATOR_SUPPLY("8921_l24", NULL),
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index a0a540f..a3380c6 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -139,7 +139,7 @@
#define MSM_LIQUID_PMEM_SIZE 0x4000000 /* 64 Mbytes */
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
-#define MSM_PMEM_KERNEL_EBI1_SIZE 0xB0C000
+#define MSM_PMEM_KERNEL_EBI1_SIZE 0x280000
#define MSM_ION_SF_SIZE MSM_PMEM_SIZE
#define MSM_ION_MM_FW_SIZE 0x200000 /* (2MB) */
#define MSM_ION_MM_SIZE MSM_PMEM_ADSP_SIZE
@@ -1397,8 +1397,8 @@
},
};
-/* configuration data */
-static const u8 mxt_config_data[] = {
+/* configuration data for mxt1386 */
+static const u8 mxt1386_config_data[] = {
/* T6 Object */
0, 0, 0, 0, 0, 0,
/* T38 Object */
@@ -1444,6 +1444,118 @@
0, 0, 0, 0, 0, 0,
};
+/* configuration data for mxt1386e using V1.0 firmware */
+static const u8 mxt1386e_config_data_v1_0[] = {
+ /* T6 Object */
+ 0, 0, 0, 0, 0, 0,
+ /* T38 Object */
+ 12, 1, 0, 17, 1, 12, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ /* T7 Object */
+ 100, 16, 50,
+ /* T8 Object */
+ 25, 0, 20, 20, 0, 0, 20, 50, 0, 0,
+ /* T9 Object */
+ 131, 0, 0, 26, 42, 0, 32, 80, 2, 5,
+ 0, 5, 5, 0, 10, 30, 10, 10, 255, 2,
+ 85, 5, 10, 10, 10, 10, 135, 55, 70, 40,
+ 10, 5, 0, 0, 0,
+ /* T18 Object */
+ 0, 0,
+ /* T24 Object */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* T25 Object */
+ 3, 0, 60, 115, 156, 99,
+ /* T27 Object */
+ 0, 0, 0, 0, 0, 0, 0,
+ /* T40 Object */
+ 0, 0, 0, 0, 0,
+ /* T42 Object */
+ 2, 0, 255, 0, 255, 0, 0, 0, 0, 0,
+ /* T43 Object */
+ 0, 0, 0, 0, 0, 0, 0,
+ /* T46 Object */
+ 64, 0, 20, 20, 0, 0, 0, 0, 0,
+ /* T47 Object */
+ 0, 0, 0, 0, 0, 0, 3, 64, 66, 0,
+ /* T48 Object */
+ 31, 64, 64, 0, 0, 0, 0, 0, 0, 0,
+ 48, 40, 0, 10, 10, 0, 0, 100, 10, 80,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+ 52, 0, 12, 0, 17, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ /* T56 Object */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 99, 33,
+};
+
+/* configuration data for mxt1386e using V2.1 firmware */
+static const u8 mxt1386e_config_data_v2_1[] = {
+ /* T6 Object */
+ 0, 0, 0, 0, 0, 0,
+ /* T38 Object */
+ 12, 2, 0, 17, 1, 12, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ /* T7 Object */
+ 100, 16, 50,
+ /* T8 Object */
+ 25, 0, 20, 20, 0, 0, 20, 50, 0, 0,
+ /* T9 Object */
+ 131, 0, 0, 26, 42, 0, 32, 80, 2, 5,
+ 0, 5, 5, 0, 10, 30, 10, 10, 255, 2,
+ 85, 5, 10, 10, 10, 10, 135, 55, 70, 40,
+ 10, 5, 0, 0, 0,
+ /* T18 Object */
+ 0, 0,
+ /* T24 Object */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* T25 Object */
+ 3, 0, 60, 115, 156, 99,
+ /* T27 Object */
+ 0, 0, 0, 0, 0, 0, 0,
+ /* T40 Object */
+ 0, 0, 0, 0, 0,
+ /* T42 Object */
+ 2, 0, 255, 0, 255, 0, 0, 0, 0, 0,
+ /* T43 Object */
+ 0, 0, 0, 0, 0, 0, 0, 64, 0, 8,
+ 16,
+ /* T46 Object */
+ 64, 0, 20, 20, 0, 0, 0, 0, 0,
+ /* T47 Object */
+ 0, 0, 0, 0, 0, 0, 3, 64, 66, 0,
+ /* T48 Object */
+ 31, 64, 64, 0, 0, 0, 0, 0, 0, 0,
+ 48, 40, 0, 10, 10, 0, 0, 100, 10, 80,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+ 52, 0, 12, 0, 17, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ /* T56 Object */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 99, 33, 0, 149, 24, 193, 255, 255, 255,
+ 255,
+};
+
#define MXT_TS_GPIO_IRQ 11
#define MXT_TS_LDO_EN_GPIO 50
#define MXT_TS_RESET_GPIO 52
@@ -1472,9 +1584,36 @@
gpio_free(MXT_TS_LDO_EN_GPIO);
}
+static struct mxt_config_info mxt_config_array[] = {
+ {
+ .config = mxt1386_config_data,
+ .config_length = ARRAY_SIZE(mxt1386_config_data),
+ .family_id = 0xA0,
+ .variant_id = 0x0,
+ .version = 0x10,
+ .build = 0xAA,
+ },
+ {
+ .config = mxt1386e_config_data_v1_0,
+ .config_length = ARRAY_SIZE(mxt1386e_config_data_v1_0),
+ .family_id = 0xA0,
+ .variant_id = 0x2,
+ .version = 0x10,
+ .build = 0xAA,
+ },
+ {
+ .config = mxt1386e_config_data_v2_1,
+ .config_length = ARRAY_SIZE(mxt1386e_config_data_v2_1),
+ .family_id = 0xA0,
+ .variant_id = 0x7,
+ .version = 0x21,
+ .build = 0xAA,
+ },
+};
+
static struct mxt_platform_data mxt_platform_data = {
- .config = mxt_config_data,
- .config_length = ARRAY_SIZE(mxt_config_data),
+ .config_array = mxt_config_array,
+ .config_array_size = ARRAY_SIZE(mxt_config_array),
.x_size = 1365,
.y_size = 767,
.irqflags = IRQF_TRIGGER_FALLING,
@@ -1572,14 +1711,6 @@
.tsens_num_sensor = 5,
};
-static struct platform_device msm_tsens_device = {
- .name = "tsens8960-tm",
- .id = -1,
- .dev = {
- .platform_data = &msm_tsens_pdata,
- },
-};
-
#ifdef CONFIG_MSM_FAKE_BATTERY
static struct platform_device fish_battery_device = {
.name = "fish_battery",
@@ -1797,7 +1928,6 @@
&msm_bus_mm_fabric,
&msm_bus_sys_fpb,
&msm_bus_cpss_fpb,
- &msm_tsens_device,
};
static void __init msm8960_i2c_init(void)
@@ -2177,6 +2307,7 @@
&msm8960_device_watchdog.dev.platform_data;
wdog_pdata->bark_time = 15000;
+ msm_tsens_early_init(&msm_tsens_pdata);
BUG_ON(msm_rpm_init(&msm8960_rpm_data));
BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data));
regulator_suppress_info_printing();
@@ -2213,6 +2344,7 @@
static void __init msm8960_rumi3_init(void)
{
+ msm_tsens_early_init(&msm_tsens_pdata);
BUG_ON(msm_rpm_init(&msm8960_rpm_data));
BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data));
regulator_suppress_info_printing();
@@ -2247,6 +2379,7 @@
if (meminfo_init(SYS_MEMORY, SZ_256M) < 0)
pr_err("meminfo_init() failed!\n");
+ msm_tsens_early_init(&msm_tsens_pdata);
BUG_ON(msm_rpm_init(&msm8960_rpm_data));
BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data));
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index a21abb8..1f2a5ec 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -793,10 +793,75 @@
__func__, rc);
}
+/* 8625 keypad device information */
+static unsigned int kp_row_gpios_8625[] = {31};
+static unsigned int kp_col_gpios_8625[] = {36, 37};
+
+static const unsigned short keymap_8625[] = {
+ KEY_VOLUMEUP,
+ KEY_VOLUMEDOWN,
+};
+
+static struct gpio_event_matrix_info kp_matrix_info_8625 = {
+ .info.func = gpio_event_matrix_func,
+ .keymap = keymap_8625,
+ .output_gpios = kp_row_gpios_8625,
+ .input_gpios = kp_col_gpios_8625,
+ .noutputs = ARRAY_SIZE(kp_row_gpios_8625),
+ .ninputs = ARRAY_SIZE(kp_col_gpios_8625),
+ .settle_time.tv_nsec = 40 * NSEC_PER_USEC,
+ .poll_time.tv_nsec = 20 * NSEC_PER_MSEC,
+ .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE |
+ GPIOKPF_PRINT_UNMAPPED_KEYS,
+};
+
+static struct gpio_event_info *kp_info_8625[] = {
+ &kp_matrix_info_8625.info,
+};
+static struct gpio_event_platform_data kp_pdata_8625 = {
+ .name = "8625_kp",
+ .info = kp_info_8625,
+ .info_count = ARRAY_SIZE(kp_info_8625)
+};
+
+static struct platform_device kp_pdev_8625 = {
+ .name = GPIO_EVENT_DEV_NAME,
+ .id = -1,
+ .dev = {
+ .platform_data = &kp_pdata_8625,
+ },
+};
+
+#define LED_RED_GPIO_8625 49
+#define LED_GREEN_GPIO_8625 34
+
+static struct gpio_led gpio_leds_config_8625[] = {
+ {
+ .name = "green",
+ .gpio = LED_GREEN_GPIO_8625,
+ },
+ {
+ .name = "red",
+ .gpio = LED_RED_GPIO_8625,
+ },
+};
+
+static struct gpio_led_platform_data gpio_leds_pdata_8625 = {
+ .num_leds = ARRAY_SIZE(gpio_leds_config_8625),
+ .leds = gpio_leds_config_8625,
+};
+
+static struct platform_device gpio_leds_8625 = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &gpio_leds_pdata_8625,
+ },
+};
+
static void msm7627a_add_io_devices(void)
{
- if (machine_is_msm7627a_evb())
- return;
+ int rc;
#if defined(CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C) || \
defined(CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C_MODULE)
@@ -810,6 +875,30 @@
msm_init_pmic_vibrator();
#endif
+ /* keypad */
+ if (machine_is_msm7627a_evb())
+ platform_device_register(&kp_pdev_8625);
+
+ /* leds */
+ if (machine_is_msm7627a_evb()) {
+ rc = gpio_tlmm_config(GPIO_CFG(LED_RED_GPIO_8625, 0,
+ GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP,
+ GPIO_CFG_16MA), GPIO_CFG_ENABLE);
+ if (rc) {
+ pr_err("%s: gpio_tlmm_config for %d failed\n",
+ __func__, LED_RED_GPIO_8625);
+ }
+
+ rc = gpio_tlmm_config(GPIO_CFG(LED_GREEN_GPIO_8625, 0,
+ GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP,
+ GPIO_CFG_16MA), GPIO_CFG_ENABLE);
+ if (rc) {
+ pr_err("%s: gpio_tlmm_config for %d failed\n",
+ __func__, LED_GREEN_GPIO_8625);
+ }
+
+ platform_device_register(&gpio_leds_8625);
+ }
}
#define UART1DM_RX_GPIO 45
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index d9e1724..e16cac5 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -1293,10 +1293,10 @@
}
static struct clk_freq_tbl clk_tbl_gsbi_uart[] = {
F_GSBI_UART( 0, gnd, 1, 0, 0),
- F_GSBI_UART( 1843200, pll8, 1, 3, 625),
- F_GSBI_UART( 3686400, pll8, 1, 6, 625),
- F_GSBI_UART( 7372800, pll8, 1, 12, 625),
- F_GSBI_UART(14745600, pll8, 1, 24, 625),
+ F_GSBI_UART( 1843200, pll8, 2, 6, 625),
+ F_GSBI_UART( 3686400, pll8, 2, 12, 625),
+ F_GSBI_UART( 7372800, pll8, 2, 24, 625),
+ F_GSBI_UART(14745600, pll8, 2, 48, 625),
F_GSBI_UART(16000000, pll8, 4, 1, 6),
F_GSBI_UART(24000000, pll8, 4, 1, 4),
F_GSBI_UART(32000000, pll8, 4, 1, 3),
@@ -4813,6 +4813,8 @@
static struct clk_lookup msm_clocks_8064[] = {
CLK_LOOKUP("cxo", cxo_clk.c, NULL),
+ CLK_LOOKUP("cxo", cxo_clk.c, "wcnss_wlan.0"),
+ CLK_LOOKUP("cxo", cxo_clk.c, "pil_riva"),
CLK_LOOKUP("pll2", pll2_clk.c, NULL),
CLK_LOOKUP("pll8", pll8_clk.c, NULL),
CLK_LOOKUP("pll4", pll4_clk.c, NULL),
@@ -5050,6 +5052,8 @@
static struct clk_lookup msm_clocks_8960_v1[] __initdata = {
CLK_LOOKUP("cxo", cxo_clk.c, NULL),
+ CLK_LOOKUP("cxo", cxo_clk.c, "wcnss_wlan.0"),
+ CLK_LOOKUP("cxo", cxo_clk.c, "pil_riva"),
CLK_LOOKUP("pll2", pll2_clk.c, NULL),
CLK_LOOKUP("pll8", pll8_clk.c, NULL),
CLK_LOOKUP("pll4", pll4_clk.c, NULL),
diff --git a/arch/arm/mach-msm/clock-9615.c b/arch/arm/mach-msm/clock-9615.c
index 5ddeef9..3592c43 100644
--- a/arch/arm/mach-msm/clock-9615.c
+++ b/arch/arm/mach-msm/clock-9615.c
@@ -483,9 +483,9 @@
}
static struct clk_freq_tbl clk_tbl_gsbi_uart[] = {
F_GSBI_UART( 0, gnd, 1, 0, 0),
- F_GSBI_UART( 3686400, pll8, 1, 6, 625),
- F_GSBI_UART( 7372800, pll8, 1, 12, 625),
- F_GSBI_UART(14745600, pll8, 1, 24, 625),
+ F_GSBI_UART( 3686400, pll8, 2, 12, 625),
+ F_GSBI_UART( 7372800, pll8, 2, 24, 625),
+ F_GSBI_UART(14745600, pll8, 2, 48, 625),
F_GSBI_UART(16000000, pll8, 4, 1, 6),
F_GSBI_UART(24000000, pll8, 4, 1, 4),
F_GSBI_UART(32000000, pll8, 4, 1, 3),
diff --git a/arch/arm/mach-msm/clock-local.c b/arch/arm/mach-msm/clock-local.c
index 050ec41..2d5ae78 100644
--- a/arch/arm/mach-msm/clock-local.c
+++ b/arch/arm/mach-msm/clock-local.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-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
@@ -454,12 +454,20 @@
* Frequency-related functions
*/
-/* Set a clock's frequency. */
-static int _rcg_clk_set_rate(struct rcg_clk *clk, struct clk_freq_tbl *nf)
+/* Set a clock to an exact rate. */
+int rcg_clk_set_rate(struct clk *c, unsigned long rate)
{
- struct clk_freq_tbl *cf;
- int rc = 0;
+ struct rcg_clk *clk = to_rcg_clk(c);
+ struct clk_freq_tbl *nf, *cf;
struct clk *chld;
+ int rc = 0;
+
+ for (nf = clk->freq_tbl; nf->freq_hz != FREQ_END
+ && nf->freq_hz != rate; nf++)
+ ;
+
+ if (nf->freq_hz == FREQ_END)
+ return -EINVAL;
/* Check if frequency is actually changed. */
cf = clk->current_freq;
@@ -523,22 +531,6 @@
return rc;
}
-/* Set a clock to an exact rate. */
-int rcg_clk_set_rate(struct clk *c, unsigned long rate)
-{
- struct rcg_clk *clk = to_rcg_clk(c);
- struct clk_freq_tbl *nf;
-
- for (nf = clk->freq_tbl; nf->freq_hz != FREQ_END
- && nf->freq_hz != rate; nf++)
- ;
-
- if (nf->freq_hz == FREQ_END)
- return -EINVAL;
-
- return _rcg_clk_set_rate(clk, nf);
-}
-
/* Get the currently-set rate of a clock in Hz. */
unsigned long rcg_clk_get_rate(struct clk *c)
{
diff --git a/arch/arm/mach-msm/clock-local.h b/arch/arm/mach-msm/clock-local.h
index 9195fea..9292e5d 100644
--- a/arch/arm/mach-msm/clock-local.h
+++ b/arch/arm/mach-msm/clock-local.h
@@ -34,12 +34,12 @@
/* NS Registers */
#define NS(n_msb, n_lsb, n, m, mde_lsb, d_msb, d_lsb, d, s_msb, s_lsb, s) \
- (BVAL(n_msb, n_lsb, ~(n-m)) \
+ (BVAL(n_msb, n_lsb, ~(n-m) * !!(n)) \
| (BVAL((mde_lsb+1), mde_lsb, MN_MODE_DUAL_EDGE) * !!(n)) \
| BVAL(d_msb, d_lsb, (d-1)) | BVAL(s_msb, s_lsb, s))
#define NS_MM(n_msb, n_lsb, n, m, d_msb, d_lsb, d, s_msb, s_lsb, s) \
- (BVAL(n_msb, n_lsb, ~(n-m)) | BVAL(d_msb, d_lsb, (d-1)) \
+ (BVAL(n_msb, n_lsb, ~(n-m) * !!(n))|BVAL(d_msb, d_lsb, (d-1)) \
| BVAL(s_msb, s_lsb, s))
#define NS_DIVSRC(d_msb, d_lsb, d, s_msb, s_lsb, s) \
@@ -52,14 +52,14 @@
BVAL(s_msb, s_lsb, s)
#define NS_MND_BANKED4(n0_lsb, n1_lsb, n, m, s0_lsb, s1_lsb, s) \
- (BVAL((n0_lsb+3), n0_lsb, ~(n-m)) \
- | BVAL((n1_lsb+3), n1_lsb, ~(n-m)) \
+ (BVAL((n0_lsb+3), n0_lsb, ~(n-m) * !!(n)) \
+ | BVAL((n1_lsb+3), n1_lsb, ~(n-m) * !!(n)) \
| BVAL((s0_lsb+2), s0_lsb, s) \
| BVAL((s1_lsb+2), s1_lsb, s))
#define NS_MND_BANKED8(n0_lsb, n1_lsb, n, m, s0_lsb, s1_lsb, s) \
- (BVAL((n0_lsb+7), n0_lsb, ~(n-m)) \
- | BVAL((n1_lsb+7), n1_lsb, ~(n-m)) \
+ (BVAL((n0_lsb+7), n0_lsb, ~(n-m) * !!(n)) \
+ | BVAL((n1_lsb+7), n1_lsb, ~(n-m) * !!(n)) \
| BVAL((s0_lsb+2), s0_lsb, s) \
| BVAL((s1_lsb+2), s1_lsb, s))
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index d463c41..0ced722 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -1662,12 +1662,19 @@
};
#endif
+static struct platform_device msm_lvds_device = {
+ .name = "lvds",
+ .id = 0,
+};
+
void __init msm_fb_register_device(char *name, void *data)
{
if (!strncmp(name, "mdp", 3))
msm_register_device(&msm_mdp_device, data);
else if (!strncmp(name, "mipi_dsi", 8))
msm_register_device(&msm_mipi_dsi1_device, data);
+ else if (!strncmp(name, "lvds", 4))
+ msm_register_device(&msm_lvds_device, data);
#ifdef CONFIG_MSM_BUS_SCALING
else if (!strncmp(name, "dtv", 3))
msm_register_device(&msm_dtv_device, data);
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index d9bc654..d11c13f 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -303,6 +303,8 @@
MDP_REV_40,
MDP_REV_41,
MDP_REV_42,
+ MDP_REV_43,
+ MDP_REV_44,
};
struct msm_panel_common_pdata {
diff --git a/arch/arm/mach-msm/msm_xo.c b/arch/arm/mach-msm/msm_xo.c
index 802ee2a..af272af 100644
--- a/arch/arm/mach-msm/msm_xo.c
+++ b/arch/arm/mach-msm/msm_xo.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -149,7 +149,14 @@
(msm_xo_sources[MSM_XO_TCXO_A0].mode << 16) |
(msm_xo_sources[MSM_XO_TCXO_A1].mode << 24) |
(msm_xo_sources[MSM_XO_TCXO_A2].mode << 28) |
- ((msm_xo_sources[MSM_XO_CORE].mode ? 1 : 0) << 20);
+ /*
+ * 8660 RPM has XO_CORE at bit 18 and 8960 RPM has
+ * XO_CORE at bit 20. Since the opposite bit is
+ * reserved in both cases, just set both and be
+ * done with it.
+ */
+ ((msm_xo_sources[MSM_XO_CORE].mode ? 1 : 0) << 20) |
+ ((msm_xo_sources[MSM_XO_CORE].mode ? 1 : 0) << 18);
ret = msm_rpm_set_noirq(MSM_RPM_CTX_SET_0, &cmd, 1);
}
diff --git a/arch/arm/mach-msm/pil-q6v4.c b/arch/arm/mach-msm/pil-q6v4.c
index d8ebab5..cdbfc48 100644
--- a/arch/arm/mach-msm/pil-q6v4.c
+++ b/arch/arm/mach-msm/pil-q6v4.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
@@ -19,6 +19,7 @@
#include <linux/elf.h>
#include <linux/delay.h>
#include <linux/err.h>
+#include <linux/workqueue.h>
#include <mach/msm_bus.h>
#include <mach/msm_iomap.h>
@@ -68,9 +69,10 @@
void __iomem *modem_base;
unsigned long start_addr;
struct regulator *vreg;
+ struct regulator *pll_supply;
bool vreg_enabled;
struct msm_xo_voter *xo;
- struct timer_list xo_timer;
+ struct delayed_work work;
};
static int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
@@ -87,27 +89,32 @@
return 0;
}
-static void pil_q6v4_make_xo_proxy_votes(struct device *dev)
+static void pil_q6v4_make_proxy_votes(struct device *dev)
{
struct q6v4_data *drv = dev_get_drvdata(dev);
+ int ret;
msm_xo_mode_vote(drv->xo, MSM_XO_MODE_ON);
- mod_timer(&drv->xo_timer, jiffies+msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
+ if (drv->pll_supply) {
+ ret = regulator_enable(drv->pll_supply);
+ if (ret)
+ dev_err(dev, "failed to enable pll supply\n");
+ }
+ schedule_delayed_work(&drv->work, msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
}
-static void pil_q6v4_remove_xo_proxy_votes(unsigned long data)
+static void pil_q6v4_remove_proxy_votes(struct work_struct *work)
{
- struct q6v4_data *drv = (struct q6v4_data *)data;
-
+ struct q6v4_data *drv = container_of(work, struct q6v4_data, work.work);
+ if (drv->pll_supply)
+ regulator_disable(drv->pll_supply);
msm_xo_mode_vote(drv->xo, MSM_XO_MODE_OFF);
}
-static void pil_q6v4_remove_xo_proxy_votes_now(struct device *dev)
+static void pil_q6v4_remove_proxy_votes_now(struct device *dev)
{
struct q6v4_data *drv = dev_get_drvdata(dev);
-
- if (del_timer(&drv->xo_timer))
- pil_q6v4_remove_xo_proxy_votes((unsigned long)drv);
+ flush_delayed_work(&drv->work);
}
static int pil_q6v4_power_up(struct device *dev)
@@ -184,7 +191,7 @@
const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
- pil_q6v4_make_xo_proxy_votes(pil->dev);
+ pil_q6v4_make_proxy_votes(pil->dev);
err = pil_q6v4_power_up(pil->dev);
if (err)
@@ -300,7 +307,7 @@
drv->vreg_enabled = false;
}
- pil_q6v4_remove_xo_proxy_votes_now(pil->dev);
+ pil_q6v4_remove_proxy_votes_now(pil->dev);
return 0;
}
@@ -324,7 +331,7 @@
const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
int err;
- pil_q6v4_make_xo_proxy_votes(pil->dev);
+ pil_q6v4_make_proxy_votes(pil->dev);
err = pil_q6v4_power_up(pil->dev);
if (err)
@@ -355,7 +362,7 @@
drv->vreg_enabled = false;
}
- pil_q6v4_remove_xo_proxy_votes_now(pil->dev);
+ pil_q6v4_remove_proxy_votes_now(pil->dev);
return ret;
}
@@ -373,6 +380,7 @@
struct q6v4_data *drv;
struct resource *res;
struct pil_desc *desc;
+ int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
@@ -396,9 +404,26 @@
}
desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
- if (!drv)
+ if (!desc)
return -ENOMEM;
+ drv->pll_supply = regulator_get(&pdev->dev, "pll_vdd");
+ if (IS_ERR(drv->pll_supply)) {
+ drv->pll_supply = NULL;
+ } else {
+ ret = regulator_set_voltage(drv->pll_supply, 1800000, 1800000);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to set pll voltage\n");
+ goto err;
+ }
+
+ ret = regulator_set_optimum_mode(drv->pll_supply, 100000);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to set pll optimum mode\n");
+ goto err;
+ }
+ }
+
desc->name = pdata->name;
desc->depends_on = pdata->depends;
desc->dev = &pdev->dev;
@@ -412,26 +437,39 @@
}
drv->vreg = regulator_get(&pdev->dev, "core_vdd");
- if (IS_ERR(drv->vreg))
- return PTR_ERR(drv->vreg);
-
- setup_timer(&drv->xo_timer, pil_q6v4_remove_xo_proxy_votes,
- (unsigned long)drv);
- drv->xo = msm_xo_get(pdata->xo_id, pdata->name);
- if (IS_ERR(drv->xo))
- return PTR_ERR(drv->xo);
-
- if (msm_pil_register(desc)) {
- regulator_put(drv->vreg);
- return -EINVAL;
+ if (IS_ERR(drv->vreg)) {
+ ret = PTR_ERR(drv->vreg);
+ goto err;
}
+
+ drv->xo = msm_xo_get(pdata->xo_id, pdata->name);
+ if (IS_ERR(drv->xo)) {
+ ret = PTR_ERR(drv->xo);
+ goto err_xo;
+ }
+ INIT_DELAYED_WORK(&drv->work, pil_q6v4_remove_proxy_votes);
+
+ ret = msm_pil_register(desc);
+ if (ret)
+ goto err_pil;
return 0;
+err_pil:
+ cancel_delayed_work_sync(&drv->work);
+ msm_xo_put(drv->xo);
+err_xo:
+ regulator_put(drv->vreg);
+err:
+ regulator_put(drv->pll_supply);
+ return ret;
}
static int __devexit pil_q6v4_driver_exit(struct platform_device *pdev)
{
struct q6v4_data *drv = platform_get_drvdata(pdev);
+ cancel_delayed_work_sync(&drv->work);
+ msm_xo_put(drv->xo);
regulator_put(drv->vreg);
+ regulator_put(drv->pll_supply);
return 0;
}
diff --git a/arch/arm/mach-msm/pil-riva.c b/arch/arm/mach-msm/pil-riva.c
index 6848c59..7e78a15 100644
--- a/arch/arm/mach-msm/pil-riva.c
+++ b/arch/arm/mach-msm/pil-riva.c
@@ -18,9 +18,11 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+#include <linux/clk.h>
#include <mach/msm_iomap.h>
-#include <mach/msm_xo.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -80,38 +82,40 @@
struct riva_data {
void __iomem *base;
unsigned long start_addr;
- struct msm_xo_voter *xo;
- struct timer_list xo_timer;
+ struct clk *xo;
+ bool use_cxo;
+ struct delayed_work work;
+ struct regulator *pll_supply;
};
-static void pil_riva_make_xo_proxy_votes(struct device *dev)
+static void pil_riva_make_proxy_votes(struct device *dev)
{
struct riva_data *drv = dev_get_drvdata(dev);
+ int ret;
- msm_xo_mode_vote(drv->xo, MSM_XO_MODE_ON);
- mod_timer(&drv->xo_timer, jiffies+msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
+ if (drv->use_cxo) {
+ ret = clk_prepare_enable(drv->xo);
+ if (ret)
+ dev_err(dev, "failed to enable xo\n");
+ }
+ ret = regulator_enable(drv->pll_supply);
+ if (ret)
+ dev_err(dev, "failed to enable pll supply\n");
+ schedule_delayed_work(&drv->work, msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
}
-static void pil_riva_remove_xo_proxy_votes(unsigned long data)
+static void pil_riva_remove_proxy_votes(struct work_struct *work)
{
- struct riva_data *drv = (struct riva_data *)data;
-
- msm_xo_mode_vote(drv->xo, MSM_XO_MODE_OFF);
+ struct riva_data *drv = container_of(work, struct riva_data, work.work);
+ regulator_disable(drv->pll_supply);
+ if (drv->use_cxo)
+ clk_disable_unprepare(drv->xo);
}
-static void pil_riva_remove_xo_proxy_votes_now(struct device *dev)
+static void pil_riva_remove_proxy_votes_now(struct device *dev)
{
struct riva_data *drv = dev_get_drvdata(dev);
-
- if (del_timer(&drv->xo_timer))
- pil_riva_remove_xo_proxy_votes((unsigned long)drv);
-}
-
-static bool cxo_is_needed(struct riva_data *drv)
-{
- u32 reg = readl_relaxed(drv->base + RIVA_PMU_CFG);
- return (reg & RIVA_PMU_CFG_IRIS_XO_MODE)
- != RIVA_PMU_CFG_IRIS_XO_MODE_48;
+ flush_delayed_work(&drv->work);
}
static int nop_verify_blob(struct pil_desc *pil, u32 phy_addr, size_t size)
@@ -128,30 +132,38 @@
return 0;
}
+static bool cxo_is_needed(struct riva_data *drv)
+{
+ u32 reg = readl_relaxed(drv->base + RIVA_PMU_CFG);
+ return (reg & RIVA_PMU_CFG_IRIS_XO_MODE)
+ != RIVA_PMU_CFG_IRIS_XO_MODE_48;
+}
+
static int pil_riva_reset(struct pil_desc *pil)
{
u32 reg, sel;
- bool use_cxo;
struct riva_data *drv = dev_get_drvdata(pil->dev);
void __iomem *base = drv->base;
unsigned long start_addr = drv->start_addr;
+ int ret;
+ ret = clk_prepare_enable(drv->xo);
+ if (ret)
+ return ret;
/* Enable A2XB bridge */
reg = readl_relaxed(base + RIVA_PMU_A2XB_CFG);
reg |= RIVA_PMU_A2XB_CFG_EN;
writel_relaxed(reg, base + RIVA_PMU_A2XB_CFG);
- /* Proxy-vote for CXO if it's needed */
- use_cxo = cxo_is_needed(drv);
- if (use_cxo)
- pil_riva_make_xo_proxy_votes(pil->dev);
+ drv->use_cxo = cxo_is_needed(drv);
+ pil_riva_make_proxy_votes(pil->dev);
/* Program PLL 13 to 960 MHz */
reg = readl_relaxed(RIVA_PLL_MODE);
reg &= ~(PLL_MODE_BYPASSNL | PLL_MODE_OUTCTRL | PLL_MODE_RESET_N);
writel_relaxed(reg, RIVA_PLL_MODE);
- if (use_cxo)
+ if (drv->use_cxo)
writel_relaxed(0x40000C00 | 50, RIVA_PLL_L_VAL);
else
writel_relaxed(0x40000C00 | 40, RIVA_PLL_L_VAL);
@@ -161,7 +173,7 @@
reg = readl_relaxed(RIVA_PLL_MODE);
reg &= ~(PLL_MODE_REF_XO_SEL);
- reg |= use_cxo ? PLL_MODE_REF_XO_SEL_CXO : PLL_MODE_REF_XO_SEL_RF;
+ reg |= drv->use_cxo ? PLL_MODE_REF_XO_SEL_CXO : PLL_MODE_REF_XO_SEL_RF;
writel_relaxed(reg, RIVA_PLL_MODE);
/* Enable PLL 13 */
@@ -226,6 +238,7 @@
/* Take cCPU out of reset */
reg |= RIVA_PMU_OVRD_VAL_CCPU_RESET;
writel_relaxed(reg, base + RIVA_PMU_OVRD_VAL);
+ clk_disable_unprepare(drv->xo);
return 0;
}
@@ -234,7 +247,11 @@
{
struct riva_data *drv = dev_get_drvdata(pil->dev);
u32 reg;
+ int ret;
+ ret = clk_prepare_enable(drv->xo);
+ if (ret)
+ return ret;
/* Put cCPU and cCPU clock into reset */
reg = readl_relaxed(drv->base + RIVA_PMU_OVRD_VAL);
reg &= ~(RIVA_PMU_OVRD_VAL_CCPU_RESET | RIVA_PMU_OVRD_VAL_CCPU_CLK);
@@ -253,7 +270,8 @@
writel_relaxed(0, RIVA_RESET);
mb();
- pil_riva_remove_xo_proxy_votes_now(pil->dev);
+ clk_disable_unprepare(drv->xo);
+ pil_riva_remove_proxy_votes_now(pil->dev);
return 0;
}
@@ -274,19 +292,29 @@
static int pil_riva_reset_trusted(struct pil_desc *pil)
{
struct riva_data *drv = dev_get_drvdata(pil->dev);
+ int ret;
- /* Proxy-vote for CXO if it's needed */
- if (cxo_is_needed(drv))
- pil_riva_make_xo_proxy_votes(pil->dev);
-
- return pas_auth_and_reset(PAS_RIVA);
+ ret = clk_prepare_enable(drv->xo);
+ if (ret)
+ return ret;
+ /* Proxy-vote for resources RIVA needs */
+ pil_riva_make_proxy_votes(pil->dev);
+ ret = pas_auth_and_reset(PAS_RIVA);
+ clk_disable_unprepare(drv->xo);
+ return ret;
}
static int pil_riva_shutdown_trusted(struct pil_desc *pil)
{
- int ret = pas_shutdown(PAS_RIVA);
+ int ret;
+ struct riva_data *drv = dev_get_drvdata(pil->dev);
- pil_riva_remove_xo_proxy_votes_now(pil->dev);
+ ret = clk_prepare_enable(drv->xo);
+ if (ret)
+ return ret;
+ ret = pas_shutdown(PAS_RIVA);
+ pil_riva_remove_proxy_votes_now(pil->dev);
+ clk_disable_unprepare(drv->xo);
return ret;
}
@@ -303,6 +331,7 @@
struct riva_data *drv;
struct resource *res;
struct pil_desc *desc;
+ int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
@@ -321,6 +350,23 @@
if (!desc)
return -ENOMEM;
+ drv->pll_supply = regulator_get(&pdev->dev, "pll_vdd");
+ if (IS_ERR(drv->pll_supply)) {
+ dev_err(&pdev->dev, "failed to get pll supply\n");
+ return PTR_ERR(drv->pll_supply);
+ }
+ ret = regulator_set_voltage(drv->pll_supply, 1800000, 1800000);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to set pll supply voltage\n");
+ goto err;
+ }
+
+ ret = regulator_set_optimum_mode(drv->pll_supply, 100000);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to set pll supply optimum mode\n");
+ goto err;
+ }
+
desc->name = "wcnss";
desc->dev = &pdev->dev;
@@ -332,17 +378,31 @@
dev_info(&pdev->dev, "using non-secure boot\n");
}
- setup_timer(&drv->xo_timer, pil_riva_remove_xo_proxy_votes,
- (unsigned long)drv);
- drv->xo = msm_xo_get(MSM_XO_CXO, desc->name);
- if (IS_ERR(drv->xo))
- return PTR_ERR(drv->xo);
+ drv->xo = clk_get(&pdev->dev, "cxo");
+ if (IS_ERR(drv->xo)) {
+ ret = PTR_ERR(drv->xo);
+ goto err;
+ }
+ INIT_DELAYED_WORK(&drv->work, pil_riva_remove_proxy_votes);
- return msm_pil_register(desc);
+ ret = msm_pil_register(desc);
+ if (ret)
+ goto err_register;
+ return 0;
+err_register:
+ cancel_delayed_work_sync(&drv->work);
+ clk_put(drv->xo);
+err:
+ regulator_put(drv->pll_supply);
+ return ret;
}
static int __devexit pil_riva_remove(struct platform_device *pdev)
{
+ struct riva_data *drv = platform_get_drvdata(pdev);
+ cancel_delayed_work_sync(&drv->work);
+ clk_put(drv->xo);
+ regulator_put(drv->pll_supply);
return 0;
}
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index 754d76f..9bdc2b3 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -63,6 +63,7 @@
MSM_PM_DEBUG_SUSPEND_LIMITS = BIT(2),
MSM_PM_DEBUG_CLOCK = BIT(3),
MSM_PM_DEBUG_RESET_VECTOR = BIT(4),
+ MSM_PM_DEBUG_IDLE_CLK = BIT(5),
MSM_PM_DEBUG_IDLE = BIT(6),
MSM_PM_DEBUG_IDLE_LIMITS = BIT(7),
MSM_PM_DEBUG_HOTPLUG = BIT(8),
@@ -921,6 +922,9 @@
if (sleep_delay == 0) /* 0 would mean infinite time */
sleep_delay = 1;
+ if (MSM_PM_DEBUG_IDLE_CLK & msm_pm_debug_mask)
+ clock_debug_print_enabled();
+
ret = msm_rpmrs_enter_sleep(
sleep_delay, msm_pm_idle_rs_limits, true, notify_rpm);
if (!ret) {
diff --git a/arch/arm/mach-msm/qdsp6v2/board-msm8x60-audio.c b/arch/arm/mach-msm/qdsp6v2/board-msm8x60-audio.c
index 997fdf8..0181cb5 100644
--- a/arch/arm/mach-msm/qdsp6v2/board-msm8x60-audio.c
+++ b/arch/arm/mach-msm/qdsp6v2/board-msm8x60-audio.c
@@ -2229,7 +2229,7 @@
};
static struct adie_codec_action_unit ftm_spkr_r_adie_lp_rx_actions[] =
- FTM_SPKR_RX_LB;
+ SPKR_R_RX;
static struct adie_codec_hwsetting_entry ftm_spkr_r_adie_lp_rx_settings[] = {
{
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 6853654..e147b0e 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -80,11 +80,14 @@
case MSM8660_MACHINE_ID:
return APQ8060_TOOLS_ID;
case AO8960_MACHINE_ID:
+ case MSM8260A_MACHINE_ID:
return AO8960_TOOLS_ID;
case APQ8064_MACHINE_ID:
return APQ8064_TOOLS_ID;
case MSM8930_MACHINE_ID:
return MSM8930_TOOLS_ID;
+ case MSM8974_MACHINE_ID:
+ return MSM8974_TOOLS_ID;
default:
return 0;
}
@@ -105,12 +108,28 @@
case APQ8030_MACHINE_ID:
case MSM8627_MACHINE_ID:
case MSM8227_MACHINE_ID:
+ case MSM8974_MACHINE_ID:
+ case MDM9615_MACHINE_ID:
+ case MSM8260A_MACHINE_ID:
return 1;
default:
return 0;
}
}
+/*
+ * This will return TRUE for targets which support apps as master.
+ * Thus, SW DLOAD and Mode Reset are supported on apps processor.
+ * This applies to 8960 and newer targets.
+ */
+int chk_apps_master(void)
+{
+ if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm9615())
+ return 1;
+ else
+ return 0;
+}
+
void __diag_smd_send_req(void)
{
void *buf = NULL;
@@ -532,8 +551,7 @@
} else {
if (len > 0) {
if (entry.client_id == MODEM_PROC && driver->ch) {
- if ((cpu_is_msm8960() || cpu_is_msm8930() ||
- cpu_is_msm9615()) &&
+ if (chk_apps_master() &&
(int)(*(char *)buf) == MODE_CMD)
if ((int)(*(char *)(buf+1)) ==
RESET_ID)
@@ -573,8 +591,7 @@
temp += 2;
data_type = APPS_DATA;
/* Dont send any command other than mode reset */
- if ((cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm9615()) &&
- cmd_code == MODE_CMD) {
+ if (chk_apps_master() && cmd_code == MODE_CMD) {
if (subsys_id != RESET_ID)
data_type = MODEM_DATA;
}
@@ -858,8 +875,7 @@
return 0;
}
/* Check for download command */
- else if ((cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_msm8930()
- || cpu_is_msm9615()) && (*buf == 0x3A)) {
+ else if ((cpu_is_msm8x60() || chk_apps_master()) && (*buf == 0x3A)) {
/* send response back */
driver->apps_rsp_buf[0] = *buf;
ENCODE_RSP_AND_SEND(0);
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 9730d4f..f2a2210 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.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
@@ -135,7 +135,7 @@
{
int r = 0;
- /* open control ports only on 8960 */
+ /* open control ports only on 8960 & newer targets */
if (chk_apps_only()) {
if (pdev->id == SMD_APPS_MODEM)
r = smd_open("DIAG_CNTL", &driver->ch_cntl, driver,
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index 5e325e9..6bb0739 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -438,6 +438,7 @@
ion_buffer_put(buffer);
return handle;
}
+EXPORT_SYMBOL(ion_alloc);
void ion_free(struct ion_client *client, struct ion_handle *handle)
{
@@ -455,6 +456,7 @@
}
ion_handle_put(handle);
}
+EXPORT_SYMBOL(ion_free);
static void ion_client_get(struct ion_client *client);
static int ion_client_put(struct ion_client *client);
@@ -512,6 +514,7 @@
ret = buffer->heap->ops->phys(buffer->heap, buffer, addr, len);
return ret;
}
+EXPORT_SYMBOL(ion_phys);
void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle,
unsigned long flags)
@@ -558,6 +561,7 @@
mutex_unlock(&client->lock);
return vaddr;
}
+EXPORT_SYMBOL(ion_map_kernel);
int __ion_iommu_map(struct ion_buffer *buffer,
int domain_num, int partition_num, unsigned long align,
@@ -776,6 +780,7 @@
mutex_unlock(&client->lock);
return sglist;
}
+EXPORT_SYMBOL(ion_map_dma);
void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle)
{
@@ -791,6 +796,7 @@
mutex_unlock(&buffer->lock);
mutex_unlock(&client->lock);
}
+EXPORT_SYMBOL(ion_unmap_kernel);
void ion_unmap_dma(struct ion_client *client, struct ion_handle *handle)
{
@@ -806,7 +812,7 @@
mutex_unlock(&buffer->lock);
mutex_unlock(&client->lock);
}
-
+EXPORT_SYMBOL(ion_unmap_dma);
struct ion_buffer *ion_share(struct ion_client *client,
struct ion_handle *handle)
@@ -828,6 +834,7 @@
*/
return handle->buffer;
}
+EXPORT_SYMBOL(ion_share);
struct ion_handle *ion_import(struct ion_client *client,
struct ion_buffer *buffer)
@@ -849,6 +856,7 @@
mutex_unlock(&client->lock);
return handle;
}
+EXPORT_SYMBOL(ion_import);
static int check_vaddr_bounds(unsigned long start, unsigned long end)
{
@@ -937,6 +945,7 @@
fput(file);
return handle;
}
+EXPORT_SYMBOL(ion_import_fd);
static int ion_debug_client_show(struct seq_file *s, void *unused)
{
@@ -1137,6 +1146,7 @@
if (client)
ion_client_put(client);
}
+EXPORT_SYMBOL(ion_client_destroy);
int ion_handle_get_flags(struct ion_client *client, struct ion_handle *handle,
unsigned long *flags)
diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile
index 908b63d..7b8f3e6 100644
--- a/drivers/gpu/msm/Makefile
+++ b/drivers/gpu/msm/Makefile
@@ -8,7 +8,8 @@
kgsl_pwrscale.o \
kgsl_mmu.o \
kgsl_gpummu.o \
- kgsl_iommu.o
+ kgsl_iommu.o \
+ kgsl_snapshot.o
msm_kgsl_core-$(CONFIG_DEBUG_FS) += kgsl_debugfs.o
msm_kgsl_core-$(CONFIG_MSM_KGSL_CFF_DUMP) += kgsl_cffdump.o
@@ -20,8 +21,10 @@
adreno_ringbuffer.o \
adreno_drawctxt.o \
adreno_postmortem.o \
+ adreno_snapshot.o \
adreno_a2xx.o \
adreno_a2xx_trace.o \
+ adreno_a2xx_snapshot.o \
adreno.o
msm_adreno-$(CONFIG_DEBUG_FS) += adreno_debugfs.o
diff --git a/drivers/gpu/msm/a2xx_reg.h b/drivers/gpu/msm/a2xx_reg.h
index 5ffdea1..50b2745 100644
--- a/drivers/gpu/msm/a2xx_reg.h
+++ b/drivers/gpu/msm/a2xx_reg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2002,2007-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
@@ -416,4 +416,37 @@
#define REG_A225_GRAS_UCP5W 0x2357
#define REG_A225_GRAS_UCP_ENABLED 0x2360
+/* Debug registers used by snapshot */
+#define REG_PA_SU_DEBUG_CNTL 0x0C80
+#define REG_PA_SU_DEBUG_DATA 0x0C81
+#define REG_RB_DEBUG_CNTL 0x0F26
+#define REG_RB_DEBUG_DATA 0x0F27
+#define REG_PC_DEBUG_CNTL 0x0C38
+#define REG_PC_DEBUG_DATA 0x0C39
+#define REG_GRAS_DEBUG_CNTL 0x0C80
+#define REG_GRAS_DEBUG_DATA 0x0C81
+#define REG_SQ_DEBUG_MISC 0x0D05
+#define REG_SQ_DEBUG_INPUT_FSM 0x0DAE
+#define REG_SQ_DEBUG_CONST_MGR_FSM 0x0DAF
+#define REG_SQ_DEBUG_EXP_ALLOC 0x0DB3
+#define REG_SQ_DEBUG_FSM_ALU_0 0x0DB1
+#define REG_SQ_DEBUG_FSM_ALU_1 0x0DB2
+#define REG_SQ_DEBUG_PTR_BUFF 0x0DB4
+#define REG_SQ_DEBUG_GPR_VTX 0x0DB5
+#define REG_SQ_DEBUG_GPR_PIX 0x0DB6
+#define REG_SQ_DEBUG_TB_STATUS_SEL 0x0DB7
+#define REG_SQ_DEBUG_VTX_TB_0 0x0DB8
+#define REG_SQ_DEBUG_VTX_TB_1 0x0DB9
+#define REG_SQ_DEBUG_VTX_TB_STATE_MEM 0x0DBB
+#define REG_SQ_DEBUG_TP_FSM 0x0DB0
+#define REG_SQ_DEBUG_VTX_TB_STATUS_REG 0x0DBA
+#define REG_SQ_DEBUG_PIX_TB_0 0x0DBC
+#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_0 0x0DBD
+#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_1 0x0DBE
+#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_2 0x0DBF
+#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_3 0x0DC0
+#define REG_SQ_DEBUG_PIX_TB_STATE_MEM 0x0DC1
+#define REG_SQ_DEBUG_MISC_0 0x2309
+#define REG_SQ_DEBUG_MISC_1 0x230A
+
#endif /* __A200_REG_H */
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 03e447b..ce76a8f 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -728,8 +728,21 @@
} else {
kgsl_pwrctrl_set_state(device, KGSL_STATE_DUMP_AND_RECOVER);
INIT_COMPLETION(device->recovery_gate);
- /* Detected a hang - trigger an automatic dump */
+ /* Detected a hang */
+
+
+ /*
+ * Trigger an automatic dump of the state to
+ * the console
+ */
adreno_postmortem_dump(device, 0);
+
+ /*
+ * Make a GPU snapshot. For now, do it after the PM dump so we
+ * can at least be sure the PM dump will work as it always has
+ */
+ kgsl_device_snapshot(device, 1);
+
result = adreno_recover_hang(device);
if (result)
kgsl_pwrctrl_set_state(device, KGSL_STATE_HUNG);
@@ -1094,7 +1107,8 @@
* get an interrupt */
cmds[0] = cp_type3_packet(CP_NOP, 1);
cmds[1] = 0;
- adreno_ringbuffer_issuecmds(device, 0, &cmds[0], 2);
+ adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+ &cmds[0], 2);
}
mutex_unlock(&device->mutex);
}
@@ -1341,6 +1355,7 @@
.power_stats = adreno_power_stats,
.irqctrl = adreno_irqctrl,
.gpuid = adreno_gpuid,
+ .snapshot = adreno_snapshot,
/* Optional functions */
.setstate = adreno_setstate,
.drawctxt_create = adreno_drawctxt_create,
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index a3d2c00..bdba624 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -24,6 +24,7 @@
KGSL_CONTAINER_OF(device, struct adreno_device, dev)
/* Flags to control command packet settings */
+#define KGSL_CMD_FLAGS_NONE 0x00000000
#define KGSL_CMD_FLAGS_PMODE 0x00000001
#define KGSL_CMD_FLAGS_NO_TS_CMP 0x00000002
#define KGSL_CMD_FLAGS_NOT_KERNEL_CMD 0x00000004
@@ -84,6 +85,7 @@
void (*ctxt_restore)(struct adreno_device *, struct adreno_context *);
irqreturn_t (*irq_handler)(struct adreno_device *);
void (*irq_control)(struct adreno_device *, int);
+ void * (*snapshot)(struct adreno_device *, void *, int *, int);
};
extern struct adreno_gpudev adreno_a2xx_gpudev;
@@ -108,6 +110,9 @@
uint8_t *adreno_convertaddr(struct kgsl_device *device,
unsigned int pt_base, unsigned int gpuaddr, unsigned int size);
+void *adreno_snapshot(struct kgsl_device *device, void *snapshot, int *remain,
+ int hang);
+
static inline int adreno_is_a200(struct adreno_device *adreno_dev)
{
return (adreno_dev->gpurev == ADRENO_REV_A200);
diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c
index 16402c3..cf9a60a 100644
--- a/drivers/gpu/msm/adreno_a2xx.c
+++ b/drivers/gpu/msm/adreno_a2xx.c
@@ -1405,7 +1405,8 @@
"active context flags %08x\n", context->flags);
/* save registers and constants. */
- adreno_ringbuffer_issuecmds(device, 0, context->reg_save, 3);
+ adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+ context->reg_save, 3);
if (context->flags & CTXT_FLAGS_SHADER_SAVE) {
/* save shader partitioning and instructions. */
@@ -1415,7 +1416,7 @@
/* fixup shader partitioning parameter for
* SET_SHADER_BASES.
*/
- adreno_ringbuffer_issuecmds(device, 0,
+ adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
context->shader_fixup, 3);
context->flags |= CTXT_FLAGS_SHADER_RESTORE;
@@ -1430,7 +1431,7 @@
context->context_gmem_shadow.gmem_save, 3);
/* Restore TP0_CHICKEN */
- adreno_ringbuffer_issuecmds(device, 0,
+ adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
context->chicken_restore, 3);
context->flags |= CTXT_FLAGS_GMEM_RESTORE;
@@ -1481,7 +1482,7 @@
cmds[3] = device->memstore.gpuaddr +
KGSL_DEVICE_MEMSTORE_OFFSET(current_context);
cmds[4] = (unsigned int) context;
- adreno_ringbuffer_issuecmds(device, 0, cmds, 5);
+ adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, cmds, 5);
kgsl_mmu_setstate(device, context->pagetable);
#ifndef CONFIG_MSM_KGSL_CFF_DUMP_NO_CONTEXT_MEM_DUMP
@@ -1498,26 +1499,27 @@
context->context_gmem_shadow.gmem_restore, 3);
/* Restore TP0_CHICKEN */
- adreno_ringbuffer_issuecmds(device, 0,
+ adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
context->chicken_restore, 3);
context->flags &= ~CTXT_FLAGS_GMEM_RESTORE;
}
/* restore registers and constants. */
- adreno_ringbuffer_issuecmds(device, 0,
+ adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
context->reg_restore, 3);
/* restore shader instructions & partitioning. */
if (context->flags & CTXT_FLAGS_SHADER_RESTORE) {
- adreno_ringbuffer_issuecmds(device, 0,
+ adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
context->shader_restore, 3);
}
if (adreno_is_a20x(adreno_dev)) {
cmds[0] = cp_type3_packet(CP_SET_BIN_BASE_OFFSET, 1);
cmds[1] = context->bin_base_offset;
- adreno_ringbuffer_issuecmds(device, 0, cmds, 2);
+ adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+ cmds, 2);
}
}
@@ -1704,6 +1706,10 @@
wmb();
}
+/* Defined in adreno_a2xx_snapshot.c */
+void *a2xx_snapshot(struct adreno_device *adreno_dev, void *snapshot,
+ int *remain, int hang);
+
struct adreno_gpudev adreno_a2xx_gpudev = {
.ctxt_gpustate_shadow = a2xx_ctxt_gpustate_shadow,
.ctxt_gmem_shadow = a2xx_ctxt_gmem_shadow,
@@ -1711,4 +1717,5 @@
.ctxt_restore = a2xx_ctxt_restore,
.irq_handler = a2xx_irq_handler,
.irq_control = a2xx_irq_control,
+ .snapshot = a2xx_snapshot,
};
diff --git a/drivers/gpu/msm/adreno_a2xx_snapshot.c b/drivers/gpu/msm/adreno_a2xx_snapshot.c
new file mode 100644
index 0000000..30d692b
--- /dev/null
+++ b/drivers/gpu/msm/adreno_a2xx_snapshot.c
@@ -0,0 +1,356 @@
+/* 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 "kgsl.h"
+#include "adreno.h"
+#include "kgsl_snapshot.h"
+
+#define DEBUG_SECTION_SZ(_dwords) (((_dwords) * sizeof(unsigned int)) \
+ + sizeof(struct kgsl_snapshot_debug))
+
+/* Dump the SX debug registers into a GPU snapshot debug section */
+
+#define SXDEBUG_COUNT 0x1B
+
+static int a2xx_snapshot_sxdebug(struct kgsl_device *device, void *snapshot,
+ int remain, void *priv)
+{
+ struct kgsl_snapshot_debug *header = snapshot;
+ unsigned int *data = snapshot + sizeof(*header);
+ int i;
+
+ if (remain < DEBUG_SECTION_SZ(SXDEBUG_COUNT)) {
+ SNAPSHOT_ERR_NOMEM(device, "SX DEBUG");
+ return 0;
+ }
+
+ header->type = SNAPSHOT_DEBUG_SX;
+ header->size = SXDEBUG_COUNT;
+
+ for (i = 0; i < SXDEBUG_COUNT; i++) {
+ adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1B00 | i);
+ adreno_regread(device, REG_RBBM_DEBUG_OUT, &data[i]);
+ }
+
+ adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
+
+ return DEBUG_SECTION_SZ(SXDEBUG_COUNT);
+}
+
+#define CPDEBUG_COUNT 0x20
+
+static int a2xx_snapshot_cpdebug(struct kgsl_device *device, void *snapshot,
+ int remain, void *priv)
+{
+ struct kgsl_snapshot_debug *header = snapshot;
+ unsigned int *data = snapshot + sizeof(*header);
+ int i;
+
+ if (remain < DEBUG_SECTION_SZ(CPDEBUG_COUNT)) {
+ SNAPSHOT_ERR_NOMEM(device, "CP DEBUG");
+ return 0;
+ }
+
+ header->type = SNAPSHOT_DEBUG_CP;
+ header->size = CPDEBUG_COUNT;
+
+ for (i = 0; i < CPDEBUG_COUNT; i++) {
+ adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1628);
+ adreno_regread(device, REG_RBBM_DEBUG_OUT, &data[i]);
+ }
+
+ adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
+
+ return DEBUG_SECTION_SZ(CPDEBUG_COUNT);
+}
+
+/*
+ * The contents of the SQ debug sections are dword pairs:
+ * [register offset]:[value]
+ * This macro writes both dwords for the given register
+ */
+
+#define SQ_DEBUG_WRITE(_device, _reg, _data, _offset) \
+ do { _data[(_offset)++] = (_reg); \
+ adreno_regread(_device, (_reg), &_data[(_offset)++]); } while (0)
+
+#define SQ_DEBUG_BANK_SIZE 23
+
+static int a2xx_snapshot_sqdebug(struct kgsl_device *device, void *snapshot,
+ int remain, void *priv)
+{
+ struct kgsl_snapshot_debug *header = snapshot;
+ unsigned int *data = snapshot + sizeof(*header);
+ int i, offset = 0;
+ int size = SQ_DEBUG_BANK_SIZE * 2 * 2;
+
+ if (remain < DEBUG_SECTION_SZ(size)) {
+ SNAPSHOT_ERR_NOMEM(device, "SQ Debug");
+ return 0;
+ }
+
+ header->type = SNAPSHOT_DEBUG_SQ;
+ header->size = size;
+
+ for (i = 0; i < 2; i++) {
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_CONST_MGR_FSM+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_EXP_ALLOC+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_FSM_ALU_0+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_FSM_ALU_1+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_GPR_PIX+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_GPR_VTX+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_INPUT_FSM+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_MISC+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_MISC_0+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_MISC_1+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_0+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATE_MEM+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device,
+ REG_SQ_DEBUG_PIX_TB_STATUS_REG_0+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device,
+ REG_SQ_DEBUG_PIX_TB_STATUS_REG_1+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device,
+ REG_SQ_DEBUG_PIX_TB_STATUS_REG_2+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device,
+ REG_SQ_DEBUG_PIX_TB_STATUS_REG_3+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PTR_BUFF+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_TB_STATUS_SEL+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_TP_FSM+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_0+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_1+i*0x1000,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_STATE_MEM+i*0x1000,
+ data, offset);
+ }
+
+ return DEBUG_SECTION_SZ(size);
+}
+
+#define SQ_DEBUG_THREAD_SIZE 7
+
+static int a2xx_snapshot_sqthreaddebug(struct kgsl_device *device,
+ void *snapshot, int remain, void *priv)
+{
+ struct kgsl_snapshot_debug *header = snapshot;
+ unsigned int *data = snapshot + sizeof(*header);
+ int i, offset = 0;
+ int size = SQ_DEBUG_THREAD_SIZE * 2 * 16;
+
+ if (remain < DEBUG_SECTION_SZ(size)) {
+ SNAPSHOT_ERR_NOMEM(device, "SQ THREAD DEBUG");
+ return 0;
+ }
+
+ header->type = SNAPSHOT_DEBUG_SQTHREAD;
+ header->size = size;
+
+ for (i = 0; i < 16; i++) {
+ adreno_regwrite(device, REG_SQ_DEBUG_TB_STATUS_SEL,
+ i | (6<<4) | (i<<7) | (1<<11) | (1<<12)
+ | (i<<16) | (6<<20) | (i<<23));
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_STATE_MEM,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_STATUS_REG,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATE_MEM,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_0,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_1,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_2,
+ data, offset);
+ SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_3,
+ data, offset);
+ }
+
+ return DEBUG_SECTION_SZ(size);
+}
+
+#define MIUDEBUG_COUNT 0x10
+
+static int a2xx_snapshot_miudebug(struct kgsl_device *device, void *snapshot,
+ int remain, void *priv)
+{
+ struct kgsl_snapshot_debug *header = snapshot;
+ unsigned int *data = snapshot + sizeof(*header);
+ int i;
+
+ if (remain < DEBUG_SECTION_SZ(MIUDEBUG_COUNT)) {
+ SNAPSHOT_ERR_NOMEM(device, "MIU DEBUG");
+ return 0;
+ }
+
+ header->type = SNAPSHOT_DEBUG_MIU;
+ header->size = MIUDEBUG_COUNT;
+
+ for (i = 0; i < MIUDEBUG_COUNT; i++) {
+ adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1600 | i);
+ adreno_regread(device, REG_RBBM_DEBUG_OUT, &data[i]);
+ }
+
+ adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
+
+ return DEBUG_SECTION_SZ(MIUDEBUG_COUNT);
+}
+
+/* Helper function to snapshot a section of indexed registers */
+
+static void *a2xx_snapshot_indexed_registers(struct kgsl_device *device,
+ void *snapshot, int *remain,
+ unsigned int index, unsigned int data, unsigned int start,
+ unsigned int count)
+{
+ struct kgsl_snapshot_indexed_registers iregs;
+ iregs.index = index;
+ iregs.data = data;
+ iregs.start = start;
+ iregs.count = count;
+
+ return kgsl_snapshot_add_section(device,
+ KGSL_SNAPSHOT_SECTION_INDEXED_REGS, snapshot,
+ remain, kgsl_snapshot_dump_indexed_regs, &iregs);
+}
+
+/* A2XX GPU snapshot function - this is where all of the A2XX specific
+ * bits and pieces are grabbed into the snapshot memory
+ */
+
+void *a2xx_snapshot(struct adreno_device *adreno_dev, void *snapshot,
+ int *remain, int hang)
+{
+ struct kgsl_device *device = &adreno_dev->dev;
+ struct kgsl_snapshot_registers regs;
+ unsigned int pmoverride;
+
+ /* Choose the register set to dump */
+
+ if (adreno_is_a20x(adreno_dev)) {
+ regs.regs = (unsigned int *) a200_registers;
+ regs.count = a200_registers_count;
+ } else {
+ regs.regs = (unsigned int *) a220_registers;
+ regs.count = a220_registers_count;
+ }
+
+ /* Master set of (non debug) registers */
+ snapshot = kgsl_snapshot_add_section(device,
+ KGSL_SNAPSHOT_SECTION_REGS, snapshot, remain,
+ kgsl_snapshot_dump_regs, ®s);
+
+ /* CP_STATE_DEBUG indexed registers */
+ snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+ remain, REG_CP_STATE_DEBUG_INDEX,
+ REG_CP_STATE_DEBUG_DATA, 0x0, 0x14);
+
+ /* CP_ME indexed registers */
+ snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+ remain, REG_CP_ME_CNTL, REG_CP_ME_STATUS,
+ 64, 44);
+
+ /*
+ * Need to temporarily turn off clock gating for the debug bus to
+ * work
+ */
+
+ adreno_regread(device, REG_RBBM_PM_OVERRIDE2, &pmoverride);
+ adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xFF);
+
+ /* SX debug registers */
+ snapshot = kgsl_snapshot_add_section(device,
+ KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+ a2xx_snapshot_sxdebug, NULL);
+
+ /* SU debug indexed registers (only for < 470) */
+ if (!adreno_is_a22x(adreno_dev))
+ snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+ remain, REG_PA_SU_DEBUG_CNTL,
+ REG_PA_SU_DEBUG_DATA,
+ 0, 0x1B);
+
+ /* CP debug registers */
+ snapshot = kgsl_snapshot_add_section(device,
+ KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+ a2xx_snapshot_cpdebug, NULL);
+
+ /* MH debug indexed registers */
+ snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+ remain, MH_DEBUG_CTRL, MH_DEBUG_DATA, 0x0, 0x40);
+
+ /* Leia only register sets */
+ if (adreno_is_a22x(adreno_dev)) {
+ /* RB DEBUG indexed regisers */
+ snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+ remain, REG_RB_DEBUG_CNTL, REG_RB_DEBUG_DATA, 0, 8);
+
+ /* RB DEBUG indexed registers bank 2 */
+ snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+ remain, REG_RB_DEBUG_CNTL, REG_RB_DEBUG_DATA + 0x1000,
+ 0, 8);
+
+ /* PC_DEBUG indexed registers */
+ snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+ remain, REG_PC_DEBUG_CNTL, REG_PC_DEBUG_DATA, 0, 8);
+
+ /* GRAS_DEBUG indexed registers */
+ snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+ remain, REG_GRAS_DEBUG_CNTL, REG_GRAS_DEBUG_DATA, 0, 4);
+
+ /* MIU debug registers */
+ snapshot = kgsl_snapshot_add_section(device,
+ KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+ a2xx_snapshot_miudebug, NULL);
+
+ /* SQ DEBUG debug registers */
+ snapshot = kgsl_snapshot_add_section(device,
+ KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+ a2xx_snapshot_sqdebug, NULL);
+
+ /*
+ * Reading SQ THREAD causes bad things to happen on a running
+ * system, so only read it if the GPU is already hung
+ */
+
+ if (hang) {
+ /* SQ THREAD debug registers */
+ snapshot = kgsl_snapshot_add_section(device,
+ KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+ a2xx_snapshot_sqthreaddebug, NULL);
+ }
+ }
+
+ /* Reset the clock gating */
+ adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, pmoverride);
+
+ return snapshot;
+}
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
new file mode 100644
index 0000000..fb88a72
--- /dev/null
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -0,0 +1,332 @@
+/* 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 "kgsl.h"
+#include "kgsl_sharedmem.h"
+#include "kgsl_snapshot.h"
+
+#include "adreno.h"
+#include "adreno_pm4types.h"
+#include "a2xx_reg.h"
+
+/* Number of dwords of ringbuffer history to record */
+#define NUM_DWORDS_OF_RINGBUFFER_HISTORY 100
+
+/* Maintain a list of the objects we see during parsing */
+
+#define SNAPSHOT_OBJ_BUFSIZE 64
+
+#define SNAPSHOT_OBJ_TYPE_IB 0
+
+static struct kgsl_snapshot_obj {
+ int type;
+ uint32_t gpuaddr;
+ uint32_t ptbase;
+ void *ptr;
+ int dwords;
+} objbuf[SNAPSHOT_OBJ_BUFSIZE];
+
+/* Pointer to the next open entry in the object list */
+static int objbufptr;
+
+/* Push a new buffer object onto the list */
+static void push_object(struct kgsl_device *device, int type, uint32_t ptbase,
+ uint32_t gpuaddr, int dwords)
+{
+ int index;
+ void *ptr;
+
+ /* Go through the list and see that object has already been seen */
+ for (index = 0; index < objbufptr; index++) {
+ if (objbuf[index].gpuaddr == gpuaddr &&
+ objbuf[index].ptbase == ptbase)
+ return;
+ }
+
+ if (objbufptr == SNAPSHOT_OBJ_BUFSIZE) {
+ KGSL_DRV_ERR(device, "snapshot: too many snapshot objects\n");
+ return;
+ }
+
+ /*
+ * adreno_convertaddr verifies that the IB size is valid - at least in
+ * the context of it being smaller then the allocated memory space
+ */
+ ptr = adreno_convertaddr(device, ptbase, gpuaddr, dwords << 2);
+
+ if (ptr == NULL) {
+ KGSL_DRV_ERR(device,
+ "snapshot: Can't find GPU address for %x\n", gpuaddr);
+ return;
+ }
+
+ /* Put it on the list of things to parse */
+ objbuf[objbufptr].type = type;
+ objbuf[objbufptr].gpuaddr = gpuaddr;
+ objbuf[objbufptr].ptbase = ptbase;
+ objbuf[objbufptr].dwords = dwords;
+ objbuf[objbufptr++].ptr = ptr;
+}
+
+/* Snapshot the istore memory */
+static int snapshot_istore(struct kgsl_device *device, void *snapshot,
+ int remain, void *priv)
+{
+ struct kgsl_snapshot_istore *header = snapshot;
+ unsigned int *data = snapshot + sizeof(*header);
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ int count, i;
+
+ count = adreno_dev->istore_size * ADRENO_ISTORE_WORDS;
+
+ if (remain < (count * 4) + sizeof(*header)) {
+ KGSL_DRV_ERR(device,
+ "snapshot: Not enough memory for the istore section");
+ return 0;
+ }
+
+ header->count = adreno_dev->istore_size;
+
+ for (i = 0; i < count; i++)
+ kgsl_regread(device, ADRENO_ISTORE_START + i, &data[i]);
+
+ return (count * 4) + sizeof(*header);
+}
+
+/* Snapshot the ringbuffer memory */
+static int snapshot_rb(struct kgsl_device *device, void *snapshot,
+ int remain, void *priv)
+{
+ struct kgsl_snapshot_rb *header = snapshot;
+ unsigned int *data = snapshot + sizeof(*header);
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+ unsigned int rbbase, ptbase, rptr, *rbptr;
+ int start, stop, index;
+ int numitems, size;
+
+ /* Get the GPU address of the ringbuffer */
+ kgsl_regread(device, REG_CP_RB_BASE, &rbbase);
+
+ /* Get the physical address of the MMU pagetable */
+ ptbase = kgsl_mmu_get_current_ptbase(device);
+
+ /* Get the current read pointers for the RB */
+ kgsl_regread(device, REG_CP_RB_RPTR, &rptr);
+
+ /* start the dump at the rptr minus some history */
+ start = (int) rptr - NUM_DWORDS_OF_RINGBUFFER_HISTORY;
+ if (start < 0)
+ start += rb->sizedwords;
+
+ /*
+ * Stop the dump at the point where the software last wrote. Don't use
+ * the hardware value here on the chance that it didn't get properly
+ * updated
+ */
+
+ stop = (int) rb->wptr + 16;
+ if (stop > rb->sizedwords)
+ stop -= rb->sizedwords;
+
+ /* Set up the header for the section */
+
+ numitems = (stop > start) ? stop - start :
+ (rb->sizedwords - start) + stop;
+
+ size = (numitems << 2);
+
+ if (remain < size + sizeof(*header)) {
+ KGSL_DRV_ERR(device,
+ "snapshot: Not enough memory for the rb section");
+ return 0;
+ }
+
+ /* Write the sub-header for the section */
+ header->start = start;
+ header->end = stop;
+ header->wptr = rb->wptr;
+ header->rbsize = rb->sizedwords;
+ header->count = numitems;
+
+ index = start;
+ rbptr = rb->buffer_desc.hostptr;
+
+ /*
+ * Loop through the RB, copying the data and looking for indirect
+ * buffers and MMU pagetable changes
+ */
+
+ while (index != rb->wptr) {
+ *data = rbptr[index];
+
+ if (rbptr[index] == cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2))
+ push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase,
+ rbptr[index + 1], rbptr[index + 2]);
+
+ /*
+ * FIXME: Handle upcoming MMU pagetable changes, but only
+ * between the rptr and the wptr
+ */
+
+ index = index + 1;
+
+ if (index == rb->sizedwords)
+ index = 0;
+
+ data++;
+ }
+
+ /* Dump 16 dwords past the wptr, but don't bother interpeting it */
+
+ while (index != stop) {
+ *data = rbptr[index];
+ index = index + 1;
+
+ if (index == rb->sizedwords)
+ index = 0;
+
+ data++;
+ }
+
+ /* Return the size of the section */
+ return size + sizeof(*header);
+}
+
+/* Snapshot the memory for an indirect buffer */
+static int snapshot_ib(struct kgsl_device *device, void *snapshot,
+ int remain, void *priv)
+{
+ struct kgsl_snapshot_ib *header = snapshot;
+ struct kgsl_snapshot_obj *obj = priv;
+ unsigned int *src = obj->ptr;
+ unsigned int *dst = snapshot + sizeof(*header);
+ int i;
+
+ if (remain < (obj->dwords << 2) + sizeof(*header)) {
+ KGSL_DRV_ERR(device,
+ "snapshot: Not enough memory for the ib section");
+ return 0;
+ }
+
+ /* Write the sub-header for the section */
+ header->gpuaddr = obj->gpuaddr;
+ header->ptbase = obj->ptbase;
+ header->size = obj->dwords;
+
+ /* Write the contents of the ib */
+ for (i = 0; i < obj->dwords; i++) {
+ *dst = *src;
+ /* If another IB is discovered, then push it on the list too */
+
+ if (*src == cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2)) {
+ push_object(device, SNAPSHOT_OBJ_TYPE_IB, obj->ptbase,
+ *(src + 1), *(src + 2));
+ }
+
+ src++;
+ dst++;
+ }
+
+ return (obj->dwords << 2) + sizeof(*header);
+}
+
+/* Dump another item on the current pending list */
+static void *dump_object(struct kgsl_device *device, int obj, void *snapshot,
+ int *remain)
+{
+ switch (objbuf[obj].type) {
+ case SNAPSHOT_OBJ_TYPE_IB:
+ snapshot = kgsl_snapshot_add_section(device,
+ KGSL_SNAPSHOT_SECTION_IB, snapshot, remain,
+ snapshot_ib, &objbuf[obj]);
+ break;
+ default:
+ KGSL_DRV_ERR(device,
+ "snapshot: Invalid snapshot object type: %d\n",
+ objbuf[obj].type);
+ break;
+ }
+
+ return snapshot;
+}
+
+/* adreno_snapshot - Snapshot the Adreno GPU state
+ * @device - KGSL device to snapshot
+ * @snapshot - Pointer to the start of memory to write into
+ * @remain - A pointer to how many bytes of memory are remaining in the snapshot
+ * @hang - set if this snapshot was automatically triggered by a GPU hang
+ * This is a hook function called by kgsl_snapshot to snapshot the
+ * Adreno specific information for the GPU snapshot. In turn, this function
+ * calls the GPU specific snapshot function to get core specific information.
+ */
+
+void *adreno_snapshot(struct kgsl_device *device, void *snapshot, int *remain,
+ int hang)
+{
+ int i;
+ uint32_t ptbase, ibbase, ibsize;
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+ /* Reset the list of objects */
+ objbufptr = 0;
+
+ /* Get the physical address of the MMU pagetable */
+ ptbase = kgsl_mmu_get_current_ptbase(device);
+
+ /* Dump the ringbuffer */
+ snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_RB,
+ snapshot, remain, snapshot_rb, NULL);
+
+ /*
+ * Make sure that the IBs described in the CP registers are on the
+ * list of objects
+ */
+ kgsl_regread(device, REG_CP_IB1_BASE, &ibbase);
+ kgsl_regread(device, REG_CP_IB1_BUFSZ, &ibsize);
+
+ if (ibsize)
+ push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase,
+ ibbase, ibsize);
+
+ kgsl_regread(device, REG_CP_IB2_BASE, &ibbase);
+ kgsl_regread(device, REG_CP_IB2_BUFSZ, &ibsize);
+
+ if (ibsize)
+ push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase,
+ ibbase, ibsize);
+
+ /*
+ * Go through the list of found objects and dump each one. As the IBs
+ * are parsed, more objects might be found, and objbufptr will increase
+ */
+ for (i = 0; i < objbufptr; i++)
+ snapshot = dump_object(device, i, snapshot, remain);
+
+ /*
+ * Only dump the istore on a hang - reading it on a running system
+ * has a non 0 chance of hanging the GPU
+ */
+
+ if (hang) {
+ snapshot = kgsl_snapshot_add_section(device,
+ KGSL_SNAPSHOT_SECTION_ISTORE, snapshot, remain,
+ snapshot_istore, NULL);
+ }
+
+ /* Add GPU specific sections - registers mainly, but other stuff too */
+ if (adreno_dev->gpudev->snapshot)
+ snapshot = adreno_dev->gpudev->snapshot(adreno_dev, snapshot,
+ remain, hang);
+
+ return snapshot;
+}
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index c6e78dd..1b6696b 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -2097,6 +2097,8 @@
if (minor == KGSL_DEVICE_MAX)
return;
+ kgsl_device_snapshot_close(device);
+
kgsl_cffdump_close(device->id);
kgsl_pwrctrl_uninit_sysfs(device);
@@ -2199,6 +2201,9 @@
idr_init(&device->context_idr);
+ /* Initalize the snapshot engine */
+ kgsl_device_snapshot_init(device);
+
/* sysfs and debugfs initalization - failure here is non fatal */
/* Initialize logging */
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index f82b038..8ea5279 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2002,2007-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
@@ -90,6 +90,8 @@
struct kgsl_power_stats *stats);
void (*irqctrl)(struct kgsl_device *device, int state);
unsigned int (*gpuid)(struct kgsl_device *device);
+ void * (*snapshot)(struct kgsl_device *device, void *snapshot,
+ int *remain, int hang);
/* Optional functions - these functions are not mandatory. The
driver will check that the function pointer is not NULL before
calling the hook */
@@ -164,6 +166,15 @@
struct idr context_idr;
struct early_suspend display_off;
+ void *snapshot; /* Pointer to the snapshot memory region */
+ int snapshot_maxsize; /* Max size of the snapshot region */
+ int snapshot_size; /* Current size of the snapshot region */
+ u32 snapshot_timestamp; /* Timestamp of the last valid snapshot */
+ int snapshot_frozen; /* 1 if the snapshot output is frozen until
+ it gets read by the user. This avoids
+ losing the output on multiple hangs */
+ struct kobject snapshot_kobj;
+
/* Logging levels */
int cmd_log;
int ctxt_log;
@@ -320,4 +331,8 @@
const char *kgsl_pwrstate_to_str(unsigned int state);
+int kgsl_device_snapshot_init(struct kgsl_device *device);
+int kgsl_device_snapshot(struct kgsl_device *device, int hang);
+void kgsl_device_snapshot_close(struct kgsl_device *device);
+
#endif /* __KGSL_DEVICE_H */
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
new file mode 100644
index 0000000..72df148b
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -0,0 +1,484 @@
+/* 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/vmalloc.h>
+#include <linux/time.h>
+#include <linux/sysfs.h>
+#include <linux/utsname.h>
+#include <linux/sched.h>
+#include <linux/idr.h>
+
+#include "kgsl.h"
+#include "kgsl_log.h"
+#include "kgsl_device.h"
+#include "kgsl_sharedmem.h"
+#include "kgsl_snapshot.h"
+
+/* idr_for_each function to count the number of contexts */
+
+static int snapshot_context_count(int id, void *ptr, void *data)
+{
+ int *count = data;
+ *count = *count + 1;
+
+ return 0;
+}
+
+/*
+ * To simplify the iterator loop use a global pointer instead of trying
+ * to pass around double star references to the snapshot data
+ */
+
+static void *_ctxtptr;
+
+static int snapshot_context_info(int id, void *ptr, void *data)
+{
+ struct kgsl_snapshot_linux_context *header = _ctxtptr;
+ struct kgsl_context *context = ptr;
+ struct kgsl_device *device = context->dev_priv->device;
+
+ header->id = id;
+
+ /* Future-proof for per-context timestamps - for now, just
+ * return the global timestamp for all contexts
+ */
+
+ header->timestamp_queued = -1;
+ header->timestamp_retired = device->ftbl->readtimestamp(device,
+ KGSL_TIMESTAMP_RETIRED);
+
+ _ctxtptr += sizeof(struct kgsl_snapshot_linux_context);
+
+ return 0;
+}
+
+/* Snapshot the Linux specific information */
+static int snapshot_os(struct kgsl_device *device,
+ void *snapshot, int remain, void *priv)
+{
+ struct kgsl_snapshot_linux *header = snapshot;
+ struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+ struct task_struct *task;
+ pid_t pid;
+ int hang = (int) priv;
+ int ctxtcount = 0;
+ int size = sizeof(*header);
+
+ /* Figure out how many active contexts there are - these will
+ * be appended on the end of the structure */
+
+ idr_for_each(&device->context_idr, snapshot_context_count, &ctxtcount);
+
+ size += ctxtcount * sizeof(struct kgsl_snapshot_linux_context);
+
+ /* Make sure there is enough room for the data */
+ if (remain < size) {
+ SNAPSHOT_ERR_NOMEM(device, "OS");
+ return 0;
+ }
+
+ memset(header, 0, sizeof(*header));
+
+ header->osid = KGSL_SNAPSHOT_OS_LINUX;
+
+ header->state = hang ? SNAPSHOT_STATE_HUNG : SNAPSHOT_STATE_RUNNING;
+
+ /* Get the kernel build information */
+ strlcpy(header->release, utsname()->release, sizeof(header->release));
+ strlcpy(header->version, utsname()->version, sizeof(header->version));
+
+ /* Get the Unix time for the timestamp */
+ header->seconds = get_seconds();
+
+ /* Remember the power information */
+ header->power_flags = pwr->power_flags;
+ header->power_level = pwr->active_pwrlevel;
+ header->power_interval_timeout = pwr->interval_timeout;
+ header->grpclk = kgsl_get_clkrate(pwr->grp_clks[0]);
+ header->busclk = kgsl_get_clkrate(pwr->ebi1_clk);
+
+ /* Future proof for per-context timestamps */
+ header->current_context = -1;
+
+ /* Get the current PT base */
+ header->ptbase = kgsl_mmu_get_current_ptbase(device);
+ /* And the PID for the task leader */
+ pid = header->pid = kgsl_mmu_get_ptname_from_ptbase(header->ptbase);
+
+ task = find_task_by_vpid(pid);
+
+ if (task)
+ get_task_comm(header->comm, task);
+
+ header->ctxtcount = ctxtcount;
+
+ /* append information for each context */
+ _ctxtptr = snapshot + sizeof(*header);
+ idr_for_each(&device->context_idr, snapshot_context_info, NULL);
+
+ /* Return the size of the data segment */
+ return size;
+}
+/*
+ * kgsl_snapshot_dump_indexed_regs - helper function to dump indexed registers
+ * @device - the device to dump registers from
+ * @snapshot - pointer to the start of the region of memory for the snapshot
+ * @remain - a pointer to the number of bytes remaining in the snapshot
+ * @priv - A pointer to the kgsl_snapshot_indexed_registers data
+ *
+ * Given a indexed register cmd/data pair and a count, dump each indexed
+ * register
+ */
+
+int kgsl_snapshot_dump_indexed_regs(struct kgsl_device *device,
+ void *snapshot, int remain, void *priv)
+{
+ struct kgsl_snapshot_indexed_registers *iregs = priv;
+ struct kgsl_snapshot_indexed_regs *header = snapshot;
+ unsigned int *data = snapshot + sizeof(*header);
+ int i;
+
+ if (remain < (iregs->count * 4) + sizeof(*header)) {
+ SNAPSHOT_ERR_NOMEM(device, "INDEXED REGS");
+ return 0;
+ }
+
+ header->index_reg = iregs->index;
+ header->data_reg = iregs->data;
+ header->count = iregs->count;
+ header->start = iregs->start;
+
+ for (i = 0; i < iregs->count; i++) {
+ kgsl_regwrite(device, iregs->index, iregs->start + i);
+ kgsl_regread(device, iregs->data, &data[i]);
+ }
+
+ return (iregs->count * 4) + sizeof(*header);
+}
+EXPORT_SYMBOL(kgsl_snapshot_dump_indexed_regs);
+
+/*
+ * kgsl_snapshot_dump_regs - helper function to dump device registers
+ * @device - the device to dump registers from
+ * @snapshot - pointer to the start of the region of memory for the snapshot
+ * @remain - a pointer to the number of bytes remaining in the snapshot
+ * @priv - A pointer to the kgsl_snapshot_registers data
+ *
+ * Given an array of register ranges pairs (start,end [inclusive]), dump the
+ * registers into a snapshot register section. The snapshot region stores a
+ * part of dwords for each register - the word address of the register, and
+ * the value.
+ */
+int kgsl_snapshot_dump_regs(struct kgsl_device *device, void *snapshot,
+ int remain, void *priv)
+{
+ struct kgsl_snapshot_regs *header = snapshot;
+ struct kgsl_snapshot_registers *regs = priv;
+ unsigned int *data = snapshot + sizeof(*header);
+ int count = 0, i, j;
+
+ /* Figure out how many registers we are going to dump */
+
+ for (i = 0; i < regs->count; i++) {
+ int start = regs->regs[i * 2];
+ int end = regs->regs[i * 2 + 1];
+
+ count += (end - start + 1);
+ }
+
+ if (remain < (count * 8) + sizeof(*header)) {
+ SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
+ return 0;
+ }
+
+ for (i = 0; i < regs->count; i++) {
+ unsigned int start = regs->regs[i * 2];
+ unsigned int end = regs->regs[i * 2 + 1];
+
+ for (j = start; j <= end; j++) {
+ unsigned int val;
+
+ kgsl_regread(device, j, &val);
+ *data++ = j;
+ *data++ = val;
+ }
+ }
+
+ header->count = count;
+
+ /* Return the size of the section */
+ return (count * 8) + sizeof(*header);
+}
+EXPORT_SYMBOL(kgsl_snapshot_dump_regs);
+
+/*
+ * kgsl_snapshot - construct a device snapshot
+ * @device - device to snapshot
+ * @hang - set to 1 if the snapshot was triggered following a hnag
+ * Given a device, construct a binary snapshot dump of the current device state
+ * and store it in the device snapshot memory.
+ */
+int kgsl_device_snapshot(struct kgsl_device *device, int hang)
+{
+ struct kgsl_snapshot_header *header = device->snapshot;
+ int remain = device->snapshot_maxsize - sizeof(*header);
+ void *snapshot;
+
+ /*
+ * The first hang is always the one we are interested in. To
+ * avoid a subsequent hang blowing away the first, the snapshot
+ * is frozen until it is dumped via sysfs.
+ *
+ * Note that triggered snapshots are always taken regardless
+ * of the state and never frozen.
+ */
+
+ if (hang && device->snapshot_frozen == 1)
+ return 0;
+
+ if (device->snapshot == NULL) {
+ KGSL_DRV_ERR(device,
+ "snapshot: No snapshot memory available\n");
+ return -ENOMEM;
+ }
+
+ if (remain < sizeof(*header)) {
+ KGSL_DRV_ERR(device,
+ "snapshot: Not enough memory for the header\n");
+ return -ENOMEM;
+ }
+
+ header->magic = SNAPSHOT_MAGIC;
+
+ header->gpuid = kgsl_gpuid(device);
+
+ /* Get a pointer to the first section (right after the header) */
+ snapshot = ((void *) device->snapshot) + sizeof(*header);
+
+ /* Build the Linux specific header */
+ snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_OS,
+ snapshot, &remain, snapshot_os, (void *) hang);
+
+ /* Get the device specific sections */
+ if (device->ftbl->snapshot)
+ snapshot = device->ftbl->snapshot(device, snapshot, &remain,
+ hang);
+
+ /* Add the empty end section to let the parser know we are done */
+ snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_END,
+ snapshot, &remain, NULL, NULL);
+
+ device->snapshot_timestamp = get_seconds();
+ device->snapshot_size = (int) (snapshot - device->snapshot);
+
+ /* Freeze the snapshot on a hang until it gets read */
+ device->snapshot_frozen = (hang) ? 1 : 0;
+
+ return 0;
+}
+EXPORT_SYMBOL(kgsl_device_snapshot);
+
+/* An attribute for showing snapshot details */
+struct kgsl_snapshot_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct kgsl_device *device, char *buf);
+ ssize_t (*store)(struct kgsl_device *device, const char *buf,
+ size_t count);
+};
+
+#define to_snapshot_attr(a) \
+container_of(a, struct kgsl_snapshot_attribute, attr)
+
+#define kobj_to_device(a) \
+container_of(a, struct kgsl_device, snapshot_kobj)
+
+/* Dump the sysfs binary data to the user */
+static ssize_t snapshot_show(struct file *filep, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t off,
+ size_t count)
+{
+ struct kgsl_device *device = kobj_to_device(kobj);
+
+ if (device == NULL)
+ return 0;
+
+ /* Return nothing if we haven't taken a snapshot yet */
+ if (device->snapshot_timestamp == 0)
+ return 0;
+
+ /* Get the mutex to keep things from changing while we are dumping */
+ mutex_lock(&device->mutex);
+
+ /*
+ * Release the freeze on the snapshot the first time the buffer is read
+ */
+
+ device->snapshot_frozen = 0;
+
+ if (off >= device->snapshot_size) {
+ count = 0;
+ goto exit;
+ }
+
+ if (off + count > device->snapshot_size)
+ count = device->snapshot_size - off;
+
+ memcpy(buf, device->snapshot + off, count);
+
+exit:
+ mutex_unlock(&device->mutex);
+ return count;
+}
+
+/* Show the timestamp of the last collected snapshot */
+static ssize_t timestamp_show(struct kgsl_device *device, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%x\n", device->snapshot_timestamp);
+}
+
+/* manually trigger a new snapshot to be collected */
+static ssize_t trigger_store(struct kgsl_device *device, const char *buf,
+ size_t count)
+{
+ if (device && count > 0) {
+ mutex_lock(&device->mutex);
+ kgsl_device_snapshot(device, 0);
+ mutex_unlock(&device->mutex);
+ }
+
+ return count;
+}
+
+static struct bin_attribute snapshot_attr = {
+ .attr.name = "dump",
+ .attr.mode = 0444,
+ .size = 0,
+ .read = snapshot_show
+};
+
+#define SNAPSHOT_ATTR(_name, _mode, _show, _store) \
+struct kgsl_snapshot_attribute attr_##_name = { \
+ .attr = { .name = __stringify(_name), .mode = _mode }, \
+ .show = _show, \
+ .store = _store, \
+}
+
+SNAPSHOT_ATTR(trigger, 0600, NULL, trigger_store);
+SNAPSHOT_ATTR(timestamp, 0444, timestamp_show, NULL);
+
+static void snapshot_sysfs_release(struct kobject *kobj)
+{
+}
+
+static ssize_t snapshot_sysfs_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr);
+ struct kgsl_device *device = kobj_to_device(kobj);
+ ssize_t ret;
+
+ if (device && pattr->show)
+ ret = pattr->show(device, buf);
+ else
+ ret = -EIO;
+
+ return ret;
+}
+
+static ssize_t snapshot_sysfs_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr);
+ struct kgsl_device *device = kobj_to_device(kobj);
+ ssize_t ret;
+
+ if (device && pattr->store)
+ ret = pattr->store(device, buf, count);
+ else
+ ret = -EIO;
+
+ return ret;
+}
+
+static const struct sysfs_ops snapshot_sysfs_ops = {
+ .show = snapshot_sysfs_show,
+ .store = snapshot_sysfs_store,
+};
+
+static struct kobj_type ktype_snapshot = {
+ .sysfs_ops = &snapshot_sysfs_ops,
+ .default_attrs = NULL,
+ .release = snapshot_sysfs_release,
+};
+
+/* kgsl_device_snapshot_init - Add resources for the device GPU snapshot
+ * @device - The device to initalize
+ *
+ * Allocate memory for a GPU snapshot for the specified device,
+ * and create the sysfs files to manage it
+ */
+
+int kgsl_device_snapshot_init(struct kgsl_device *device)
+{
+ int ret;
+
+ if (device->snapshot == NULL)
+ device->snapshot = vmalloc(KGSL_SNAPSHOT_MEMSIZE);
+
+ if (device->snapshot == NULL)
+ return -ENOMEM;
+
+ device->snapshot_maxsize = KGSL_SNAPSHOT_MEMSIZE;
+ device->snapshot_timestamp = 0;
+
+ ret = kobject_init_and_add(&device->snapshot_kobj, &ktype_snapshot,
+ &device->dev->kobj, "snapshot");
+ if (ret)
+ goto done;
+
+ ret = sysfs_create_bin_file(&device->snapshot_kobj, &snapshot_attr);
+ if (ret)
+ goto done;
+
+ ret = sysfs_create_file(&device->snapshot_kobj, &attr_trigger.attr);
+ if (ret)
+ goto done;
+
+ ret = sysfs_create_file(&device->snapshot_kobj, &attr_timestamp.attr);
+
+done:
+ return ret;
+}
+EXPORT_SYMBOL(kgsl_device_snapshot_init);
+
+/* kgsl_device_snapshot_close - Take down snapshot memory for a device
+ * @device - Pointer to the kgsl_device
+ *
+ * Remove the sysfs files and free the memory allocated for the GPU
+ * snapshot
+ */
+
+void kgsl_device_snapshot_close(struct kgsl_device *device)
+{
+ sysfs_remove_bin_file(&device->snapshot_kobj, &snapshot_attr);
+ sysfs_remove_file(&device->snapshot_kobj, &attr_trigger.attr);
+ sysfs_remove_file(&device->snapshot_kobj, &attr_timestamp.attr);
+
+ kobject_put(&device->snapshot_kobj);
+
+ vfree(device->snapshot);
+
+ device->snapshot = NULL;
+ device->snapshot_maxsize = 0;
+ device->snapshot_timestamp = 0;
+}
+EXPORT_SYMBOL(kgsl_device_snapshot_close);
diff --git a/drivers/gpu/msm/kgsl_snapshot.h b/drivers/gpu/msm/kgsl_snapshot.h
new file mode 100644
index 0000000..4fdc8a1
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_snapshot.h
@@ -0,0 +1,259 @@
+/* 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.
+ *
+ */
+
+#ifndef _KGSL_SNAPSHOT_H_
+#define _KGSL_SNAPSHOT_H_
+
+#include <linux/types.h>
+
+/* Snapshot header */
+
+#define SNAPSHOT_MAGIC 0x504D0001
+
+/* GPU ID scheme:
+ * [16:31] - core identifer (0x0002 for 2D or 0x0003 for 3D)
+ * [00:16] - GPU specific identifier
+ */
+
+struct kgsl_snapshot_header {
+ __u32 magic; /* Magic identifier */
+ __u32 gpuid; /* GPU ID - see above */
+} __packed;
+
+/* Section header */
+#define SNAPSHOT_SECTION_MAGIC 0xABCD
+
+struct kgsl_snapshot_section_header {
+ __u16 magic; /* Magic identifier */
+ __u16 id; /* Type of section */
+ __u32 size; /* Size of the section including this header */
+} __packed;
+
+/* Section identifiers */
+#define KGSL_SNAPSHOT_SECTION_OS 0x0101
+#define KGSL_SNAPSHOT_SECTION_REGS 0x0201
+#define KGSL_SNAPSHOT_SECTION_RB 0x0301
+#define KGSL_SNAPSHOT_SECTION_IB 0x0401
+#define KGSL_SNAPSHOT_SECTION_INDEXED_REGS 0x0501
+#define KGSL_SNAPSHOT_SECTION_ISTORE 0x0801
+#define KGSL_SNAPSHOT_SECTION_DEBUG 0x0901
+#define KGSL_SNAPSHOT_SECTION_END 0xFFFF
+
+/* OS sub-section header */
+#define KGSL_SNAPSHOT_OS_LINUX 0x0001
+
+/* Linux OS specific information */
+
+#define SNAPSHOT_STATE_HUNG 0
+#define SNAPSHOT_STATE_RUNNING 1
+
+struct kgsl_snapshot_linux {
+ int osid; /* subsection OS identifier */
+ int state; /* 1 if the thread is running, 0 for hung */
+ __u32 seconds; /* Unix timestamp for the snapshot */
+ __u32 power_flags; /* Current power flags */
+ __u32 power_level; /* Current power level */
+ __u32 power_interval_timeout; /* Power interval timeout */
+ __u32 grpclk; /* Current GP clock value */
+ __u32 busclk; /* Current busclk value */
+ __u32 ptbase; /* Current ptbase */
+ __u32 pid; /* PID of the process that owns the PT */
+ __u32 current_context; /* ID of the current context */
+ __u32 ctxtcount; /* Number of contexts appended to section */
+ unsigned char release[32]; /* kernel release */
+ unsigned char version[32]; /* kernel version */
+ unsigned char comm[16]; /* Name of the process that owns the PT */
+} __packed;
+
+/*
+ * This structure contains a record of an active context.
+ * These are appended one after another in the OS section below
+ * the header above
+ */
+
+struct kgsl_snapshot_linux_context {
+ __u32 id; /* The context ID */
+ __u32 timestamp_queued; /* The last queued timestamp */
+ __u32 timestamp_retired; /* The last timestamp retired by HW */
+};
+
+/* Ringbuffer sub-section header */
+struct kgsl_snapshot_rb {
+ int start; /* dword at the start of the dump */
+ int end; /* dword at the end of the dump */
+ int rbsize; /* Size (in dwords) of the ringbuffer */
+ int wptr; /* Current index of the CPU write pointer */
+ int rptr; /* Current index of the GPU read pointer */
+ int count; /* Number of dwords in the dump */
+} __packed;
+
+/* Indirect buffer sub-section header */
+struct kgsl_snapshot_ib {
+ __u32 gpuaddr; /* GPU address of the the IB */
+ __u32 ptbase; /* Base for the pagetable the GPU address is valid in */
+ int size; /* Size of the IB */
+} __packed;
+
+/* Register sub-section header */
+struct kgsl_snapshot_regs {
+ __u32 count; /* Number of register pairs in the section */
+} __packed;
+
+/* Indexed register sub-section header */
+struct kgsl_snapshot_indexed_regs {
+ __u32 index_reg; /* Offset of the index register for this section */
+ __u32 data_reg; /* Offset of the data register for this section */
+ int start; /* Starting index */
+ int count; /* Number of dwords in the data */
+} __packed;
+
+/* Istore sub-section header */
+struct kgsl_snapshot_istore {
+ int count; /* Number of instructions in the istore */
+} __packed;
+
+/* Debug data sub-section header */
+
+#define SNAPSHOT_DEBUG_SX 1
+#define SNAPSHOT_DEBUG_CP 2
+#define SNAPSHOT_DEBUG_SQ 3
+#define SNAPSHOT_DEBUG_SQTHREAD 4
+#define SNAPSHOT_DEBUG_MIU 5
+
+struct kgsl_snapshot_debug {
+ int type; /* Type identifier for the attached tata */
+ int size; /* Size of the section in bytes */
+} __packed;
+
+#ifdef __KERNEL__
+
+/* Allocate 512K for each device snapshot */
+#define KGSL_SNAPSHOT_MEMSIZE (512 * 1024)
+
+struct kgsl_device;
+/*
+ * A helper macro to print out "not enough memory functions" - this
+ * makes it easy to standardize the messages as well as cut down on
+ * the number of strings in the binary
+ */
+
+#define SNAPSHOT_ERR_NOMEM(_d, _s) \
+ KGSL_DRV_ERR((_d), \
+ "snapshot: not enough snapshot memory for section %s\n", (_s))
+
+/*
+ * kgsl_snapshot_add_section - Add a new section to the GPU snapshot
+ * @device - the KGSL device being snapshotted
+ * @id - the section id
+ * @snapshot - pointer to the memory for the snapshot
+ * @remain - pointer to the number of bytes left in the snapshot region
+ * @func - Function pointer to fill the section
+ * @priv - Priv pointer to pass to the function
+ *
+ * Set up a KGSL snapshot header by filling the memory with the callback
+ * function and adding the standard section header
+ */
+
+static inline void *kgsl_snapshot_add_section(struct kgsl_device *device,
+ u16 id, void *snapshot, int *remain,
+ int (*func)(struct kgsl_device *, void *, int, void *), void *priv)
+{
+ struct kgsl_snapshot_section_header *header = snapshot;
+ void *data = snapshot + sizeof(*header);
+ int ret = 0;
+
+ /*
+ * Sanity check to make sure there is enough for the header. The
+ * callback will check to make sure there is enough for the rest
+ * of the data. If there isn't enough room then don't advance the
+ * pointer.
+ */
+
+ if (*remain < sizeof(*header))
+ return snapshot;
+
+ /* It is legal to have no function (i.e. - make an empty section) */
+
+ if (func) {
+ ret = func(device, data, *remain, priv);
+
+ /*
+ * If there wasn't enough room for the data then don't bother
+ * setting up the header.
+ */
+
+ if (ret == 0)
+ return snapshot;
+ }
+
+ header->magic = SNAPSHOT_SECTION_MAGIC;
+ header->id = id;
+ header->size = ret + sizeof(*header);
+
+ /* Decrement the room left in the snapshot region */
+ *remain -= header->size;
+ /* Advance the pointer to the end of the next function */
+ return snapshot + header->size;
+}
+
+/* A common helper function to dump a range of registers. This will be used in
+ * the GPU specific devices like this:
+ *
+ * struct kgsl_snapshot_registers priv;
+ * priv.regs = registers_array;;
+ * priv.count = num_registers;
+ *
+ * kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS, snapshot,
+ * remain, kgsl_snapshot_dump_regs, &priv).
+ *
+ * Pass in an array of register range pairs in the form of:
+ * start reg, stop reg
+ * All the registers between start and stop inclusive will be dumped
+ */
+
+struct kgsl_snapshot_registers {
+ unsigned int *regs; /* Pointer to the array of register ranges */
+ int count; /* Number of entries in the array */
+};
+
+int kgsl_snapshot_dump_regs(struct kgsl_device *device, void *snapshot,
+ int remain, void *priv);
+
+/*
+ * A common helper function to dump a set of indexed registers. Use it
+ * like this:
+ *
+ * struct kgsl_snapshot_indexed_registers priv;
+ * priv.index = REG_INDEX;
+ * priv.data = REG_DATA;
+ * priv.count = num_registers
+ *
+ * kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_INDEXED_REGS,
+ * snapshot, remain, kgsl_snapshot_dump_indexed_regs, &priv).
+ *
+ * The callback function will write an index from 0 to priv.count to
+ * the index register and read the data from the data register.
+ */
+
+struct kgsl_snapshot_indexed_registers {
+ unsigned int index; /* Offset of the index register */
+ unsigned int data; /* Offset of the data register */
+ unsigned int start; /* Index to start with */
+ unsigned int count; /* Number of values to read from the pair */
+};
+
+int kgsl_snapshot_dump_indexed_regs(struct kgsl_device *device,
+ void *snapshot, int remain, void *priv);
+
+#endif
+#endif
diff --git a/drivers/hwmon/pm8xxx-adc.c b/drivers/hwmon/pm8xxx-adc.c
index 57d34b9..5f8faee 100644
--- a/drivers/hwmon/pm8xxx-adc.c
+++ b/drivers/hwmon/pm8xxx-adc.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -145,7 +145,7 @@
struct wake_lock adc_wakelock;
int msm_suspend_check;
struct pm8xxx_adc_amux_properties *conv;
- struct pm8xxx_adc_arb_btm_param batt[0];
+ struct pm8xxx_adc_arb_btm_param batt;
struct sensor_device_attribute sens_attr[0];
};
@@ -451,8 +451,8 @@
spin_lock_irqsave(&adc_pmic->btm_lock, flags);
warm_status = irq_read_line(adc_pmic->btm_warm_irq);
- if (adc_pmic->batt->btm_warm_fn != NULL)
- adc_pmic->batt->btm_warm_fn(warm_status);
+ if (adc_pmic->batt.btm_warm_fn != NULL)
+ adc_pmic->batt.btm_warm_fn(warm_status);
spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
}
@@ -465,8 +465,8 @@
spin_lock_irqsave(&adc_pmic->btm_lock, flags);
cool_status = irq_read_line(adc_pmic->btm_cool_irq);
- if (adc_pmic->batt->btm_cool_fn != NULL)
- adc_pmic->batt->btm_cool_fn(cool_status);
+ if (adc_pmic->batt.btm_cool_fn != NULL)
+ adc_pmic->batt.btm_cool_fn(cool_status);
spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
}
@@ -860,7 +860,7 @@
if (rc < 0)
goto write_err;
- adc_pmic->batt->btm_cool_fn = btm_param->btm_cool_fn;
+ adc_pmic->batt.btm_cool_fn = btm_param->btm_cool_fn;
}
if (btm_param->btm_warm_fn != NULL) {
@@ -874,7 +874,7 @@
if (rc < 0)
goto write_err;
- adc_pmic->batt->btm_warm_fn = btm_param->btm_warm_fn;
+ adc_pmic->batt.btm_warm_fn = btm_param->btm_warm_fn;
}
rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_BTM_CNTRL1, &arb_btm_cntrl1);
@@ -952,10 +952,10 @@
if (rc < 0)
goto write_err;
- if (pmic_adc->batt->btm_warm_fn != NULL)
+ if (pmic_adc->batt.btm_warm_fn != NULL)
enable_irq(adc_pmic->btm_warm_irq);
- if (pmic_adc->batt->btm_cool_fn != NULL)
+ if (pmic_adc->batt.btm_cool_fn != NULL)
enable_irq(adc_pmic->btm_cool_irq);
write_err:
@@ -1159,7 +1159,6 @@
}
adc_pmic = devm_kzalloc(&pdev->dev, sizeof(struct pm8xxx_adc) +
- sizeof(struct pm8xxx_adc_arb_btm_param) +
(sizeof(struct sensor_device_attribute) *
pdata->adc_num_board_channel), GFP_KERNEL);
if (!adc_pmic) {
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 2d69256..f99a546 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-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
@@ -308,12 +308,12 @@
{
dev->clk_state = state;
if (state != 0) {
- clk_enable(dev->clk);
- clk_enable(dev->pclk);
+ clk_prepare_enable(dev->clk);
+ clk_prepare_enable(dev->pclk);
} else {
qup_update_state(dev, QUP_RESET_STATE);
- clk_disable(dev->clk);
- clk_disable(dev->pclk);
+ clk_disable_unprepare(dev->clk);
+ clk_disable_unprepare(dev->pclk);
}
}
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 2453b6b..71bdcde 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -22,6 +22,8 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/gpio.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include <linux/regulator/consumer.h>
#if defined(CONFIG_HAS_EARLYSUSPEND)
@@ -80,6 +82,7 @@
#define MXT_PROCI_PALM_T41 41
#define MXT_PROCI_TOUCHSUPPRESSION_T42 42
#define MXT_PROCI_STYLUS_T47 47
+#define MXT_PROCI_SHIELDLESS_T56 56
#define MXT_PROCG_NOISESUPPRESSION_T48 48
#define MXT_SPT_COMMSCONFIG_T18 18
#define MXT_SPT_GPIOPWM_T19 19
@@ -206,11 +209,11 @@
#define MXT_BOOT_VALUE 0xa5
#define MXT_BACKUP_VALUE 0x55
#define MXT_BACKUP_TIME 25 /* msec */
-#define MXT224_RESET_TIME 65 /* msec */
-#define MXT224E_RESET_TIME 22 /* msec */
-#define MXT1386_RESET_TIME 250 /* msec */
+#define MXT224_RESET_TIME 65 /* msec */
+#define MXT224E_RESET_TIME 22 /* msec */
+#define MXT1386_RESET_TIME 250 /* msec */
#define MXT_RESET_TIME 250 /* msec */
-#define MXT_RESET_NOCHGREAD 400 /* msec */
+#define MXT_RESET_NOCHGREAD 400 /* msec */
#define MXT_FWRESET_TIME 175 /* msec */
@@ -252,6 +255,9 @@
#define MXT_MAX_RW_TRIES 3
#define MXT_BLOCK_SIZE 256
+#define MXT_DEBUGFS_DIR "atmel_mxt_ts"
+#define MXT_DEBUGFS_FILE "object"
+
struct mxt_info {
u8 family_id;
u8 variant_id;
@@ -292,6 +298,7 @@
struct i2c_client *client;
struct input_dev *input_dev;
const struct mxt_platform_data *pdata;
+ const struct mxt_config_info *config_info;
struct mxt_object *object_table;
struct mxt_info info;
struct mxt_finger finger[MXT_MAX_FINGER];
@@ -312,8 +319,12 @@
u8 t9_min_reportid;
u8 t15_max_reportid;
u8 t15_min_reportid;
+ u8 curr_cfg_version;
+ int cfg_version_idx;
};
+static struct dentry *debug_base;
+
static bool mxt_object_readable(unsigned int type)
{
switch (type) {
@@ -334,6 +345,7 @@
case MXT_PROCI_PALM_T41:
case MXT_PROCI_TOUCHSUPPRESSION_T42:
case MXT_PROCI_STYLUS_T47:
+ case MXT_PROCI_SHIELDLESS_T56:
case MXT_PROCG_NOISESUPPRESSION_T48:
case MXT_SPT_COMMSCONFIG_T18:
case MXT_SPT_GPIOPWM_T19:
@@ -366,6 +378,7 @@
case MXT_PROCI_PALM_T41:
case MXT_PROCI_TOUCHSUPPRESSION_T42:
case MXT_PROCI_STYLUS_T47:
+ case MXT_PROCI_SHIELDLESS_T56:
case MXT_PROCG_NOISESUPPRESSION_T48:
case MXT_SPT_COMMSCONFIG_T18:
case MXT_SPT_GPIOPWM_T19:
@@ -750,13 +763,13 @@
static int mxt_check_reg_init(struct mxt_data *data)
{
- const struct mxt_platform_data *pdata = data->pdata;
+ const struct mxt_config_info *config_info = data->config_info;
struct mxt_object *object;
struct device *dev = &data->client->dev;
int index = 0;
int i, j, config_offset;
- if (!pdata->config) {
+ if (!config_info) {
dev_dbg(dev, "No cfg data defined, skipping reg init\n");
return 0;
}
@@ -769,12 +782,12 @@
for (j = 0; j < object->size + 1; j++) {
config_offset = index + j;
- if (config_offset > pdata->config_length) {
+ if (config_offset > config_info->config_length) {
dev_err(dev, "Not enough config data!\n");
return -EINVAL;
}
mxt_write_object(data, object->type, j,
- pdata->config[config_offset]);
+ config_info->config[config_offset]);
}
index += object->size + 1;
}
@@ -846,6 +859,7 @@
u16 reg;
u8 reportid = 0;
u8 buf[MXT_OBJECT_SIZE];
+ bool found_t38 = false;
for (i = 0; i < data->info.object_num; i++) {
struct mxt_object *object = data->object_table + i;
@@ -866,11 +880,94 @@
(object->instances + 1);
object->max_reportid = reportid;
}
+
+ /* Calculate index for config major version in config array.
+ * Major version is the first byte in object T38.
+ */
+ if (object->type == MXT_SPT_USERDATA_T38)
+ found_t38 = true;
+ if (!found_t38 && mxt_object_writable(object->type))
+ data->cfg_version_idx += object->size + 1;
}
return 0;
}
+static int mxt_search_config_array(struct mxt_data *data, bool version_match)
+{
+
+ const struct mxt_platform_data *pdata = data->pdata;
+ const struct mxt_config_info *cfg_info;
+ struct mxt_info *info = &data->info;
+ int i;
+ u8 cfg_version;
+
+ for (i = 0; i < pdata->config_array_size; i++) {
+
+ cfg_info = &pdata->config_array[i];
+
+ if (!cfg_info->config || !cfg_info->config_length)
+ continue;
+
+ if (info->family_id == cfg_info->family_id &&
+ info->variant_id == cfg_info->variant_id &&
+ info->version == cfg_info->version &&
+ info->build == cfg_info->build) {
+
+ cfg_version = cfg_info->config[data->cfg_version_idx];
+ if (data->curr_cfg_version == cfg_version ||
+ !version_match) {
+ data->config_info = cfg_info;
+ return 0;
+ }
+ }
+ }
+
+ dev_info(&data->client->dev,
+ "Config not found: F: %d, V: %d, FW: %d.%d.%d, CFG: %d\n",
+ info->family_id, info->variant_id,
+ info->version >> 4, info->version & 0xF, info->build,
+ data->curr_cfg_version);
+ return -EINVAL;
+}
+
+static int mxt_get_config(struct mxt_data *data)
+{
+ const struct mxt_platform_data *pdata = data->pdata;
+ struct device *dev = &data->client->dev;
+ struct mxt_object *object;
+ int error;
+
+ if (!pdata->config_array || !pdata->config_array_size) {
+ dev_dbg(dev, "No cfg data provided by platform data\n");
+ return 0;
+ }
+
+ /* Get current config version */
+ object = mxt_get_object(data, MXT_SPT_USERDATA_T38);
+ if (!object) {
+ dev_err(dev, "Unable to obtain USERDATA object\n");
+ return -EINVAL;
+ }
+
+ error = mxt_read_reg(data->client, object->start_address,
+ &data->curr_cfg_version);
+ if (error) {
+ dev_err(dev, "Unable to read config version\n");
+ return error;
+ }
+
+ /* It is possible that the config data on the controller is not
+ * versioned and the version number returns 0. In this case,
+ * find a match without the config version checking.
+ */
+ error = mxt_search_config_array(data,
+ data->curr_cfg_version != 0 ? true : false);
+ if (error)
+ return error;
+
+ return 0;
+}
static void mxt_reset_delay(struct mxt_data *data)
{
struct mxt_info *info = &data->info;
@@ -919,6 +1016,11 @@
if (error)
goto free_object_table;
+ /* Get config data from platform data */
+ error = mxt_get_config(data);
+ if (error)
+ dev_dbg(&client->dev, "Config info not found.\n");
+
/* Check register init values */
error = mxt_check_reg_init(data);
if (error)
@@ -1622,6 +1724,72 @@
};
#endif
+static int mxt_debugfs_object_show(struct seq_file *m, void *v)
+{
+ struct mxt_data *data = m->private;
+ struct mxt_object *object;
+ struct device *dev = &data->client->dev;
+ int i, j, k;
+ int error;
+ int obj_size;
+ u8 val;
+
+ for (i = 0; i < data->info.object_num; i++) {
+ object = data->object_table + i;
+ obj_size = object->size + 1;
+
+ seq_printf(m, "Object[%d] (Type %d)\n", i + 1, object->type);
+
+ for (j = 0; j < object->instances + 1; j++) {
+ seq_printf(m, "[Instance %d]\n", j);
+
+ for (k = 0; k < obj_size; k++) {
+ error = mxt_read_object(data, object->type,
+ j * obj_size + k, &val);
+ if (error) {
+ dev_err(dev,
+ "Failed to read object %d "
+ "instance %d at offset %d\n",
+ object->type, j, k);
+ return error;
+ }
+
+ seq_printf(m, "Byte %d: 0x%02x (%d)\n",
+ k, val, val);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int mxt_debugfs_object_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mxt_debugfs_object_show, inode->i_private);
+}
+
+static const struct file_operations mxt_object_fops = {
+ .owner = THIS_MODULE,
+ .open = mxt_debugfs_object_open,
+ .read = seq_read,
+ .release = single_release,
+};
+
+static void __init mxt_debugfs_init(struct mxt_data *data)
+{
+ debug_base = debugfs_create_dir(MXT_DEBUGFS_DIR, NULL);
+ if (IS_ERR_OR_NULL(debug_base))
+ pr_err("atmel_mxt_ts: Failed to create debugfs dir\n");
+ if (IS_ERR_OR_NULL(debugfs_create_file(MXT_DEBUGFS_FILE,
+ 0444,
+ debug_base,
+ data,
+ &mxt_object_fops))) {
+ pr_err("atmel_mxt_ts: Failed to create object file\n");
+ debugfs_remove_recursive(debug_base);
+ }
+}
+
static int __devinit mxt_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -1773,6 +1941,8 @@
register_early_suspend(&data->early_suspend);
#endif
+ mxt_debugfs_init(data);
+
return 0;
err_unregister_device:
@@ -1834,6 +2004,8 @@
kfree(data->object_table);
kfree(data);
+ debugfs_remove_recursive(debug_base);
+
return 0;
}
diff --git a/drivers/input/touchscreen/cyttsp-i2c.c b/drivers/input/touchscreen/cyttsp-i2c.c
index bb02be6..a60dbf5 100644
--- a/drivers/input/touchscreen/cyttsp-i2c.c
+++ b/drivers/input/touchscreen/cyttsp-i2c.c
@@ -2407,27 +2407,29 @@
regulator_put(ts->vdd[i]);
goto error_vdd;
}
- }
- rc = regulator_set_optimum_mode(ts->vdd[i],
+ rc = regulator_set_optimum_mode(ts->vdd[i],
reg_info[i].hpm_load_uA);
- if (rc < 0) {
- pr_err("%s: regulator_set_optimum_mode failed rc=%d\n",
- __func__, rc);
+ if (rc < 0) {
+ pr_err("%s: regulator_set_optimum_mode failed "
+ "rc=%d\n", __func__, rc);
- regulator_set_voltage(ts->vdd[i], 0,
- reg_info[i].max_uV);
- regulator_put(ts->vdd[i]);
- goto error_vdd;
+ regulator_set_voltage(ts->vdd[i], 0,
+ reg_info[i].max_uV);
+ regulator_put(ts->vdd[i]);
+ goto error_vdd;
+ }
}
rc = regulator_enable(ts->vdd[i]);
if (rc) {
pr_err("%s: regulator_enable failed rc =%d\n",
__func__, rc);
- regulator_set_optimum_mode(ts->vdd[i], 0);
- regulator_set_voltage(ts->vdd[i], 0,
- reg_info[i].max_uV);
+ if (regulator_count_voltages(ts->vdd[i]) > 0) {
+ regulator_set_optimum_mode(ts->vdd[i], 0);
+ regulator_set_voltage(ts->vdd[i], 0,
+ reg_info[i].max_uV);
+ }
regulator_put(ts->vdd[i]);
goto error_vdd;
}
@@ -2439,10 +2441,11 @@
i = ts->platform_data->num_regulators;
error_vdd:
while (--i >= 0) {
- if (regulator_count_voltages(ts->vdd[i]) > 0)
+ if (regulator_count_voltages(ts->vdd[i]) > 0) {
regulator_set_voltage(ts->vdd[i], 0,
reg_info[i].max_uV);
- regulator_set_optimum_mode(ts->vdd[i], 0);
+ regulator_set_optimum_mode(ts->vdd[i], 0);
+ }
regulator_disable(ts->vdd[i]);
regulator_put(ts->vdd[i]);
}
@@ -2834,6 +2837,8 @@
goto regulator_hpm;
for (i = 0; i < num_reg; i++) {
+ if (regulator_count_voltages(ts->vdd[i]) < 0)
+ continue;
rc = regulator_set_optimum_mode(ts->vdd[i],
reg_info[i].lpm_load_uA);
if (rc < 0) {
@@ -2848,6 +2853,8 @@
regulator_hpm:
for (i = 0; i < num_reg; i++) {
+ if (regulator_count_voltages(ts->vdd[i]) < 0)
+ continue;
rc = regulator_set_optimum_mode(ts->vdd[i],
reg_info[i].hpm_load_uA);
if (rc < 0) {
@@ -2860,16 +2867,22 @@
return 0;
fail_regulator_lpm:
- while (i--)
+ while (i--) {
+ if (regulator_count_voltages(ts->vdd[i]) < 0)
+ continue;
regulator_set_optimum_mode(ts->vdd[i],
reg_info[i].hpm_load_uA);
+ }
return rc;
fail_regulator_hpm:
- while (i--)
+ while (i--) {
+ if (regulator_count_voltages(ts->vdd[i]) < 0)
+ continue;
regulator_set_optimum_mode(ts->vdd[i],
reg_info[i].lpm_load_uA);
+ }
return rc;
}
diff --git a/drivers/media/video/msm/msm_vfe31.c b/drivers/media/video/msm/msm_vfe31.c
index 2b3732c..f1d70aa 100644
--- a/drivers/media/video/msm/msm_vfe31.c
+++ b/drivers/media/video/msm/msm_vfe31.c
@@ -3892,6 +3892,7 @@
spin_lock_init(&vfe31_ctrl->io_lock);
spin_lock_init(&vfe31_ctrl->update_ack_lock);
spin_lock_init(&vfe31_ctrl->tasklet_lock);
+ spin_lock_init(&vfe31_ctrl->xbar_lock);
INIT_LIST_HEAD(&vfe31_ctrl->tasklet_q);
vfe31_init_free_buf_queue();
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index fa30ef2..2c99a7f 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -42,6 +42,7 @@
#define PM8921_VERSION_MASK 0xFFF0
#define PM8921_VERSION_VALUE 0x06F0
#define PM8922_VERSION_VALUE 0x0AF0
+#define PM8917_VERSION_VALUE 0x12F0
#define PM8921_REVISION_MASK 0x000F
#define REG_PM8921_PON_CNTRL_3 0x01D
@@ -116,6 +117,9 @@
else if ((pmic->rev_registers & PM8921_VERSION_MASK)
== PM8922_VERSION_VALUE)
version = PM8XXX_VERSION_8922;
+ else if ((pmic->rev_registers & PM8921_VERSION_MASK)
+ == PM8917_VERSION_VALUE)
+ version = PM8XXX_VERSION_8917;
return version;
}
@@ -138,7 +142,7 @@
.pmic_get_revision = pm8921_get_revision,
};
-static const struct resource gpio_cell_resources[] __devinitconst = {
+static struct resource gpio_cell_resources[] = {
[0] = {
.start = PM8921_IRQ_BLOCK_BIT(PM8921_GPIO_BLOCK_START, 0),
.end = PM8921_IRQ_BLOCK_BIT(PM8921_GPIO_BLOCK_START, 0)
@@ -167,7 +171,7 @@
.num_resources = ARRAY_SIZE(adc_cell_resources),
};
-static const struct resource mpp_cell_resources[] __devinitconst = {
+static struct resource mpp_cell_resources[] = {
{
.start = PM8921_IRQ_BLOCK_BIT(PM8921_MPP_BLOCK_START, 0),
.end = PM8921_IRQ_BLOCK_BIT(PM8921_MPP_BLOCK_START, 0)
@@ -545,7 +549,14 @@
}
if (pdata->gpio_pdata) {
- pdata->gpio_pdata->gpio_cdata.ngpios = PM8921_NR_GPIOS;
+ if (version == PM8XXX_VERSION_8917) {
+ gpio_cell_resources[0].end = gpio_cell_resources[0].end
+ + PM8917_NR_GPIOS
+ - PM8921_NR_GPIOS;
+ pdata->gpio_pdata->gpio_cdata.ngpios = PM8917_NR_GPIOS;
+ } else {
+ pdata->gpio_pdata->gpio_cdata.ngpios = PM8921_NR_GPIOS;
+ }
gpio_cell.platform_data = pdata->gpio_pdata;
gpio_cell.pdata_size = sizeof(struct pm8xxx_gpio_platform_data);
ret = mfd_add_devices(pmic->dev, 0, &gpio_cell, 1,
@@ -557,7 +568,14 @@
}
if (pdata->mpp_pdata) {
- pdata->mpp_pdata->core_data.nmpps = PM8921_NR_MPPS;
+ if (version == PM8XXX_VERSION_8917) {
+ mpp_cell_resources[0].end = mpp_cell_resources[0].end
+ + PM8917_NR_MPPS
+ - PM8921_NR_MPPS;
+ pdata->mpp_pdata->core_data.nmpps = PM8917_NR_MPPS;
+ } else {
+ pdata->mpp_pdata->core_data.nmpps = PM8921_NR_MPPS;
+ }
pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
mpp_cell.platform_data = pdata->mpp_pdata;
mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
@@ -665,12 +683,6 @@
goto bail;
}
- ret = mfd_add_devices(pmic->dev, 0, &pwm_cell, 1, NULL, 0);
- if (ret) {
- pr_err("Failed to add pwm subdevice ret=%d\n", ret);
- goto bail;
- }
-
if (pdata->misc_pdata) {
misc_cell.platform_data = pdata->misc_pdata;
misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
@@ -682,16 +694,6 @@
}
}
- if (pdata->leds_pdata) {
- leds_cell.platform_data = pdata->leds_pdata;
- leds_cell.pdata_size = sizeof(struct pm8xxx_led_platform_data);
- ret = mfd_add_devices(pmic->dev, 0, &leds_cell, 1, NULL, 0);
- if (ret) {
- pr_err("Failed to add leds subdevice ret=%d\n", ret);
- goto bail;
- }
- }
-
ret = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
irq_base);
if (ret) {
@@ -708,16 +710,37 @@
goto bail;
}
- if (pdata->vibrator_pdata) {
- vibrator_cell.platform_data = pdata->vibrator_pdata;
- vibrator_cell.pdata_size =
- sizeof(struct pm8xxx_vibrator_platform_data);
- ret = mfd_add_devices(pmic->dev, 0, &vibrator_cell, 1, NULL, 0);
+ if (version != PM8XXX_VERSION_8917) {
+ ret = mfd_add_devices(pmic->dev, 0, &pwm_cell, 1, NULL, 0);
if (ret) {
- pr_err("Failed to add vibrator subdevice ret=%d\n",
- ret);
+ pr_err("Failed to add pwm subdevice ret=%d\n", ret);
goto bail;
}
+
+ if (pdata->leds_pdata) {
+ leds_cell.platform_data = pdata->leds_pdata;
+ leds_cell.pdata_size =
+ sizeof(struct pm8xxx_led_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &leds_cell,
+ 1, NULL, 0);
+ if (ret) {
+ pr_err("Failed to add leds subdevice ret=%d\n",
+ ret);
+ goto bail;
+ }
+ }
+
+ if (pdata->vibrator_pdata) {
+ vibrator_cell.platform_data = pdata->vibrator_pdata;
+ vibrator_cell.pdata_size =
+ sizeof(struct pm8xxx_vibrator_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &vibrator_cell,
+ 1, NULL, 0);
+ if (ret) {
+ pr_err("Failed to add vibrator ret=%d\n", ret);
+ goto bail;
+ }
+ }
}
if (pdata->ccadc_pdata) {
@@ -768,6 +791,11 @@
[PM8XXX_REVISION_8922_2p0] = "2.0",
};
+static const char * const pm8917_rev_names[] = {
+ [PM8XXX_REVISION_8917_TEST] = "test",
+ [PM8XXX_REVISION_8917_1p0] = "1.0",
+};
+
static int __devinit pm8921_probe(struct platform_device *pdev)
{
const struct pm8921_platform_data *pdata = pdev->dev.platform_data;
@@ -823,9 +851,14 @@
if (revision >= 0 && revision < ARRAY_SIZE(pm8922_rev_names))
revision_name = pm8922_rev_names[revision];
pr_info("PMIC version: PM8922 rev %s\n", revision_name);
+ } else if (version == PM8XXX_VERSION_8917) {
+ if (revision >= 0 && revision < ARRAY_SIZE(pm8917_rev_names))
+ revision_name = pm8917_rev_names[revision];
+ pr_info("PMIC version: PM8917 rev %s\n", revision_name);
} else {
WARN_ON(version != PM8XXX_VERSION_8921
- && version != PM8XXX_VERSION_8922);
+ && version != PM8XXX_VERSION_8922
+ && version != PM8XXX_VERSION_8917);
}
/* Log human readable restart reason */
diff --git a/drivers/net/wireless/wcnss/wcnss_riva.c b/drivers/net/wireless/wcnss/wcnss_riva.c
index fe6abf2..f6997ac 100644
--- a/drivers/net/wireless/wcnss/wcnss_riva.c
+++ b/drivers/net/wireless/wcnss/wcnss_riva.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
@@ -22,6 +22,7 @@
#include <linux/semaphore.h>
#include <linux/list.h>
#include <linux/slab.h>
+#include <linux/clk.h>
#include <mach/msm_xo.h>
#include <mach/msm_iomap.h>
@@ -79,10 +80,15 @@
};
-static int configure_iris_xo(bool use_48mhz_xo, int on)
+static int configure_iris_xo(struct device *dev, bool use_48mhz_xo, int on)
{
u32 reg = 0;
int rc = 0;
+ struct clk *cxo = clk_get(dev, "cxo");
+ if (IS_ERR(cxo)) {
+ pr_err("Couldn't get cxo clock\n");
+ return PTR_ERR(cxo);
+ }
if (on) {
msm_riva_base = ioremap(MSM_RIVA_PHYS, SZ_256);
@@ -92,6 +98,11 @@
}
/* Enable IRIS XO */
+ rc = clk_prepare_enable(cxo);
+ if (rc) {
+ pr_err("cxo enable failed\n");
+ goto fail;
+ }
writel_relaxed(0, RIVA_PMU_CFG);
reg = readl_relaxed(RIVA_PMU_CFG);
reg |= RIVA_PMU_CFG_GC_BUS_MUX_SEL_TOP |
@@ -119,6 +130,7 @@
reg &= ~(RIVA_PMU_CFG_GC_BUS_MUX_SEL_TOP |
RIVA_PMU_CFG_IRIS_XO_CFG);
writel_relaxed(reg, RIVA_PMU_CFG);
+ clk_disable_unprepare(cxo);
if (!use_48mhz_xo) {
wlan_clock = msm_xo_get(MSM_XO_TCXO_A2, id);
@@ -148,12 +160,14 @@
/* Add some delay for XO to settle */
msleep(20);
+ clk_put(cxo);
return rc;
msm_xo_vote_fail:
msm_xo_put(wlan_clock);
fail:
+ clk_put(cxo);
return rc;
}
@@ -302,13 +316,15 @@
goto fail_iris_on;
/* Configure IRIS XO */
- rc = configure_iris_xo(cfg->use_48mhz_xo, WCNSS_WLAN_SWITCH_ON);
+ rc = configure_iris_xo(dev, cfg->use_48mhz_xo,
+ WCNSS_WLAN_SWITCH_ON);
if (rc)
goto fail_iris_xo;
up(&riva_power_on_lock);
} else {
- configure_iris_xo(cfg->use_48mhz_xo, WCNSS_WLAN_SWITCH_OFF);
+ configure_iris_xo(dev, cfg->use_48mhz_xo,
+ WCNSS_WLAN_SWITCH_OFF);
wcnss_iris_vregs_off();
wcnss_riva_vregs_off();
}
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index c0c398b..c40f13e 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -220,8 +220,8 @@
unsigned int update_time;
unsigned int max_voltage_mv;
unsigned int min_voltage_mv;
- unsigned int cool_temp_dc;
- unsigned int warm_temp_dc;
+ int cool_temp_dc;
+ int warm_temp_dc;
unsigned int temp_check_period;
unsigned int cool_bat_chg_current;
unsigned int warm_bat_chg_current;
@@ -2301,8 +2301,16 @@
{
int rc;
- btm_config.btm_warm_fn = battery_warm;
- btm_config.btm_cool_fn = battery_cool;
+ if (chip->warm_temp_dc != INT_MIN)
+ btm_config.btm_warm_fn = battery_warm;
+ else
+ btm_config.btm_warm_fn = NULL;
+
+ if (chip->cool_temp_dc != INT_MIN)
+ btm_config.btm_cool_fn = battery_cool;
+ else
+ btm_config.btm_cool_fn = NULL;
+
btm_config.low_thr_temp = chip->cool_temp_dc;
btm_config.high_thr_temp = chip->warm_temp_dc;
btm_config.interval = chip->temp_check_period;
@@ -3042,7 +3050,7 @@
int rc;
struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
- if (!(chip->cool_temp_dc == 0 && chip->warm_temp_dc == 0)
+ if (!(chip->cool_temp_dc == INT_MIN && chip->warm_temp_dc == INT_MIN)
&& !(chip->keep_btm_on_suspend)) {
rc = pm8xxx_adc_btm_configure(&btm_config);
if (rc)
@@ -3064,7 +3072,7 @@
int rc;
struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
- if (!(chip->cool_temp_dc == 0 && chip->warm_temp_dc == 0)
+ if (!(chip->cool_temp_dc == INT_MIN && chip->warm_temp_dc == INT_MIN)
&& !(chip->keep_btm_on_suspend)) {
rc = pm8xxx_adc_btm_end();
if (rc)
@@ -3110,8 +3118,16 @@
chip->batt_id_channel = pdata->charger_cdata.batt_id_channel;
chip->batt_id_min = pdata->batt_id_min;
chip->batt_id_max = pdata->batt_id_max;
- chip->cool_temp_dc = pdata->cool_temp * 10;
- chip->warm_temp_dc = pdata->warm_temp * 10;
+ if (pdata->cool_temp != INT_MIN)
+ chip->cool_temp_dc = pdata->cool_temp * 10;
+ else
+ chip->cool_temp_dc = INT_MIN;
+
+ if (pdata->warm_temp != INT_MIN)
+ chip->warm_temp_dc = pdata->warm_temp * 10;
+ else
+ chip->warm_temp_dc = INT_MIN;
+
chip->temp_check_period = pdata->temp_check_period;
chip->max_bat_chg_current = pdata->max_bat_chg_current;
chip->cool_bat_chg_current = pdata->cool_bat_chg_current;
@@ -3185,10 +3201,10 @@
enable_irq_wake(chip->pmic_chg_irq[BAT_TEMP_OK_IRQ]);
enable_irq_wake(chip->pmic_chg_irq[VBATDET_LOW_IRQ]);
/*
- * if both the cool_temp_dc and warm_temp_dc are zero the device doesnt
+ * if both the cool_temp_dc and warm_temp_dc are invalid device doesnt
* care for jeita compliance
*/
- if (!(chip->cool_temp_dc == 0 && chip->warm_temp_dc == 0)) {
+ if (!(chip->cool_temp_dc == INT_MIN && chip->warm_temp_dc == INT_MIN)) {
rc = configure_btm(chip);
if (rc) {
pr_err("couldn't register with btm rc=%d\n", rc);
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index a541d8b..1ac301f 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -1074,7 +1074,7 @@
else
mc = SLIM_MSG_MC_CONNECT_SINK;
buf[0] = pn;
- buf[1] = ch;
+ buf[1] = ctrl->chans[ch].chan;
if (la == SLIM_LA_MANAGER)
ctrl->ports[pn].flow = flow;
ret = slim_processtxn(ctrl, SLIM_MSG_DEST_LOGICALADDR, mc, 0,
@@ -1269,6 +1269,9 @@
int i, j;
int *len;
int sl = slc->seglen << slc->rootexp;
+ /* Channel is already active and other end is transmitting data */
+ if (slc->state >= SLIM_CH_ACTIVE)
+ return;
if (slc->coeff == SLIM_COEFF_1) {
arr = ctrl->sched.chc1;
len = &ctrl->sched.num_cc1;
@@ -1507,9 +1510,8 @@
* slim_alloc_ch: Allocate a slimbus channel and return its handle.
* @sb: client handle.
* @chanh: return channel handle
- * Slimbus channels are limited to 256 per specification. LSB of the handle
- * indicates channel number and MSB of the handle is used by the slimbus
- * framework. -EXFULL is returned if all channels are in use.
+ * Slimbus channels are limited to 256 per specification.
+ * -EXFULL is returned if all channels are in use.
* Although slimbus specification supports 256 channels, a controller may not
* support that many channels.
*/
@@ -1532,6 +1534,7 @@
*chanh = i;
ctrl->chans[i].nextgrp = 0;
ctrl->chans[i].state = SLIM_CH_ALLOCATED;
+ ctrl->chans[i].chan = (u8)(ctrl->reserved + i);
mutex_unlock(&ctrl->m_ctrl);
return 0;
@@ -1539,9 +1542,68 @@
EXPORT_SYMBOL_GPL(slim_alloc_ch);
/*
+ * slim_query_ch: Get reference-counted handle for a channel number. Every
+ * channel is reference counted by upto one as producer and the others as
+ * consumer)
+ * @sb: client handle
+ * @chan: slimbus channel number
+ * @chanh: return channel handle
+ * If request channel number is not in use, it is allocated, and reference
+ * count is set to one. If the channel was was already allocated, this API
+ * will return handle to that channel and reference count is incremented.
+ * -EXFULL is returned if all channels are in use
+ */
+int slim_query_ch(struct slim_device *sb, u8 ch, u16 *chanh)
+{
+ struct slim_controller *ctrl = sb->ctrl;
+ u16 i, j;
+ int ret = 0;
+ if (!ctrl || !chanh)
+ return -EINVAL;
+ mutex_lock(&ctrl->m_ctrl);
+ /* start with modulo number */
+ i = ch % ctrl->nchans;
+
+ for (j = 0; j < ctrl->nchans; j++) {
+ if (ctrl->chans[i].chan == ch) {
+ *chanh = i;
+ ctrl->chans[i].ref++;
+ if (ctrl->chans[i].state == SLIM_CH_FREE)
+ ctrl->chans[i].state = SLIM_CH_ALLOCATED;
+ goto query_out;
+ }
+ i = (i + 1) % ctrl->nchans;
+ }
+
+ /* Channel not in table yet */
+ ret = -EXFULL;
+ for (j = 0; j < ctrl->nchans; j++) {
+ if (ctrl->chans[i].state == SLIM_CH_FREE) {
+ ctrl->chans[i].state =
+ SLIM_CH_ALLOCATED;
+ *chanh = i;
+ ctrl->chans[i].ref++;
+ ctrl->chans[i].chan = ch;
+ ctrl->chans[i].nextgrp = 0;
+ ret = 0;
+ break;
+ }
+ i = (i + 1) % ctrl->nchans;
+ }
+query_out:
+ mutex_unlock(&ctrl->m_ctrl);
+ dev_dbg(&ctrl->dev, "query ch:%d,hdl:%d,ref:%d,ret:%d",
+ ch, i, ctrl->chans[i].ref, ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(slim_query_ch);
+
+/*
* slim_dealloc_ch: Deallocate channel allocated using the API above
* -EISCONN is returned if the channel is tried to be deallocated without
* being removed first.
+ * -ENOTCONN is returned if deallocation is tried on a channel that's not
+ * allocated.
*/
int slim_dealloc_ch(struct slim_device *sb, u16 chanh)
{
@@ -1552,15 +1614,29 @@
return -EINVAL;
mutex_lock(&ctrl->m_ctrl);
+ if (slc->state == SLIM_CH_FREE) {
+ mutex_unlock(&ctrl->m_ctrl);
+ return -ENOTCONN;
+ }
+ if (slc->ref > 1) {
+ slc->ref--;
+ mutex_unlock(&ctrl->m_ctrl);
+ dev_dbg(&ctrl->dev, "remove chan:%d,hdl:%d,ref:%d",
+ slc->chan, chanh, slc->ref);
+ return 0;
+ }
if (slc->state >= SLIM_CH_PENDING_ACTIVE) {
dev_err(&ctrl->dev, "Channel:%d should be removed first", chan);
mutex_unlock(&ctrl->m_ctrl);
return -EISCONN;
}
+ slc->ref--;
kfree(slc->srch);
slc->srch = NULL;
slc->state = SLIM_CH_FREE;
mutex_unlock(&ctrl->m_ctrl);
+ dev_dbg(&ctrl->dev, "remove chan:%d,hdl:%d,ref:%d",
+ slc->chan, chanh, slc->ref);
return 0;
}
EXPORT_SYMBOL_GPL(slim_dealloc_ch);
@@ -1589,8 +1665,8 @@
* Channels can be grouped if multiple channels use same parameters
* (e.g. 5.1 audio has 6 channels with same parameters. They will all be grouped
* and given 1 handle for simplicity and avoid repeatedly calling the API)
- * -EISCONN is returned if the channel is already connected. -EBUSY is
- * returned if the channel is already allocated to some other client.
+ * -EISCONN is returned if channel is already used with different parameters.
+ * -ENXIO is returned if the channel is not yet allocated.
*/
int slim_define_ch(struct slim_device *sb, struct slim_ch *prop, u16 *chanh,
u8 nchan, bool grp, u16 *grph)
@@ -1603,26 +1679,28 @@
mutex_lock(&ctrl->m_ctrl);
for (i = 0; i < nchan; i++) {
u8 chan = (u8)(chanh[i] & 0xFF);
- dev_dbg(&ctrl->dev, "define_ch: port:%d, state:%d", chanh[i],
+ struct slim_ich *slc = &ctrl->chans[chan];
+ dev_dbg(&ctrl->dev, "define_ch: ch:%d, state:%d", chan,
(int)ctrl->chans[chan].state);
- if (ctrl->chans[chan].state < SLIM_CH_ALLOCATED ||
- ctrl->chans[chan].state > SLIM_CH_DEFINED) {
- int j;
- for (j = i - 1; j >= 0; j--)
- ctrl->chans[chan].state = SLIM_CH_ALLOCATED;
- ret = -EBUSY;
+ if (slc->state < SLIM_CH_ALLOCATED) {
+ ret = -ENXIO;
goto err_define_ch;
}
- ctrl->chans[chan].prop = *prop;
- ret = slim_nextdefine_ch(sb, chan);
- if (ret) {
- int j;
- for (j = i - 1; j >= 0; j--) {
- chan = chanh[j] & 0xFF;
- ctrl->chans[chan].nextgrp = 0;
- ctrl->chans[chan].state = SLIM_CH_ALLOCATED;
+ if (slc->state >= SLIM_CH_DEFINED && slc->ref >= 2) {
+ if (prop->ratem != slc->prop.ratem ||
+ prop->sampleszbits != slc->prop.sampleszbits ||
+ prop->baser != slc->prop.baser) {
+ ret = -EISCONN;
+ goto err_define_ch;
}
+ } else if (slc->state > SLIM_CH_DEFINED) {
+ ret = -EISCONN;
goto err_define_ch;
+ } else {
+ ctrl->chans[chan].prop = *prop;
+ ret = slim_nextdefine_ch(sb, chan);
+ if (ret)
+ goto err_define_ch;
}
if (i < (nchan - 1))
ctrl->chans[chan].nextgrp = chanh[i + 1];
@@ -1630,13 +1708,18 @@
ctrl->chans[chan].nextgrp |= SLIM_START_GRP;
if (i == (nchan - 1))
ctrl->chans[chan].nextgrp |= SLIM_END_GRP;
-
- ctrl->chans[chan].state = SLIM_CH_DEFINED;
}
if (grp)
*grph = chanh[0];
+ for (i = 0; i < nchan; i++) {
+ u8 chan = (u8)(chanh[i] & 0xFF);
+ struct slim_ich *slc = &ctrl->chans[chan];
+ if (slc->state == SLIM_CH_ALLOCATED)
+ slc->state = SLIM_CH_DEFINED;
+ }
err_define_ch:
+ dev_dbg(&ctrl->dev, "define_ch: ch:%d, ret:%d", *chanh, ret);
mutex_unlock(&ctrl->m_ctrl);
return ret;
}
@@ -2310,14 +2393,17 @@
struct slim_pending_ch, pending);
slc = &ctrl->chans[pch->chan];
if (revert) {
- u32 sl = slc->seglen << slc->rootexp;
- if (slc->coeff == SLIM_COEFF_3)
- sl *= 3;
- ctrl->sched.usedslots -= sl;
- slim_remove_ch(ctrl, slc);
- slc->state = SLIM_CH_DEFINED;
+ if (slc->state == SLIM_CH_PENDING_ACTIVE) {
+ u32 sl = slc->seglen << slc->rootexp;
+ if (slc->coeff == SLIM_COEFF_3)
+ sl *= 3;
+ ctrl->sched.usedslots -= sl;
+ slim_remove_ch(ctrl, slc);
+ slc->state = SLIM_CH_DEFINED;
+ }
} else {
slc->state = SLIM_CH_ACTIVE;
+ slc->def++;
}
list_del_init(&pch->pending);
kfree(pch);
@@ -2333,6 +2419,7 @@
if (slc->coeff == SLIM_COEFF_3)
sl *= 3;
ctrl->sched.usedslots += sl;
+ slc->def = 1;
slc->state = SLIM_CH_ACTIVE;
} else
slim_remove_ch(ctrl, slc);
@@ -2383,7 +2470,8 @@
list_for_each_entry(pch, &sb->mark_define, pending) {
struct slim_ich *slc = &ctrl->chans[pch->chan];
slim_add_ch(ctrl, slc);
- slc->state = SLIM_CH_PENDING_ACTIVE;
+ if (slc->state < SLIM_CH_ACTIVE)
+ slc->state = SLIM_CH_PENDING_ACTIVE;
}
list_for_each_entry(pch, &sb->mark_removal, pending) {
@@ -2433,7 +2521,7 @@
list_for_each_entry(pch, &sb->mark_define, pending) {
struct slim_ich *slc = &ctrl->chans[pch->chan];
/* Define content */
- wbuf[0] = pch->chan;
+ wbuf[0] = slc->chan;
wbuf[1] = slc->prrate;
wbuf[2] = slc->prop.dataf | (slc->prop.auxf << 4);
wbuf[3] = slc->prop.sampleszbits / SLIM_CL_PER_SL;
@@ -2456,8 +2544,9 @@
}
list_for_each_entry(pch, &sb->mark_removal, pending) {
+ struct slim_ich *slc = &ctrl->chans[pch->chan];
dev_dbg(&ctrl->dev, "remove chan:%x\n", pch->chan);
- wbuf[0] = pch->chan;
+ wbuf[0] = slc->chan;
ret = slim_processtxn(ctrl, SLIM_MSG_DEST_BROADCAST,
SLIM_MSG_MC_NEXT_REMOVE_CHANNEL, 0,
SLIM_MSG_MT_CORE, NULL, wbuf, 1, 4,
@@ -2466,8 +2555,9 @@
goto revert_reconfig;
}
list_for_each_entry(pch, &sb->mark_suspend, pending) {
+ struct slim_ich *slc = &ctrl->chans[pch->chan];
dev_dbg(&ctrl->dev, "suspend chan:%x\n", pch->chan);
- wbuf[0] = pch->chan;
+ wbuf[0] = slc->chan;
ret = slim_processtxn(ctrl, SLIM_MSG_DEST_BROADCAST,
SLIM_MSG_MC_NEXT_DEACTIVATE_CHANNEL, 0,
SLIM_MSG_MT_CORE, NULL, wbuf, 1, 4,
@@ -2489,13 +2579,13 @@
dev_dbg(&ctrl->dev, "new-off:%d, old-off:%d\n",
slc->newoff, slc->offset);
- if (slc->state < SLIM_CH_ACTIVE ||
+ if (slc->state < SLIM_CH_ACTIVE || slc->def < slc->ref ||
slc->newintr != slc->interval ||
slc->newoff != slc->offset) {
segdist |= 0x200;
segdist >>= curexp;
segdist |= (slc->newoff << (curexp + 1)) & 0xC00;
- wbuf[0] = (u8)(slc - ctrl->chans);
+ wbuf[0] = slc->chan;
wbuf[1] = (u8)(segdist & 0xFF);
wbuf[2] = (u8)((segdist & 0xF00) >> 8) |
(slc->prop.prot << 4);
@@ -2522,13 +2612,13 @@
dev_dbg(&ctrl->dev, "new-off:%d, old-off:%d\n",
slc->newoff, slc->offset);
- if (slc->state < SLIM_CH_ACTIVE ||
+ if (slc->state < SLIM_CH_ACTIVE || slc->def < slc->ref ||
slc->newintr != slc->interval ||
slc->newoff != slc->offset) {
segdist |= 0x200;
segdist >>= curexp;
segdist |= 0xC00;
- wbuf[0] = (u8)(slc - ctrl->chans);
+ wbuf[0] = slc->chan;
wbuf[1] = (u8)(segdist & 0xFF);
wbuf[2] = (u8)((segdist & 0xF00) >> 8) |
(slc->prop.prot << 4);
@@ -2607,6 +2697,8 @@
mutex_lock(&ctrl->m_ctrl);
do {
slc = &ctrl->chans[chan];
+ dev_dbg(&ctrl->dev, "chan:%d,ctrl:%d,def:%d", chan, chctrl,
+ slc->def);
if (slc->state < SLIM_CH_DEFINED) {
ret = -ENOTCONN;
break;
@@ -2616,7 +2708,7 @@
if (ret)
break;
} else if (chctrl == SLIM_CH_ACTIVATE) {
- if (slc->state >= SLIM_CH_ACTIVE) {
+ if (slc->state > SLIM_CH_ACTIVE) {
ret = -EISCONN;
break;
}
@@ -2628,7 +2720,10 @@
ret = -ENOTCONN;
break;
}
- ret = add_pending_ch(&sb->mark_removal, chan);
+ if (slc->def > 0)
+ slc->def--;
+ if (slc->def == 0)
+ ret = add_pending_ch(&sb->mark_removal, chan);
if (ret)
break;
}
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index 17b9f0d..e2d77e9 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -1283,8 +1283,8 @@
if (dd->use_rlock)
remote_mutex_lock(&dd->r_lock);
- clk_enable(dd->clk);
- clk_enable(dd->pclk);
+ clk_prepare_enable(dd->clk);
+ clk_prepare_enable(dd->pclk);
msm_spi_enable_irqs(dd);
if (!msm_spi_is_valid_state(dd)) {
@@ -1311,8 +1311,8 @@
spin_unlock_irqrestore(&dd->queue_lock, flags);
msm_spi_disable_irqs(dd);
- clk_disable(dd->clk);
- clk_disable(dd->pclk);
+ clk_disable_unprepare(dd->clk);
+ clk_disable_unprepare(dd->pclk);
if (dd->use_rlock)
remote_mutex_unlock(&dd->r_lock);
@@ -1400,8 +1400,8 @@
if (dd->use_rlock)
remote_mutex_lock(&dd->r_lock);
- clk_enable(dd->clk);
- clk_enable(dd->pclk);
+ clk_prepare_enable(dd->clk);
+ clk_prepare_enable(dd->pclk);
spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select;
@@ -1429,8 +1429,8 @@
/* Ensure previous write completed before disabling the clocks */
mb();
- clk_disable(dd->clk);
- clk_disable(dd->pclk);
+ clk_disable_unprepare(dd->clk);
+ clk_disable_unprepare(dd->pclk);
if (dd->use_rlock)
remote_mutex_unlock(&dd->r_lock);
@@ -1925,7 +1925,7 @@
if (pdata && pdata->max_clock_speed)
msm_spi_clock_set(dd, dd->pdata->max_clock_speed);
- rc = clk_enable(dd->clk);
+ rc = clk_prepare_enable(dd->clk);
if (rc) {
dev_err(&pdev->dev, "%s: unable to enable core_clk\n",
__func__);
@@ -1933,7 +1933,7 @@
}
clk_enabled = 1;
- rc = clk_enable(dd->pclk);
+ rc = clk_prepare_enable(dd->pclk);
if (rc) {
dev_err(&pdev->dev, "%s: unable to enable iface_clk\n",
__func__);
@@ -1966,8 +1966,8 @@
if (rc)
goto err_probe_state;
- clk_disable(dd->clk);
- clk_disable(dd->pclk);
+ clk_disable_unprepare(dd->clk);
+ clk_disable_unprepare(dd->pclk);
clk_enabled = 0;
pclk_enabled = 0;
@@ -2009,10 +2009,10 @@
err_probe_dma:
err_probe_gsbi:
if (pclk_enabled)
- clk_disable(dd->pclk);
+ clk_disable_unprepare(dd->pclk);
err_probe_pclk_enable:
if (clk_enabled)
- clk_disable(dd->clk);
+ clk_disable_unprepare(dd->clk);
err_probe_clk_enable:
clk_put(dd->pclk);
err_probe_pclk_get:
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 177176e..86f8a25 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -426,6 +426,23 @@
kfree(f->name);
}
+static void frmnet_suspend(struct usb_function *f)
+{
+ struct f_rmnet *dev = func_to_rmnet(f);
+ unsigned port_num;
+
+ if (!atomic_read(&dev->online))
+ return;
+ /* This is a workaround for a bug in Windows 7/XP hosts in which
+ * the DTR bit is not set low when going into suspend. Hence force it
+ * low here when this function driver is suspended.
+ */
+ if (dev->port.notify_modem) {
+ port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
+ dev->port.notify_modem(&dev->port, port_num, ~ACM_CTRL_DTR);
+ }
+}
+
static void frmnet_disable(struct usb_function *f)
{
struct f_rmnet *dev = func_to_rmnet(f);
@@ -937,6 +954,7 @@
f->disable = frmnet_disable;
f->set_alt = frmnet_set_alt;
f->setup = frmnet_setup;
+ f->suspend = frmnet_suspend;
dev->port.send_cpkt_response = frmnet_send_cpkt_response;
dev->port.disconnect = frmnet_disconnect;
dev->port.connect = frmnet_connect;
diff --git a/drivers/usb/gadget/u_rmnet_ctrl_smd.c b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
index 90cbe54..edba510 100644
--- a/drivers/usb/gadget/u_rmnet_ctrl_smd.c
+++ b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
@@ -412,10 +412,13 @@
spin_unlock_irqrestore(&port->port_lock, flags);
- if (test_bit(CH_OPENED, &c->flags)) {
- /* this should send the dtr zero */
+ if (test_and_clear_bit(CH_OPENED, &c->flags))
+ /* send dtr zero */
+ smd_tiocmset(c->ch, c->cbits_tomodem, ~c->cbits_tomodem);
+
+ if (c->ch) {
smd_close(c->ch);
- clear_bit(CH_OPENED, &c->flags);
+ c->ch = NULL;
}
}
@@ -464,7 +467,10 @@
if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) {
clear_bit(CH_READY, &c->flags);
clear_bit(CH_OPENED, &c->flags);
- smd_close(c->ch);
+ if (c->ch) {
+ smd_close(c->ch);
+ c->ch = NULL;
+ }
break;
}
}
@@ -603,8 +609,8 @@
c->cbits_tomodem ? "HIGH" : "LOW",
test_bit(CH_OPENED, &c->flags),
test_bit(CH_READY, &c->flags),
- smd_read_avail(c->ch),
- smd_write_avail(c->ch));
+ c->ch ? smd_read_avail(c->ch) : 0,
+ c->ch ? smd_write_avail(c->ch) : 0);
spin_unlock_irqrestore(&port->port_lock, flags);
}
diff --git a/drivers/usb/gadget/u_smd.c b/drivers/usb/gadget/u_smd.c
index caccade..93a96be 100644
--- a/drivers/usb/gadget/u_smd.c
+++ b/drivers/usb/gadget/u_smd.c
@@ -711,17 +711,18 @@
port->n_read = 0;
spin_unlock_irqrestore(&port->port_lock, flags);
- if (!test_bit(CH_OPENED, &port->pi->flags))
- return;
+ if (test_and_clear_bit(CH_OPENED, &port->pi->flags)) {
+ /* lower the dtr */
+ port->cbits_to_modem = 0;
+ smd_tiocmset(port->pi->ch,
+ port->cbits_to_modem,
+ ~port->cbits_to_modem);
+ }
- /* lower the dtr */
- port->cbits_to_modem = 0;
- smd_tiocmset(port->pi->ch,
- port->cbits_to_modem,
- ~port->cbits_to_modem);
-
- smd_close(port->pi->ch);
- clear_bit(CH_OPENED, &port->pi->flags);
+ if (port->pi->ch) {
+ smd_close(port->pi->ch);
+ port->pi->ch = NULL;
+ }
}
#define SMD_CH_MAX_LEN 20
@@ -765,7 +766,10 @@
if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
clear_bit(CH_READY, &pi->flags);
clear_bit(CH_OPENED, &pi->flags);
- smd_close(pi->ch);
+ if (pi->ch) {
+ smd_close(pi->ch);
+ pi->ch = NULL;
+ }
break;
}
}
@@ -821,6 +825,7 @@
size_t count, loff_t *ppos)
{
struct gsmd_port *port;
+ struct smd_port_info *pi;
char *buf;
unsigned long flags;
int temp = 0;
@@ -833,6 +838,7 @@
for (i = 0; i < n_smd_ports; i++) {
port = smd_ports[i].port;
+ pi = port->pi;
spin_lock_irqsave(&port->port_lock, flags);
temp += scnprintf(buf + temp, 512 - temp,
"###PORT:%d###\n"
@@ -848,10 +854,10 @@
i, port->nbytes_tolaptop, port->nbytes_tomodem,
port->cbits_to_modem, port->cbits_to_laptop,
port->n_read,
- smd_read_avail(port->pi->ch),
- smd_write_avail(port->pi->ch),
- test_bit(CH_OPENED, &port->pi->flags),
- test_bit(CH_READY, &port->pi->flags));
+ pi->ch ? smd_read_avail(pi->ch) : 0,
+ pi->ch ? smd_write_avail(pi->ch) : 0,
+ test_bit(CH_OPENED, &pi->flags),
+ test_bit(CH_READY, &pi->flags));
spin_unlock_irqrestore(&port->port_lock, flags);
}
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 787514c..3d739ae 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -2450,7 +2450,7 @@
goto free_regs;
}
- motg->xo_handle = msm_xo_get(MSM_XO_TCXO_D0, "usb");
+ motg->xo_handle = msm_xo_get(MSM_XO_CXO, "usb");
if (IS_ERR(motg->xo_handle)) {
dev_err(&pdev->dev, "%s not able to get the handle "
"to vote for TCXO D0 buffer\n", __func__);
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index f27bd49..1449c8e 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -338,6 +338,8 @@
{
unsigned long flag;
int ret = 0;
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
mutex_lock(&mdp_hist_mutex);
if (!mdp_is_hist_start) {
printk(KERN_ERR "%s histogram already stopped\n", __func__);
@@ -349,6 +351,14 @@
mdp_is_hist_start = FALSE;
spin_unlock_irqrestore(&mdp_spin_lock, flag);
+ if (!mfd->panel_power_on) {
+
+ mdp_is_hist_data = FALSE;
+ complete(&mdp_hist_comp);
+ ret = -EINVAL;
+ goto mdp_hist_stop_err;
+ }
+
ret = _mdp_histogram_ctrl(FALSE);
mdp_hist_stop_err:
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index 1d1d5f1..1bef55c 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -1589,7 +1589,7 @@
continue;
init_completion(&pipe->comp);
init_completion(&pipe->dmas_comp);
- pr_info("%s: pipe=%x ndx=%d num=%d\n", __func__,
+ pr_debug("%s: pipe=%x ndx=%d num=%d\n", __func__,
(int)pipe, pipe->pipe_ndx, pipe->pipe_num);
return pipe;
}
@@ -1605,7 +1605,7 @@
{
uint32 ptype, num, ndx, mixer;
- pr_info("%s: pipe=%x ndx=%d\n", __func__, (int)pipe, pipe->pipe_ndx);
+ pr_debug("%s: pipe=%x ndx=%d\n", __func__, (int)pipe, pipe->pipe_ndx);
ptype = pipe->pipe_type;
num = pipe->pipe_num;
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
index 5f5d632..96a1eda 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -585,7 +585,7 @@
dsi_pipe->blt_addr = 0;
change++;
}
- pr_info("%s: enable=%d blt_addr=%x\n", __func__,
+ pr_debug("%s: enable=%d blt_addr=%x\n", __func__,
enable, (int)dsi_pipe->blt_addr);
spin_unlock_irqrestore(&mdp_spin_lock, flag);
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index a4a5b06..d82672e 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -2604,14 +2604,14 @@
if (!IS_ERR_OR_NULL(mfd->iclient)) {
if (!IS_ERR_OR_NULL(buf->ihdl)) {
ion_free(mfd->iclient, buf->ihdl);
- pr_info("%s:%d free writeback imem\n", __func__,
+ pr_debug("%s:%d free writeback imem\n", __func__,
__LINE__);
buf->ihdl = NULL;
}
} else {
if (buf->phys_addr) {
free_contiguous_memory_by_paddr(buf->phys_addr);
- pr_info("%s:%d free writeback pmem\n", __func__,
+ pr_debug("%s:%d free writeback pmem\n", __func__,
__LINE__);
}
}
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index bdfadd5..42fa2ba 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -3148,6 +3148,9 @@
break;
case MSMFB_HISTOGRAM_START:
+ if (!mfd->panel_power_on)
+ return -EPERM;
+
if (!mfd->do_histogram)
return -ENODEV;
ret = mdp_start_histogram(info);
diff --git a/drivers/video/msm/vidc/common/init/vidc_init.c b/drivers/video/msm/vidc/common/init/vidc_init.c
index f65f835..d24d43c 100644
--- a/drivers/video/msm/vidc/common/init/vidc_init.c
+++ b/drivers/video/msm/vidc/common/init/vidc_init.c
@@ -506,7 +506,7 @@
if (IS_ERR_OR_NULL(buff_ion_handle)) {
ERR("%s(): get_ION_handle failed\n",
__func__);
- goto ion_error;
+ goto bail_out_add;
}
if (ion_handle_get_flags(client_ctx->user_ion_client,
buff_ion_handle,
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index bdbaffd..d1c7733 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-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,7 @@
#define APQ8060_MACHINE_ID 86
#define AO8960_MACHINE_ID 87
#define MSM8660_MACHINE_ID 71
+#define MDM9615_MACHINE_ID 104
#define APQ8064_MACHINE_ID 109
#define MSM8930_MACHINE_ID 116
#define MSM8630_MACHINE_ID 117
@@ -47,10 +48,13 @@
#define APQ8030_MACHINE_ID 119
#define MSM8627_MACHINE_ID 120
#define MSM8227_MACHINE_ID 121
+#define MSM8260A_MACHINE_ID 123
+#define MSM8974_MACHINE_ID 126
#define APQ8060_TOOLS_ID 4062
#define AO8960_TOOLS_ID 4064
#define APQ8064_TOOLS_ID 4072
#define MSM8930_TOOLS_ID 4072
+#define MSM8974_TOOLS_ID 4072
#define MSG_MASK_0 (0x00000001)
#define MSG_MASK_1 (0x00000002)
diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h
index 9aa557a..7ae8342 100644
--- a/include/linux/i2c/atmel_mxt_ts.h
+++ b/include/linux/i2c/atmel_mxt_ts.h
@@ -29,10 +29,20 @@
/* MXT_TOUCH_KEYARRAY_T15 */
#define MXT_KEYARRAY_MAX_KEYS 32
-/* The platform data for the Atmel maXTouch touchscreen driver */
-struct mxt_platform_data {
+/* Config data for a given maXTouch controller with a specific firmware */
+struct mxt_config_info {
const u8 *config;
size_t config_length;
+ u8 family_id;
+ u8 variant_id;
+ u8 version;
+ u8 build;
+};
+
+/* The platform data for the Atmel maXTouch touchscreen driver */
+struct mxt_platform_data {
+ const struct mxt_config_info *config_array;
+ size_t config_array_size;
unsigned int x_size;
unsigned int y_size;
diff --git a/include/linux/mfd/pm8xxx/core.h b/include/linux/mfd/pm8xxx/core.h
index 5ed615b..e7bf820 100644
--- a/include/linux/mfd/pm8xxx/core.h
+++ b/include/linux/mfd/pm8xxx/core.h
@@ -1,5 +1,5 @@
/*
- * 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
@@ -28,6 +28,7 @@
PM8XXX_VERSION_8018,
PM8XXX_VERSION_8922,
PM8XXX_VERSION_8038,
+ PM8XXX_VERSION_8917,
};
/* PMIC version specific silicon revisions */
@@ -68,6 +69,9 @@
#define PM8XXX_REVISION_8038_2p0 2
#define PM8XXX_REVISION_8038_2p1 3
+#define PM8XXX_REVISION_8917_TEST 0
+#define PM8XXX_REVISION_8917_1p0 1
+
struct pm8xxx_drvdata {
int (*pmic_readb) (const struct device *dev,
u16 addr, u8 *val);
diff --git a/include/linux/mfd/pm8xxx/pm8921-charger.h b/include/linux/mfd/pm8xxx/pm8921-charger.h
index a954364..31af535 100644
--- a/include/linux/mfd/pm8xxx/pm8921-charger.h
+++ b/include/linux/mfd/pm8xxx/pm8921-charger.h
@@ -63,9 +63,11 @@
* after the battery has been fully charged
* @term_current: the charger current (mA) at which EOC happens
* @cool_temp: the temperature (degC) at which the battery is
- * considered cool charging current and voltage is reduced
+ * considered cool charging current and voltage is reduced.
+ * Use INT_MIN to indicate not valid.
* @warm_temp: the temperature (degC) at which the battery is
* considered warm charging current and voltage is reduced
+ * Use INT_MIN to indicate not valid.
* @temp_check_period: The polling interval in seconds to check battery
* temeperature if it has gone to cool or warm temperature
* area
@@ -108,8 +110,8 @@
unsigned int min_voltage;
unsigned int resume_voltage_delta;
unsigned int term_current;
- unsigned int cool_temp;
- unsigned int warm_temp;
+ int cool_temp;
+ int warm_temp;
unsigned int temp_check_period;
unsigned int max_bat_chg_current;
unsigned int cool_bat_chg_current;
diff --git a/include/linux/mfd/pm8xxx/pm8921.h b/include/linux/mfd/pm8xxx/pm8921.h
index 35792d5..ee1216d 100644
--- a/include/linux/mfd/pm8xxx/pm8921.h
+++ b/include/linux/mfd/pm8xxx/pm8921.h
@@ -1,5 +1,5 @@
/*
- * 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,8 +40,10 @@
#define PM8921_NR_IRQS 256
#define PM8921_NR_GPIOS 44
+#define PM8917_NR_GPIOS 38
#define PM8921_NR_MPPS 12
+#define PM8917_NR_MPPS 10
#define PM8921_GPIO_BLOCK_START 24
#define PM8921_MPP_BLOCK_START 16
diff --git a/include/linux/slimbus/slimbus.h b/include/linux/slimbus/slimbus.h
index 2b5adb3..d161d41 100644
--- a/include/linux/slimbus/slimbus.h
+++ b/include/linux/slimbus/slimbus.h
@@ -381,6 +381,16 @@
* @srch: Source ports used by this channel.
* @nsrc: number of source ports used by this channel.
* @sinkh: Sink port used by this channel.
+ * @chan: Channel number sent on hardware lines for this channel. May not be
+ * equal to array-index into chans if client requested to use number beyond
+ * channel-array for the controller.
+ * @ref: Reference number to keep track of how many clients (upto 2) are using
+ * this channel.
+ * @def: Used to keep track of how many times the channel definition is sent
+ * to hardware and this will decide if channel-remove can be sent for the
+ * channel. Channel definition may be sent upto twice (once per producer
+ * and once per consumer). Channel removal should be sent only once to
+ * avoid clients getting underflow/overflow errors.
*/
struct slim_ich {
struct slim_ch prop;
@@ -397,6 +407,9 @@
u32 *srch;
int nsrc;
u32 sinkh;
+ u8 chan;
+ int ref;
+ int def;
};
/*
@@ -472,6 +485,8 @@
* @nports: Number of ports supported by the controller
* @chans: Channels associated with this controller
* @nchans: Number of channels supported
+ * @reserved: Reserved channels that controller wants to use internally
+ * Clients will be assigned channel numbers after this number
* @sched: scheduler structure used by the controller
* @dev_released: completion used to signal when sysfs has released this
* controller so that it can be deleted during shutdown
@@ -516,6 +531,7 @@
int nports;
struct slim_ich *chans;
int nchans;
+ u8 reserved;
struct slim_sched sched;
struct completion dev_released;
int (*xfer_msg)(struct slim_controller *ctrl,
@@ -784,18 +800,32 @@
* slim_alloc_ch: Allocate a slimbus channel and return its handle.
* @sb: client handle.
* @chanh: return channel handle
- * Slimbus channels are limited to 256 per specification. LSB of the handle
- * indicates channel number and MSB of the handle is used by the slimbus
- * framework. -EXFULL is returned if all channels are in use.
+ * Slimbus channels are limited to 256 per specification.
+ * -EXFULL is returned if all channels are in use.
* Although slimbus specification supports 256 channels, a controller may not
* support that many channels.
*/
extern int slim_alloc_ch(struct slim_device *sb, u16 *chanh);
/*
+ * slim_query_ch: Get reference-counted handle for a channel number. Every
+ * channel is reference counted by one as producer and the others as
+ * consumer)
+ * @sb: client handle
+ * @chan: slimbus channel number
+ * @chanh: return channel handle
+ * If request channel number is not in use, it is allocated, and reference
+ * count is set to one. If the channel was was already allocated, this API
+ * will return handle to that channel and reference count is incremented.
+ * -EXFULL is returned if all channels are in use
+ */
+extern int slim_query_ch(struct slim_device *sb, u8 chan, u16 *chanh);
+/*
* slim_dealloc_ch: Deallocate channel allocated using the API above
* -EISCONN is returned if the channel is tried to be deallocated without
* being removed first.
+ * -ENOTCONN is returned if deallocation is tried on a channel that's not
+ * allocated.
*/
extern int slim_dealloc_ch(struct slim_device *sb, u16 chanh);
@@ -813,8 +843,8 @@
* (e.g. 5.1 audio has 6 channels with same parameters. They will all be
* grouped and given 1 handle for simplicity and avoid repeatedly calling
* the API)
- * -EISCONN is returned if the channel is already connected. -EBUSY is
- * returned if the channel is already allocated to some other client.
+ * -EISCONN is returned if channel is already used with different parameters.
+ * -ENXIO is returned if the channel is not yet allocated.
*/
extern int slim_define_ch(struct slim_device *sb, struct slim_ch *prop,
u16 *chanh, u8 nchan, bool grp, u16 *grph);
diff --git a/include/sound/compress_offload.h b/include/sound/compress_offload.h
index 423264d..f746780 100644
--- a/include/sound/compress_offload.h
+++ b/include/sound/compress_offload.h
@@ -56,6 +56,7 @@
size_t decoded;
size_t rendered;
__u32 sampling_rate;
+ uint64_t timestamp;
};
/**
diff --git a/include/sound/jack.h b/include/sound/jack.h
index 5fedf19..ccdc341 100644
--- a/include/sound/jack.h
+++ b/include/sound/jack.h
@@ -51,6 +51,8 @@
SND_JACK_BTN_3 = 0x0800,
SND_JACK_BTN_4 = 0x0400,
SND_JACK_BTN_5 = 0x0200,
+ SND_JACK_BTN_6 = 0x0100,
+ SND_JACK_BTN_7 = 0x0080,
};
struct snd_jack {
@@ -59,7 +61,7 @@
int type;
const char *id;
char name[100];
- unsigned int key[6]; /* Keep in sync with definitions above */
+ unsigned int key[8]; /* Keep in sync with definitions above */
void *private_data;
void (*private_free)(struct snd_jack *);
};
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 6dd815d..8c07206 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1530,8 +1530,6 @@
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state != SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
pr_err("%s called with cmd = %d\n", __func__, cmd);
err = substream->ops->ioctl(substream, cmd, arg);
return err;
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 8de9dec..50dede5 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -23,7 +23,6 @@
#include <linux/mfd/wcd9310/pdata.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
-#include <sound/jack.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
@@ -54,6 +53,7 @@
#define TABLA_MCLK_RATE_9600KHZ 9600000
#define TABLA_FAKE_INS_THRESHOLD_MS 2500
+#define TABLA_FAKE_REMOVAL_MIN_PERIOD_MS 50
static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
@@ -118,7 +118,6 @@
u16 v_brh;
u16 v_brl;
u16 v_no_mic;
- u8 nready;
u8 npoll;
u8 nbounce_wait;
};
@@ -2611,14 +2610,15 @@
if ((tabla->bandgap_type == TABLA_BANDGAP_OFF) &&
(choice == TABLA_BANDGAP_AUDIO_MODE)) {
tabla_codec_enable_audio_mode_bandgap(codec);
- } else if ((tabla->bandgap_type == TABLA_BANDGAP_AUDIO_MODE) &&
- (choice == TABLA_BANDGAP_MBHC_MODE)) {
+ } else if (choice == TABLA_BANDGAP_MBHC_MODE) {
snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x2,
0x2);
snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x80,
0x80);
snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x4,
0x4);
+ snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x01,
+ 0x01);
usleep_range(1000, 1000);
snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x80,
0x00);
@@ -2705,9 +2705,21 @@
tabla->clock_active = false;
}
+static int tabla_codec_mclk_index(const struct tabla_priv *tabla)
+{
+ if (tabla->mclk_freq == TABLA_MCLK_RATE_12288KHZ)
+ return 0;
+ else if (tabla->mclk_freq == TABLA_MCLK_RATE_9600KHZ)
+ return 1;
+ else {
+ BUG_ON(1);
+ return -EINVAL;
+ }
+}
+
static void tabla_codec_calibrate_hs_polling(struct snd_soc_codec *codec)
{
- u8 *n_cic;
+ u8 *n_ready, *n_cic;
struct tabla_mbhc_btn_detect_cfg *btn_det;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
@@ -2738,15 +2750,16 @@
snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B12_CTL,
(tabla->mbhc_data.v_brl >> 8) & 0xFF);
+ n_ready = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_READY);
snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B1_CTL,
- tabla->mbhc_data.nready);
+ n_ready[tabla_codec_mclk_index(tabla)]);
snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B2_CTL,
tabla->mbhc_data.npoll);
snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B3_CTL,
tabla->mbhc_data.nbounce_wait);
-
n_cic = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_CIC);
- snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B6_CTL, n_cic[0]);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B6_CTL,
+ n_cic[tabla_codec_mclk_index(tabla)]);
}
static int tabla_startup(struct snd_pcm_substream *substream,
@@ -2781,6 +2794,10 @@
tabla_codec_enable_clock_block(codec, 0);
tabla_codec_calibrate_hs_polling(codec);
tabla_codec_start_hs_polling(codec);
+ } else {
+ tabla_codec_enable_bandgap(codec,
+ TABLA_BANDGAP_AUDIO_MODE);
+ tabla_codec_enable_clock_block(codec, 0);
}
} else {
@@ -2802,6 +2819,10 @@
}
snd_soc_update_bits(codec, TABLA_A_CLK_BUFF_EN1,
0x05, 0x01);
+ } else {
+ tabla_codec_disable_clock_block(codec);
+ tabla_codec_enable_bandgap(codec,
+ TABLA_BANDGAP_OFF);
}
}
return 0;
@@ -3106,7 +3127,6 @@
/* Turn off the override after measuring mic voltage */
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
- pr_debug("read microphone bias value %04x\n", bias_value);
return bias_value;
}
@@ -3388,7 +3408,7 @@
*/
btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration);
n_cic = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_CIC);
- ncic = n_cic[0];
+ ncic = n_cic[tabla_codec_mclk_index(tabla)];
nmeas = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration)->n_meas;
navg = TABLA_MBHC_CAL_GENERAL_PTR(tabla->calibration)->mbhc_navg;
mclk_rate = tabla->mclk_freq;
@@ -3475,7 +3495,7 @@
ret += sizeof(btn_det->_n_cic);
case TABLA_BTN_DET_N_CIC:
ret += sizeof(btn_det->_n_ready);
- case TABLA_BTN_DET_V_N_READY:
+ case TABLA_BTN_DET_N_READY:
ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
case TABLA_BTN_DET_V_BTN_HIGH:
ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
@@ -3496,25 +3516,25 @@
struct tabla_mbhc_btn_detect_cfg *btn_det;
struct tabla_mbhc_plug_type_cfg *plug_type;
u16 *btn_high;
+ u8 *n_ready;
int i;
tabla = snd_soc_codec_get_drvdata(codec);
btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration);
plug_type = TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->calibration);
+ n_ready = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_READY);
if (tabla->mclk_freq == TABLA_MCLK_RATE_12288KHZ) {
- tabla->mbhc_data.nready = 3;
tabla->mbhc_data.npoll = 9;
tabla->mbhc_data.nbounce_wait = 30;
} else if (tabla->mclk_freq == TABLA_MCLK_RATE_9600KHZ) {
- tabla->mbhc_data.nready = 2;
tabla->mbhc_data.npoll = 7;
tabla->mbhc_data.nbounce_wait = 23;
- } else
- WARN(1, "Unsupported mclk freq %d\n", tabla->mclk_freq);
+ }
tabla->mbhc_data.t_sta_dce = ((1000 * 256) / (tabla->mclk_freq / 1000) *
- tabla->mbhc_data.nready) + 10;
+ n_ready[tabla_codec_mclk_index(tabla)]) +
+ 10;
tabla->mbhc_data.v_ins_hu =
tabla_codec_v_sta_dce(codec, STA, plug_type->v_hs_max);
tabla->mbhc_data.v_ins_h =
@@ -3573,10 +3593,11 @@
n_cic = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_CIC);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_TIMER_B6_CTL, 0xFF,
- n_cic[0]);
+ n_cic[tabla_codec_mclk_index(tabla)]);
gain = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_GAIN);
- snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B2_CTL, 0x78, gain[0] << 3);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B2_CTL, 0x78,
+ gain[tabla_codec_mclk_index(tabla)] << 3);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
generic->mbhc_nsa << 4);
@@ -3695,6 +3716,16 @@
pr_err("Error: no codec or calibration\n");
return -EINVAL;
}
+
+ if (mclk_rate != TABLA_MCLK_RATE_12288KHZ) {
+ if (mclk_rate == TABLA_MCLK_RATE_9600KHZ)
+ pr_err("Error: clock rate %dHz is not yet supported\n",
+ mclk_rate);
+ else
+ pr_err("Error: unsupported clock rate %d\n", mclk_rate);
+ return -EINVAL;
+ }
+
tabla = snd_soc_codec_get_drvdata(codec);
tabla->headset_jack = headset_jack;
tabla->button_jack = button_jack;
@@ -3738,39 +3769,139 @@
}
EXPORT_SYMBOL_GPL(tabla_hs_detect);
+static int tabla_determine_button(const struct tabla_priv *priv,
+ const s32 bias_mv)
+{
+ s16 *v_btn_low, *v_btn_high;
+ struct tabla_mbhc_btn_detect_cfg *btn_det;
+ int i, btn = -1;
+
+ btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(priv->calibration);
+ v_btn_low = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_V_BTN_LOW);
+ v_btn_high = tabla_mbhc_cal_btn_det_mp(btn_det,
+ TABLA_BTN_DET_V_BTN_HIGH);
+ for (i = 0; i < btn_det->num_btn; i++) {
+ if ((v_btn_low[i] <= bias_mv) && (v_btn_high[i] >= bias_mv)) {
+ btn = i;
+ break;
+ }
+ }
+
+ if (btn == -1)
+ pr_debug("%s: couldn't find button number for mic mv %d\n",
+ __func__, bias_mv);
+
+ return btn;
+}
+
+static int tabla_get_button_mask(const int btn)
+{
+ int mask = 0;
+ switch (btn) {
+ case 0:
+ mask = SND_JACK_BTN_0;
+ break;
+ case 1:
+ mask = SND_JACK_BTN_1;
+ break;
+ case 2:
+ mask = SND_JACK_BTN_2;
+ break;
+ case 3:
+ mask = SND_JACK_BTN_3;
+ break;
+ case 4:
+ mask = SND_JACK_BTN_4;
+ break;
+ case 5:
+ mask = SND_JACK_BTN_5;
+ break;
+ case 6:
+ mask = SND_JACK_BTN_6;
+ break;
+ case 7:
+ mask = SND_JACK_BTN_7;
+ break;
+ }
+ return mask;
+}
+
static irqreturn_t tabla_dce_handler(int irq, void *data)
{
+ int i, mask;
+ short bias_value_dce;
+ s32 bias_mv_dce;
+ int btn = -1, meas = 0;
struct tabla_priv *priv = data;
+ const struct tabla_mbhc_btn_detect_cfg *d =
+ TABLA_MBHC_CAL_BTN_DET_PTR(priv->calibration);
+ short btnmeas[d->n_btn_meas + 1];
struct snd_soc_codec *codec = priv->codec;
- short bias_value;
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
tabla_lock_sleep(priv);
- bias_value = tabla_codec_read_dce_result(codec);
- pr_debug("%s: button press interrupt, DCE: %d,%d\n",
- __func__, bias_value,
- tabla_codec_sta_dce_v(codec, 1, bias_value));
+ bias_value_dce = tabla_codec_read_dce_result(codec);
+ bias_mv_dce = tabla_codec_sta_dce_v(codec, 1, bias_value_dce);
- bias_value = tabla_codec_read_sta_result(codec);
- pr_debug("%s: button press interrupt, STA: %d,%d\n",
- __func__, bias_value,
- tabla_codec_sta_dce_v(codec, 0, bias_value));
- /*
- * TODO: If button pressed is not button 0,
- * report the button press event immediately.
- */
- priv->buttons_pressed |= SND_JACK_BTN_0;
-
- msleep(100);
-
- if (schedule_delayed_work(&priv->btn0_dwork,
- msecs_to_jiffies(400)) == 0) {
- WARN(1, "Button pressed twice without release event\n");
- tabla_unlock_sleep(priv);
+ /* determine pressed button */
+ btnmeas[meas++] = tabla_determine_button(priv, bias_mv_dce);
+ pr_debug("%s: meas %d - DCE %d,%d, button %d\n", __func__,
+ meas - 1, bias_value_dce, bias_mv_dce, btnmeas[meas - 1]);
+ if (d->n_btn_meas == 0)
+ btn = btnmeas[0];
+ for (; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1))); meas++) {
+ bias_value_dce = tabla_codec_sta_dce(codec, 1);
+ bias_mv_dce = tabla_codec_sta_dce_v(codec, 1, bias_value_dce);
+ btnmeas[meas] = tabla_determine_button(priv, bias_mv_dce);
+ pr_debug("%s: meas %d - DCE %d,%d, button %d\n",
+ __func__, meas, bias_value_dce, bias_mv_dce,
+ btnmeas[meas]);
+ /* if large enough measurements are collected,
+ * start to check if last all n_btn_con measurements were
+ * in same button low/high range */
+ if (meas + 1 >= d->n_btn_con) {
+ for (i = 0; i < d->n_btn_con; i++)
+ if ((btnmeas[meas] < 0) ||
+ (btnmeas[meas] != btnmeas[meas - i]))
+ break;
+ if (i == d->n_btn_con) {
+ /* button pressed */
+ btn = btnmeas[meas];
+ break;
+ }
+ }
+ /* if left measurements are less than n_btn_con,
+ * it's impossible to find button number */
+ if ((d->n_btn_meas - meas) < d->n_btn_con)
+ break;
}
+ if (btn >= 0) {
+ mask = tabla_get_button_mask(btn);
+ priv->buttons_pressed |= mask;
+
+ msleep(100);
+
+ /* XXX: assuming button 0 has the lowest micbias voltage */
+ if (btn == 0) {
+ if (schedule_delayed_work(&priv->btn0_dwork,
+ msecs_to_jiffies(400)) == 0) {
+ WARN(1, "Button pressed twice without release"
+ "event\n");
+ tabla_unlock_sleep(priv);
+ }
+ } else {
+ pr_debug("%s: Reporting short button %d(0x%x) press\n",
+ __func__, btn, mask);
+ tabla_snd_soc_jack_report(priv, priv->button_jack, mask,
+ mask);
+ }
+ } else
+ pr_debug("%s: bogus button press, too short press?\n",
+ __func__);
+
return IRQ_HANDLED;
}
@@ -3778,18 +3909,18 @@
{
struct tabla_priv *priv = data;
struct snd_soc_codec *codec = priv->codec;
- int ret, mb_v;
+ int ret;
+ short mb_v;
- pr_debug("%s\n", __func__);
+ pr_debug("%s: enter\n", __func__);
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE);
tabla_lock_sleep(priv);
if (priv->buttons_pressed & SND_JACK_BTN_0) {
ret = cancel_delayed_work(&priv->btn0_dwork);
-
if (ret == 0) {
- pr_debug("%s: Reporting long button release event\n",
- __func__);
+ pr_debug("%s: Reporting long button 0 release event\n",
+ __func__);
if (priv->button_jack)
tabla_snd_soc_jack_report(priv,
priv->button_jack, 0,
@@ -3803,25 +3934,37 @@
__func__, mb_v,
tabla_codec_sta_dce_v(codec, 0, mb_v));
- if (mb_v < -2000 || mb_v > -670)
+ if (mb_v < (short)priv->mbhc_data.v_b1_hu ||
+ mb_v > (short)priv->mbhc_data.v_ins_hu)
pr_debug("%s: Fake buttton press interrupt\n",
- __func__);
+ __func__);
else if (priv->button_jack) {
- pr_debug("%s:reporting short button "
+ pr_debug("%s: Reporting short button 0 "
"press and release\n", __func__);
tabla_snd_soc_jack_report(priv,
priv->button_jack,
SND_JACK_BTN_0,
SND_JACK_BTN_0);
tabla_snd_soc_jack_report(priv,
- priv->button_jack,
- 0, SND_JACK_BTN_0);
+ priv->button_jack, 0,
+ SND_JACK_BTN_0);
}
}
priv->buttons_pressed &= ~SND_JACK_BTN_0;
}
+ if (priv->buttons_pressed) {
+ pr_debug("%s:reporting button release mask 0x%x\n", __func__,
+ priv->buttons_pressed);
+ tabla_snd_soc_jack_report(priv, priv->button_jack, 0,
+ priv->buttons_pressed);
+ /* hardware doesn't detect another button press until
+ * already pressed button is released.
+ * therefore buttons_pressed has only one button's mask. */
+ priv->buttons_pressed &= ~TABLA_JACK_BUTTON_MASK;
+ }
+
tabla_codec_start_hs_polling(codec);
tabla_unlock_sleep(priv);
return IRQ_HANDLED;
@@ -3859,8 +4002,8 @@
if (!tabla->mclk_enabled) {
snd_soc_update_bits(codec, TABLA_A_TX_COM_BIAS, 0xE0, 0x00);
- tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_AUDIO_MODE);
- tabla_codec_enable_clock_block(codec, 0);
+ tabla_codec_disable_clock_block(codec);
+ tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_OFF);
}
tabla->mbhc_polling_active = false;
@@ -4131,12 +4274,15 @@
static irqreturn_t tabla_hs_remove_irq(int irq, void *data)
{
+ short bias_value;
struct tabla_priv *priv = data;
struct snd_soc_codec *codec = priv->codec;
const struct tabla_mbhc_general_cfg *generic =
TABLA_MBHC_CAL_GENERAL_PTR(priv->calibration);
- short bias_value;
+ int fake_removal = 0;
+ int min_us = TABLA_FAKE_REMOVAL_MIN_PERIOD_MS * 1000;
+ pr_debug("%s: enter, removal interrupt\n", __func__);
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE);
@@ -4145,11 +4291,18 @@
usleep_range(generic->t_shutdown_plug_rem,
generic->t_shutdown_plug_rem);
- bias_value = tabla_codec_sta_dce(codec, 1);
- pr_debug("removal interrupt, DCE: %d,%d\n",
- bias_value, tabla_codec_sta_dce_v(codec, 1, bias_value));
+ do {
+ bias_value = tabla_codec_sta_dce(codec, 1);
+ pr_debug("%s: DCE %d,%d, %d us left\n", __func__, bias_value,
+ tabla_codec_sta_dce_v(codec, 1, bias_value), min_us);
+ if (bias_value < (short)priv->mbhc_data.v_ins_h) {
+ fake_removal = 1;
+ break;
+ }
+ min_us -= priv->mbhc_data.t_dce;
+ } while (min_us > 0);
- if (bias_value < (short) priv->mbhc_data.v_ins_h) {
+ if (fake_removal) {
pr_debug("False alarm, headset not actually removed\n");
tabla_codec_start_hs_polling(codec);
} else {
@@ -4502,10 +4655,6 @@
goto err_pdata;
}
- /* TODO only enable bandgap when necessary in order to save power */
- tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_AUDIO_MODE);
- tabla_codec_enable_clock_block(codec, 0);
-
snd_soc_add_controls(codec, tabla_snd_controls,
ARRAY_SIZE(tabla_snd_controls));
snd_soc_dapm_new_controls(dapm, tabla_dapm_widgets,
diff --git a/sound/soc/codecs/wcd9310.h b/sound/soc/codecs/wcd9310.h
index 77bfc73..6f77e9f 100644
--- a/sound/soc/codecs/wcd9310.h
+++ b/sound/soc/codecs/wcd9310.h
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*/
#include <sound/soc.h>
+#include <sound/jack.h>
#define TABLA_VERSION_1_0 0
#define TABLA_VERSION_1_1 1
@@ -28,6 +29,11 @@
#define STA 0
#define DCE 1
+#define TABLA_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
+ SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
+ SND_JACK_BTN_6 | SND_JACK_BTN_7)
+
extern const u8 tabla_reg_readable[TABLA_CACHE_SIZE];
extern const u8 tabla_reg_defaults[TABLA_CACHE_SIZE];
@@ -66,7 +72,7 @@
enum tabla_mbhc_btn_det_mem {
TABLA_BTN_DET_V_BTN_LOW,
TABLA_BTN_DET_V_BTN_HIGH,
- TABLA_BTN_DET_V_N_READY,
+ TABLA_BTN_DET_N_READY,
TABLA_BTN_DET_N_CIC,
TABLA_BTN_DET_GAIN
};
diff --git a/sound/soc/msm/msm-compr-q6.c b/sound/soc/msm/msm-compr-q6.c
index d0497b1..f0f17a9 100644
--- a/sound/soc/msm/msm-compr-q6.c
+++ b/sound/soc/msm/msm-compr-q6.c
@@ -450,8 +450,37 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct compr_audio *compr = runtime->private_data;
struct msm_audio *prtd = &compr->prtd;
+ uint64_t timestamp;
+ uint64_t temp;
switch (cmd) {
+ case SNDRV_COMPRESS_TSTAMP: {
+ struct snd_compr_tstamp tstamp;
+ pr_debug("SNDRV_COMPRESS_TSTAMP\n");
+
+ memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));
+ timestamp = q6asm_get_session_time(prtd->audio_client);
+ if (timestamp < 0) {
+ pr_err("%s: Get Session Time return value =%lld\n",
+ __func__, timestamp);
+ return -EAGAIN;
+ }
+ temp = (timestamp * 2 * runtime->channels);
+ temp = temp * (runtime->rate/1000);
+ temp = div_u64(temp, 1000);
+ tstamp.sampling_rate = runtime->rate;
+ tstamp.rendered = (size_t)(temp & 0xFFFFFFFF);
+ tstamp.decoded = (size_t)((temp >> 32) & 0xFFFFFFFF);
+ tstamp.timestamp = timestamp;
+ pr_debug("%s: bytes_consumed:lsb = %d, msb = %d,"
+ "timestamp = %lld,\n",
+ __func__, tstamp.rendered, tstamp.decoded,
+ tstamp.timestamp);
+ if (copy_to_user((void *) arg, &tstamp,
+ sizeof(struct snd_compr_tstamp)))
+ return -EFAULT;
+ return 0;
+ }
case SNDRV_COMPRESS_GET_CAPS:
pr_debug("SNDRV_COMPRESS_GET_CAPS\n");
if (copy_to_user((void *) arg, &compr->info.compr_cap,
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index 0dc3b57..75bb75f 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -55,7 +55,7 @@
#define TABLA_EXT_CLK_RATE 12288000
-#define TABLA_MBHC_DEF_BUTTONS 3
+#define TABLA_MBHC_DEF_BUTTONS 8
#define TABLA_MBHC_DEF_RLOADS 5
static u32 top_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(18);
@@ -571,7 +571,7 @@
void *tabla_cal;
struct tabla_mbhc_btn_detect_cfg *btn_cfg;
u16 *btn_low, *btn_high;
- u8 *n_cic, *gain;
+ u8 *n_ready, *n_cic, *gain;
tabla_cal = kzalloc(TABLA_MBHC_CAL_SIZE(TABLA_MBHC_DEF_BUTTONS,
TABLA_MBHC_DEF_RLOADS),
@@ -597,20 +597,16 @@
#undef S
#define S(X, Y) ((TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla_cal)->X) = (Y))
S(v_no_mic, 30);
- S(v_hs_max, 1450);
+ S(v_hs_max, 1550);
#undef S
#define S(X, Y) ((TABLA_MBHC_CAL_BTN_DET_PTR(tabla_cal)->X) = (Y))
- S(c[0], 6);
- S(c[1], 10);
- S(c[2], 10);
- S(c[3], 14);
- S(c[4], 14);
- S(c[5], 16);
- S(c[6], 0);
- S(c[7], 0);
- S(nc, 5);
- S(n_meas, 11);
+ S(c[0], 62);
+ S(c[1], 124);
+ S(nc, 1);
+ S(n_meas, 3);
S(mbhc_nsc, 11);
+ S(n_btn_meas, 1);
+ S(n_btn_con, 2);
S(num_btn, TABLA_MBHC_DEF_BUTTONS);
S(v_btn_press_delta_sta, 100);
S(v_btn_press_delta_cic, 50);
@@ -618,15 +614,28 @@
btn_cfg = TABLA_MBHC_CAL_BTN_DET_PTR(tabla_cal);
btn_low = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_V_BTN_LOW);
btn_high = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_V_BTN_HIGH);
- btn_low[0] = 0;
- btn_high[0] = 40;
- btn_low[1] = 60;
- btn_high[1] = 140;
- btn_low[2] = 160;
- btn_high[2] = 240;
+ btn_low[0] = -50;
+ btn_high[0] = 10;
+ btn_low[1] = 11;
+ btn_high[1] = 38;
+ btn_low[2] = 39;
+ btn_high[2] = 64;
+ btn_low[3] = 65;
+ btn_high[3] = 91;
+ btn_low[4] = 92;
+ btn_high[4] = 115;
+ btn_low[5] = 116;
+ btn_high[5] = 141;
+ btn_low[6] = 142;
+ btn_high[6] = 163;
+ btn_low[7] = 164;
+ btn_high[7] = 250;
+ n_ready = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_N_READY);
+ n_ready[0] = 48;
+ n_ready[1] = 38;
n_cic = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_N_CIC);
- n_cic[0] = 120;
- n_cic[1] = 94;
+ n_cic[0] = 60;
+ n_cic[1] = 47;
gain = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_GAIN);
gain[0] = 11;
gain[1] = 9;
@@ -668,15 +677,16 @@
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),
+ &hs_jack);
if (err) {
pr_err("failed to create new jack\n");
return err;
}
err = snd_soc_jack_new(codec, "Button Jack",
- SND_JACK_BTN_0, &button_jack);
+ TABLA_JACK_BUTTON_MASK, &button_jack);
if (err) {
pr_err("failed to create new jack\n");
return err;
diff --git a/sound/soc/msm/qdsp6/q6adm.c b/sound/soc/msm/qdsp6/q6adm.c
index d8d9c64..177e1d8 100644
--- a/sound/soc/msm/qdsp6/q6adm.c
+++ b/sound/soc/msm/qdsp6/q6adm.c
@@ -615,7 +615,7 @@
if (afe_validate_port(port_id) < 0)
return -EINVAL;
- pr_info("%s port_id=%d index %d\n", __func__, port_id, index);
+ pr_debug("%s port_id=%d index %d\n", __func__, port_id, index);
if (!(atomic_read(&this_adm.copp_cnt[index]))) {
pr_err("%s: copp count for port[%d]is 0\n", __func__, port_id);
diff --git a/sound/soc/msm/qdsp6/q6afe.c b/sound/soc/msm/qdsp6/q6afe.c
index a214529..302ef57 100644
--- a/sound/soc/msm/qdsp6/q6afe.c
+++ b/sound/soc/msm/qdsp6/q6afe.c
@@ -386,7 +386,7 @@
ret = -EINVAL;
return ret;
}
- pr_info("%s: %d %d\n", __func__, port_id, rate);
+ pr_debug("%s: %d %d\n", __func__, port_id, rate);
if ((port_id == RT_PROXY_DAI_001_RX) ||
(port_id == RT_PROXY_DAI_002_TX))
@@ -1428,7 +1428,7 @@
ret = -EINVAL;
goto fail_cmd;
}
- pr_info("%s: port_id=%d\n", __func__, port_id);
+ pr_debug("%s: port_id=%d\n", __func__, port_id);
port_id = afe_convert_virtual_to_portid(port_id);
stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -1466,7 +1466,7 @@
ret = -EINVAL;
goto fail_cmd;
}
- pr_info("%s: port_id=%d\n", __func__, port_id);
+ pr_debug("%s: port_id=%d\n", __func__, port_id);
port_id = afe_convert_virtual_to_portid(port_id);
stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,