Merge "msm: mdss: Support GCDB on 8x10 target"
diff --git a/Documentation/devicetree/bindings/input/misc/stk-stk3x1x.txt b/Documentation/devicetree/bindings/input/misc/stk-stk3x1x.txt
index aa50140..59136ee 100644
--- a/Documentation/devicetree/bindings/input/misc/stk-stk3x1x.txt
+++ b/Documentation/devicetree/bindings/input/misc/stk-stk3x1x.txt
@@ -25,6 +25,10 @@
- stk,ps-thdl : Low threshold for proximity sensor, sensor will report
"far" if the proximity sensor reading is larger than this value.
+Optional properties:
+
+ - stk,use-fir : Boolean to enable light data jitter suppressing FIR filter.
+
Example:
i2c@f9925000 {
stk@48 {
@@ -43,4 +47,5 @@
stk,wait-reg = <0x07>;
stk,ps-thdh = <1700>;
stk,ps-thdl = <1500>;
+ stk,use-fir;
};
diff --git a/Documentation/devicetree/bindings/power/qpnp-bms.txt b/Documentation/devicetree/bindings/power/qpnp-bms.txt
index 1162298..b076475 100644
--- a/Documentation/devicetree/bindings/power/qpnp-bms.txt
+++ b/Documentation/devicetree/bindings/power/qpnp-bms.txt
@@ -13,7 +13,15 @@
to determine whether the BMS is using an internal or external
rsense to accumulate the Coulomb Counter and read current.
-Additionally, an optional subnode may be included:
+Additionally, optional subnodes may be included:
+- qcom,batt-pres-status : A subnode with a register address for the SMBB
+ battery interface's BATT_PRES_STATUS register. If this node is
+ added, then the BMS will try to detect offmode battery removal
+ via the battery interface's offmode battery removal circuit.
+- qcom,soc-storage-reg : A subnode with a register address to a spare PMIC
+ register. If this node is included, then the BMS will store its
+ shutdown SOC in the specified register instead of the default
+ BMS data register.
- qcom,battery-data : A phandle to a node containing the available batterydata
profiles. See the batterydata bindings documentation for more
details.
@@ -75,8 +83,8 @@
- qcom,min-fcc-learning-soc: An interger value which defines the minimum SOC
to start FCC learning. This is applicable only if
FCC learning is enabled.
-- qcom,min-fcc-ocv-pc: An interger value which defines the minimum PC-lookup(OCV)
- to start FCC learning. This is applicable only if
+- qcom,min-fcc-ocv-pc: An interger value which defines the minimum PC-lookup
+ (OCV) to start FCC learning. This is applicable only if
FCC learning is enabled.
- qcom,min-fcc-learning-samples: An interger value which defines the minimum
number of the FCC measurement cycles required to
@@ -103,14 +111,23 @@
- qcom,use-ocv-thresholds : A boolean that controls whether BMS will take
new OCVs only between the defined thresholds.
- qcom,enable-fcc-learning: A boolean that defines if FCC learning is enabled.
-- qcom,bms-adc_tm: Corresponding ADC_TM device's phandle to set recurring measurements
- and receive notifications for die_temperature, vbatt.
+- qcom,bms-adc_tm: Corresponding ADC_TM device's phandle to set recurring
+ measurements and receive notifications for
+ die_temperature and vbatt.
+qcom,batt-pres-status node required properties:
+- reg : offset and length of the PMIC SMBB battery interface BATT_PRES_STATUS
+ register.
-All sub node required properties:
+qcom,soc-storage-reg node required properties:
+- reg : address and length of the spare PMIC register that the BMS will store
+ shutdown SoC in.
+
+qcom,bms-iadc node required properties:
- reg : offset and length of the PMIC peripheral register map.
qcom,bms-bms node required properties:
+- reg : offset and length of the PMIC peripheral register map.
- interrupts : the interrupt mappings.
The format should be
<slave-id peripheral-id interrupt-number>.
@@ -156,6 +173,10 @@
qcom,bms-iadc = <&pm8941_iadc>;
qcom,bms-adc_tm = <&pm8941_adc_tm>;
+ qcom,batt-pres-status@1208 {
+ reg = <0x1208 0x1>;
+ }
+
qcom,bms-iadc@3800 {
reg = <0x3800 0x100>;
};
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index f2a9940..53e912a 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -52,6 +52,10 @@
- qcom,dwc_usb3-adc_tm: Corresponding ADC_TM device's phandle to set recurring
measurements on USB_ID channel when using ADC and receive
notifications for set thresholds.
+- qcom,dwc-usb3-msm-tx-fifo-size: If present, represents RAM size available for
+ TX fifo allocation in bytes
+- qcom,dwc-usb3-msm-qdss-tx-fifo-size: If present, represent RAM size available
+ for TX fifo allocation in QDSS composition
Sub nodes:
- Sub node for "DWC3- USB3 controller".
@@ -76,6 +80,8 @@
qcom,vdd-voltage-level = <1 5 7>;
qcom,dwc-hsphy-init = <0x00D195A4>;
qcom,dwc_usb3-adc_tm = <&pm8941_adc_tm>;
+ qcom,dwc-usb3-msm-tx-fifo-size = <29696>;
+ qcom,dwc-usb3-msm-qdss-tx-fifo-size = <16384>;
qcom,msm_bus,name = "usb3";
qcom,msm_bus,num_cases = <2>;
diff --git a/arch/arm/boot/dts/msm8610-qrd-skuab.dtsi b/arch/arm/boot/dts/msm8610-qrd-skuab.dtsi
index 649bc71..ff70c25 100644
--- a/arch/arm/boot/dts/msm8610-qrd-skuab.dtsi
+++ b/arch/arm/boot/dts/msm8610-qrd-skuab.dtsi
@@ -82,6 +82,7 @@
stk,wait-reg = <0x07>;
stk,ps-thdh = <150>;
stk,ps-thdl = <100>;
+ stk,use-fir;
};
};
diff --git a/arch/arm/boot/dts/msm8610-v2-pm.dtsi b/arch/arm/boot/dts/msm8610-v2-pm.dtsi
index e593b31..257d7a4 100644
--- a/arch/arm/boot/dts/msm8610-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8610-v2-pm.dtsi
@@ -212,45 +212,43 @@
qcom,gpio-parent = <&msmgpio>;
qcom,gpio-map = <3 1>,
- <4 4 >,
- <5 5 >,
- <6 9 >,
- <7 13>,
- <8 17>,
- <9 21>,
- <10 27>,
- <11 29>,
- <12 31>,
- <13 33>,
- <14 35>,
- <15 37>,
- <16 38>,
- <17 39>,
- <18 41>,
- <19 46>,
- <20 48>,
- <21 49>,
- <22 50>,
- <23 51>,
- <24 52>,
- <25 54>,
- <26 62>,
- <27 63>,
- <28 64>,
- <29 65>,
- <30 66>,
- <31 67>,
- <32 68>,
- <33 69>,
- <34 71>,
- <35 72>,
- <36 106>,
- <37 107>,
- <38 108>,
- <39 109>,
- <40 110>,
- <54 111>,
- <55 113>;
+ <4 5 >,
+ <5 9 >,
+ <6 14>,
+ <7 15>,
+ <8 32>,
+ <9 33>,
+ <10 34>,
+ <11 35>,
+ <12 41>,
+ <13 42>,
+ <14 72>,
+ <15 73>,
+ <16 74>,
+ <17 75>,
+ <18 76>,
+ <19 77>,
+ <20 78>,
+ <21 79>,
+ <22 80>,
+ <23 81>,
+ <24 82>,
+ <25 83>,
+ <26 84>,
+ <27 85>,
+ <28 87>,
+ <29 90>,
+ <30 91>,
+ <31 92>,
+ <32 93>,
+ <33 94>,
+ <34 95>,
+ <35 96>,
+ <36 97>,
+ <37 98>,
+ <38 99>,
+ <39 100>,
+ <40 101>;
};
qcom,pm-8x60@fe805664 {
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index cb53a2f..65307ae 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -912,6 +912,8 @@
qcom,dwc-hsphy-init = <0x00D191A4>;
qcom,misc-ref = <&pm8941_misc>;
dwc_usb3-adc_tm = <&pm8941_adc_tm>;
+ qcom,dwc-usb3-msm-tx-fifo-size = <29696>;
+ qcom,dwc-usb3-msm-qdss-tx-fifo-size = <16384>;
qcom,msm-bus,name = "usb3";
qcom,msm-bus,num-cases = <2>;
@@ -925,6 +927,7 @@
interrupt-parent = <&intc>;
interrupts = <0 131 0>, <0 179 0>;
interrupt-names = "irq", "otg_irq";
+ tx-fifo-resize;
};
};
@@ -1480,7 +1483,7 @@
qcom,src-bam-pipe-index = <0>;
qcom,dst-bam-physical-address = <0xf9304000>;
qcom,dst-bam-pipe-index = <2>;
- qcom,data-fifo-offset = <0xf0000>;
+ qcom,data-fifo-offset = <0xf2000>;
qcom,data-fifo-size = <0x1800>;
qcom,descriptor-fifo-offset = <0xf4000>;
qcom,descriptor-fifo-size = <0x1400>;
diff --git a/arch/arm/mach-msm/clock-8226.c b/arch/arm/mach-msm/clock-8226.c
index b72cdab..ba792ab 100644
--- a/arch/arm/mach-msm/clock-8226.c
+++ b/arch/arm/mach-msm/clock-8226.c
@@ -1603,7 +1603,15 @@
{ &gcc_ce1_axi_clk.c, GCC_BASE, 0x0139 },
{ &gcc_ce1_ahb_clk.c, GCC_BASE, 0x013a },
{ &gcc_lpass_q6_axi_clk.c, GCC_BASE, 0x0160 },
- {&dummy_clk, N_BASES, 0x0000},
+ { &pnoc_clk.c, GCC_BASE, 0x010},
+ { &snoc_clk.c, GCC_BASE, 0x000},
+ { &cnoc_clk.c, GCC_BASE, 0x008},
+ /*
+ * measure the gcc_bimc_kpss_axi_clk instead to account for the DDR
+ * rate being gcc_bimc_clk/2.
+ */
+ { &bimc_clk.c, GCC_BASE, 0x155},
+ { &dummy_clk, N_BASES, 0x0000},
};
static struct pll_vote_clk mmpll0_pll = {
@@ -2725,6 +2733,7 @@
{ &camss_csi1rdi_clk.c, MMSS_BASE, 0x0049 },
{ &camss_csi1pix_clk.c, MMSS_BASE, 0x004a },
{ &camss_ispif_ahb_clk.c, MMSS_BASE, 0x0055 },
+ { &mmssnoc_ahb_clk.c, MMSS_BASE, 0x0001 },
{&dummy_clk, N_BASES, 0x0000},
};
diff --git a/arch/arm/mach-msm/peripheral-loader.c b/arch/arm/mach-msm/peripheral-loader.c
index 94281b1..157dc01 100644
--- a/arch/arm/mach-msm/peripheral-loader.c
+++ b/arch/arm/mach-msm/peripheral-loader.c
@@ -482,6 +482,9 @@
if (ret)
return ret;
+ pil_info(desc, "loading from %pa to %pa\n", &priv->region_start,
+ &priv->region_end);
+
for (i = 0; i < mdt->hdr.e_phnum; i++) {
phdr = &mdt->phdr[i];
if (!segment_is_loadable(phdr))
diff --git a/drivers/input/misc/mma8x5x.c b/drivers/input/misc/mma8x5x.c
index 8775ab9..f49ff14 100644
--- a/drivers/input/misc/mma8x5x.c
+++ b/drivers/input/misc/mma8x5x.c
@@ -43,7 +43,7 @@
#define POLL_INTERVAL 100 /* msecs */
/* if sensor is standby ,set POLL_STOP_TIME to slow down the poll */
-#define POLL_STOP_TIME 200
+#define POLL_STOP_TIME 10000
#define INPUT_FUZZ 32
#define INPUT_FLAT 32
#define INPUT_DATA_DIVIDER 16
diff --git a/drivers/input/misc/stk3x1x.c b/drivers/input/misc/stk3x1x.c
index 61f3530..937bf6c 100644
--- a/drivers/input/misc/stk3x1x.c
+++ b/drivers/input/misc/stk3x1x.c
@@ -170,7 +170,7 @@
#define MIN_ALS_POLL_DELAY_NS 110000000
#define DEVICE_NAME "stk_ps"
-#define ALS_NAME "lightsensor-level"
+#define ALS_NAME "stk3x1x-ls"
#define PS_NAME "proximity"
/* POWER SUPPLY VOLTAGE RANGE */
@@ -179,6 +179,15 @@
#define STK3X1X_VIO_MIN_UV 1750000
#define STK3X1X_VIO_MAX_UV 1950000
+#define STK_FIR_LEN 16
+#define MAX_FIR_LEN 32
+struct data_filter {
+ u16 raw[MAX_FIR_LEN];
+ int sum;
+ int number;
+ int idx;
+};
+
struct stk3x1x_data {
struct i2c_client *client;
#if (!defined(STK_POLL_PS) || !defined(STK_POLL_ALS))
@@ -218,6 +227,9 @@
struct regulator *vdd;
struct regulator *vio;
bool power_enabled;
+ bool use_fir;
+ struct data_filter fir;
+ atomic_t firlength;
};
#if( !defined(CONFIG_STK_PS_ALS_USE_CHANGE_THRESHOLD))
@@ -387,6 +399,13 @@
printk(KERN_ERR "%s: write i2c error\n", __func__);
return ret;
}
+
+ ret = i2c_smbus_write_byte_data(ps_data->client, 0x87, 0x60);
+ if (ret < 0) {
+ dev_err(&ps_data->client->dev,
+ "%s: write i2c error\n", __func__);
+ return ret;
+ }
return 0;
}
@@ -708,9 +727,32 @@
return ret;
}
+static inline int32_t stk3x1x_filter_reading(struct stk3x1x_data *ps_data,
+ int32_t word_data)
+{
+ int index;
+ int firlen = atomic_read(&ps_data->firlength);
+
+ if (ps_data->fir.number < firlen) {
+ ps_data->fir.raw[ps_data->fir.number] = word_data;
+ ps_data->fir.sum += word_data;
+ ps_data->fir.number++;
+ ps_data->fir.idx++;
+ } else {
+ index = ps_data->fir.idx % firlen;
+ ps_data->fir.sum -= ps_data->fir.raw[index];
+ ps_data->fir.raw[index] = word_data;
+ ps_data->fir.sum += word_data;
+ ps_data->fir.idx++;
+ word_data = ps_data->fir.sum/firlen;
+ }
+ return word_data;
+}
+
static inline int32_t stk3x1x_get_als_reading(struct stk3x1x_data *ps_data)
{
int32_t word_data, tmp_word_data;
+
tmp_word_data = i2c_smbus_read_word_data(ps_data->client, STK_DATA1_ALS_REG);
if(tmp_word_data < 0)
{
@@ -718,6 +760,9 @@
return tmp_word_data;
}
word_data = ((tmp_word_data & 0xFF00) >> 8) | ((tmp_word_data & 0x00FF) << 8) ;
+ if (ps_data->use_fir)
+ word_data = stk3x1x_filter_reading(ps_data, word_data);
+
return word_data;
}
@@ -912,7 +957,12 @@
return scnprintf(buf, PAGE_SIZE, "%lld\n", ktime_to_ns(ps_data->als_poll_delay));
}
-
+static inline void stk_als_delay_store_fir(struct stk3x1x_data *ps_data)
+{
+ ps_data->fir.number = 0;
+ ps_data->fir.idx = 0;
+ ps_data->fir.sum = 0;
+}
static ssize_t stk_als_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
uint64_t value = 0;
@@ -936,6 +986,10 @@
mutex_lock(&ps_data->io_lock);
if(value != ktime_to_ns(ps_data->als_poll_delay))
ps_data->als_poll_delay = ns_to_ktime(value);
+
+ if (ps_data->use_fir)
+ stk_als_delay_store_fir(ps_data);
+
mutex_unlock(&ps_data->io_lock);
return size;
}
@@ -948,6 +1002,77 @@
return scnprintf(buf, PAGE_SIZE, "%d\n", reading);
}
+static ssize_t stk_als_firlen_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ int len = atomic_read(&ps_data->firlength);
+
+ dev_dbg(dev, "%s: len = %2d, idx = %2d\n",
+ __func__, len, ps_data->fir.idx);
+ dev_dbg(dev, "%s: sum = %5d, ave = %5d\n",
+ __func__, ps_data->fir.sum, ps_data->fir.sum/len);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", len);
+}
+
+static ssize_t stk_als_firlen_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ uint64_t value = 0;
+ int ret;
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ ret = kstrtoull(buf, 10, &value);
+ if (ret < 0) {
+ dev_err(dev, "%s:strict_strtoull failed, ret=0x%x\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (value > MAX_FIR_LEN) {
+ dev_err(dev, "%s: firlen exceed maximum filter length\n",
+ __func__);
+ } else if (value < 1) {
+ atomic_set(&ps_data->firlength, 1);
+ memset(&ps_data->fir, 0x00, sizeof(ps_data->fir));
+ } else {
+ atomic_set(&ps_data->firlength, value);
+ memset(&ps_data->fir, 0x00, sizeof(ps_data->fir));
+ }
+ return size;
+}
+
+static ssize_t stk_als_fir_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", ps_data->use_fir);
+}
+
+static ssize_t stk_als_fir_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ uint64_t value = 0;
+ int ret;
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ ret = kstrtoull(buf, 10, &value);
+ if (ret < 0) {
+ dev_err(dev, "%s:strict_strtoull failed, ret=0x%x\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (value) {
+ ps_data->use_fir = true;
+ memset(&ps_data->fir, 0x00, sizeof(ps_data->fir));
+ } else {
+ ps_data->use_fir = false;
+ }
+ return size;
+}
static ssize_t stk_ps_code_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
@@ -1432,6 +1557,11 @@
static struct device_attribute als_poll_delay_attribute =
__ATTR(poll_delay, 0664, stk_als_delay_show, stk_als_delay_store);
static struct device_attribute als_ir_code_attribute = __ATTR(ircode,0444,stk_als_ir_code_show,NULL);
+static struct device_attribute als_firlen_attribute =
+ __ATTR(firlen, 0664, stk_als_firlen_show, stk_als_firlen_store);
+static struct device_attribute als_fir_enable_attribute =
+ __ATTR(fir_enable, 0664, stk_als_fir_enable_show,
+ stk_als_fir_enable_store);
static struct attribute *stk_als_attrs [] =
{
@@ -1441,6 +1571,8 @@
&als_transmittance_attribute.attr,
&als_poll_delay_attribute.attr,
&als_ir_code_attribute.attr,
+ &als_firlen_attribute.attr,
+ &als_fir_enable_attribute.attr,
NULL
};
@@ -1673,6 +1805,13 @@
return IRQ_HANDLED;
}
#endif /* #if (!defined(STK_POLL_PS) || !defined(STK_POLL_ALS)) */
+
+static inline void stk3x1x_init_fir(struct stk3x1x_data *ps_data)
+{
+ memset(&ps_data->fir, 0x00, sizeof(ps_data->fir));
+ atomic_set(&ps_data->firlength, STK_FIR_LEN);
+}
+
static int32_t stk3x1x_init_all_setting(struct i2c_client *client, struct stk3x1x_platform_data *plat_data)
{
int32_t ret;
@@ -1697,6 +1836,10 @@
#ifndef CONFIG_STK_PS_ALS_USE_CHANGE_THRESHOLD
stk_init_code_threshold_table(ps_data);
#endif
+
+ if (plat_data->use_fir)
+ stk3x1x_init_fir(ps_data);
+
return 0;
}
@@ -1993,6 +2136,8 @@
return rc;
}
+ pdata->use_fir = of_property_read_bool(np, "stk,use-fir");
+
return 0;
}
#else
@@ -2059,6 +2204,7 @@
}
ps_data->als_transmittance = plat_data->transmittance;
ps_data->int_pin = plat_data->int_pin;
+ ps_data->use_fir = plat_data->use_fir;
if (ps_data->als_transmittance == 0) {
dev_err(&client->dev,
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index 8bb4f90..4be727f 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -356,7 +356,7 @@
/*
* Enable USB PRIVATE RAM to be used for BAM FIFOs
* HSUSB: Only RAM13 is used for BAM FIFOs
- * SSUSB: RAM11, 12, 13 are used for BAM FIFOs
+ * SSUSB: RAM12, 13 are used for BAM FIFOs
*/
bam = pipe_connect->bam_type;
if (bam < 0)
@@ -365,7 +365,7 @@
if (bam == HSUSB_BAM)
ram1_value = 0x4;
else
- ram1_value = 0x7;
+ ram1_value = 0x6;
pr_debug("Writing 0x%x to QSCRATCH_RAM1\n", ram1_value);
writel_relaxed(ram1_value, ctx.qscratch_ram1_reg);
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index 38fb895..6706e0d 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -83,7 +83,7 @@
#define IAVG_STEP_SIZE_MA 50
#define IAVG_START 600
#define IAVG_INVALID 0xFF
-#define SOC_INVALID 0xFF
+#define SOC_INVALID 0x7E
#define IAVG_SAMPLES 16
@@ -147,6 +147,8 @@
wait_queue_head_t bms_wait_queue;
u16 base;
u16 iadc_base;
+ u16 batt_pres_addr;
+ u16 soc_storage_addr;
u8 revision1;
u8 revision2;
@@ -1510,24 +1512,6 @@
pr_debug("UUC = %uuAh\n", params->uuc_uah);
}
-static bool is_shutdown_soc_within_limits(struct qpnp_bms_chip *chip, int soc)
-{
- if (chip->shutdown_soc_invalid) {
- pr_debug("NOT forcing shutdown soc = %d\n", chip->shutdown_soc);
- return 0;
- }
-
- if (abs(chip->shutdown_soc - soc) > chip->shutdown_soc_valid_limit) {
- pr_debug("rejecting shutdown soc = %d, soc = %d limit = %d\n",
- chip->shutdown_soc, soc,
- chip->shutdown_soc_valid_limit);
- chip->shutdown_soc_invalid = true;
- return 0;
- }
-
- return 1;
-}
-
static int bound_soc(int soc)
{
soc = max(0, soc);
@@ -1614,6 +1598,7 @@
module_param_cb(bms_reset, &bms_reset_ops, &bms_reset, 0644);
+#define SOC_STORAGE_MASK 0xFE
static void backup_soc_and_iavg(struct qpnp_bms_chip *chip, int batt_temp,
int soc)
{
@@ -1626,15 +1611,12 @@
else
temp = 0;
- rc = qpnp_write_wrapper(chip, &temp,
- chip->base + IAVG_STORAGE_REG, 1);
-
- temp = soc;
+ rc = qpnp_write_wrapper(chip, &temp, chip->base + IAVG_STORAGE_REG, 1);
/* don't store soc if temperature is below 5degC */
if (batt_temp > IGNORE_SOC_TEMP_DECIDEG)
- rc = qpnp_write_wrapper(chip, &temp,
- chip->base + SOC_STORAGE_REG, 1);
+ qpnp_masked_write_base(chip, chip->soc_storage_addr,
+ SOC_STORAGE_MASK, (soc + 1) << 1);
}
static int scale_soc_while_chg(struct qpnp_bms_chip *chip, int chg_time_sec,
@@ -1822,6 +1804,7 @@
int vbat_uv, int ibat_ua, int batt_temp)
{
int chg_soc, soc_ibat, batt_terminal_uv, weight_ibat, weight_cc;
+ int new_ocv_uv;
batt_terminal_uv = vbat_uv + (ibat_ua * chip->r_conn_mohm) / 1000;
@@ -1873,8 +1856,6 @@
/* always report a higher soc */
if (chg_soc > chip->prev_chg_soc) {
- int new_ocv_uv;
-
chip->prev_chg_soc = chg_soc;
find_ocv_for_soc(chip, params, batt_temp, chg_soc, &new_ocv_uv);
@@ -2186,37 +2167,22 @@
(uint16_t)ocv_raw);
}
-#define SLEEP_RECALC_INTERVAL 3
-static int calculate_state_of_charge(struct qpnp_bms_chip *chip,
+static int calculate_raw_soc(struct qpnp_bms_chip *chip,
struct raw_soc_params *raw,
+ struct soc_params *params,
int batt_temp)
{
- int soc, new_ocv_uv, previous_soc;
- int shutdown_soc, new_calculated_soc, remaining_usable_charge_uah;
- struct soc_params params;
+ int soc, new_ocv_uv;
+ int remaining_usable_charge_uah;
- if (!is_battery_present(chip)) {
- pr_debug("battery gone, reporting 100\n");
- new_calculated_soc = 100;
- goto done_calculating;
- }
- calculate_soc_params(chip, raw, ¶ms, batt_temp);
/* calculate remaining usable charge */
- remaining_usable_charge_uah = params.ocv_charge_uah
- - params.cc_uah
- - params.uuc_uah;
-
+ remaining_usable_charge_uah = params->ocv_charge_uah
+ - params->cc_uah
+ - params->uuc_uah;
pr_debug("RUC = %duAh\n", remaining_usable_charge_uah);
- if (params.fcc_uah - params.uuc_uah <= 0) {
- pr_debug("FCC = %duAh, UUC = %duAh forcing soc = 0\n",
- params.fcc_uah,
- params.uuc_uah);
- new_calculated_soc = 0;
- goto done_calculating;
- }
soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100),
- (params.fcc_uah - params.uuc_uah));
+ (params->fcc_uah - params->uuc_uah));
if (chip->first_time_calc_soc && soc < 0) {
/*
@@ -2224,16 +2190,16 @@
* in a bad soc. Adjust ocv to get 0 soc
*/
pr_debug("soc is %d, adjusting pon ocv to make it 0\n", soc);
- find_ocv_for_soc(chip, ¶ms, batt_temp, 0, &new_ocv_uv);
+ find_ocv_for_soc(chip, params, batt_temp, 0, &new_ocv_uv);
chip->last_ocv_uv = new_ocv_uv;
- remaining_usable_charge_uah = params.ocv_charge_uah
- - params.cc_uah
- - params.uuc_uah;
+ remaining_usable_charge_uah = params->ocv_charge_uah
+ - params->cc_uah
+ - params->uuc_uah;
soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100),
- (params.fcc_uah
- - params.uuc_uah));
+ (params->fcc_uah
+ - params->uuc_uah));
pr_debug("DONE for O soc is %d, pon ocv adjusted to %duV\n",
soc, chip->last_ocv_uv);
}
@@ -2244,20 +2210,49 @@
if (soc < 0) {
pr_debug("bad rem_usb_chg = %d rem_chg %d, cc_uah %d, unusb_chg %d\n",
remaining_usable_charge_uah,
- params.ocv_charge_uah,
- params.cc_uah, params.uuc_uah);
+ params->ocv_charge_uah,
+ params->cc_uah, params->uuc_uah);
pr_debug("for bad rem_usb_chg last_ocv_uv = %d batt_temp = %d fcc = %d soc =%d\n",
chip->last_ocv_uv, batt_temp,
- params.fcc_uah, soc);
+ params->fcc_uah, soc);
soc = 0;
}
+ return soc;
+}
+
+#define SLEEP_RECALC_INTERVAL 3
+static int calculate_state_of_charge(struct qpnp_bms_chip *chip,
+ struct raw_soc_params *raw,
+ int batt_temp)
+{
+ struct soc_params params;
+ int soc, previous_soc, shutdown_soc, new_calculated_soc;
+ int remaining_usable_charge_uah, new_ocv_uv;
+
+ calculate_soc_params(chip, raw, ¶ms, batt_temp);
+ if (!is_battery_present(chip)) {
+ pr_debug("battery gone, reporting 100\n");
+ new_calculated_soc = 100;
+ goto done_calculating;
+ }
+
+ if (params.fcc_uah - params.uuc_uah <= 0) {
+ pr_debug("FCC = %duAh, UUC = %duAh forcing soc = 0\n",
+ params.fcc_uah,
+ params.uuc_uah);
+ new_calculated_soc = 0;
+ goto done_calculating;
+ }
+
+ soc = calculate_raw_soc(chip, raw, ¶ms, batt_temp);
+
mutex_lock(&chip->soc_invalidation_mutex);
shutdown_soc = chip->shutdown_soc;
if (chip->first_time_calc_soc && soc != shutdown_soc
- && is_shutdown_soc_within_limits(chip, soc)) {
+ && !chip->shutdown_soc_invalid) {
/*
* soc for the first time - use shutdown soc
* to adjust pon ocv since it is a small percent away from
@@ -2367,6 +2362,53 @@
return voltage_based_soc;
}
+static int recalculate_raw_soc(struct qpnp_bms_chip *chip)
+{
+ int batt_temp, rc, soc;
+ struct qpnp_vadc_result result;
+ struct raw_soc_params raw;
+ struct soc_params params;
+
+ bms_stay_awake(&chip->soc_wake_source);
+ if (chip->use_voltage_soc) {
+ soc = calculate_soc_from_voltage(chip);
+ } else {
+ if (!chip->batfet_closed)
+ qpnp_iadc_calibrate_for_trim(chip->iadc_dev, true);
+ rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX1_BATT_THERM,
+ &result);
+ if (rc) {
+ pr_err("error reading vadc LR_MUX1_BATT_THERM = %d, rc = %d\n",
+ LR_MUX1_BATT_THERM, rc);
+ soc = chip->calculated_soc;
+ } else {
+ pr_debug("batt_temp phy = %lld meas = 0x%llx\n",
+ result.physical,
+ result.measurement);
+ batt_temp = (int)result.physical;
+
+ mutex_lock(&chip->last_ocv_uv_mutex);
+ read_soc_params_raw(chip, &raw, batt_temp);
+ calculate_soc_params(chip, &raw, ¶ms, batt_temp);
+ if (!is_battery_present(chip)) {
+ pr_debug("battery gone\n");
+ soc = 0;
+ } else if (params.fcc_uah - params.uuc_uah <= 0) {
+ pr_debug("FCC = %duAh, UUC = %duAh forcing soc = 0\n",
+ params.fcc_uah,
+ params.uuc_uah);
+ soc = 0;
+ } else {
+ soc = calculate_raw_soc(chip, &raw,
+ ¶ms, batt_temp);
+ }
+ mutex_unlock(&chip->last_ocv_uv_mutex);
+ }
+ }
+ bms_relax(&chip->soc_wake_source);
+ return soc;
+}
+
static int recalculate_soc(struct qpnp_bms_chip *chip)
{
int batt_temp, rc, soc;
@@ -3234,61 +3276,120 @@
return 0;
}
-static void read_shutdown_soc_and_iavg(struct qpnp_bms_chip *chip)
+static int read_shutdown_iavg_ma(struct qpnp_bms_chip *chip)
{
+ u8 iavg;
int rc;
- u8 temp;
- if (chip->ignore_shutdown_soc) {
- chip->shutdown_soc_invalid = true;
- chip->shutdown_soc = 0;
- chip->shutdown_iavg_ma = 0;
+ rc = qpnp_read_wrapper(chip, &iavg, chip->base + IAVG_STORAGE_REG, 1);
+ if (rc) {
+ pr_err("failed to read addr = %d %d assuming %d\n",
+ chip->base + IAVG_STORAGE_REG, rc,
+ IAVG_START);
+ return IAVG_START;
+ } else if (iavg == IAVG_INVALID) {
+ pr_err("invalid iavg read from BMS1_DATA_REG_1, using %d\n",
+ IAVG_START);
+ return IAVG_START;
} else {
- rc = qpnp_read_wrapper(chip, &temp,
- chip->base + IAVG_STORAGE_REG, 1);
- if (rc) {
- pr_err("failed to read addr = %d %d assuming %d\n",
- chip->base + IAVG_STORAGE_REG, rc,
- IAVG_START);
- chip->shutdown_iavg_ma = IAVG_START;
- } else if (temp == IAVG_INVALID) {
- pr_err("invalid iavg read from BMS1_DATA_REG_1, using %d\n",
- IAVG_START);
- chip->shutdown_iavg_ma = IAVG_START;
- } else {
- if (temp == 0) {
- chip->shutdown_iavg_ma = IAVG_START;
- } else {
- chip->shutdown_iavg_ma = IAVG_START
- + IAVG_STEP_SIZE_MA * (temp + 1);
- }
- }
+ if (iavg == 0)
+ return IAVG_START;
+ else
+ return IAVG_START + IAVG_STEP_SIZE_MA * (iavg + 1);
+ }
+}
- rc = qpnp_read_wrapper(chip, &temp,
- chip->base + SOC_STORAGE_REG, 1);
- if (rc) {
- pr_err("failed to read addr = %d %d\n",
- chip->base + SOC_STORAGE_REG, rc);
- } else {
- chip->shutdown_soc = temp;
+static int read_shutdown_soc(struct qpnp_bms_chip *chip)
+{
+ u8 stored_soc;
+ int rc, shutdown_soc;
- if (chip->shutdown_soc == SOC_INVALID) {
- pr_debug("No shutdown soc available\n");
- chip->shutdown_soc_invalid = true;
- chip->shutdown_iavg_ma = 0;
- }
- }
+ /*
+ * The previous SOC is stored in the first 7 bits of the register as
+ * (Shutdown SOC + 1). This allows for register reset values of both
+ * 0x00 and 0x7F.
+ */
+ rc = qpnp_read_wrapper(chip, &stored_soc, chip->soc_storage_addr, 1);
+ if (rc) {
+ pr_err("failed to read addr = %d %d\n",
+ chip->soc_storage_addr, rc);
+ return SOC_INVALID;
}
- /* read the SOC storage to determine if there was a battery removal */
- rc = qpnp_read_wrapper(chip, &temp, chip->base + SOC_STORAGE_REG, 1);
- if (!rc) {
- if (temp == SOC_INVALID)
- chip->battery_removed = true;
+ if ((stored_soc >> 1) > 0)
+ shutdown_soc = (stored_soc >> 1) - 1;
+ else
+ shutdown_soc = SOC_INVALID;
+
+ pr_debug("stored soc = 0x%02x, shutdown_soc = %d\n",
+ stored_soc, shutdown_soc);
+ return shutdown_soc;
+}
+
+#define BAT_REMOVED_OFFMODE_BIT BIT(6)
+static bool is_battery_replaced_in_offmode(struct qpnp_bms_chip *chip)
+{
+ u8 batt_pres;
+ int rc;
+
+ if (chip->batt_pres_addr) {
+ rc = qpnp_read_wrapper(chip, &batt_pres,
+ chip->batt_pres_addr, 1);
+ pr_debug("offmode removed: %02x\n", batt_pres);
+ if (!rc && (batt_pres & BAT_REMOVED_OFFMODE_BIT))
+ return true;
+ }
+ return false;
+}
+
+static void load_shutdown_data(struct qpnp_bms_chip *chip)
+{
+ int calculated_soc, shutdown_soc;
+ bool invalid_stored_soc;
+ bool offmode_battery_replaced;
+ bool shutdown_soc_out_of_limit;
+
+ /*
+ * Read the saved shutdown SoC from the configured register and
+ * check if the value has been reset
+ */
+ shutdown_soc = read_shutdown_soc(chip);
+ invalid_stored_soc = (shutdown_soc == SOC_INVALID);
+
+ /*
+ * Do a quick run of SoC calculation to find whether the shutdown soc
+ * is close enough.
+ */
+ calculated_soc = recalculate_raw_soc(chip);
+ shutdown_soc_out_of_limit = (abs(shutdown_soc - calculated_soc)
+ > chip->shutdown_soc_valid_limit);
+ pr_debug("calculated_soc = %d, valid_limit = %d\n",
+ calculated_soc, chip->shutdown_soc_valid_limit);
+
+ /*
+ * Check if the battery has been replaced while the system was powered
+ * down.
+ */
+ offmode_battery_replaced = is_battery_replaced_in_offmode(chip);
+
+ /* Invalidate the shutdown SoC if any of these conditions hold true */
+ if (chip->ignore_shutdown_soc
+ || invalid_stored_soc
+ || offmode_battery_replaced
+ || shutdown_soc_out_of_limit) {
+ chip->battery_removed = true;
+ chip->shutdown_soc_invalid = true;
+ chip->shutdown_iavg_ma = 0;
+ pr_debug("Ignoring shutdown SoC: invalid = %d, offmode = %d, out_of_limit = %d\n",
+ invalid_stored_soc, offmode_battery_replaced,
+ shutdown_soc_out_of_limit);
+ } else {
+ chip->shutdown_iavg_ma = read_shutdown_iavg_ma(chip);
+ chip->shutdown_soc = shutdown_soc;
}
-
- pr_debug("shutdown_soc = %d shutdown_iavg = %d shutdown_soc_invalid = %d, battery_removed = %d\n",
+ pr_debug("raw_soc = %d shutdown_soc = %d shutdown_iavg = %d shutdown_soc_invalid = %d, battery_removed = %d\n",
+ calculated_soc,
chip->shutdown_soc,
chip->shutdown_iavg_ma,
chip->shutdown_soc_invalid,
@@ -3646,6 +3747,18 @@
return -ENXIO;
}
+ pr_debug("Node name = %s\n", spmi_resource->of_node->name);
+
+ if (strcmp("qcom,batt-pres-status",
+ spmi_resource->of_node->name) == 0) {
+ chip->batt_pres_addr = resource->start;
+ continue;
+ } else if (strcmp("qcom,soc-storage-reg",
+ spmi_resource->of_node->name) == 0) {
+ chip->soc_storage_addr = resource->start;
+ continue;
+ }
+
rc = qpnp_read_wrapper(chip, &type,
resource->start + REG_OFFSET_PERP_TYPE, 1);
if (rc) {
@@ -3684,7 +3797,14 @@
dev_err(&spmi->dev, "BMS_IADC peripheral was not registered\n");
return -EINVAL;
}
+ if (chip->soc_storage_addr == 0) {
+ /* default to dvdd backed BMS data reg0 */
+ chip->soc_storage_addr = chip->base + SOC_STORAGE_REG;
+ }
+ pr_debug("bms-base = 0x%04x, iadc-base = 0x%04x, bat-pres-reg = 0x%04x, soc-storage-reg = 0x%04x\n",
+ chip->base, chip->iadc_base,
+ chip->batt_pres_addr, chip->soc_storage_addr);
return 0;
}
@@ -3954,7 +4074,10 @@
INIT_WORK(&chip->recalc_work, recalculate_work);
INIT_WORK(&chip->batfet_open_work, batfet_open_work);
- read_shutdown_soc_and_iavg(chip);
+ dev_set_drvdata(&spmi->dev, chip);
+ device_init_wakeup(&spmi->dev, 1);
+
+ load_shutdown_data(chip);
if (chip->enable_fcc_learning) {
if (chip->battery_removed) {
@@ -3974,9 +4097,6 @@
}
}
- dev_set_drvdata(&spmi->dev, chip);
- device_init_wakeup(&spmi->dev, 1);
-
rc = setup_vbat_monitoring(chip);
if (rc < 0) {
pr_err("failed to set up voltage notifications: %d\n", rc);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 328f6f4..674efb3 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -693,6 +693,7 @@
* @mem: points to start of memory which is used for this struct.
* @hwparams: copy of hwparams registers
* @root: debugfs root folder pointer
+ * @tx_fifo_size: Available RAM size for TX fifo allocation
*/
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
@@ -778,6 +779,7 @@
/* Indicate if software connect was issued by the usb_gadget_driver */
bool softconnect;
void (*notify_event) (struct dwc3 *, unsigned);
+ int tx_fifo_size;
};
/* -------------------------------------------------------------------------- */
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index d7721dc..f43affb 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -223,6 +223,8 @@
unsigned int vdd_no_vol_level;
unsigned int vdd_low_vol_level;
unsigned int vdd_high_vol_level;
+ unsigned int tx_fifo_size;
+ unsigned int qdss_tx_fifo_size;
bool vbus_active;
bool ext_inuse;
enum dwc3_id_state id_state;
@@ -1028,6 +1030,19 @@
}
EXPORT_SYMBOL(msm_ep_unconfig);
+void dwc3_tx_fifo_resize_request(struct usb_ep *ep, bool qdss_enabled)
+{
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);
+
+ if (qdss_enabled)
+ dwc->tx_fifo_size = mdwc->qdss_tx_fifo_size;
+ else
+ dwc->tx_fifo_size = mdwc->tx_fifo_size;
+}
+EXPORT_SYMBOL(dwc3_tx_fifo_resize_request);
+
static void dwc3_restart_usb_work(struct work_struct *w)
{
struct dwc3_msm *mdwc = container_of(w, struct dwc3_msm,
@@ -1490,6 +1505,7 @@
"DWC3_CONTROLLER_POST_RESET_EVENT received\n");
dwc3_msm_qscratch_reg_init(mdwc,
DWC3_CONTROLLER_POST_RESET_EVENT);
+ dwc->tx_fifo_size = mdwc->tx_fifo_size;
break;
default:
dev_dbg(mdwc->dev, "unknown dwc3 event\n");
@@ -2963,6 +2979,17 @@
ret = -ENODEV;
goto disable_hs_ldo;
}
+
+ if (of_property_read_u32(node, "qcom,dwc-usb3-msm-tx-fifo-size",
+ &mdwc->tx_fifo_size))
+ dev_err(&pdev->dev,
+ "unable to read platform data tx fifo size\n");
+
+ if (of_property_read_u32(node, "qcom,dwc-usb3-msm-qdss-tx-fifo-size",
+ &mdwc->qdss_tx_fifo_size))
+ dev_err(&pdev->dev,
+ "unable to read platform data qdss tx fifo size\n");
+
dwc3_set_notifier(&dwc3_msm_notify_event);
/* usb_psy required only for vbus_notifications or charging support */
if (mdwc->ext_xceiv.otg_capability ||
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 8cf8a6d..aaa22f3 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -36,7 +36,6 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -58,11 +57,6 @@
#include "debug.h"
#include "io.h"
-static bool tx_fifo_resize_enable;
-module_param(tx_fifo_resize_enable, bool, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(tx_fifo_resize_enable,
- "Enable allocating Tx fifo for endpoints");
-
static void dwc3_gadget_usb2_phy_suspend(struct dwc3 *dwc, int suspend);
static void dwc3_gadget_usb3_phy_suspend(struct dwc3 *dwc, int suspend);
@@ -188,7 +182,7 @@
int mdwidth;
int num;
- if (!dwc->needs_fifo_resize && !tx_fifo_resize_enable)
+ if (!dwc->needs_fifo_resize)
return 0;
ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
@@ -233,6 +227,17 @@
* packets
*/
tmp = mult * (dep->endpoint.maxpacket + mdwidth);
+
+ if (dwc->tx_fifo_size &&
+ (usb_endpoint_xfer_bulk(dep->endpoint.desc)
+ || usb_endpoint_xfer_isoc(dep->endpoint.desc)))
+ /*
+ * Allocate 3KB fifo size for bulk and isochronous TX
+ * endpoints irrespective of speed. For interrupt
+ * endpoint, allocate fifo size of endpoint maxpacket.
+ */
+ tmp = 3 * (1024 + mdwidth);
+
tmp += mdwidth;
fifo_size = DIV_ROUND_UP(tmp, mdwidth);
@@ -242,10 +247,21 @@
dev_vdbg(dwc->dev, "%s: Fifo Addr %04x Size %d\n",
dep->name, last_fifo_depth, fifo_size & 0xffff);
+ last_fifo_depth += (fifo_size & 0xffff);
+ if (dwc->tx_fifo_size &&
+ (last_fifo_depth >= dwc->tx_fifo_size)) {
+ /*
+ * Fifo size allocated exceeded available RAM size.
+ * Hence return error.
+ */
+ dev_err(dwc->dev, "Fifosize(%d) > available RAM(%d)\n",
+ last_fifo_depth, dwc->tx_fifo_size);
+ return -ENOMEM;
+ }
+
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(fifo_number),
fifo_size);
- last_fifo_depth += (fifo_size & 0xffff);
}
return 0;
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
index a4192ca..6d7dd3d 100644
--- a/drivers/usb/gadget/f_mtp.c
+++ b/drivers/usb/gadget/f_mtp.c
@@ -151,7 +151,7 @@
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 2 values can be tweaked if necessary */
- /* .bMaxBurst = 0, */
+ .bMaxBurst = 2,
/* .bmAttributes = 0, */
};
@@ -168,7 +168,7 @@
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 2 values can be tweaked if necessary */
- /* .bMaxBurst = 0, */
+ .bMaxBurst = 2,
/* .bmAttributes = 0, */
};
diff --git a/drivers/usb/gadget/f_qdss.c b/drivers/usb/gadget/f_qdss.c
index 3b94dd5..f649248 100644
--- a/drivers/usb/gadget/f_qdss.c
+++ b/drivers/usb/gadget/f_qdss.c
@@ -395,6 +395,7 @@
goto fail;
}
}
+ dwc3_tx_fifo_resize_request(qdss->data, true);
return 0;
fail:
@@ -406,8 +407,11 @@
static void qdss_unbind(struct usb_configuration *c, struct usb_function *f)
{
+ struct f_qdss *qdss = func_to_qdss(f);
+
pr_debug("qdss_unbind\n");
+ dwc3_tx_fifo_resize_request(qdss->data, false);
clear_eps(f);
clear_desc(c->cdev->gadget, f);
}
diff --git a/include/linux/stk3x1x.h b/include/linux/stk3x1x.h
index d9d2cf6..6dd446c 100644
--- a/include/linux/stk3x1x.h
+++ b/include/linux/stk3x1x.h
@@ -24,6 +24,7 @@
int int_pin;
uint32_t transmittance;
uint32_t int_flags;
+ bool use_fir;
};
#endif /* __STK3X1X_H__ */
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index ac8d7f8..f462b64 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -563,6 +563,7 @@
#ifdef CONFIG_USB_DWC3_MSM
int msm_ep_config(struct usb_ep *ep);
int msm_ep_unconfig(struct usb_ep *ep);
+void dwc3_tx_fifo_resize_request(struct usb_ep *ep, bool qdss_enable);
int msm_data_fifo_config(struct usb_ep *ep, u32 addr, u32 size,
u8 dst_pipe_idx);
@@ -586,6 +587,12 @@
return -ENODEV;
}
+static inline void dwc3_tx_fifo_resize_request(
+ struct usb_ep *ep, bool qdss_enable)
+{
+ return;
+}
+
static inline void msm_dwc3_restart_usb_session(struct usb_gadget *gadget)
{
return;
diff --git a/sound/soc/codecs/wcd9306.c b/sound/soc/codecs/wcd9306.c
index c509b08..a6ef992 100644
--- a/sound/soc/codecs/wcd9306.c
+++ b/sound/soc/codecs/wcd9306.c
@@ -45,6 +45,9 @@
#define TAPAN_VDD_CX_OPTIMAL_UA 10000
#define TAPAN_VDD_CX_SLEEP_UA 2000
+/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
+#define TAPAN_WG_TIME_FACTOR_US 240
+
static struct regulator *tapan_codec_find_regulator(
struct snd_soc_codec *codec,
const char *name);
@@ -97,6 +100,12 @@
static enum tapan_codec_type codec_ver;
+/*
+ * Multiplication factor to compute impedance on Tapan
+ * This is computed from (Vx / (m*Ical)) = (10mV/(180*30uA))
+ */
+#define TAPAN_ZDET_MUL_FACTOR 1852
+
enum {
AIF1_PB = 0,
AIF1_CAP,
@@ -251,6 +260,12 @@
/* pointers to regulators required for chargepump */
struct regulator *cp_regulators[CP_REG_MAX];
+
+ /*
+ * list used to save/restore registers at start and
+ * end of impedance measurement
+ */
+ struct list_head reg_save_restore;
};
static const u32 comp_shift[] = {
@@ -4917,6 +4932,255 @@
return WCD9XXX_CDC_TYPE_TAPAN;
}
+static void wcd9xxx_prepare_hph_pa(struct wcd9xxx_mbhc *mbhc,
+ struct list_head *lh)
+{
+ int i;
+ struct snd_soc_codec *codec = mbhc->codec;
+ u32 delay;
+
+ const struct wcd9xxx_reg_mask_val reg_set_paon[] = {
+ {WCD9XXX_A_CDC_CLSH_B1_CTL, 0x0F, 0x00},
+ {WCD9XXX_A_RX_HPH_CHOP_CTL, 0xFF, 0xA4},
+ {WCD9XXX_A_RX_HPH_OCP_CTL, 0xFF, 0x67},
+ {WCD9XXX_A_RX_HPH_L_TEST, 0x1, 0x0},
+ {WCD9XXX_A_RX_HPH_R_TEST, 0x1, 0x0},
+ {WCD9XXX_A_RX_HPH_BIAS_WG_OCP, 0xFF, 0x1A},
+ {WCD9XXX_A_RX_HPH_CNP_WG_CTL, 0xFF, 0xDB},
+ {WCD9XXX_A_RX_HPH_CNP_WG_TIME, 0xFF, 0x2A},
+ {TAPAN_A_CDC_CONN_RX2_B2_CTL, 0xFF, 0x10},
+ {WCD9XXX_A_CDC_CLK_OTHR_CTL, 0xFF, 0x05},
+ {WCD9XXX_A_CDC_RX1_B6_CTL, 0xFF, 0x81},
+ {WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x03, 0x03},
+ {WCD9XXX_A_RX_HPH_L_GAIN, 0xFF, 0x2C},
+ {WCD9XXX_A_CDC_RX2_B6_CTL, 0xFF, 0x81},
+ {WCD9XXX_A_RX_HPH_R_GAIN, 0xFF, 0x2C},
+ {WCD9XXX_A_BUCK_CTRL_CCL_4, 0xFF, 0x50},
+ {WCD9XXX_A_BUCK_CTRL_VCL_1, 0xFF, 0x08},
+ {WCD9XXX_A_BUCK_CTRL_CCL_1, 0xFF, 0x5B},
+ {WCD9XXX_A_NCP_CLK, 0xFF, 0x9C},
+ {WCD9XXX_A_NCP_CLK, 0xFF, 0xFC},
+ {WCD9XXX_A_BUCK_MODE_3, 0xFF, 0xCE},
+ {WCD9XXX_A_BUCK_CTRL_CCL_3, 0xFF, 0x6B},
+ {WCD9XXX_A_BUCK_CTRL_CCL_3, 0xFF, 0x6F},
+ {TAPAN_A_RX_BUCK_BIAS1, 0xFF, 0x62},
+ {TAPAN_A_RX_HPH_BIAS_PA, 0xFF, 0x7A},
+ {TAPAN_A_CDC_CLK_RDAC_CLK_EN_CTL, 0xFF, 0x02},
+ {TAPAN_A_CDC_CLK_RDAC_CLK_EN_CTL, 0xFF, 0x06},
+ {WCD9XXX_A_RX_COM_BIAS, 0xFF, 0x80},
+ {WCD9XXX_A_BUCK_MODE_3, 0xFF, 0xC6},
+ {WCD9XXX_A_BUCK_MODE_4, 0xFF, 0xE6},
+ {WCD9XXX_A_BUCK_MODE_5, 0xFF, 0x02},
+ {WCD9XXX_A_BUCK_MODE_1, 0xFF, 0xA1},
+ /* Delay 1ms */
+ {WCD9XXX_A_NCP_EN, 0xFF, 0xFF},
+ /* Delay 1ms */
+ {WCD9XXX_A_BUCK_MODE_5, 0xFF, 0x03},
+ {WCD9XXX_A_BUCK_MODE_5, 0xFF, 0x7B},
+ {WCD9XXX_A_CDC_CLSH_B1_CTL, 0xFF, 0xE6},
+ {WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xFF, 0x40},
+ {WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xFF, 0xC0},
+ {WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xFF, 0x40},
+ {WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xFF, 0xC0},
+ {WCD9XXX_A_NCP_STATIC, 0xFF, 0x08},
+ {WCD9XXX_A_RX_HPH_L_DAC_CTL, 0x03, 0x01},
+ {WCD9XXX_A_RX_HPH_R_DAC_CTL, 0x03, 0x01},
+ };
+
+ /*
+ * Configure PA in class-AB, -18dB gain,
+ * companding off, OCP off, Chopping ON
+ */
+ for (i = 0; i < ARRAY_SIZE(reg_set_paon); i++) {
+ /*
+ * Some of the codec registers like BUCK_MODE_1
+ * and NCP_EN requires 1ms wait time for them
+ * to take effect. Other register writes for
+ * PA configuration do not require any wait time.
+ */
+ if (reg_set_paon[i].reg == WCD9XXX_A_BUCK_MODE_1 ||
+ reg_set_paon[i].reg == WCD9XXX_A_NCP_EN)
+ delay = 1000;
+ else
+ delay = 0;
+ wcd9xxx_soc_update_bits_push(codec, lh,
+ reg_set_paon[i].reg,
+ reg_set_paon[i].mask,
+ reg_set_paon[i].val, delay);
+ }
+ pr_debug("%s: PAs are prepared\n", __func__);
+ return;
+}
+
+static int wcd9xxx_enable_static_pa(struct wcd9xxx_mbhc *mbhc, bool enable)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ int wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) *
+ TAPAN_WG_TIME_FACTOR_US;
+ /*
+ * Tapan requires additional time to enable PA.
+ * It is observed during experiments that we need
+ * an additional wait time about 0.35 times of
+ * the WG_TIME
+ */
+ wg_time += (int) (wg_time * 35) / 100;
+
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30,
+ enable ? 0x30 : 0x0);
+ /* Wait for wave gen time to avoid pop noise */
+ usleep_range(wg_time, wg_time + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ pr_debug("%s: PAs are %s as static mode (wg_time %d)\n", __func__,
+ enable ? "enabled" : "disabled", wg_time);
+ return 0;
+}
+
+static int tapan_setup_zdet(struct wcd9xxx_mbhc *mbhc,
+ enum mbhc_impedance_detect_stages stage)
+{
+
+ int ret = 0;
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct tapan_priv *tapan = snd_soc_codec_get_drvdata(codec);
+ const int mux_wait_us = 25;
+
+ switch (stage) {
+
+ case PRE_MEAS:
+ INIT_LIST_HEAD(&tapan->reg_save_restore);
+ /* Configure PA */
+ wcd9xxx_prepare_hph_pa(mbhc, &tapan->reg_save_restore);
+
+#define __wr(reg, mask, value) \
+ do { \
+ ret = wcd9xxx_soc_update_bits_push(codec, \
+ &tapan->reg_save_restore, \
+ reg, mask, value, 0); \
+ if (ret < 0) \
+ return ret; \
+ } while (0)
+
+ /* Setup MBHC */
+ __wr(WCD9XXX_A_MBHC_SCALING_MUX_1, 0x7F, 0x40);
+ __wr(WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0);
+ __wr(WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0xFF, 0x78);
+ __wr(WCD9XXX_A_TX_7_MBHC_EN, 0xFF, 0xEC);
+ __wr(WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0xFF, 0x45);
+ __wr(WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL, 0xFF, 0x80);
+
+ __wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xFF, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+ __wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xFF, 0x02);
+
+ /* Enable Impedance Detection */
+ __wr(WCD9XXX_A_MBHC_HPH, 0xFF, 0xC8);
+
+ /*
+ * CnP setup for 0mV
+ * Route static data as input to noise shaper
+ */
+ __wr(TAPAN_A_CDC_RX1_B3_CTL, 0xFF, 0x02);
+ __wr(TAPAN_A_CDC_RX2_B3_CTL, 0xFF, 0x02);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_TEST,
+ 0x02, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_TEST,
+ 0x02, 0x00);
+
+ /* Reset the HPHL static data pointer */
+ __wr(TAPAN_A_CDC_RX1_B2_CTL, 0xFF, 0x00);
+ /* Four consecutive writes to set 0V as static data input */
+ snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0x00);
+ snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0x00);
+ snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0x00);
+ snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0x00);
+
+ /* Reset the HPHR static data pointer */
+ __wr(TAPAN_A_CDC_RX2_B2_CTL, 0xFF, 0x00);
+ /* Four consecutive writes to set 0V as static data input */
+ snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0x00);
+ snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0x00);
+ snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0x00);
+ snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0x00);
+
+ /* Enable the HPHL and HPHR PA */
+ wcd9xxx_enable_static_pa(mbhc, true);
+ break;
+ case POST_MEAS:
+ /* Turn off ICAL */
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0);
+
+ wcd9xxx_enable_static_pa(mbhc, false);
+
+ /*
+ * Setup CnP wavegen to ramp to the desired
+ * output using a 40ms ramp
+ */
+
+ /* CnP wavegen current to 0.5uA */
+ snd_soc_write(codec, WCD9XXX_A_RX_HPH_BIAS_WG_OCP, 0x1A);
+ /* Set the current division ratio to 2000 */
+ snd_soc_write(codec, WCD9XXX_A_RX_HPH_CNP_WG_CTL, 0xDF);
+ /* Set the wavegen timer to max (60msec) */
+ snd_soc_write(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME, 0xA0);
+ /* Set the CnP reference current to sc_bias */
+ snd_soc_write(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x6D);
+
+ snd_soc_write(codec, TAPAN_A_CDC_RX1_B2_CTL, 0x00);
+ /* Four consecutive writes to set -10mV as static data input */
+ snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0x00);
+ snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0x1F);
+ snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0x19);
+ snd_soc_write(codec, TAPAN_A_CDC_RX1_B1_CTL, 0xAA);
+
+ snd_soc_write(codec, TAPAN_A_CDC_RX2_B2_CTL, 0x00);
+ /* Four consecutive writes to set -10mV as static data input */
+ snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0x00);
+ snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0x1F);
+ snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0x19);
+ snd_soc_write(codec, TAPAN_A_CDC_RX2_B1_CTL, 0xAA);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_TEST,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_TEST,
+ 0x02, 0x02);
+ /* Enable the HPHL and HPHR PA and wait for 60mS */
+ wcd9xxx_enable_static_pa(mbhc, true);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x7F, 0x40);
+ usleep_range(mux_wait_us,
+ mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ break;
+ case PA_DISABLE:
+ wcd9xxx_enable_static_pa(mbhc, false);
+ wcd9xxx_restore_registers(codec, &tapan->reg_save_restore);
+ break;
+ }
+#undef __wr
+
+ return ret;
+}
+
+static void tapan_compute_impedance(s16 *l, s16 *r, uint32_t *zl, uint32_t *zr)
+{
+ int zln, zld;
+ int zrn, zrd;
+ int rl = 0, rr = 0;
+
+ zln = (l[1] - l[0]) * TAPAN_ZDET_MUL_FACTOR;
+ zld = (l[2] - l[0]);
+ if (zld)
+ rl = zln / zld;
+
+ zrn = (r[1] - r[0]) * TAPAN_ZDET_MUL_FACTOR;
+ zrd = (r[2] - r[0]);
+ if (zrd)
+ rr = zrn / zrd;
+
+ *zl = rl;
+ *zr = rr;
+}
+
static const struct wcd9xxx_mbhc_cb mbhc_cb = {
.enable_mux_bias_block = tapan_enable_mux_bias_block,
.cfilt_fast_mode = tapan_put_cfilt_fast_mode,
@@ -4926,6 +5190,8 @@
.select_cfilt = tapan_select_cfilt,
.free_irq = tapan_free_irq,
.get_cdc_type = tapan_get_cdc_type,
+ .setup_zdet = tapan_setup_zdet,
+ .compute_impedance = tapan_compute_impedance,
};
int tapan_hs_detect(struct snd_soc_codec *codec,
@@ -5000,7 +5266,8 @@
rco_clk_rate = TAPAN_MCLK_CLK_9P6MHZ;
ret = wcd9xxx_mbhc_init(&tapan->mbhc, &tapan->resmgr, codec, NULL,
- &mbhc_cb, rco_clk_rate, false);
+ &mbhc_cb, rco_clk_rate,
+ TAPAN_CDC_ZDET_SUPPORTED);
if (ret)
pr_err("%s: mbhc init failed %d\n", __func__, ret);
else
@@ -5203,7 +5470,8 @@
rco_clk_rate = TAPAN_MCLK_CLK_9P6MHZ;
ret = wcd9xxx_mbhc_init(&tapan->mbhc, &tapan->resmgr, codec, NULL,
- &mbhc_cb, rco_clk_rate, false);
+ &mbhc_cb, rco_clk_rate,
+ TAPAN_CDC_ZDET_SUPPORTED);
if (ret) {
pr_err("%s: mbhc init failed %d\n", __func__, ret);
diff --git a/sound/soc/codecs/wcd9306.h b/sound/soc/codecs/wcd9306.h
index fdd62d1..c4788cc 100644
--- a/sound/soc/codecs/wcd9306.h
+++ b/sound/soc/codecs/wcd9306.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -24,6 +24,8 @@
#define TAPAN_REG_VAL(reg, val) {reg, 0, val}
+#define TAPAN_CDC_ZDET_SUPPORTED true
+
extern const u8 tapan_reg_readable[TAPAN_CACHE_SIZE];
extern const u8 tapan_reset_reg_defaults[TAPAN_CACHE_SIZE];
struct tapan_codec_dai_data {
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 3621879..e52eb9d 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -49,6 +49,9 @@
#define DAPM_MICBIAS2_EXTERNAL_STANDALONE "MIC BIAS2 External Standalone"
+/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
+#define TAIKO_WG_TIME_FACTOR_US 240
+
static atomic_t kp_taiko_priv;
static int spkr_drv_wrnd_param_set(const char *val,
const struct kernel_param *kp);
@@ -509,6 +512,12 @@
int (*machine_codec_event_cb)(struct snd_soc_codec *codec,
enum wcd9xxx_codec_event);
+
+ /*
+ * list used to save/restore registers at start and
+ * end of impedance measurement
+ */
+ struct list_head reg_save_restore;
};
static const u32 comp_shift[] = {
@@ -6239,6 +6248,198 @@
return 0;
}
+static int wcd9xxx_prepare_static_pa(struct wcd9xxx_mbhc *mbhc,
+ struct list_head *lh)
+{
+ int i;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ const struct wcd9xxx_reg_mask_val reg_set_paon[] = {
+ {WCD9XXX_A_RX_HPH_OCP_CTL, 0x18, 0x00},
+ {WCD9XXX_A_RX_HPH_L_TEST, 0x1, 0x0},
+ {WCD9XXX_A_RX_HPH_R_TEST, 0x1, 0x0},
+ {WCD9XXX_A_RX_HPH_BIAS_WG_OCP, 0xff, 0x1A},
+ {WCD9XXX_A_RX_HPH_CNP_WG_CTL, 0xff, 0xDB},
+ {WCD9XXX_A_RX_HPH_CNP_WG_TIME, 0xff, 0x15},
+ {WCD9XXX_A_CDC_RX1_B6_CTL, 0xff, 0x81},
+ {WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x01, 0x01},
+ {WCD9XXX_A_RX_HPH_CHOP_CTL, 0xff, 0xA4},
+ {WCD9XXX_A_RX_HPH_L_GAIN, 0xff, 0x2C},
+ {WCD9XXX_A_CDC_RX2_B6_CTL, 0xff, 0x81},
+ {WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x02, 0x02},
+ {WCD9XXX_A_RX_HPH_R_GAIN, 0xff, 0x2C},
+ {WCD9XXX_A_NCP_CLK, 0xff, 0xFC},
+ {WCD9XXX_A_BUCK_CTRL_CCL_3, 0xff, 0x60},
+ {WCD9XXX_A_RX_COM_BIAS, 0xff, 0x80},
+ {WCD9XXX_A_BUCK_MODE_3, 0xff, 0xC6},
+ {WCD9XXX_A_BUCK_MODE_4, 0xff, 0xE6},
+ {WCD9XXX_A_BUCK_MODE_5, 0xff, 0x02},
+ {WCD9XXX_A_BUCK_MODE_1, 0xff, 0xA1},
+ {WCD9XXX_A_NCP_EN, 0xff, 0xFF},
+ {WCD9XXX_A_BUCK_MODE_5, 0xff, 0x7B},
+ {WCD9XXX_A_CDC_CLSH_B1_CTL, 0xff, 0xE6},
+ {WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xff, 0xC0},
+ {WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xff, 0xC0},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(reg_set_paon); i++)
+ wcd9xxx_soc_update_bits_push(codec, lh,
+ reg_set_paon[i].reg,
+ reg_set_paon[i].mask,
+ reg_set_paon[i].val, 0);
+ pr_debug("%s: PAs are prepared\n", __func__);
+
+ return 0;
+}
+
+static int wcd9xxx_enable_static_pa(struct wcd9xxx_mbhc *mbhc, bool enable)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ const int wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) *
+ TAIKO_WG_TIME_FACTOR_US;
+
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30,
+ enable ? 0x30 : 0x0);
+ /* Wait for wave gen time to avoid pop noise */
+ usleep_range(wg_time, wg_time + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ pr_debug("%s: PAs are %s as static mode (wg_time %d)\n", __func__,
+ enable ? "enabled" : "disabled", wg_time);
+ return 0;
+}
+
+static int taiko_setup_zdet(struct wcd9xxx_mbhc *mbhc,
+ enum mbhc_impedance_detect_stages stage)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+ const int ramp_wait_us = 18 * 1000;
+
+#define __wr(reg, mask, value) \
+ do { \
+ ret = wcd9xxx_soc_update_bits_push(codec, \
+ &taiko->reg_save_restore, \
+ reg, mask, value, 0); \
+ if (ret < 0) \
+ return ret; \
+ } while (0)
+
+ switch (stage) {
+
+ case PRE_MEAS:
+ INIT_LIST_HEAD(&taiko->reg_save_restore);
+ wcd9xxx_prepare_static_pa(mbhc, &taiko->reg_save_restore);
+ wcd9xxx_enable_static_pa(mbhc, true);
+
+ /*
+ * save old value of registers and write the new value to
+ * restore old value back, WCD9XXX_A_CDC_PA_RAMP_B{1,2,3,4}_CTL
+ * registers don't need to be restored as those are solely used
+ * by impedance detection.
+ */
+ /* Phase 1 */
+ /* Reset the PA Ramp */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x1C);
+ /*
+ * Connect the PA Ramp to PA chain and release reset with
+ * keep it connected.
+ */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x1F);
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03);
+ /*
+ * Program the PA Ramp to FS_48K, L shift 1 and sample
+ * num to 24
+ */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL,
+ 0x3 << 4 | 0x6);
+ /* 0x56 for 10mv. 0xC0 is for 50mv */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0xC0);
+ /* Enable MBHC MUX, Set MUX current to 37.5uA and ADC7 */
+ __wr(WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0);
+ __wr(WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0);
+ __wr(WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0xFF, 0x78);
+ __wr(WCD9XXX_A_TX_7_MBHC_EN, 0xFF, 0x8C);
+ /* Change NSA and NAVG */
+ __wr(WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x4 << 4, 0x4 << 4);
+ __wr(WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL, 0xFF, 0x10);
+ /* Reset MBHC and set it up for STA */
+ __wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xFF, 0x0A);
+ __wr(WCD9XXX_A_CDC_MBHC_EN_CTL, 0xFF, 0x02);
+ __wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xFF, 0x02);
+
+ /* Set HPH_MBHC for zdet */
+ __wr(WCD9XXX_A_MBHC_HPH, 0xB3, 0x80);
+ break;
+ case POST_MEAS:
+ /* Phase 2 */
+ /* Start the PA ramp on HPH L and R */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x05);
+ /* Ramp generator takes ~17ms */
+ usleep_range(ramp_wait_us,
+ ramp_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+
+ /* Disable Ical */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);
+ /* Ramp generator takes ~17ms */
+ usleep_range(ramp_wait_us,
+ ramp_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ break;
+ case PA_DISABLE:
+ /* Ramp HPH L & R back to Zero */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x0A);
+ /* Ramp generator takes ~17ms */
+ usleep_range(ramp_wait_us,
+ ramp_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);
+
+ /* Clean up starts */
+ /* Turn off PA ramp generator */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x0);
+ wcd9xxx_enable_static_pa(mbhc, false);
+ wcd9xxx_restore_registers(codec, &taiko->reg_save_restore);
+ break;
+ }
+#undef __wr
+
+ return ret;
+}
+
+static void taiko_compute_impedance(s16 *l, s16 *r, uint32_t *zl, uint32_t *zr)
+{
+
+ int64_t rl, rr = 0; /* milliohm */
+ const int alphal = 364; /* 0.005555 * 65536 = 364.05 */
+ const int alphar = 364; /* 0.005555 * 65536 = 364.05 */
+ const int beta = 3855; /* 0.011765 * 5 * 65536 = 3855.15 */
+ const int rref = 11333; /* not scaled up */
+ const int shift = 16;
+
+ rl = (int)(l[0] - l[1]) * 1000 / (l[0] - l[2]);
+ rl = rl * rref * alphal;
+ rl = rl >> shift;
+ rl = rl * beta;
+ rl = rl >> shift;
+ *zl = rl;
+
+ rr = (int)(r[0] - r[1]) * 1000 / (r[0] - r[2]);
+ rr = rr * rref * alphar;
+ rr = rr >> shift;
+ rr = rr * beta;
+ rr = rr >> shift;
+ *zr = rr;
+}
+
+static enum wcd9xxx_cdc_type taiko_get_cdc_type(void)
+{
+ return WCD9XXX_CDC_TYPE_TAIKO;
+}
+
+static const struct wcd9xxx_mbhc_cb mbhc_cb = {
+ .get_cdc_type = taiko_get_cdc_type,
+ .setup_zdet = taiko_setup_zdet,
+ .compute_impedance = taiko_compute_impedance,
+};
+
static int taiko_post_reset_cb(struct wcd9xxx *wcd9xxx)
{
int ret = 0;
@@ -6284,7 +6485,7 @@
ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
taiko_enable_mbhc_micbias,
- NULL, rco_clk_rate, true);
+ &mbhc_cb, rco_clk_rate, true);
if (ret)
pr_err("%s: mbhc init failed %d\n", __func__, ret);
else
@@ -6473,7 +6674,7 @@
/* init and start mbhc */
ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
taiko_enable_mbhc_micbias,
- NULL, rco_clk_rate, true);
+ &mbhc_cb, rco_clk_rate, true);
if (ret) {
pr_err("%s: mbhc init failed %d\n", __func__, ret);
goto err_init;
diff --git a/sound/soc/codecs/wcd9xxx-common.c b/sound/soc/codecs/wcd9xxx-common.c
index d00b843..05f2191 100644
--- a/sound/soc/codecs/wcd9xxx-common.c
+++ b/sound/soc/codecs/wcd9xxx-common.c
@@ -11,6 +11,7 @@
*/
#include <linux/module.h>
+#include <linux/slab.h>
#include <sound/soc.h>
#include <linux/kernel.h>
#include <linux/delay.h>
@@ -32,6 +33,8 @@
#define MAX_IMPED_PARAMS 13
+#define USLEEP_RANGE_MARGIN_US 100
+
struct wcd9xxx_imped_val {
u32 imped_val;
u8 index;
@@ -614,6 +617,46 @@
}
}
+
+int wcd9xxx_soc_update_bits_push(struct snd_soc_codec *codec,
+ struct list_head *list,
+ uint16_t reg, uint8_t mask,
+ uint8_t value, int delay)
+{
+ int rc;
+ struct wcd9xxx_register_save_node *node;
+
+ node = kmalloc(sizeof(*node), GFP_KERNEL);
+ if (unlikely(!node)) {
+ pr_err("%s: Not enough memory\n", __func__);
+ return -ENOMEM;
+ }
+ node->reg = reg;
+ node->value = snd_soc_read(codec, reg);
+ list_add(&node->lh, list);
+ if (mask == 0xFF)
+ rc = snd_soc_write(codec, reg, value);
+ else
+ rc = snd_soc_update_bits(codec, reg, mask, value);
+ if (delay)
+ usleep_range(delay, delay + USLEEP_RANGE_MARGIN_US);
+ return rc;
+}
+EXPORT_SYMBOL(wcd9xxx_soc_update_bits_push);
+
+void wcd9xxx_restore_registers(struct snd_soc_codec *codec,
+ struct list_head *lh)
+{
+ struct wcd9xxx_register_save_node *node, *nodetmp;
+
+ list_for_each_entry_safe(node, nodetmp, lh, lh) {
+ snd_soc_write(codec, node->reg, node->value);
+ list_del(&node->lh);
+ kfree(node);
+ }
+}
+EXPORT_SYMBOL(wcd9xxx_restore_registers);
+
static void wcd9xxx_enable_buck_mode(struct snd_soc_codec *codec,
u8 buck_vref)
{
diff --git a/sound/soc/codecs/wcd9xxx-common.h b/sound/soc/codecs/wcd9xxx-common.h
index 654964e..9fba8e3 100644
--- a/sound/soc/codecs/wcd9xxx-common.h
+++ b/sound/soc/codecs/wcd9xxx-common.h
@@ -81,4 +81,16 @@
WCD9XXX_CODEC_EVENT_CODEC_UP = 0,
};
+struct wcd9xxx_register_save_node {
+ struct list_head lh;
+ u16 reg;
+ u16 value;
+};
+
+extern int wcd9xxx_soc_update_bits_push(struct snd_soc_codec *codec,
+ struct list_head *lh,
+ uint16_t reg, uint8_t mask,
+ uint8_t value, int delay);
+extern void wcd9xxx_restore_registers(struct snd_soc_codec *codec,
+ struct list_head *lh);
#endif
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index acb86416..4e95fa9 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -142,12 +142,6 @@
enum wcd9xxx_mbhc_plug_type _type;
};
-struct wcd9xxx_register_save_node {
- struct list_head lh;
- u16 reg;
- u16 value;
-};
-
enum meas_type {
STA = 0,
DCE,
@@ -182,6 +176,8 @@
uint32_t *zr);
static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
const enum wcd9xxx_current_v_idx idx);
+static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z);
+static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc);
static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
{
@@ -973,6 +969,7 @@
if (noreldetection)
wcd9xxx_turn_onoff_rel_detection(codec, false);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x0);
/* Turn on the override */
if (!override_bypass)
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
@@ -982,6 +979,8 @@
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
0x0);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
+ 0x2);
usleep_range(mbhc->mbhc_data.t_sta_dce,
mbhc->mbhc_data.t_sta_dce);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
@@ -993,6 +992,8 @@
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
0x0);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
+ 0x2);
usleep_range(mbhc->mbhc_data.t_sta_dce,
mbhc->mbhc_data.t_sta_dce);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
@@ -1055,6 +1056,10 @@
struct snd_soc_codec *codec = mbhc->codec;
short bias_value;
u8 cfilt_mode;
+ s16 reg;
+ int change;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ s16 sta_z = 0, dce_z = 0;
WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
@@ -1064,6 +1069,8 @@
return -ENODEV;
}
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+
/*
* Request BG and clock.
* These will be released by wcd9xxx_cleanup_hs_polling
@@ -1102,14 +1109,48 @@
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- if (!is_cs_enable)
- wcd9xxx_calibrate_hs_polling(mbhc);
-
/* don't flip override */
bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+ /* recalibrate dce_z and sta_z */
+ reg = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
+ change = snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
+ btn_det->mbhc_nsc << 3);
+ wcd9xxx_get_z(mbhc, &dce_z, &sta_z);
+ if (change)
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
+ if (dce_z && sta_z) {
+ pr_debug("%s: sta_z 0x%x -> 0x%x, dce_z 0x%x -> 0x%x\n",
+ __func__,
+ mbhc->mbhc_data.sta_z, sta_z & 0xffff,
+ mbhc->mbhc_data.dce_z, dce_z & 0xffff);
+ mbhc->mbhc_data.dce_z = dce_z;
+ mbhc->mbhc_data.sta_z = sta_z;
+ wcd9xxx_mbhc_calc_thres(mbhc);
+ wcd9xxx_calibrate_hs_polling(mbhc);
+ } else {
+ pr_warn("%s: failed get new dce_z/sta_z 0x%x/0x%x\n", __func__,
+ dce_z, sta_z);
+ }
+
+ if (is_cs_enable) {
+ /* recalibrate dce_nsc_cs_z */
+ reg = snd_soc_read(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x78, WCD9XXX_MBHC_NSC_CS << 3);
+ wcd9xxx_get_z(mbhc, &dce_z, NULL);
+ snd_soc_write(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
+ if (dce_z) {
+ pr_debug("%s: dce_nsc_cs_z 0x%x -> 0x%x\n", __func__,
+ mbhc->mbhc_data.dce_nsc_cs_z, dce_z & 0xffff);
+ mbhc->mbhc_data.dce_nsc_cs_z = dce_z;
+ } else {
+ pr_debug("%s: failed get new dce_nsc_cs_z\n", __func__);
+ }
+ }
+
return bias_value;
}
@@ -2946,9 +2987,10 @@
return mask;
}
-void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z)
+static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z)
{
s16 reg0, reg1;
+ int change;
struct snd_soc_codec *codec = mbhc->codec;
WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
@@ -2959,16 +3001,21 @@
snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 1 << 7, 0);
/* Disconnect override from micbias */
- snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
+ change = snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
+ 1 << 0);
usleep_range(1000, 1000 + 1000);
- *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, false);
- *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, false);
+ if (sta_z)
+ *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, false);
+ if (dce_z)
+ *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, false);
pr_debug("%s: sta_z 0x%x, dce_z 0x%x\n", __func__, *sta_z & 0xFFFF,
*dce_z & 0xFFFF);
/* Connect override from micbias */
- snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 4);
+ if (change)
+ snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
+ 1 << 4);
/* Disable pull down micbias to ground */
snd_soc_write(codec, mbhc->mbhc_bias_regs.mbhc_reg, reg1);
snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
@@ -3740,6 +3787,11 @@
p->dce_z, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
+ n += scnprintf(buffer + n, size - n, "dce_nsc_cs_z = %x(%dmv)\n",
+ p->dce_nsc_cs_z,
+ __wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_nsc_cs_z,
+ p->dce_nsc_cs_z,
+ VDDIO_MICBIAS_MV));
n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
@@ -4158,107 +4210,6 @@
return ret;
}
-static int wcd9xxx_soc_update_bits_push(struct snd_soc_codec *codec,
- struct list_head *list,
- uint16_t reg, uint8_t mask,
- uint8_t value)
-{
- int rc;
- struct wcd9xxx_register_save_node *node;
-
- node = kmalloc(sizeof(*node), GFP_KERNEL);
- if (unlikely(!node)) {
- pr_err("%s: Not enough memory\n", __func__);
- return -ENOMEM;
- }
- node->reg = reg;
- node->value = snd_soc_read(codec, reg);
- list_add(&node->lh, list);
- if (mask == 0xFF)
- rc = snd_soc_write(codec, reg, value);
- else
- rc = snd_soc_update_bits(codec, reg, mask, value);
- return rc;
-}
-
-static int wcd9xxx_prepare_static_pa(struct wcd9xxx_mbhc *mbhc,
- struct list_head *lh)
-{
- int i;
- struct snd_soc_codec *codec = mbhc->codec;
-
- const struct wcd9xxx_reg_mask_val reg_set_paon[] = {
- {WCD9XXX_A_RX_HPH_OCP_CTL, 0x18, 0x00},
- {WCD9XXX_A_RX_HPH_L_TEST, 0x1, 0x0},
- {WCD9XXX_A_RX_HPH_R_TEST, 0x1, 0x0},
- {WCD9XXX_A_RX_HPH_BIAS_WG_OCP, 0xff, 0x1A},
- {WCD9XXX_A_RX_HPH_CNP_WG_CTL, 0xff, 0xDB},
- {WCD9XXX_A_RX_HPH_CNP_WG_TIME, 0xff, 0x15},
- {WCD9XXX_A_CDC_RX1_B6_CTL, 0xff, 0x81},
- {WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x01, 0x01},
- {WCD9XXX_A_RX_HPH_CHOP_CTL, 0xff, 0xA4},
- {WCD9XXX_A_RX_HPH_L_GAIN, 0xff, 0x2C},
- {WCD9XXX_A_CDC_RX2_B6_CTL, 0xff, 0x81},
- {WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x02, 0x02},
- {WCD9XXX_A_RX_HPH_R_GAIN, 0xff, 0x2C},
- {WCD9XXX_A_NCP_CLK, 0xff, 0xFC},
- {WCD9XXX_A_BUCK_CTRL_CCL_3, 0xff, 0x60},
- {WCD9XXX_A_RX_COM_BIAS, 0xff, 0x80},
- {WCD9XXX_A_BUCK_MODE_3, 0xff, 0xC6},
- {WCD9XXX_A_BUCK_MODE_4, 0xff, 0xE6},
- {WCD9XXX_A_BUCK_MODE_5, 0xff, 0x02},
- {WCD9XXX_A_BUCK_MODE_1, 0xff, 0xA1},
- {WCD9XXX_A_NCP_EN, 0xff, 0xFF},
- {WCD9XXX_A_BUCK_MODE_5, 0xff, 0x7B},
- {WCD9XXX_A_CDC_CLSH_B1_CTL, 0xff, 0xE6},
- {WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xff, 0xC0},
- {WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xff, 0xC0},
- };
-
- for (i = 0; i < ARRAY_SIZE(reg_set_paon); i++)
- wcd9xxx_soc_update_bits_push(codec, lh,
- reg_set_paon[i].reg,
- reg_set_paon[i].mask,
- reg_set_paon[i].val);
- pr_debug("%s: PAs are prepared\n", __func__);
-
- return 0;
-}
-
-static void wcd9xxx_restore_registers(struct wcd9xxx_mbhc *mbhc,
- struct list_head *lh)
-{
- struct wcd9xxx_register_save_node *node, *nodetmp;
- struct snd_soc_codec *codec = mbhc->codec;
-
- list_for_each_entry_safe(node, nodetmp, lh, lh) {
- snd_soc_write(codec, node->reg, node->value);
- list_del(&node->lh);
- kfree(node);
- }
-}
-
-static void wcd9xxx_unprepare_static_pa(struct wcd9xxx_mbhc *mbhc,
- struct list_head *lh)
-{
- wcd9xxx_restore_registers(mbhc, lh);
-}
-
-static int wcd9xxx_enable_static_pa(struct wcd9xxx_mbhc *mbhc, bool enable)
-{
- struct snd_soc_codec *codec = mbhc->codec;
- const int wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) *
- WCD9XXX_WG_TIME_FACTOR_US;
-
- snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30,
- enable ? 0x30 : 0x0);
- /* Wait for wave gen time to avoid pop noise */
- usleep_range(wg_time, wg_time + WCD9XXX_USLEEP_RANGE_MARGIN_US);
- pr_debug("%s: PAs are %s as static mode (wg_time %d)\n", __func__,
- enable ? "enabled" : "disabled", wg_time);
- return 0;
-}
-
static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
uint32_t *zr)
{
@@ -4268,17 +4219,8 @@
s16 *z[] = {
&l[0], &r[0], &r[1], &l[1], &l[2], &r[2],
};
- LIST_HEAD(lh);
- LIST_HEAD(lhpa);
struct snd_soc_codec *codec = mbhc->codec;
- const int ramp_wait_us = 18 * 1000;
const int mux_wait_us = 25;
- const int alphal = 364; /* 0.005555 * 65536 = 364.05 */
- const int alphar = 364; /* 0.005555 * 65536 = 364.05 */
- const int beta = 3855; /* 0.011765 * 5 * 65536 = 3855.15 */
- const int rref = 11333; /* not scaled up */
- const int shift = 16;
- int64_t rl, rr = 0; /* milliohm */
const struct wcd9xxx_reg_mask_val reg_set_mux[] = {
/* Phase 1 */
/* Set MBHC_MUX for HPHL without ical */
@@ -4299,6 +4241,11 @@
pr_debug("%s: enter\n", __func__);
WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+ if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet ||
+ !mbhc->mbhc_cb->compute_impedance || !zl ||
+ !zr)
+ return -EINVAL;
+
/*
* Impedance detection is an intrusive function as it mutes RX paths,
* enable PAs and etc. Therefore codec drvier including ALSA
@@ -4317,127 +4264,66 @@
wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
+ wcd9xxx_turn_onoff_override(codec, true);
pr_debug("%s: Setting impedance detection\n", __func__);
- wcd9xxx_prepare_static_pa(mbhc, &lhpa);
- wcd9xxx_enable_static_pa(mbhc, true);
- /*
- * save old value of registers and write the new value to restore old
- * value back, WCD9XXX_A_CDC_PA_RAMP_B{1,2,3,4}_CTL registers don't
- * need to be restored as those are solely used by impedance detection.
- */
-#define __w(reg, mask, value) \
- do { \
- ret = wcd9xxx_soc_update_bits_push(codec, &lh, reg, mask, \
- value); \
- if (ret < 0) \
- return ret; \
- } while (0)
-
- /* Reset the PA Ramp */
- snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x1C);
- /*
- * Connect the PA Ramp to PA chain and release reset with keep it
- * connected.
- */
- snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x1F);
- snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03);
- /* Program the PA Ramp to FS_48K, L shift 1 and sample num to 24 */
- snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL, 0x3 << 4 | 0x6);
- /* 0x56 for 10mv. 0xC0 is for 50mv */
- snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0xC0);
- /* Enable MBHC MUX, Set MUX current to 37.5uA and ADC7 */
- __w(WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0);
- __w(WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0);
- __w(WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0xFF, 0x78);
- __w(WCD9XXX_A_TX_7_MBHC_EN, 0xFF, 0x8C);
- /* Change NSA and NAVG */
- __w(WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x4 << 4, 0x4 << 4);
- __w(WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL, 0xFF, 0x10);
- /* Reset MBHC and set it up for STA */
- __w(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xFF, 0x0A);
- __w(WCD9XXX_A_CDC_MBHC_EN_CTL, 0xFF, 0x02);
- __w(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xFF, 0x02);
-
- /* Set HPH_MBHC for zdet */
- __w(WCD9XXX_A_MBHC_HPH, 0xB3, 0x80);
+ /* Codec specific setup for L0, R0, L1 and R1 measurements */
+ mbhc->mbhc_cb->setup_zdet(mbhc, PRE_MEAS);
pr_debug("%s: Performing impedance detection\n", __func__);
- /* Phase 1 */
for (i = 0; i < ARRAY_SIZE(reg_set_mux) - 2; i++) {
- __w(reg_set_mux[i].reg, reg_set_mux[i].mask,
- reg_set_mux[i].val);
+ snd_soc_update_bits(codec, reg_set_mux[i].reg,
+ reg_set_mux[i].mask,
+ reg_set_mux[i].val);
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
/* 25us is required after mux change to settle down */
usleep_range(mux_wait_us,
mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
- *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, false, false);
+ *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
}
- /* Phase 2 */
- /* Start the PA ramp on HPH L and R */
- snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x05);
- /* Ramp generator takes ~17ms */
- usleep_range(ramp_wait_us,
- ramp_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ /* Codec specific setup for L2 and R2 measurements */
+ mbhc->mbhc_cb->setup_zdet(mbhc, POST_MEAS);
- /* Disable Ical */
- snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);
- /* Ramp generator takes ~17ms */
- usleep_range(ramp_wait_us,
- ramp_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
for (; i < ARRAY_SIZE(reg_set_mux); i++) {
- __w(reg_set_mux[i].reg, reg_set_mux[i].mask,
- reg_set_mux[i].val);
+ snd_soc_update_bits(codec, reg_set_mux[i].reg,
+ reg_set_mux[i].mask,
+ reg_set_mux[i].val);
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
/* 25us is required after mux change to settle down */
usleep_range(mux_wait_us,
mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
- *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, false, false);
+ *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
}
- /* Ramp HPH L & R back to Zero */
- snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x0A);
- /* Ramp generator takes ~17ms */
- usleep_range(ramp_wait_us,
- ramp_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
- snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);
-#undef __w
-
- /* Clean up starts */
- /* Turn off PA ramp generator */
- snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x0);
- wcd9xxx_enable_static_pa(mbhc, false);
- wcd9xxx_restore_registers(mbhc, &lh);
- wcd9xxx_unprepare_static_pa(mbhc, &lhpa);
+ mbhc->mbhc_cb->setup_zdet(mbhc, PA_DISABLE);
mutex_unlock(&codec->mutex);
WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
- wcd9xxx_resmgr_put_bandgap(mbhc->resmgr, WCD9XXX_BANDGAP_MBHC_MODE);
+ wcd9xxx_resmgr_put_bandgap(mbhc->resmgr, WCD9XXX_BANDGAP_AUDIO_MODE);
wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
- rl = (int)(l[0] - l[1]) * 1000 / (l[0] - l[2]);
- rl = rl * rref * alphal;
- rl = rl >> shift;
- rl = rl * beta;
- rl = rl >> shift;
- *zl = rl;
+ wcd9xxx_turn_onoff_override(codec, false);
+ mbhc->mbhc_cb->compute_impedance(l, r, zl, zr);
- rr = (int)(r[0] - r[1]) * 1000 / (r[0] - r[2]);
- rr = rr * rref * alphar;
- rr = rr >> shift;
- rr = rr * beta;
- rr = rr >> shift;
- *zr = rr;
-
- pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d), rl: %lld\n",
+ pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d)\n",
__func__,
- l[0] & 0xffff, l[0], l[1] & 0xffff, l[1], l[2] & 0xffff, l[2],
- rl);
- pr_debug("%s: R0: 0x%x(%d), R1: 0x%x(%d), R2: 0x%x(%d), rr: %lld\n",
+ l[0] & 0xffff, l[0], l[1] & 0xffff, l[1], l[2] & 0xffff, l[2]);
+ pr_debug("%s: R0: 0x%x(%d), R1: 0x%x(%d), R2: 0x%x(%d)\n",
__func__,
- r[0] & 0xffff, r[0], r[1] & 0xffff, r[1], r[2] & 0xffff, r[2],
- rr);
+ r[0] & 0xffff, r[0], r[1] & 0xffff, r[1], r[2] & 0xffff, r[2]);
pr_debug("%s: RL %d milliohm, RR %d milliohm\n", __func__, *zl, *zr);
pr_debug("%s: Impedance detection completed\n", __func__);
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
index 3040bc4..e5259c3 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.h
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -19,6 +19,8 @@
#define WCD9XXX_CFILT_EXT_PRCHG_EN 0x30
#define WCD9XXX_CFILT_EXT_PRCHG_DSBL 0x00
+#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100
+
struct mbhc_micbias_regs {
u16 cfilt_val;
u16 cfilt_ctl;
@@ -40,6 +42,12 @@
MBHC_CAL_NUM,
};
+enum mbhc_impedance_detect_stages {
+ PRE_MEAS,
+ POST_MEAS,
+ PA_DISABLE,
+};
+
/* Data used by MBHC */
struct mbhc_internal_cal_data {
u16 dce_z;
@@ -239,6 +247,9 @@
void (*free_irq) (struct wcd9xxx_mbhc *);
enum wcd9xxx_cdc_type (*get_cdc_type) (void);
void (*enable_clock_gate) (struct snd_soc_codec *, bool);
+ int (*setup_zdet) (struct wcd9xxx_mbhc *,
+ enum mbhc_impedance_detect_stages stage);
+ void (*compute_impedance) (s16 *, s16 *, uint32_t *, uint32_t *);
};
struct wcd9xxx_mbhc {