Merge "NFC: Fix for wrong chip version."
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
index d502f78..8c41926 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -55,6 +55,9 @@
no default value that the driver assumes if this property
is not specified. So if this property is not specified,
then SDHC driver will not vote for PM QOS.
+ - qcom,dat1-mpm-int: specifies MPM interrupt number (e.g. sdhc_2 node below)
+ corresponding to DAT1 line of SDHC (used only if slot has dedicated
+ DAT1 MSM pin (not GPIO))
In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage).
- qcom,<supply>-always-on - specifies whether supply should be kept "on" always.
@@ -165,4 +168,5 @@
<81 512 106496 212992>, /* 208 MB/s */
<81 512 2147483647 4294967295>; /* Max. bandwidth */
qcom,bus-bw-vectors-bps = <0 13631488 27262976 54525952 109051904 218103808 4294967295>;
+ qcom,dat1-mpm-int = <44>;
};
diff --git a/arch/arm/boot/dts/msm8226-bus.dtsi b/arch/arm/boot/dts/msm8226-bus.dtsi
index 74b4a30..a2f91cf 100644
--- a/arch/arm/boot/dts/msm8226-bus.dtsi
+++ b/arch/arm/boot/dts/msm8226-bus.dtsi
@@ -998,7 +998,7 @@
qcom,fabclk-dual = "mem_clk";
qcom,fabclk-active = "mem_a_clk";
qcom,ntieredslaves = <0>;
- qcom,qos-freq = <4800>;
+ qcom,qos-freq = <19200>;
qcom,hw-sel = "BIMC";
qcom,rpm-en;
@@ -1008,12 +1008,18 @@
qcom,masterp = <0>;
qcom,tier = <2>;
qcom,hw-sel = "BIMC";
- qcom,mode = "Fixed";
+ qcom,mode = "Limiter";
qcom,qport = <0>;
qcom,ws = <10000>;
qcom,mas-hw-id = <0>;
qcom,prio-rd = <0>;
qcom,prio-wr = <0>;
+ qcom,mode-thresh = "Fixed";
+ qcom,thresh = <1800000>;
+ qcom,dual-conf;
+ qcom,bimc,bw = <450000>;
+ qcom,bimc,gp = <5000>;
+ qcom,bimc,thmp = <50>;
};
mas-mss-proc {
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index da5474d..a3b3c87 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -509,6 +509,7 @@
<78 512 800000 1600000>, /* 200 MB/s */
<78 512 2048000 4096000>; /* Max. bandwidth */
qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000 100000000 200000000 4294967295>;
+ qcom,dat1-mpm-int = <42>;
status = "disable";
};
@@ -535,6 +536,7 @@
<81 512 800000 1600000>, /* 200 MB/s */
<81 512 2048000 4096000>; /* Max. bandwidth */
qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000 100000000 200000000 4294967295>;
+ qcom,dat1-mpm-int = <44>;
status = "disable";
};
@@ -542,8 +544,17 @@
compatible = "qcom,sdhci-msm";
reg = <0xf9864900 0x11c>, <0xf9864000 0x800>;
reg-names = "hc_mem", "core_mem";
- interrupts = <0 127 0>, <0 224 0>;
- interrupt-names = "hc_irq", "pwr_irq";
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_3>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 127 0
+ 1 &intc 0 224 0
+ 2 &msmgpio 37 0x8>;
+ interrupt-names = "hc_irq", "pwr_irq", "sdiowakeup_irq";
+
gpios = <&msmgpio 40 0>, /* CLK */
<&msmgpio 39 0>, /* CMD */
<&msmgpio 38 0>, /* DATA0 */
@@ -575,8 +586,17 @@
compatible = "qcom,sdhci-msm";
reg = <0xf98e4900 0x11c>, <0xf98e4000 0x800>;
reg-names = "hc_mem", "core_mem";
- interrupts = <0 129 0>, <0 227 0>;
- interrupt-names = "hc_irq", "pwr_irq";
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_4>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 129 0
+ 1 &intc 0 227 0
+ 2 &msmgpio 95 0x8>;
+ interrupt-names = "hc_irq", "pwr_irq", "sdiowakeup_irq";
+
gpios = <&msmgpio 93 0>, /* CLK */
<&msmgpio 91 0>, /* CMD */
<&msmgpio 96 0>, /* DATA0 */
diff --git a/arch/arm/mach-msm/board-8226-gpiomux.c b/arch/arm/mach-msm/board-8226-gpiomux.c
index fe7de31..08566bb 100644
--- a/arch/arm/mach-msm/board-8226-gpiomux.c
+++ b/arch/arm/mach-msm/board-8226-gpiomux.c
@@ -57,6 +57,42 @@
};
#endif
+static struct gpiomux_setting smsc_hub_act_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting smsc_hub_susp_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct msm_gpiomux_config smsc_hub_configs[] = {
+ {
+ .gpio = 114, /* reset_n */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &smsc_hub_act_cfg,
+ [GPIOMUX_SUSPENDED] = &smsc_hub_susp_cfg,
+ },
+ },
+ {
+ .gpio = 8, /* clk_en */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &smsc_hub_act_cfg,
+ [GPIOMUX_SUSPENDED] = &smsc_hub_susp_cfg,
+ },
+ },
+ {
+ .gpio = 9, /* int_n */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &smsc_hub_act_cfg,
+ [GPIOMUX_SUSPENDED] = &smsc_hub_susp_cfg,
+ },
+ },
+};
+
#define KS8851_IRQ_GPIO 115
#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
@@ -940,6 +976,9 @@
}
msm_gpiomux_install(msm_hsic_configs, ARRAY_SIZE(msm_hsic_configs));
#endif
+ if (machine_is_msm8926() && of_board_is_mtp())
+ msm_gpiomux_install(smsc_hub_configs,
+ ARRAY_SIZE(smsc_hub_configs));
}
static void wcnss_switch_to_gpio(void)
diff --git a/arch/arm/mach-msm/mpm-of.c b/arch/arm/mach-msm/mpm-of.c
index 1fbd077..f1bf64f 100644
--- a/arch/arm/mach-msm/mpm-of.c
+++ b/arch/arm/mach-msm/mpm-of.c
@@ -531,6 +531,7 @@
void msm_mpm_exit_sleep(bool from_idle)
{
unsigned long pending;
+ uint32_t *enabled_intr;
int i;
int k;
@@ -539,12 +540,16 @@
return;
}
+ enabled_intr = from_idle ? msm_mpm_enabled_irq :
+ msm_mpm_wake_irq;
+
for (i = 0; i < MSM_MPM_REG_WIDTH; i++) {
pending = msm_mpm_read(MSM_MPM_REG_STATUS, i);
+ pending &= enabled_intr[i];
if (MSM_MPM_DEBUG_PENDING_IRQ & msm_mpm_debug_mask)
- pr_info("%s: pending.%d: 0x%08lx", __func__,
- i, pending);
+ pr_info("%s: enabled_intr pending.%d: 0x%08x 0x%08lx\n",
+ __func__, i, enabled_intr[i], pending);
k = find_first_bit(&pending, 32);
while (k < 32) {
@@ -678,7 +683,8 @@
return -ENXIO;
}
ret = devm_request_irq(&pdev->dev, dev->mpm_ipc_irq, msm_mpm_irq,
- IRQF_TRIGGER_RISING, pdev->name, msm_mpm_irq);
+ IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, pdev->name,
+ msm_mpm_irq);
if (ret) {
pr_info("%s(): request_irq failed errno: %d\n", __func__, ret);
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 7a92fe6..3196911 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -2423,9 +2423,12 @@
if (tmp != adreno_dev->fast_hang_detect) {
if (adreno_dev->fast_hang_detect) {
- if (adreno_dev->gpudev->fault_detect_start)
+ if (adreno_dev->gpudev->fault_detect_start &&
+ !kgsl_active_count_get(&adreno_dev->dev)) {
adreno_dev->gpudev->fault_detect_start(
adreno_dev);
+ kgsl_active_count_put(&adreno_dev->dev);
+ }
} else {
if (adreno_dev->gpudev->fault_detect_stop)
adreno_dev->gpudev->fault_detect_stop(
@@ -2786,7 +2789,7 @@
* Return true if the RBBM status register for the GPU type indicates that the
* hardware is idle
*/
-static bool adreno_hw_isidle(struct kgsl_device *device)
+bool adreno_hw_isidle(struct kgsl_device *device)
{
unsigned int reg_rbbm_status;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
@@ -2899,6 +2902,12 @@
rptr = adreno_get_rptr(&adreno_dev->ringbuffer);
+ /*
+ * wptr is updated when we add commands to ringbuffer, add a barrier
+ * to make sure updated wptr is compared to rptr
+ */
+ smp_mb();
+
if (rptr == adreno_dev->ringbuffer.wptr)
return adreno_hw_isidle(device);
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index e2ea262..9092a03 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -341,6 +341,7 @@
ADRENO_REG_TC_CNTL_STATUS,
ADRENO_REG_TP0_CHICKEN,
ADRENO_REG_RBBM_RBBM_CTL,
+ ADRENO_REG_UCHE_INVALIDATE0,
ADRENO_REG_REGISTER_MAX,
};
@@ -413,7 +414,9 @@
#define KGSL_FT_SKIPFRAME 3
#define KGSL_FT_DISABLE 4
#define KGSL_FT_TEMP_DISABLE 5
-#define KGSL_FT_DEFAULT_POLICY (BIT(KGSL_FT_REPLAY) + BIT(KGSL_FT_SKIPIB))
+#define KGSL_FT_THROTTLE 6
+#define KGSL_FT_DEFAULT_POLICY (BIT(KGSL_FT_REPLAY) + BIT(KGSL_FT_SKIPIB) \
+ + BIT(KGSL_FT_THROTTLE))
/* This internal bit is used to skip the PM dump on replayed command batches */
#define KGSL_FT_SKIP_PMDUMP 31
@@ -431,7 +434,8 @@
{ BIT(KGSL_FT_SKIPIB), "skipib" }, \
{ BIT(KGSL_FT_SKIPFRAME), "skipframe" }, \
{ BIT(KGSL_FT_DISABLE), "disable" }, \
- { BIT(KGSL_FT_TEMP_DISABLE), "temp" }
+ { BIT(KGSL_FT_TEMP_DISABLE), "temp" }, \
+ { BIT(KGSL_FT_THROTTLE), "throttle"}
extern struct adreno_gpudev adreno_a2xx_gpudev;
extern struct adreno_gpudev adreno_a3xx_gpudev;
@@ -461,6 +465,7 @@
void adreno_coresight_remove(struct platform_device *pdev);
int adreno_coresight_init(struct platform_device *pdev);
+bool adreno_hw_isidle(struct kgsl_device *device);
int adreno_idle(struct kgsl_device *device);
bool adreno_isidle(struct kgsl_device *device);
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index eed11c3..47ba854 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -4577,6 +4577,8 @@
ADRENO_REG_DEFINE(ADRENO_REG_TC_CNTL_STATUS, REG_TC_CNTL_STATUS),
ADRENO_REG_DEFINE(ADRENO_REG_TP0_CHICKEN, REG_TP0_CHICKEN),
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_RBBM_CTL, A3XX_RBBM_RBBM_CTL),
+ ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE0,
+ A3XX_UCHE_CACHE_INVALIDATE0_REG),
};
struct adreno_reg_offsets a3xx_reg_offsets = {
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 48d0210..ab9d220 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -33,6 +33,15 @@
/* Number of command batches sent at a time from a single context */
static unsigned int _context_cmdbatch_burst = 5;
+/*
+ * GFT throttle parameters. If GFT recovered more than
+ * X times in Y ms invalidate the context and do not attempt recovery.
+ * X -> _fault_throttle_burst
+ * Y -> _fault_throttle_time
+ */
+static unsigned int _fault_throttle_time = 3000;
+static unsigned int _fault_throttle_burst = 3;
+
/* Number of command batches inflight in the ringbuffer at any time */
static unsigned int _dispatcher_inflight = 15;
@@ -78,15 +87,24 @@
static inline bool _isidle(struct kgsl_device *device)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- unsigned int ts;
+ unsigned int ts, i;
+
+ if (!kgsl_pwrctrl_isenabled(device))
+ goto ret;
ts = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED);
- if (adreno_isidle(device) == true &&
- (ts >= adreno_dev->ringbuffer.global_ts))
- return true;
+ /* If GPU HW status is idle return true */
+ if (adreno_hw_isidle(device) ||
+ (ts == adreno_dev->ringbuffer.global_ts))
+ goto ret;
return false;
+
+ret:
+ for (i = 0; i < FT_DETECT_REGS_COUNT; i++)
+ fault_detect_regs[i] = 0;
+ return true;
}
/**
@@ -1015,6 +1033,35 @@
cmdbatch = replay[0];
/*
+ * If GFT recovered more than X times in Y ms invalidate the context
+ * and do not attempt recovery.
+ * Example: X==3 and Y==3000 ms, GPU hung at 500ms, 1700ms, 25000ms and
+ * 3000ms for the same context, we will not try FT and invalidate the
+ * context @3000ms because context triggered GFT more than 3 times in
+ * last 3 seconds. If a context caused recoverable GPU hangs
+ * where 1st and 4th gpu hang are more than 3 seconds apart we
+ * won't disable GFT and invalidate the context.
+ */
+ if (test_bit(KGSL_FT_THROTTLE, &cmdbatch->fault_policy)) {
+ if (time_after(jiffies, (cmdbatch->context->fault_time
+ + msecs_to_jiffies(_fault_throttle_time)))) {
+ cmdbatch->context->fault_time = jiffies;
+ cmdbatch->context->fault_count = 1;
+ } else {
+ cmdbatch->context->fault_count++;
+ if (cmdbatch->context->fault_count >
+ _fault_throttle_burst) {
+ set_bit(KGSL_FT_DISABLE,
+ &cmdbatch->fault_policy);
+ pr_fault(device, cmdbatch,
+ "gpu fault threshold exceeded %d faults in %d msecs\n",
+ _fault_throttle_burst,
+ _fault_throttle_time);
+ }
+ }
+ }
+
+ /*
* If FT is disabled for this cmdbatch invalidate immediately
*/
@@ -1628,6 +1675,10 @@
static DISPATCHER_UINT_ATTR(context_queue_wait, 0644, 0, _context_queue_wait);
static DISPATCHER_UINT_ATTR(fault_detect_interval, 0644, 0,
_fault_timer_interval);
+static DISPATCHER_UINT_ATTR(fault_throttle_time, 0644, 0,
+ _fault_throttle_time);
+static DISPATCHER_UINT_ATTR(fault_throttle_burst, 0644, 0,
+ _fault_throttle_burst);
static struct attribute *dispatcher_attrs[] = {
&dispatcher_attr_inflight.attr,
@@ -1636,6 +1687,8 @@
&dispatcher_attr_cmdbatch_timeout.attr,
&dispatcher_attr_context_queue_wait.attr,
&dispatcher_attr_fault_detect_interval.attr,
+ &dispatcher_attr_fault_throttle_time.attr,
+ &dispatcher_attr_fault_throttle_burst.attr,
NULL,
};
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 4db045a..136456a 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -607,7 +607,7 @@
struct adreno_context *context)
{
struct kgsl_device *device;
- unsigned int cmds[5];
+ unsigned int cmds[8];
if (adreno_dev == NULL || context == NULL)
return -EINVAL;
@@ -621,8 +621,14 @@
cmds[3] = device->memstore.gpuaddr +
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context);
cmds[4] = context->base.id;
+ /* Flush the UCHE for new context */
+ cmds[5] = cp_type0_packet(
+ adreno_getreg(adreno_dev, ADRENO_REG_UCHE_INVALIDATE0), 2);
+ cmds[6] = 0;
+ if (adreno_is_a3xx(adreno_dev))
+ cmds[7] = 0x90000000;
return adreno_ringbuffer_issuecmds(device, context,
- KGSL_CMD_FLAGS_NONE, cmds, 5);
+ KGSL_CMD_FLAGS_NONE, cmds, 8);
}
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 98fd731..fc4b77e 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -349,6 +349,8 @@
* is set.
* @flags: flags from userspace controlling the behavior of this context
* @pwr_constraint: power constraint from userspace for this context
+ * @fault_count: number of times gpu hanged in last _context_throttle_time ms
+ * @fault_time: time of the first gpu hang in last _context_throttle_time ms
*/
struct kgsl_context {
struct kref refcount;
@@ -367,6 +369,8 @@
unsigned int pagefault_ts;
unsigned int flags;
struct kgsl_pwr_constraint pwr_constraint;
+ unsigned int fault_count;
+ unsigned long fault_time;
};
/**
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index e87c670..69b953f 100755
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -641,16 +641,18 @@
phys_addr_t pt_base)
{
struct kgsl_iommu_pt *iommu_pt = pt ? pt->priv : NULL;
- phys_addr_t domain_ptbase = iommu_pt ?
- iommu_get_pt_base_addr(iommu_pt->domain) : 0;
+ phys_addr_t domain_ptbase;
- /* Only compare the valid address bits of the pt_base */
- domain_ptbase &= KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
+ if (iommu_pt == NULL)
+ return 0;
+
+ domain_ptbase = iommu_get_pt_base_addr(iommu_pt->domain)
+ & KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
pt_base &= KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
- return domain_ptbase && pt_base &&
- (domain_ptbase == pt_base);
+ return (domain_ptbase == pt_base);
+
}
/*
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index b36faff..667da01 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -421,6 +421,12 @@
else if (!mmc_card_sdio(card) && mmc_use_core_runtime_pm(card->host))
pm_runtime_enable(&card->dev);
+ if (mmc_card_sdio(card)) {
+ ret = device_init_wakeup(&card->dev, true);
+ if (ret)
+ pr_err("%s: %s: failed to init wakeup: %d\n",
+ mmc_hostname(card->host), __func__, ret);
+ }
ret = device_add(&card->dev);
if (ret)
return ret;
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index c7fa876..c082f77 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -77,16 +77,40 @@
{
struct mmc_host *host = cls_dev_to_mmc_host(dev);
int ret = 0;
+ unsigned long flags;
if (!mmc_use_core_pm(host))
return 0;
+ spin_lock_irqsave(&host->clk_lock, flags);
+ /*
+ * let the driver know that suspend is in progress and must
+ * be aborted on receiving a sdio card interrupt
+ */
+ host->dev_status = DEV_SUSPENDING;
+ spin_unlock_irqrestore(&host->clk_lock, flags);
if (!pm_runtime_suspended(dev)) {
ret = mmc_suspend_host(host);
if (ret < 0)
pr_err("%s: %s: failed: ret: %d\n", mmc_hostname(host),
__func__, ret);
}
+ /*
+ * If SDIO function driver doesn't want to power off the card,
+ * atleast turn off clocks to allow deep sleep.
+ */
+ if (!ret && host->card && mmc_card_sdio(host->card) &&
+ host->ios.clock) {
+ spin_lock_irqsave(&host->clk_lock, flags);
+ host->clk_old = host->ios.clock;
+ host->ios.clock = 0;
+ host->clk_gated = true;
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+ mmc_set_ios(host);
+ }
+ spin_lock_irqsave(&host->clk_lock, flags);
+ host->dev_status = DEV_SUSPENDED;
+ spin_unlock_irqrestore(&host->clk_lock, flags);
return ret;
}
@@ -104,6 +128,7 @@
pr_err("%s: %s: failed: ret: %d\n", mmc_hostname(host),
__func__, ret);
}
+ host->dev_status = DEV_RESUMED;
return ret;
}
diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c
index 4407d91..d517205 100644
--- a/drivers/mmc/core/quirks.c
+++ b/drivers/mmc/core/quirks.c
@@ -50,6 +50,14 @@
#define SDIO_DEVICE_ID_MSM_QCA_AR6003_2 0x301
#endif
+#ifndef SDIO_DEVICE_ID_MSM_QCA_AR6004_1
+#define SDIO_DEVICE_ID_MSM_QCA_AR6004_1 0x400
+#endif
+
+#ifndef SDIO_DEVICE_ID_MSM_QCA_AR6004_2
+#define SDIO_DEVICE_ID_MSM_QCA_AR6004_2 0x401
+#endif
+
/*
* This hook just adds a quirk for all sdio devices
*/
@@ -78,6 +86,12 @@
SDIO_FIXUP(SDIO_VENDOR_ID_MSM_QCA, SDIO_DEVICE_ID_MSM_QCA_AR6003_2,
remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+ SDIO_FIXUP(SDIO_VENDOR_ID_MSM_QCA, SDIO_DEVICE_ID_MSM_QCA_AR6004_1,
+ remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+
+ SDIO_FIXUP(SDIO_VENDOR_ID_MSM_QCA, SDIO_DEVICE_ID_MSM_QCA_AR6004_2,
+ remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
add_quirk, MMC_QUIRK_NONSTD_FUNC_IF),
diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c
index 3d8ceb4..2b48f77 100644
--- a/drivers/mmc/core/sdio_irq.c
+++ b/drivers/mmc/core/sdio_irq.c
@@ -85,6 +85,7 @@
struct sched_param param = { .sched_priority = 1 };
unsigned long period, idle_period;
int ret;
+ bool ws;
sched_setscheduler(current, SCHED_FIFO, ¶m);
@@ -118,6 +119,17 @@
ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
if (ret)
break;
+ ws = false;
+ /*
+ * prevent suspend if it has started when scheduled;
+ * 100 msec (approx. value) should be enough for the system to
+ * resume and attend to the card's request
+ */
+ if ((host->dev_status == DEV_SUSPENDING) ||
+ (host->dev_status == DEV_SUSPENDED)) {
+ pm_wakeup_event(&host->card->dev, 100);
+ ws = true;
+ }
ret = process_sdio_pending_irqs(host);
host->sdio_irq_pending = false;
mmc_release_host(host);
@@ -154,6 +166,12 @@
host->ops->enable_sdio_irq(host, 1);
mmc_host_clk_release(host);
}
+ /*
+ * function drivers would have processed the event from card
+ * unless suspended, hence release wake source
+ */
+ if (ws && (host->dev_status == DEV_RESUMED))
+ pm_relax(&host->card->dev);
if (!kthread_should_stop())
schedule_timeout(period);
set_current_state(TASK_RUNNING);
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 739a237..d3a0e9e 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -31,6 +31,7 @@
#include <linux/delay.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
+#include <linux/irq.h>
#include <linux/mmc/mmc.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
@@ -38,10 +39,18 @@
#include <linux/dma-mapping.h>
#include <mach/gpio.h>
#include <mach/msm_bus.h>
+#include <mach/mpm.h>
#include <linux/iopoll.h>
#include "sdhci-pltfm.h"
+enum sdc_mpm_pin_state {
+ SDC_DAT1_DISABLE,
+ SDC_DAT1_ENABLE,
+ SDC_DAT1_ENWAKE,
+ SDC_DAT1_DISWAKE,
+};
+
#define SDHCI_VER_100 0x2B
#define CORE_HC_MODE 0x78
#define HC_MODE_EN 0x1
@@ -156,6 +165,9 @@
#define INVALID_TUNING_PHASE -1
+#define sdhci_is_valid_mpm_wakeup_int(_h) ((_h)->pdata->mpm_sdiowakeup_int >= 0)
+#define sdhci_is_valid_gpio_wakeup_int(_h) ((_h)->pdata->sdiowakeup_irq >= 0)
+
static const u32 tuning_block_64[] = {
0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
0xDDFFDFFF, 0xFBFFFBFF, 0xFF7FFFBF, 0xEFBDF777,
@@ -283,6 +295,8 @@
struct sdhci_msm_bus_voting_data *voting_data;
u32 *sup_clk_table;
unsigned char sup_clk_cnt;
+ int mpm_sdiowakeup_int;
+ int sdiowakeup_irq;
};
struct sdhci_msm_bus_vote {
@@ -318,6 +332,7 @@
bool calibration_done;
u8 saved_tuning_phase;
atomic_t controller_clock;
+ bool is_sdiowakeup_enabled;
};
enum vdd_io_level {
@@ -1338,7 +1353,7 @@
struct device_node *np = dev->of_node;
u32 bus_width = 0;
u32 cpu_dma_latency;
- int len, i;
+ int len, i, mpm_int;
int clk_table_len;
u32 *clk_table = NULL;
enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;
@@ -1433,6 +1448,12 @@
if (of_get_property(np, "qcom,nonremovable", NULL))
pdata->nonremovable = true;
+ if (!of_property_read_u32(np, "qcom,dat1-mpm-int",
+ &mpm_int))
+ pdata->mpm_sdiowakeup_int = mpm_int;
+ else
+ pdata->mpm_sdiowakeup_int = -1;
+
return pdata;
out:
return NULL;
@@ -1933,6 +1954,39 @@
return ret;
}
+/*
+ * Acquire spin-lock host->lock before calling this function
+ */
+static void sdhci_msm_cfg_sdiowakeup_gpio_irq(struct sdhci_host *host,
+ bool enable)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ if (enable && !msm_host->is_sdiowakeup_enabled)
+ enable_irq(msm_host->pdata->sdiowakeup_irq);
+ else if (!enable && msm_host->is_sdiowakeup_enabled)
+ disable_irq_nosync(msm_host->pdata->sdiowakeup_irq);
+ else
+ dev_warn(&msm_host->pdev->dev, "%s: wakeup to config: %d curr: %d\n",
+ __func__, enable, msm_host->is_sdiowakeup_enabled);
+ msm_host->is_sdiowakeup_enabled = enable;
+}
+
+static irqreturn_t sdhci_msm_sdiowakeup_irq(int irq, void *data)
+{
+ struct sdhci_host *host = (struct sdhci_host *)data;
+ unsigned long flags;
+
+ pr_debug("%s: irq (%d) received\n", __func__, irq);
+
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
{
struct sdhci_host *host = (struct sdhci_host *)data;
@@ -2609,6 +2663,38 @@
.enable_controller_clock = sdhci_msm_enable_controller_clock,
};
+static int sdhci_msm_cfg_mpm_pin_wakeup(struct sdhci_host *host, unsigned mode)
+{
+ int ret = 0;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ unsigned int pin = msm_host->pdata->mpm_sdiowakeup_int;
+
+ if (!pin)
+ return 0;
+
+ switch (mode) {
+ case SDC_DAT1_DISABLE:
+ ret = msm_mpm_enable_pin(pin, 0);
+ break;
+ case SDC_DAT1_ENABLE:
+ ret = msm_mpm_set_pin_type(pin, IRQ_TYPE_LEVEL_LOW);
+ if (!ret)
+ ret = msm_mpm_enable_pin(pin, 1);
+ break;
+ case SDC_DAT1_ENWAKE:
+ ret = msm_mpm_set_pin_wake(pin, 1);
+ break;
+ case SDC_DAT1_DISWAKE:
+ ret = msm_mpm_set_pin_wake(pin, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
static int __devinit sdhci_msm_probe(struct platform_device *pdev)
{
struct sdhci_host *host;
@@ -2619,6 +2705,7 @@
u32 vdd_max_current;
u16 host_version;
u32 pwr, irq_status, irq_ctl;
+ unsigned long flags;
pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__);
msm_host = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_msm_host),
@@ -2837,6 +2924,8 @@
host->quirks2 |= SDHCI_QUIRK2_RDWR_TX_ACTIVE_EOT;
}
+ host->quirks2 |= SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR;
+
/* Setup PWRCTL irq */
msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
if (msm_host->pwr_irq < 0) {
@@ -2891,7 +2980,7 @@
msm_host->mmc->caps2 |= MMC_CAP2_STOP_REQUEST;
msm_host->mmc->caps2 |= MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE;
msm_host->mmc->caps2 |= MMC_CAP2_CORE_PM;
- msm_host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
+ msm_host->mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ;
if (msm_host->pdata->nonremovable)
msm_host->mmc->caps |= MMC_CAP_NONREMOVABLE;
@@ -2917,6 +3006,27 @@
dev_err(&pdev->dev, "%s: Failed to set dma mask\n", __func__);
}
+ msm_host->pdata->sdiowakeup_irq = platform_get_irq_byname(pdev,
+ "sdiowakeup_irq");
+ if (msm_host->pdata->sdiowakeup_irq >= 0) {
+ msm_host->is_sdiowakeup_enabled = true;
+ ret = request_irq(msm_host->pdata->sdiowakeup_irq,
+ sdhci_msm_sdiowakeup_irq,
+ IRQF_SHARED | IRQF_TRIGGER_LOW,
+ "sdhci-msm sdiowakeup", host);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: request sdiowakeup IRQ %d: failed: %d\n",
+ __func__, msm_host->pdata->sdiowakeup_irq, ret);
+ msm_host->pdata->sdiowakeup_irq = -1;
+ msm_host->is_sdiowakeup_enabled = false;
+ goto free_cd_gpio;
+ } else {
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false);
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+ }
+
ret = sdhci_add_host(host);
if (ret) {
dev_err(&pdev->dev, "Add host failed (%d)\n", ret);
@@ -2950,6 +3060,16 @@
else if (mmc_use_core_runtime_pm(host->mmc))
pm_runtime_enable(&pdev->dev);
+ if (msm_host->pdata->mpm_sdiowakeup_int != -1) {
+ ret = sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_ENABLE);
+ if (ret) {
+ pr_err("%s: enabling wakeup: failed: ret: %d\n",
+ mmc_hostname(host->mmc), ret);
+ ret = 0;
+ msm_host->pdata->mpm_sdiowakeup_int = -1;
+ }
+ }
+
/* Successful initialization */
goto out;
@@ -2961,6 +3081,8 @@
free_cd_gpio:
if (gpio_is_valid(msm_host->pdata->status_gpio))
mmc_cd_gpio_free(msm_host->mmc);
+ if (sdhci_is_valid_gpio_wakeup_int(msm_host))
+ free_irq(msm_host->pdata->sdiowakeup_irq, host);
vreg_deinit:
sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false);
bus_unregister:
@@ -3006,6 +3128,12 @@
pm_runtime_disable(&pdev->dev);
sdhci_pltfm_free(pdev);
+ if (sdhci_is_valid_mpm_wakeup_int(msm_host))
+ sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_DISABLE);
+
+ if (sdhci_is_valid_gpio_wakeup_int(msm_host))
+ free_irq(msm_host->pdata->sdiowakeup_irq, host);
+
if (gpio_is_valid(msm_host->pdata->status_gpio))
mmc_cd_gpio_free(msm_host->mmc);
@@ -3021,13 +3149,75 @@
return 0;
}
+static int sdhci_msm_cfg_sdio_wakeup(struct sdhci_host *host, bool enable)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!(host->mmc->card && mmc_card_sdio(host->mmc->card) &&
+ (sdhci_is_valid_mpm_wakeup_int(msm_host) ||
+ sdhci_is_valid_gpio_wakeup_int(msm_host)) &&
+ mmc_card_wake_sdio_irq(host->mmc))) {
+ return 1;
+ }
+
+ spin_lock_irqsave(&host->lock, flags);
+ if (enable) {
+ /* configure DAT1 gpio if applicable */
+ if (sdhci_is_valid_gpio_wakeup_int(msm_host)) {
+ ret = enable_irq_wake(msm_host->pdata->sdiowakeup_irq);
+ if (!ret)
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, true);
+ goto out;
+ } else {
+ ret = sdhci_msm_cfg_mpm_pin_wakeup(host,
+ SDC_DAT1_ENWAKE);
+ if (ret)
+ goto out;
+ ret = enable_irq_wake(host->irq);
+ if (ret)
+ sdhci_msm_cfg_mpm_pin_wakeup(host,
+ SDC_DAT1_DISWAKE);
+ }
+ } else {
+ if (sdhci_is_valid_gpio_wakeup_int(msm_host)) {
+ ret = disable_irq_wake(msm_host->pdata->sdiowakeup_irq);
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false);
+ } else {
+ ret = sdhci_msm_cfg_mpm_pin_wakeup(host,
+ SDC_DAT1_DISWAKE);
+ if (ret)
+ goto out;
+ ret = disable_irq_wake(host->irq);
+ }
+ }
+out:
+ if (ret)
+ pr_err("%s: %s: %sable wakeup: failed: %d gpio: %d mpm: %d\n",
+ mmc_hostname(host->mmc), __func__, enable ? "en" : "dis",
+ ret, msm_host->pdata->sdiowakeup_irq,
+ msm_host->pdata->mpm_sdiowakeup_int);
+ spin_unlock_irqrestore(&host->lock, flags);
+ return ret;
+}
+
static int sdhci_msm_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int ret;
+
+ ret = sdhci_msm_cfg_sdio_wakeup(host, true);
+ /* pwr_irq is not monitored by mpm on suspend, hence disable it */
+ if (!ret)
+ goto skip_disable_host_irq;
disable_irq(host->irq);
+
+skip_disable_host_irq:
disable_irq(msm_host->pwr_irq);
/*
@@ -3048,10 +3238,17 @@
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int ret;
- enable_irq(msm_host->pwr_irq);
+ ret = sdhci_msm_cfg_sdio_wakeup(host, false);
+ if (!ret)
+ goto skip_enable_host_irq;
+
enable_irq(host->irq);
+skip_enable_host_irq:
+ enable_irq(msm_host->pwr_irq);
+
return 0;
}
@@ -3103,6 +3300,26 @@
out:
return ret;
}
+
+static int sdhci_msm_suspend_noirq(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int ret = 0;
+
+ /*
+ * ksdioirqd may get scheduled after sdhc suspend, hence retry
+ * suspend in case the clocks are ON
+ */
+ if (atomic_read(&msm_host->clks_on)) {
+ pr_warn("%s: %s: clock ON after suspend, aborting suspend\n",
+ mmc_hostname(host->mmc), __func__);
+ ret = -EAGAIN;
+ }
+
+ return ret;
+}
#endif
#ifdef CONFIG_PM
@@ -3110,6 +3327,7 @@
SET_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend, sdhci_msm_resume)
SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume,
NULL)
+ .suspend_noirq = sdhci_msm_suspend_noirq,
};
#define SDHCI_MSM_PMOPS (&sdhci_msm_pmops)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 822548e..32ff175 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -32,6 +32,7 @@
#include "sdhci.h"
#define DRIVER_NAME "sdhci"
+#define SDHCI_SUSPEND_TIMEOUT 300 /* 300 ms */
#define DBG(f, x...) \
pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x)
@@ -68,6 +69,12 @@
}
#endif
+static inline int sdhci_get_async_int_status(struct sdhci_host *host)
+{
+ return (sdhci_readw(host, SDHCI_HOST_CONTROL2) &
+ SDHCI_CTRL_ASYNC_INT_ENABLE) >> 14;
+}
+
static void sdhci_dump_state(struct sdhci_host *host)
{
struct mmc_host *mmc = host->mmc;
@@ -1579,6 +1586,33 @@
spin_unlock_irqrestore(&host->lock, flags);
}
+static void sdhci_cfg_async_intr(struct sdhci_host *host, bool enable)
+{
+ if (!host->async_int_supp)
+ return;
+
+ if (enable)
+ sdhci_writew(host,
+ sdhci_readw(host, SDHCI_HOST_CONTROL2) |
+ SDHCI_CTRL_ASYNC_INT_ENABLE,
+ SDHCI_HOST_CONTROL2);
+ else
+ sdhci_writew(host, sdhci_readw(host, SDHCI_HOST_CONTROL2) &
+ ~SDHCI_CTRL_ASYNC_INT_ENABLE,
+ SDHCI_HOST_CONTROL2);
+}
+
+static void sdhci_cfg_irq(struct sdhci_host *host, bool enable)
+{
+ if (enable && !host->irq_enabled) {
+ enable_irq(host->irq);
+ host->irq_enabled = true;
+ } else if (!enable && host->irq_enabled) {
+ disable_irq_nosync(host->irq);
+ host->irq_enabled = false;
+ }
+}
+
static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
{
unsigned long flags;
@@ -1594,9 +1628,26 @@
return;
}
- if (ios->clock)
+ spin_lock_irqsave(&host->lock, flags);
+ /* lock is being released intermittently below, hence disable irq */
+ sdhci_cfg_irq(host, false);
+ spin_unlock_irqrestore(&host->lock, flags);
+ if (ios->clock) {
sdhci_set_clock(host, ios->clock);
-
+ if (host->async_int_supp && sdhci_get_async_int_status(host)) {
+ if (host->disable_sdio_irq_deferred) {
+ pr_debug("%s: %s: disable sdio irq\n",
+ mmc_hostname(host->mmc), __func__);
+ host->mmc->ops->enable_sdio_irq(host->mmc, 0);
+ host->disable_sdio_irq_deferred = false;
+ }
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_cfg_async_intr(host, false);
+ spin_unlock_irqrestore(&host->lock, flags);
+ pr_debug("%s: %s: unconfig async intr\n",
+ mmc_hostname(host->mmc), __func__);
+ }
+ }
/*
* The controller clocks may be off during power-up and we may end up
* enabling card clock before giving power to the card. Hence, during
@@ -1622,6 +1673,7 @@
}
spin_lock_irqsave(&host->lock, flags);
if (!host->clock) {
+ sdhci_cfg_irq(host, true);
spin_unlock_irqrestore(&host->lock, flags);
mutex_unlock(&host->ios_mutex);
return;
@@ -1781,9 +1833,18 @@
if (host->vmmc && vdd_bit != -1)
mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit);
}
- if (!ios->clock)
+ if (!ios->clock) {
+ if (host->async_int_supp && host->mmc->card &&
+ mmc_card_sdio(host->mmc->card)) {
+ sdhci_cfg_async_intr(host, true);
+ pr_debug("%s: %s: config async intr\n",
+ mmc_hostname(host->mmc), __func__);
+ }
sdhci_set_clock(host, ios->clock);
-
+ }
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_cfg_irq(host, true);
+ spin_unlock_irqrestore(&host->lock, flags);
mmiowb();
mutex_unlock(&host->ios_mutex);
}
@@ -1863,6 +1924,14 @@
if (host->flags & SDHCI_DEVICE_DEAD)
goto out;
+ if (!enable && !host->clock) {
+ pr_debug("%s: %s: defered disabling card intr\n",
+ host->mmc ? mmc_hostname(host->mmc) : "null",
+ __func__);
+ host->disable_sdio_irq_deferred = true;
+ return;
+ }
+
if (enable)
host->flags |= SDHCI_SDIO_IRQ_ENABLED;
else
@@ -2742,6 +2811,23 @@
return IRQ_HANDLED;
}
+ if (!host->clock && host->mmc->card &&
+ mmc_card_sdio(host->mmc->card)) {
+ /* SDIO async. interrupt is level-sensitive */
+ sdhci_cfg_irq(host, false);
+ pr_debug("%s: got async-irq: clocks: %d gated: %d host-irq[en:1/dis:0]: %d\n",
+ mmc_hostname(host->mmc), host->clock,
+ host->mmc->clk_gated, host->irq_enabled);
+ spin_unlock(&host->lock);
+ /* prevent suspend till the ksdioirqd runs or resume happens */
+ if ((host->mmc->dev_status == DEV_SUSPENDING) ||
+ (host->mmc->dev_status == DEV_SUSPENDED))
+ pm_wakeup_event(&host->mmc->card->dev,
+ SDHCI_SUSPEND_TIMEOUT);
+ else
+ mmc_signal_sdio_irq(host->mmc);
+ return IRQ_HANDLED;
+ }
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
if (!intmask || intmask == 0xffffffff) {
@@ -2837,9 +2923,13 @@
/*
* We have to delay this as it calls back into the driver.
*/
- if (cardint)
+ if (cardint) {
+ /* clks are on, but suspend may be in progress */
+ if (host->mmc->dev_status == DEV_SUSPENDING)
+ pm_wakeup_event(&host->mmc->card->dev,
+ SDHCI_SUSPEND_TIMEOUT);
mmc_signal_sdio_irq(host->mmc);
-
+ }
return result;
}
@@ -3489,6 +3579,7 @@
if (ret)
goto untasklet;
+ host->irq_enabled = true;
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
if (IS_ERR(host->vmmc)) {
pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
@@ -3532,8 +3623,12 @@
mmc_hostname(mmc), ret);
}
+ if (caps[0] & SDHCI_ASYNC_INTR)
+ host->async_int_supp = true;
mmc_add_host(mmc);
+ if (host->quirks2 & SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR)
+ sdhci_clear_set_irqs(host, SDHCI_INT_DATA_END_BIT, 0);
pr_info("%s: SDHCI controller on %s [%s] using %s\n",
mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
(host->flags & SDHCI_USE_ADMA) ? "ADMA" :
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index db4806d..8c2320b 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -175,6 +175,7 @@
#define SDHCI_CTRL_DRV_TYPE_D 0x0030
#define SDHCI_CTRL_EXEC_TUNING 0x0040
#define SDHCI_CTRL_TUNED_CLK 0x0080
+#define SDHCI_CTRL_ASYNC_INT_ENABLE 0x4000
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
#define SDHCI_CAPABILITIES 0x40
@@ -195,6 +196,7 @@
#define SDHCI_CAN_VDD_300 0x02000000
#define SDHCI_CAN_VDD_180 0x04000000
#define SDHCI_CAN_64BIT 0x10000000
+#define SDHCI_ASYNC_INTR 0x20000000
#define SDHCI_SUPPORT_SDR50 0x00000001
#define SDHCI_SUPPORT_SDR104 0x00000002
diff --git a/drivers/nfc/nfc-nci.c b/drivers/nfc/nfc-nci.c
index 74caacc..654408c 100644
--- a/drivers/nfc/nfc-nci.c
+++ b/drivers/nfc/nfc-nci.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
+#include <linux/reboot.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/irq.h>
@@ -47,7 +48,7 @@
MODULE_DEVICE_TABLE(of, msm_match_table);
#define MAX_BUFFER_SIZE (780)
-#define PACKET_MAX_LENGTH (258)
+#define PACKET_MAX_LENGTH (258)
/* Read data */
#define PACKET_HEADER_SIZE_NCI (4)
#define PACKET_TYPE_NCI (16)
@@ -58,7 +59,7 @@
#define NTF_TIMEOUT (10000)
#define CORE_RESET_RSP_GID (0x60)
#define CORE_RESET_OID (0x00)
-#define CORE_RST_NTF_LENGTH (0x02)
+#define CORE_RST_NTF_LENGTH (0x02)
static void clk_req_update(struct work_struct *work);
@@ -115,6 +116,9 @@
static int ftm_werr_code;
+unsigned int disable_ctrl;
+bool region2_sent;
+
static void qca199x_init_stat(struct qca199x_dev *qca199x_dev)
{
qca199x_dev->count_irq = 0;
@@ -491,6 +495,12 @@
}
mutex_unlock(&qca199x_dev->read_mutex);
+ /* If we detect a Region2 command prior to power-down */
+ if ((tmp[0] == 0x2F) && (tmp[1] == 0x01) && (tmp[2] == 0x02) &&
+ (tmp[3] == 0x08) && (tmp[4] == 0x00)) {
+ region2_sent = true;
+ }
+
return ret;
}
@@ -606,7 +616,7 @@
if (r < 0)
goto err_req;
/*
- Also, set flag for initial NCI write following resetas
+ Also, set flag for initial NCI write following reset as
may wish to do some house keeping. Ensure no pending
messages in NFCC buffers which may be wrongly
construed as response to initial message
@@ -940,121 +950,117 @@
if (r < 0)
goto err_init;
- if (0x10 != (0x10 & buf)) {
- RAW(s73, 0x02);
+ RAW(s73, 0x02);
- r = nfc_i2c_write(client, &raw_s73[0], sizeof(raw_s73));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
- RAW(1p8_CONTROL_011, XTAL_CLOCK | 0x01);
-
- r = nfc_i2c_write(client, &raw_1p8_CONTROL_011[0],
- sizeof(raw_1p8_CONTROL_011));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
- RAW(1P8_CONTROL_010, (0x8));
- r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
- sizeof(raw_1P8_CONTROL_010));
- if (r < 0)
- goto err_init;
-
- usleep(10000); /* 10ms wait */
- RAW(1P8_CONTROL_010, (0xC));
- r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
- sizeof(raw_1P8_CONTROL_010));
- if (r < 0)
- goto err_init;
-
- usleep(100); /* 100uS wait */
- RAW(1P8_X0_0B0, (FREQ_SEL_19));
- r = nfc_i2c_write(client, &raw_1P8_X0_0B0[0],
- sizeof(raw_1P8_X0_0B0));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
-
- /* PWR_EN = 1 */
- RAW(1P8_CONTROL_010, (0xd));
- r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
- sizeof(raw_1P8_CONTROL_010));
- if (r < 0)
- goto err_init;
-
-
- usleep(20000); /* 20ms wait */
- /* LS_EN = 1 */
- RAW(1P8_CONTROL_010, 0xF);
- r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
- sizeof(raw_1P8_CONTROL_010));
- if (r < 0)
- goto err_init;
-
- usleep(20000); /* 20ms wait */
-
- /* Enable the PMIC clock */
- RAW(1P8_PAD_CFG_CLK_REQ, (0x1));
- r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_CLK_REQ[0],
- sizeof(raw_1P8_PAD_CFG_CLK_REQ));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
-
- RAW(1P8_PAD_CFG_PWR_REQ, (0x1));
- r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_PWR_REQ[0],
- sizeof(raw_1P8_PAD_CFG_PWR_REQ));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
-
- RAW(slave2, 0x10);
- r = nfc_i2c_write(client, &raw_slave2[0], sizeof(raw_slave2));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
-
- RAW(slave1, NCI_I2C_SLAVE);
- r = nfc_i2c_write(client, &raw_slave1[0], sizeof(raw_slave1));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
-
- /* QCA199x NFCC CPU should now boot... */
- r = i2c_master_recv(client, &raw_slave1_rd, 1);
- /* Talk on NCI slave address NCI_I2C_SLAVE 0x2C*/
- client->addr = NCI_I2C_SLAVE;
-
- /*
- Start with small delay and then we will poll until we
- get a core reset notification - This is time for chip
- & NFCC controller to come-up.
- */
- usleep(1000); /* 1 ms */
-
- do {
- ret = i2c_master_recv(client, rsp, 5);
- /* Found core reset notification */
- if (((rsp[0] == CORE_RESET_RSP_GID) &&
- (rsp[1] == CORE_RESET_OID) &&
- (rsp[2] == CORE_RST_NTF_LENGTH))
- || time_taken == NTF_TIMEOUT) {
- core_reset_completed = true;
- }
- usleep(10); /* 10us sleep before retry */
- time_taken++;
- } while (!core_reset_completed);
- r = 0;
- } else {
+ r = nfc_i2c_write(client, &raw_s73[0], sizeof(raw_s73));
+ if (r < 0)
goto err_init;
- }
+
+ usleep(1000);
+ RAW(1p8_CONTROL_011, XTAL_CLOCK | 0x01);
+
+ r = nfc_i2c_write(client, &raw_1p8_CONTROL_011[0],
+ sizeof(raw_1p8_CONTROL_011));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+ RAW(1P8_CONTROL_010, (0x8));
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ if (r < 0)
+ goto err_init;
+
+ usleep(10000); /* 10ms wait */
+ RAW(1P8_CONTROL_010, (0xC));
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ if (r < 0)
+ goto err_init;
+
+ usleep(100); /* 100uS wait */
+ RAW(1P8_X0_0B0, (FREQ_SEL_19));
+ r = nfc_i2c_write(client, &raw_1P8_X0_0B0[0],
+ sizeof(raw_1P8_X0_0B0));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+
+ /* PWR_EN = 1 */
+ RAW(1P8_CONTROL_010, (0xd));
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ if (r < 0)
+ goto err_init;
+
+
+ usleep(20000); /* 20ms wait */
+ /* LS_EN = 1 */
+ RAW(1P8_CONTROL_010, 0xF);
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ if (r < 0)
+ goto err_init;
+
+ usleep(20000); /* 20ms wait */
+
+ /* Enable the PMIC clock */
+ RAW(1P8_PAD_CFG_CLK_REQ, (0x1));
+ r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_CLK_REQ[0],
+ sizeof(raw_1P8_PAD_CFG_CLK_REQ));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+
+ RAW(1P8_PAD_CFG_PWR_REQ, (0x1));
+ r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_PWR_REQ[0],
+ sizeof(raw_1P8_PAD_CFG_PWR_REQ));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+
+ RAW(slave2, 0x10);
+ r = nfc_i2c_write(client, &raw_slave2[0], sizeof(raw_slave2));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+
+ RAW(slave1, NCI_I2C_SLAVE);
+ r = nfc_i2c_write(client, &raw_slave1[0], sizeof(raw_slave1));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+
+ /* QCA199x NFCC CPU should now boot... */
+ r = i2c_master_recv(client, &raw_slave1_rd, 1);
+ /* Talk on NCI slave address NCI_I2C_SLAVE 0x2C*/
+ client->addr = NCI_I2C_SLAVE;
+
+ /*
+ Start with small delay and then we will poll until we
+ get a core reset notification - This is time for chip
+ & NFCC controller to come-up.
+ */
+ usleep(1000); /* 1 ms */
+
+ do {
+ ret = i2c_master_recv(client, rsp, 5);
+ /* Found core reset notification */
+ if (((rsp[0] == CORE_RESET_RSP_GID) &&
+ (rsp[1] == CORE_RESET_OID) &&
+ (rsp[2] == CORE_RST_NTF_LENGTH))
+ || time_taken == NTF_TIMEOUT) {
+ core_reset_completed = true;
+ }
+ usleep(10); /* 10us sleep before retry */
+ time_taken++;
+ } while (!core_reset_completed);
+ r = 0;
return r;
err_init:
r = 1;
@@ -1154,6 +1160,7 @@
pdata->dis_gpio = of_get_named_gpio(np, "qcom,dis-gpio", 0);
if ((!gpio_is_valid(pdata->dis_gpio)))
return -EINVAL;
+ disable_ctrl = pdata->dis_gpio;
pdata->irq_gpio = of_get_named_gpio(np, "qcom,irq-gpio", 0);
if ((!gpio_is_valid(pdata->irq_gpio)))
@@ -1450,6 +1457,10 @@
}
i2c_set_clientdata(client, qca199x_dev);
gpio_set_value(platform_data->dis_gpio, 1);
+
+ /* To keep track if region2 command has been sent to controller */
+ region2_sent = false;
+
dev_dbg(&client->dev,
"nfc-nci probe: %s, probing qca1990 exited successfully\n",
__func__);
@@ -1524,17 +1535,52 @@
},
};
+
+static int nfcc_reboot(struct notifier_block *notifier, unsigned long val,
+ void *v)
+{
+ /*
+ * Set DISABLE=1 *ONLY* if the NFC service has been disabled.
+ * This will put NFCC into HPD(Hard Power Down) state for power
+ * saving when powering down(Low Batt. or Power off handset)
+ * If user requires NFC and CE mode when powered down(PD) the
+ * middleware puts NFCC into region2 prior to PD. In this case
+ * we DO NOT HPD chip as this will trash Region2 and CE support
+ * when handset is PD.
+ */
+ if (region2_sent == false) {
+ /* HPD the NFCC */
+ gpio_set_value(disable_ctrl, 1);
+ }
+ return NOTIFY_OK;
+}
+
+
+static struct notifier_block nfcc_notifier = {
+ .notifier_call = nfcc_reboot,
+ .next = NULL,
+ .priority = 0
+};
+
/*
* module load/unload record keeping
*/
static int __init qca199x_dev_init(void)
{
+ int ret;
+
+ ret = register_reboot_notifier(&nfcc_notifier);
+ if (ret) {
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
+ return ret;
+ }
return i2c_add_driver(&qca199x);
}
module_init(qca199x_dev_init);
static void __exit qca199x_dev_exit(void)
{
+ unregister_reboot_notifier(&nfcc_notifier);
i2c_del_driver(&qca199x);
}
module_exit(qca199x_dev_exit);
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index 0870202..bf294fc 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -1067,7 +1067,8 @@
return rc;
}
- rc = override_dcin_ilimit(chip, 0);
+ if (enable)
+ rc = override_dcin_ilimit(chip, 0);
return rc;
}
@@ -5120,6 +5121,14 @@
qpnp_chg_force_run_on_batt(chip, chip->charging_disabled);
qpnp_chg_set_appropriate_vddmax(chip);
+ if (chip->parallel_ovp_mode) {
+ rc = override_dcin_ilimit(chip, 1);
+ if (rc) {
+ pr_err("Override DCIN LLIMIT %d\n", rc);
+ goto unregister_dc_psy;
+ }
+ }
+
rc = qpnp_chg_request_irqs(chip);
if (rc) {
pr_err("failed to request interrupts %d\n", rc);
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 496b31d..d03c3f1 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -1119,7 +1119,7 @@
ret = wait_for_completion_timeout(&dev->qmi.slave_notify,
HZ);
if (!ret) {
- dev_err(dev->dev, "slave thread wait err:%d", ret);
+ dev_dbg(dev->dev, "slave thread wait err:%d", ret);
continue;
}
/* Probe devices for first notification */
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 21863e8..8cd70ac 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -92,13 +92,43 @@
}
EXPORT_SYMBOL(sensor_get_id);
+static void init_sensor_trip(struct sensor_info *sensor)
+{
+ int ret = 0, i = 0;
+ enum thermal_trip_type type;
+
+ for (i = 0; ((sensor->max_idx == -1) ||
+ (sensor->min_idx == -1)) &&
+ (sensor->tz->ops->get_trip_type) &&
+ (i < sensor->tz->trips); i++) {
+
+ sensor->tz->ops->get_trip_type(sensor->tz, i, &type);
+ if (type == THERMAL_TRIP_CONFIGURABLE_HI)
+ sensor->max_idx = i;
+ if (type == THERMAL_TRIP_CONFIGURABLE_LOW)
+ sensor->min_idx = i;
+ type = 0;
+ }
+
+ ret = sensor->tz->ops->get_trip_temp(sensor->tz,
+ sensor->min_idx, &sensor->threshold_min);
+ if (ret)
+ pr_err("Unable to get MIN trip temp. sensor:%d err:%d\n",
+ sensor->sensor_id, ret);
+
+ ret = sensor->tz->ops->get_trip_temp(sensor->tz,
+ sensor->max_idx, &sensor->threshold_max);
+ if (ret)
+ pr_err("Unable to get MAX trip temp. sensor:%d err:%d\n",
+ sensor->sensor_id, ret);
+}
+
static int __update_sensor_thresholds(struct sensor_info *sensor)
{
long max_of_low_thresh = LONG_MIN;
long min_of_high_thresh = LONG_MAX;
struct sensor_threshold *pos, *var;
- enum thermal_trip_type type;
- int i, ret = 0;
+ int ret = 0;
if (!sensor->tz->ops->set_trip_temp ||
!sensor->tz->ops->activate_trip_type ||
@@ -108,19 +138,8 @@
goto update_done;
}
- for (i = 0; ((sensor->max_idx == -1) || (sensor->min_idx == -1)) &&
- (sensor->tz->ops->get_trip_type) && (i < sensor->tz->trips);
- i++) {
- sensor->tz->ops->get_trip_type(sensor->tz, i, &type);
- if (type == THERMAL_TRIP_CONFIGURABLE_HI)
- sensor->max_idx = i;
- if (type == THERMAL_TRIP_CONFIGURABLE_LOW)
- sensor->min_idx = i;
- sensor->tz->ops->get_trip_temp(sensor->tz,
- THERMAL_TRIP_CONFIGURABLE_LOW, &sensor->threshold_min);
- sensor->tz->ops->get_trip_temp(sensor->tz,
- THERMAL_TRIP_CONFIGURABLE_HI, &sensor->threshold_max);
- }
+ if ((sensor->max_idx == -1) || (sensor->min_idx == -1))
+ init_sensor_trip(sensor);
list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
if (!pos->active)
@@ -368,7 +387,7 @@
sensor->sensor_id = tz->id;
sensor->tz = tz;
- sensor->threshold_min = 0;
+ sensor->threshold_min = LONG_MIN;
sensor->threshold_max = LONG_MAX;
sensor->max_idx = -1;
sensor->min_idx = -1;
diff --git a/drivers/video/msm/mdss/dsi_status_v2.c b/drivers/video/msm/mdss/dsi_status_v2.c
index d62ddf3..565401d 100644
--- a/drivers/video/msm/mdss/dsi_status_v2.c
+++ b/drivers/video/msm/mdss/dsi_status_v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -82,7 +82,19 @@
mdp3_session = pdsi_status->mfd->mdp.private1;
mutex_lock(&mdp3_session->lock);
- ret = ctrl_pdata->check_status(ctrl_pdata);
+ if (!mdp3_session->status) {
+ pr_info("display off already\n");
+ mutex_unlock(&mdp3_session->lock);
+ return;
+ }
+
+ if (mdp3_session->wait_for_dma_done)
+ ret = mdp3_session->wait_for_dma_done(mdp3_session);
+
+ if (!ret)
+ ret = ctrl_pdata->check_status(ctrl_pdata);
+ else
+ pr_err("wait_for_dma_done error\n");
mutex_unlock(&mdp3_session->lock);
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c
index 1111aeb..095a387 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.c
+++ b/drivers/video/msm/mdss/mdp3_ctrl.c
@@ -157,6 +157,7 @@
{
struct mdp3_session_data *session = (struct mdp3_session_data *)arg;
schedule_work(&session->dma_done_work);
+ complete(&session->dma_completion);
}
void vsync_count_down(void *arg)
@@ -897,9 +898,7 @@
dma->source_config.stride = stride;
dma->output_config.pack_pattern =
mdp3_ctrl_get_pack_pattern(req->src.format);
- mdp3_clk_enable(1, 0);
- mdp3_session->dma->dma_config_source(dma);
- mdp3_clk_enable(0, 0);
+ dma->update_src_cfg = true;
}
mdp3_session->overlay.id = 1;
req->id = 1;
@@ -923,14 +922,6 @@
mutex_lock(&mdp3_session->lock);
if (mdp3_session->overlay.id == ndx && ndx == 1) {
- struct mdp3_dma *dma = mdp3_session->dma;
- dma->source_config.format = format;
- dma->source_config.stride = fix->line_length;
- dma->output_config.pack_pattern =
- mdp3_ctrl_get_pack_pattern(mfd->fb_imgType);
- mdp3_clk_enable(1, 0);
- mdp3_session->dma->dma_config_source(dma);
- mdp3_clk_enable(0, 0);
mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
mdp3_bufq_deinit(&mdp3_session->bufq_in);
} else {
@@ -1046,7 +1037,8 @@
MDP_NOTIFY_FRAME_DONE);
}
}
-
+ mdp3_session->dma_active = 1;
+ init_completion(&mdp3_session->dma_completion);
mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
mdp3_bufq_push(&mdp3_session->bufq_out, data);
}
@@ -1140,6 +1132,8 @@
MDP_NOTIFY_FRAME_DONE);
}
}
+ mdp3_session->dma_active = 1;
+ init_completion(&mdp3_session->dma_completion);
mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
} else {
pr_debug("mdp3_ctrl_pan_display no memory, stop interface");
@@ -1761,6 +1755,23 @@
return rc;
}
+int mdp3_wait_for_dma_done(struct mdp3_session_data *session)
+{
+ int rc = 0;
+
+ if (session->dma_active) {
+ rc = wait_for_completion_timeout(&session->dma_completion,
+ KOFF_TIMEOUT);
+ if (rc > 0) {
+ session->dma_active = 0;
+ rc = 0;
+ } else if (rc == 0) {
+ rc = -ETIME;
+ }
+ }
+ return rc;
+}
+
int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
{
struct device *dev = mfd->fbi->dev;
@@ -1835,6 +1846,9 @@
mdp3_session->vsync_timer.data = (u32)mdp3_session;
mdp3_session->vsync_period = 1000 / mfd->panel_info->mipi.frame_rate;
mfd->mdp.private1 = mdp3_session;
+ init_completion(&mdp3_session->dma_completion);
+ if (intf_type != MDP3_DMA_OUTPUT_SEL_DSI_VIDEO)
+ mdp3_session->wait_for_dma_done = mdp3_wait_for_dma_done;
rc = sysfs_create_group(&dev->kobj, &vsync_fs_attr_group);
if (rc) {
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.h b/drivers/video/msm/mdss/mdp3_ctrl.h
index cfad1d3..416b7c2 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.h
+++ b/drivers/video/msm/mdss/mdp3_ctrl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -61,6 +61,10 @@
int vsync_enabled;
atomic_t vsync_countdown; /* Used to count down */
+
+ bool dma_active;
+ struct completion dma_completion;
+ int (*wait_for_dma_done)(struct mdp3_session_data *session);
};
int mdp3_ctrl_init(struct msm_fb_data_type *mfd);
diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c
index 993a36f..8a13de2 100644
--- a/drivers/video/msm/mdss/mdp3_dma.c
+++ b/drivers/video/msm/mdss/mdp3_dma.c
@@ -605,6 +605,13 @@
}
}
}
+ if (dma->update_src_cfg) {
+ if (dma->output_config.out_sel ==
+ MDP3_DMA_OUTPUT_SEL_DSI_VIDEO && intf->active)
+ pr_err("configuring dma source while dma is active\n");
+ dma->dma_config_source(dma);
+ dma->update_src_cfg = false;
+ }
spin_lock_irqsave(&dma->dma_lock, flag);
MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_ADDR, (u32)buf);
dma->source_config.buf = buf;
@@ -961,6 +968,7 @@
dma->vsync_client.handler = NULL;
dma->vsync_client.arg = NULL;
dma->histo_state = MDP3_DMA_HISTO_STATE_IDLE;
+ dma->update_src_cfg = false;
memset(&dma->cursor, 0, sizeof(dma->cursor));
memset(&dma->ccs_config, 0, sizeof(dma->ccs_config));
diff --git a/drivers/video/msm/mdss/mdp3_dma.h b/drivers/video/msm/mdss/mdp3_dma.h
index 207168f..80ebb9b 100644
--- a/drivers/video/msm/mdss/mdp3_dma.h
+++ b/drivers/video/msm/mdss/mdp3_dma.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -259,6 +259,7 @@
int histo_state;
struct mdp3_dma_histogram_data histo_data;
unsigned int vsync_status;
+ bool update_src_cfg;
int (*dma_config)(struct mdp3_dma *dma,
struct mdp3_dma_source *source_config,
diff --git a/drivers/video/msm/mdss/mdss_dsi_host.c b/drivers/video/msm/mdss/mdss_dsi_host.c
index c2fbc1a..f342c56 100644
--- a/drivers/video/msm/mdss/mdss_dsi_host.c
+++ b/drivers/video/msm/mdss/mdss_dsi_host.c
@@ -90,6 +90,8 @@
ctrl->ndx = DSI_CTRL_1;
}
+ ctrl->panel_mode = ctrl->panel_data.panel_info.mipi.mode;
+
ctrl_list[ctrl->ndx] = ctrl; /* keep it */
if (ctrl->shared_pdata.broadcast_enable)
@@ -269,8 +271,6 @@
pinfo->rgb_swap = DSI_RGB_SWAP_RGB;
- ctrl_pdata->panel_mode = pinfo->mode;
-
if (pinfo->mode == DSI_VIDEO_MODE) {
data = 0;
if (pinfo->pulse_mode_hsa_he)
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 9a469a4..f3b7ce1 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -452,6 +452,16 @@
};
#define is_vig_pipe(_pipe_id_) ((_pipe_id_) <= MDSS_MDP_SSPP_VIG2)
+
+static inline struct mdss_mdp_ctl *mdss_mdp_get_split_ctl(
+ struct mdss_mdp_ctl *ctl)
+{
+ if (ctl && ctl->mixer_right && (ctl->mixer_right->ctl != ctl))
+ return ctl->mixer_right->ctl;
+
+ return NULL;
+}
+
static inline void mdss_mdp_ctl_write(struct mdss_mdp_ctl *ctl,
u32 reg, u32 val)
{
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index fa64448..fe42669 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -1133,15 +1133,6 @@
return 0;
}
-static inline struct mdss_mdp_ctl *mdss_mdp_get_split_ctl(
- struct mdss_mdp_ctl *ctl)
-{
- if (ctl && ctl->mixer_right && (ctl->mixer_right->ctl != ctl))
- return ctl->mixer_right->ctl;
-
- return NULL;
-}
-
int mdss_mdp_ctl_splash_finish(struct mdss_mdp_ctl *ctl, bool handoff)
{
struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl);
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
index 55a4a4d..f8bdc04 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
@@ -298,6 +298,7 @@
{
struct mdss_mdp_video_ctx *ctx;
struct mdss_mdp_vsync_handler *tmp, *handle;
+ struct mdss_mdp_ctl *sctl;
int rc;
u32 frame_rate = 0;
@@ -335,6 +336,10 @@
mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_UNDER_RUN,
ctl->intf_num);
+ sctl = mdss_mdp_get_split_ctl(ctl);
+ if (sctl)
+ mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_UNDER_RUN,
+ sctl->intf_num);
}
list_for_each_entry_safe(handle, tmp, &ctx->vsync_handlers, list)
@@ -641,6 +646,7 @@
static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
{
struct mdss_mdp_video_ctx *ctx;
+ struct mdss_mdp_ctl *sctl;
int rc;
pr_debug("kickoff ctl=%d\n", ctl->num);
@@ -674,6 +680,11 @@
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_UNDER_RUN, ctl->intf_num);
+ sctl = mdss_mdp_get_split_ctl(ctl);
+ if (sctl)
+ mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_UNDER_RUN,
+ sctl->intf_num);
+
mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 1);
wmb();
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index d5da709..34cfe23 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -2698,6 +2698,56 @@
return 0;
}
+static int __mdss_mdp_ctl_handoff(struct mdss_mdp_ctl *ctl,
+ struct mdss_data_type *mdata)
+{
+ int rc = 0;
+ int i, j;
+ u32 mixercfg;
+ struct mdss_mdp_pipe *pipe = NULL;
+
+ if (!ctl || !mdata)
+ return -EINVAL;
+
+ for (i = 0; i < mdata->nmixers_intf; i++) {
+ mixercfg = mdss_mdp_ctl_read(ctl, MDSS_MDP_REG_CTL_LAYER(i));
+ pr_debug("for lm%d mixercfg = 0x%09x\n", i, mixercfg);
+
+ j = MDSS_MDP_SSPP_VIG0;
+ for (; j < MDSS_MDP_MAX_SSPP && mixercfg; j++) {
+ u32 cfg = j * 3;
+ if ((j == MDSS_MDP_SSPP_VIG3) ||
+ (j == MDSS_MDP_SSPP_RGB3)) {
+ /* Add 2 to account for Cursor & Border bits */
+ cfg += 2;
+ }
+ if (mixercfg & (0x7 << cfg)) {
+ pr_debug("Pipe %d staged\n", j);
+ pipe = mdss_mdp_pipe_search(mdata, BIT(j));
+ if (!pipe) {
+ pr_warn("Invalid pipe %d staged\n", j);
+ continue;
+ }
+
+ rc = mdss_mdp_pipe_handoff(pipe);
+ if (rc) {
+ pr_err("Failed to handoff pipe%d\n",
+ pipe->num);
+ goto exit;
+ }
+
+ rc = mdss_mdp_mixer_handoff(ctl, i, pipe);
+ if (rc) {
+ pr_err("failed to handoff mix%d\n", i);
+ goto exit;
+ }
+ }
+ }
+ }
+exit:
+ return rc;
+}
+
/**
* mdss_mdp_overlay_handoff() - Read MDP registers to handoff an active ctl path
* @mfd: Msm frame buffer structure associated with the fb device.
@@ -2713,10 +2763,8 @@
int rc = 0;
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
- int i, j;
- u32 reg;
- struct mdss_mdp_pipe *pipe = NULL;
struct mdss_mdp_ctl *ctl = NULL;
+ struct mdss_mdp_ctl *sctl = NULL;
if (!mdp5_data->ctl) {
ctl = __mdss_mdp_overlay_ctl_init(mfd);
@@ -2740,38 +2788,23 @@
ctl->clk_rate = mdss_mdp_get_clk_rate(MDSS_CLK_MDP_SRC);
pr_debug("Set the ctl clock rate to %d Hz\n", ctl->clk_rate);
- for (i = 0; i < mdata->nmixers_intf; i++) {
- reg = mdss_mdp_ctl_read(ctl, MDSS_MDP_REG_CTL_LAYER(i));
- pr_debug("for lm%d reg = 0x%09x\n", i, reg);
- for (j = MDSS_MDP_SSPP_VIG0; j < MDSS_MDP_MAX_SSPP; j++) {
- u32 cfg = j * 3;
- if ((j == MDSS_MDP_SSPP_VIG3) ||
- (j == MDSS_MDP_SSPP_RGB3)) {
- /* Add 2 to account for Cursor & Border bits */
- cfg += 2;
- }
- if (reg & (0x7 << cfg)) {
- pr_debug("Pipe %d staged\n", j);
- pipe = mdss_mdp_pipe_search(mdata, BIT(j));
- if (!pipe) {
- pr_warn("Invalid pipe %d staged\n", j);
- continue;
- }
+ rc = __mdss_mdp_ctl_handoff(ctl, mdata);
+ if (rc) {
+ pr_err("primary ctl handoff failed. rc=%d\n", rc);
+ goto error;
+ }
- rc = mdss_mdp_pipe_handoff(pipe);
- if (rc) {
- pr_err("Failed to handoff pipe num %d\n"
- , pipe->num);
- goto error;
- }
-
- rc = mdss_mdp_mixer_handoff(ctl, i, pipe);
- if (rc) {
- pr_err("failed to handoff mixer num %d\n"
- , i);
- goto error;
- }
- }
+ if (mfd->split_display) {
+ sctl = mdss_mdp_get_split_ctl(ctl);
+ if (!sctl) {
+ pr_err("cannot get secondary ctl. fail the handoff\n");
+ rc = -EPERM;
+ goto error;
+ }
+ rc = __mdss_mdp_ctl_handoff(sctl, mdata);
+ if (rc) {
+ pr_err("secondary ctl handoff failed. rc=%d\n", rc);
+ goto error;
}
}
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index 70b3eef..3ab25c3 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -51,10 +51,10 @@
* bound (greatest lower bound)
*/
#define DEVFREQ_FLAG_LEAST_UPPER_BOUND 0x1
-#define DEVFREQ_FLAG_WAKEUP_MAXFREQ 0x2
-#define DEVFREQ_FLAG_FAST_HINT 0x2
-#define DEVFREQ_FLAG_SLOW_HINT 0x4
+#define DEVFREQ_FLAG_FAST_HINT 0x2
+#define DEVFREQ_FLAG_SLOW_HINT 0x4
+#define DEVFREQ_FLAG_WAKEUP_MAXFREQ 0x8
/**
* struct devfreq_governor_data - mapping to per device governor data
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index bff056d..e53c350 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -197,6 +197,12 @@
void *handler_priv;
};
+enum dev_state {
+ DEV_SUSPENDING = 1,
+ DEV_SUSPENDED,
+ DEV_RESUMED,
+};
+
struct mmc_host {
struct device *parent;
struct device class_dev;
@@ -421,6 +427,7 @@
struct delayed_work work;
enum mmc_load state;
} clk_scaling;
+ enum dev_state dev_status;
unsigned long private[0] ____cacheline_aligned;
};
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 08ca341..65a3fe1 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -160,6 +160,11 @@
*/
#define SDHCI_QUIRK2_DIVIDE_TOUT_BY_4 (1 << 9)
+/*
+ * Some SDHC controllers are unable to handle data-end bit error in
+ * 1-bit mode of SDIO.
+ */
+#define SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR (1<<9)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
@@ -252,6 +257,9 @@
struct mutex ios_mutex;
enum sdhci_power_policy power_policy;
+ bool irq_enabled; /* host irq status flag */
+ bool async_int_supp; /* async support to rxv int, when clks are off */
+ bool disable_sdio_irq_deferred; /* status of disabling sdio irq */
u32 auto_cmd_err_sts;
unsigned long private[0] ____cacheline_aligned;
};
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 50660b3..dce56a6 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -131,6 +131,8 @@
* most likely due to retrans in 3WHS.
*/
+#define TCP_DELACK_SEG 1 /*Number of full MSS to receive before Acking RFC2581*/
+
#define TCP_RESOURCE_PROBE_INTERVAL ((unsigned)(HZ/2U)) /* Maximal interval between probes
* for local resources.
*/
@@ -253,6 +255,10 @@
extern int sysctl_tcp_thin_linear_timeouts;
extern int sysctl_tcp_thin_dupack;
+/* sysctl variables for controlling various tcp parameters */
+extern int sysctl_tcp_delack_seg;
+extern int sysctl_tcp_use_userconfig;
+
extern atomic_long_t tcp_memory_allocated;
extern struct percpu_counter tcp_sockets_allocated;
extern int tcp_memory_pressure;
@@ -346,6 +352,10 @@
struct pipe_inode_info *pipe, size_t len,
unsigned int flags);
+extern int tcp_use_userconfig_sysctl_handler(struct ctl_table *, int,
+ void __user *, size_t *, loff_t *);
+extern int tcp_proc_delayed_ack_control(struct ctl_table *, int,
+ void __user *, size_t *, loff_t *);
static inline void tcp_dec_quickack_mode(struct sock *sk,
const unsigned int pkts)
{
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 7a7724d..6bd622f 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -36,6 +36,10 @@
static int ip_ttl_max = 255;
static int ip_ping_group_range_min[] = { 0, 0 };
static int ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX };
+static int tcp_delack_seg_min = TCP_DELACK_MIN;
+static int tcp_delack_seg_max = 60;
+static int tcp_use_userconfig_min;
+static int tcp_use_userconfig_max = 1;
/* Update system visible IP port range */
static void set_local_port_range(int range[2])
@@ -699,6 +703,25 @@
.proc_handler = proc_dointvec_minmax,
.extra1 = &zero
},
+ {
+ .procname = "tcp_delack_seg",
+ .data = &sysctl_tcp_delack_seg,
+ .maxlen = sizeof(sysctl_tcp_delack_seg),
+ .mode = 0644,
+ .proc_handler = tcp_proc_delayed_ack_control,
+ .extra1 = &tcp_delack_seg_min,
+ .extra2 = &tcp_delack_seg_max,
+ },
+ {
+ .procname = "tcp_use_userconfig",
+ .data = &sysctl_tcp_use_userconfig,
+ .maxlen = sizeof(sysctl_tcp_use_userconfig),
+ .mode = 0644,
+ .proc_handler = tcp_use_userconfig_sysctl_handler,
+ .extra1 = &tcp_use_userconfig_min,
+ .extra2 = &tcp_use_userconfig_max,
+ },
+
{ }
};
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 74a286c..706899e 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -294,6 +294,12 @@
EXPORT_SYMBOL(sysctl_tcp_rmem);
EXPORT_SYMBOL(sysctl_tcp_wmem);
+int sysctl_tcp_delack_seg __read_mostly = TCP_DELACK_SEG;
+EXPORT_SYMBOL(sysctl_tcp_delack_seg);
+
+int sysctl_tcp_use_userconfig __read_mostly;
+EXPORT_SYMBOL(sysctl_tcp_use_userconfig);
+
atomic_long_t tcp_memory_allocated; /* Current allocated memory. */
EXPORT_SYMBOL(tcp_memory_allocated);
@@ -1213,8 +1219,11 @@
/* Delayed ACKs frequently hit locked sockets during bulk
* receive. */
if (icsk->icsk_ack.blocked ||
- /* Once-per-two-segments ACK was not sent by tcp_input.c */
- tp->rcv_nxt - tp->rcv_wup > icsk->icsk_ack.rcv_mss ||
+ /* Once-per-sysctl_tcp_delack_seg segments
+ * ACK was not sent by tcp_input.c
+ */
+ tp->rcv_nxt - tp->rcv_wup > (icsk->icsk_ack.rcv_mss) *
+ sysctl_tcp_delack_seg ||
/*
* If this read emptied read buffer, we send ACK, if
* connection is not bidirectional, user drained
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 257b617..7c3612b 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5047,7 +5047,8 @@
struct tcp_sock *tp = tcp_sk(sk);
/* More than one full frame received... */
- if (((tp->rcv_nxt - tp->rcv_wup) > inet_csk(sk)->icsk_ack.rcv_mss &&
+ if (((tp->rcv_nxt - tp->rcv_wup) > (inet_csk(sk)->icsk_ack.rcv_mss) *
+ sysctl_tcp_delack_seg &&
/* ... and right edge of window advances far enough.
* (tcp_recvmsg() will send ACK otherwise). Or...
*/
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 34d4a02..d1b4792 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -34,7 +34,39 @@
static void tcp_write_timer(unsigned long);
static void tcp_delack_timer(unsigned long);
-static void tcp_keepalive_timer (unsigned long data);
+static void tcp_keepalive_timer(unsigned long data);
+
+/*Function to reset tcp_ack related sysctl on resetting master control */
+void set_tcp_default(void)
+{
+ sysctl_tcp_delack_seg = TCP_DELACK_SEG;
+}
+
+/*sysctl handler for tcp_ack realted master control */
+int tcp_proc_delayed_ack_control(ctl_table *table, int write,
+ void __user *buffer, size_t *length, loff_t *ppos)
+{
+ int ret = proc_dointvec_minmax(table, write, buffer, length, ppos);
+
+ /* The ret value will be 0 if the input validation is successful
+ * and the values are written to sysctl table. If not, the stack
+ * will continue to work with currently configured values
+ */
+ return ret;
+}
+
+/*sysctl handler for tcp_ack realted master control */
+int tcp_use_userconfig_sysctl_handler(ctl_table *table, int write,
+ void __user *buffer, size_t *length, loff_t *ppos)
+{
+ int ret = proc_dointvec_minmax(table, write, buffer, length, ppos);
+
+ if (write && ret == 0) {
+ if (!sysctl_tcp_use_userconfig)
+ set_tcp_default();
+ }
+ return ret;
+}
void tcp_init_xmit_timers(struct sock *sk)
{
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index b72590f..7b1a04e 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -6797,18 +6797,21 @@
snd_soc_card_change_online_state(codec->card, 1);
mutex_lock(&codec->mutex);
- if (codec->reg_def_copy) {
- pr_debug("%s: Update ASOC cache", __func__);
- kfree(codec->reg_cache);
- codec->reg_cache = kmemdup(codec->reg_def_copy,
- codec->reg_size, GFP_KERNEL);
- }
+
+ taiko_update_reg_defaults(codec);
+ if (wcd9xxx->mclk_rate == TAIKO_MCLK_CLK_12P288MHZ)
+ snd_soc_update_bits(codec, TAIKO_A_CHIP_CTL, 0x06, 0x0);
+ else if (wcd9xxx->mclk_rate == TAIKO_MCLK_CLK_9P6MHZ)
+ snd_soc_update_bits(codec, TAIKO_A_CHIP_CTL, 0x06, 0x2);
+ taiko_codec_init_reg(codec);
if (spkr_drv_wrnd == 1)
snd_soc_update_bits(codec, TAIKO_A_SPKR_DRV_EN, 0x80, 0x80);
- taiko_update_reg_defaults(codec);
- taiko_codec_init_reg(codec);
+ codec->cache_sync = true;
+ snd_soc_cache_sync(codec);
+ codec->cache_sync = false;
+
ret = taiko_handle_pdata(taiko);
if (IS_ERR_VALUE(ret))
pr_err("%s: bad pdata\n", __func__);
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index 8e69a2b..02ced89 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -3865,6 +3865,8 @@
{"VOC_EXT_EC MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
{"VOC_EXT_EC MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
{"CS-VOICE_UL1", NULL, "VOC_EXT_EC MUX"},
+ {"VOIP_UL", NULL, "VOC_EXT_EC MUX"},
+ {"VoLTE_UL", NULL, "VOC_EXT_EC MUX"},
{"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
{"Voice_Tx Mixer", "PRI_MI2S_TX_Voice", "PRI_MI2S_TX"},
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index badc3c3..48bf2f4 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -3748,6 +3748,11 @@
mutex_lock(&port->lock);
dsp_buf = port->dsp_buf;
+ if (port->buf == NULL) {
+ pr_err("%s buf is NULL\n", __func__);
+ mutex_unlock(&port->lock);
+ return -EINVAL;
+ }
ab = &port->buf[dsp_buf];
pr_debug("%s:session[%d]dsp-buf[%d][%p]cpu_buf[%d][%p]\n",