Merge "input: cyttsp-i2c: Don't set mode on unsettable regulators" 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..bf4011f 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -291,6 +291,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..6248c98 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -292,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/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 69ea6ee..929f280 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -1701,14 +1701,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 +1883,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 +2204,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-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 6683f90..d464375 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -1572,14 +1572,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 +1789,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 +2168,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 +2205,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 +2240,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..afd3612d 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -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-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/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/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/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/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/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/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/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.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/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/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..2bdb917 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -118,7 +118,6 @@
u16 v_brh;
u16 v_brl;
u16 v_no_mic;
- u8 nready;
u8 npoll;
u8 nbounce_wait;
};
@@ -2705,9 +2704,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 +2749,17 @@
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,
@@ -3388,7 +3401,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 +3488,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 +3509,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 +3586,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 +3709,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;
diff --git a/sound/soc/codecs/wcd9310.h b/sound/soc/codecs/wcd9310.h
index 77bfc73..7f70010 100644
--- a/sound/soc/codecs/wcd9310.h
+++ b/sound/soc/codecs/wcd9310.h
@@ -66,7 +66,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..cb73ab6 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -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),
@@ -624,6 +624,9 @@
btn_high[1] = 140;
btn_low[2] = 160;
btn_high[2] = 240;
+ 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;