summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/power/smb1360-charger-fg.txt34
-rw-r--r--drivers/i2c/busses/i2c-msm-v2.c6
-rw-r--r--drivers/power/smb1360-charger-fg.c796
-rw-r--r--drivers/staging/android/ion/ion_heap.c52
-rw-r--r--drivers/staging/android/ion/ion_page_pool.c24
-rw-r--r--drivers/staging/android/ion/ion_priv.h5
-rw-r--r--drivers/staging/android/ion/ion_system_heap.c142
-rw-r--r--sound/soc/codecs/msm8x16-wcd.c131
-rw-r--r--sound/soc/codecs/msm8x16-wcd.h6
-rw-r--r--sound/soc/msm/msm8x16.c51
10 files changed, 1035 insertions, 212 deletions
diff --git a/Documentation/devicetree/bindings/power/smb1360-charger-fg.txt b/Documentation/devicetree/bindings/power/smb1360-charger-fg.txt
index 07346f268db3..c86a0041162b 100644
--- a/Documentation/devicetree/bindings/power/smb1360-charger-fg.txt
+++ b/Documentation/devicetree/bindings/power/smb1360-charger-fg.txt
@@ -58,6 +58,27 @@ Optional Properties:
- qcom,stat-pulsed-irq: A boolean flag to indicate the state-irq pin will generate pulse
signal when interrupt happened. If this property is not specified,
the default configuration is static level irq.
+- qcom,shdn-after-pwroff: A bool property to configure smb1360 for shutdown at power-off.
+- qcom,batt-profile-select A boolean flag to indicate of battery-profile
+ selection is enabled.
+- qcom,profile-a-rid-kohm The battery-ID resistor (RID) in Kohm supported by
+ profile-A of SMB1360. This should be specified
+ if 'batt-profile-select' is defined.
+- qcom,profile-b-rid-kohm The battery-ID resistor (RID) in Kohm supported by
+ profile-B of SMB1360. This should be specified
+ if 'batt-profile-select' is defined.
+- qcom,batt-id-vref-uv The reference voltage on the battery-ID line
+ specified in micro-volts.
+- qcom,batt-id-rpullup-kohm The pull-up resistor connected on the battery-ID
+ (vref) line.
+- qcom,smb1360-vadc VADC device phandle (used for reading the RID)
+- qcom,fg-batt-capacity-mah The new battery capacity to be supported.
+ This updates the NOMINAL_CAPACITY of the profile.
+- qcom,fg-cc-soc-coeff The CC_TO_SOC_COEFF of the new battery capacity.
+ This is updated only if 'fg-batt-capacity-mah'
+ is specified. This value is calculated from the
+ batt-capacity and then encoded as floating number.
+ cc_soc_coeff = HALF_ENCODING(8192 / batt-capacity)
Example:
i2c@f9967000 {
@@ -68,6 +89,19 @@ Example:
interrupts = <0x00 0xcd 0>;
pinctrl-names = "default";
pinctrl-0 = <&smb_int_default>;
+
+ /* battery-profile selection properties */
+ qcom,batt-profile-select;
+ qcom,smb1360-vadc = <&pm8916_vadc>;
+ qcom,batt-id-vref-uv = <1800000>;
+ qcom,batt-id-rpullup-kohm = <100>;
+ qcom,profile-a-rid-kohm = <78>;
+ qcom,profile-b-rid-kohm = <200>;
+
+ /* battery capacity */
+ qcom,fg-batt-capacity-mah = <4000>;
+ qcom,fg-cc-soc-coeff = <0x8009>
+
qcom,float-voltage-mv = <4200>;
qcom,iterm-ma = <100>;
qcom,charging-disabled;
diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c
index 609e50c56223..916a2c97155a 100644
--- a/drivers/i2c/busses/i2c-msm-v2.c
+++ b/drivers/i2c/busses/i2c-msm-v2.c
@@ -3080,7 +3080,7 @@ static bool i2c_msm_xfer_buf_is_last(struct i2c_msm_ctrl *ctrl)
struct i2c_msg *cur_msg = ctrl->xfer.msgs + cur_buf->msg_idx;
return i2c_msm_xfer_msg_is_last(ctrl) &&
- ((cur_buf->byte_idx + ctrl->ver.max_buf_size) > cur_msg->len);
+ ((cur_buf->byte_idx + ctrl->ver.max_buf_size) >= cur_msg->len);
}
static void i2c_msm_xfer_create_cur_tag(struct i2c_msm_ctrl *ctrl,
@@ -3235,6 +3235,8 @@ static void i2c_msm_pm_xfer_end(struct i2c_msm_ctrl *ctrl)
{
/* efectively disabling our ISR */
atomic_set(&ctrl->xfer.is_active, 0);
+ if (ctrl->xfer.mode_id == I2C_MSM_XFER_MODE_BAM)
+ i2c_msm_bam_teardown(ctrl);
i2c_msm_pm_clk_disable_unprepare(ctrl);
if (pm_runtime_enabled(ctrl->dev)) {
pm_runtime_mark_last_busy(ctrl->dev);
@@ -3244,8 +3246,6 @@ static void i2c_msm_pm_xfer_end(struct i2c_msm_ctrl *ctrl)
}
disable_irq(ctrl->rsrcs.irq);
- if (ctrl->xfer.mode_id == I2C_MSM_XFER_MODE_BAM)
- i2c_msm_bam_teardown(ctrl);
mutex_unlock(&ctrl->xfer.mtx);
}
diff --git a/drivers/power/smb1360-charger-fg.c b/drivers/power/smb1360-charger-fg.c
index 4f0f495a5d55..ffc15544f880 100644
--- a/drivers/power/smb1360-charger-fg.c
+++ b/drivers/power/smb1360-charger-fg.c
@@ -18,6 +18,7 @@
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/interrupt.h>
+#include <linux/math64.h>
#include <linux/slab.h>
#include <linux/power_supply.h>
#include <linux/regulator/driver.h>
@@ -26,6 +27,7 @@
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/bitops.h>
+#include <linux/qpnp/qpnp-adc.h>
#define _SMB1360_MASK(BITS, POS) \
((unsigned char)(((1 << (BITS)) - 1) << (POS)))
@@ -46,6 +48,7 @@
#define AC_INPUT_PIN_HIGH_BIT BIT(6)
#define RESET_STATE_USB_500 BIT(5)
#define INPUT_CURR_LIM_MASK SMB1360_MASK(3, 0)
+#define INPUT_CURR_LIM_300MA 0x0
#define CFG_GLITCH_FLT_REG 0x06
#define AICL_ENABLED_BIT BIT(0)
@@ -61,6 +64,7 @@
#define CFG_STAT_CTRL_REG 0x09
#define CHG_STAT_IRQ_ONLY_BIT BIT(4)
+#define CHG_TEMP_CHG_ERR_BLINK_BIT BIT(3)
#define CHG_STAT_ACTIVE_HIGH_BIT BIT(1)
#define CHG_STAT_DISABLE_BIT BIT(0)
@@ -76,6 +80,9 @@
#define BATT_ID_ENABLED_BIT BIT(5)
#define CHG_BATT_ID_FAIL BIT(4)
#define BATT_ID_FAIL_SELECT_PROFILE BIT(3)
+#define BATT_PROFILE_SELECT_MASK SMB1360_MASK(3, 0)
+#define BATT_PROFILEA_MASK 0x0
+#define BATT_PROFILEB_MASK 0xF
#define IRQ_CFG_REG 0x0F
#define IRQ_BAT_HOT_COLD_HARD_BIT BIT(7)
@@ -105,15 +112,23 @@
#define BATT_CHG_FLT_VTG_REG 0x15
#define VFLOAT_MASK SMB1360_MASK(6, 0)
+#define SHDN_CTRL_REG 0x1A
+#define SHDN_CMD_USE_BIT BIT(1)
+#define SHDN_CMD_POLARITY_BIT BIT(2)
+
/* Command Registers */
#define CMD_I2C_REG 0x40
#define ALLOW_VOLATILE_BIT BIT(6)
+#define FG_ACCESS_ENABLED_BIT BIT(5)
+#define FG_RESET_BIT BIT(4)
+#define CYCLE_STRETCH_CLEAR_BIT BIT(3)
#define CMD_IL_REG 0x41
#define USB_CTRL_MASK SMB1360_MASK(1 , 0)
#define USB_100_BIT 0x01
#define USB_500_BIT 0x00
#define USB_AC_BIT 0x02
+#define SHDN_CMD_BIT BIT(7)
#define CMD_CHG_REG 0x42
#define CMD_CHG_EN BIT(1)
@@ -130,6 +145,9 @@
#define BATT_TAPER_CHG_VAL 0x3
#define CHG_EN_BIT BIT(0)
+#define STATUS_4_REG 0x4C
+#define CYCLE_STRETCH_ACTIVE_BIT BIT(5)
+
#define REVISION_CTRL_REG 0x4F
#define DEVICE_REV_MASK SMB1360_MASK(3, 0)
@@ -157,6 +175,9 @@
#define IRQ_H_REG 0x57
#define IRQ_I_REG 0x58
+#define FG_ACCESS_ALLOWED_BIT BIT(0)
+#define BATT_ID_RESULT_BIT SMB1360_MASK(6, 4)
+#define BATT_ID_SHIFT 4
/* FG registers - IRQ config register */
#define SOC_MAX_REG 0x24
@@ -167,12 +188,18 @@
/* FG SHADOW registers */
#define SHDW_FG_ESR_ACTUAL 0x20
+#define SHDW_FG_BATT_STATUS 0x60
+#define BATTERY_PROFILE_BIT BIT(0)
+
#define SHDW_FG_MSYS_SOC 0x61
#define SHDW_FG_CAPACITY 0x62
#define SHDW_FG_VTG_NOW 0x69
#define SHDW_FG_CURR_NOW 0x6B
#define SHDW_FG_BATT_TEMP 0x6D
+#define CC_TO_SOC_COEFF 0xBA
+#define NOMINAL_CAPACITY_REG 0xBC
+
#define FG_I2C_CFG_MASK SMB1360_MASK(2, 1)
#define FG_CFG_I2C_ADDR 0x2
#define FG_PROFILE_A_ADDR 0x4
@@ -193,8 +220,6 @@ enum {
enum {
USER = BIT(0),
- THERMAL = BIT(1),
- CURRENT = BIT(2),
};
enum fg_i2c_access_type {
@@ -203,6 +228,12 @@ enum fg_i2c_access_type {
FG_ACCESS_PROFILE_B = 0x3
};
+enum {
+ BATTERY_PROFILE_A,
+ BATTERY_PROFILE_B,
+ BATTERY_PROFILE_MAX,
+};
+
struct smb1360_otg_regulator {
struct regulator_desc rdesc;
struct regulator_dev *rdev;
@@ -223,10 +254,12 @@ struct smb1360_chip {
bool recharge_disabled;
bool chg_inhibit_disabled;
bool iterm_disabled;
+ bool shdn_after_pwroff;
int iterm_ma;
int vfloat_mv;
int safety_time;
int resume_delta_mv;
+ u32 default_batt_profile;
unsigned int thermal_levels;
unsigned int therm_lvl_sel;
unsigned int *thermal_mitigation;
@@ -237,6 +270,8 @@ struct smb1360_chip {
int delta_soc;
int voltage_min_mv;
int voltage_empty_mv;
+ int batt_capacity_mah;
+ int cc_soc_coeff;
/* status tracking */
bool usb_present;
@@ -253,6 +288,8 @@ struct smb1360_chip {
u8 irq_cfg_mask[3];
int usb_psy_ma;
int charging_disabled_status;
+ u32 connected_rid;
+ u32 profile_rid[BATTERY_PROFILE_MAX];
u32 peek_poke_address;
u32 fg_access_type;
@@ -261,6 +298,7 @@ struct smb1360_chip {
int skip_reads;
struct dentry *debug_root;
+ struct qpnp_vadc_chip *vadc_dev;
struct power_supply *usb_psy;
struct power_supply batt_psy;
struct smb1360_otg_regulator otg_vreg;
@@ -319,6 +357,7 @@ static int __smb1360_read(struct smb1360_chip *chip, int reg,
} else {
*val = ret;
}
+ pr_debug("Reading 0x%02x=0x%02x\n", reg, *val);
return 0;
}
@@ -427,6 +466,27 @@ static int smb1360_read_bytes(struct smb1360_chip *chip, int reg,
return (rc < 0) ? rc : 0;
}
+static int smb1360_write_bytes(struct smb1360_chip *chip, int reg,
+ u8 *val, u8 bytes)
+{
+ s32 rc;
+
+ if (chip->skip_writes) {
+ *val = 0;
+ return 0;
+ }
+
+ mutex_lock(&chip->read_write_lock);
+ rc = i2c_smbus_write_i2c_block_data(chip->client, reg, bytes, val);
+ if (rc < 0)
+ dev_err(chip->dev,
+ "i2c write fail: can't read %d bytes from %02x: %d\n",
+ bytes, reg, rc);
+ mutex_unlock(&chip->read_write_lock);
+
+ return (rc < 0) ? rc : 0;
+}
+
static int smb1360_masked_write(struct smb1360_chip *chip, int reg,
u8 mask, u8 val)
{
@@ -454,6 +514,48 @@ out:
return rc;
}
+static int smb1360_enable_fg_access(struct smb1360_chip *chip)
+{
+ int rc;
+ u8 reg = 0, timeout = 50;
+
+ rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_ACCESS_ENABLED_BIT,
+ FG_ACCESS_ENABLED_BIT);
+ if (rc) {
+ pr_err("Couldn't enable FG access rc=%d\n", rc);
+ return rc;
+ }
+
+ while (timeout) {
+ /* delay for FG access to be granted */
+ msleep(200);
+ rc = smb1360_read(chip, IRQ_I_REG, &reg);
+ if (rc)
+ pr_err("Could't read IRQ_I_REG rc=%d\n", rc);
+ else if (reg & FG_ACCESS_ALLOWED_BIT)
+ break;
+ timeout--;
+ }
+
+ pr_debug("timeout=%d\n", timeout);
+
+ if (!timeout)
+ return -EBUSY;
+
+ return 0;
+}
+
+static int smb1360_disable_fg_access(struct smb1360_chip *chip)
+{
+ int rc;
+
+ rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_ACCESS_ENABLED_BIT, 0);
+ if (rc)
+ pr_err("Couldn't disable FG access rc=%d\n", rc);
+
+ return rc;
+}
+
static int smb1360_enable_volatile_writes(struct smb1360_chip *chip)
{
int rc;
@@ -812,6 +914,28 @@ static int smb1360_get_prop_current_now(struct smb1360_chip *chip)
return temp * 1000;
}
+static int smb1360_set_minimum_usb_current(struct smb1360_chip *chip)
+{
+ int rc = 0;
+
+ pr_debug("set USB current to minimum\n");
+ /* set input current limit to minimum (300mA)*/
+ rc = smb1360_masked_write(chip, CFG_BATT_CHG_ICL_REG,
+ INPUT_CURR_LIM_MASK,
+ INPUT_CURR_LIM_300MA);
+ if (rc)
+ pr_err("Couldn't set ICL mA rc=%d\n", rc);
+
+ if (!(chip->workaround_flags & WRKRND_USB100_FAIL)) {
+ rc = smb1360_masked_write(chip, CMD_IL_REG,
+ USB_CTRL_MASK, USB_100_BIT);
+ if (rc)
+ pr_err("Couldn't configure for USB100 rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
static int smb1360_set_appropriate_usb_current(struct smb1360_chip *chip)
{
int rc = 0, i, therm_ma, current_ma;
@@ -842,14 +966,15 @@ static int smb1360_set_appropriate_usb_current(struct smb1360_chip *chip)
if (current_ma <= 2) {
/*
- * SMB1360 does not support USB suspend so
- * disable charging if current <= 2
+ * SMB1360 does not support USB suspend -
+ * so set the current-limit to minimum in suspend.
*/
- pr_debug("current_ma=%d <= 2 disable charging\n", current_ma);
-
- rc = smb1360_charging_disable(chip, CURRENT, true);
+ pr_debug("current_ma=%d <= 2 set USB current to minimum\n",
+ current_ma);
+ rc = smb1360_set_minimum_usb_current(chip);
if (rc < 0)
- pr_err("Unable to disable charging rc=%d\n", rc);
+ pr_err("Couldn't to set minimum USB current rc = %d\n",
+ rc);
return rc;
}
@@ -914,11 +1039,6 @@ static int smb1360_set_appropriate_usb_current(struct smb1360_chip *chip)
pr_debug("fast-chg current set to = %d\n", fastchg_current[i]);
}
- /* enable charging, as it could have been disabled earlier */
- rc = smb1360_charging_disable(chip, CURRENT, false);
- if (rc < 0)
- pr_err("Unable to enable charging rc=%d\n", rc);
-
return rc;
}
@@ -950,30 +1070,18 @@ static int smb1360_system_temp_level_set(struct smb1360_chip *chip,
mutex_lock(&chip->current_change_lock);
prev_therm_lvl = chip->therm_lvl_sel;
chip->therm_lvl_sel = lvl_sel;
+
if (chip->therm_lvl_sel == (chip->thermal_levels - 1)) {
- /* Disable charging if highest value selected */
- rc = smb1360_charging_disable(chip, THERMAL, true);
- if (rc < 0) {
- pr_err("Couldn't disable charging rc %d\n", rc);
- goto out;
- }
- goto out;
+ rc = smb1360_set_minimum_usb_current(chip);
+ if (rc)
+ pr_err("Couldn't set USB current to minimum rc = %d\n",
+ rc);
+ } else {
+ rc = smb1360_set_appropriate_usb_current(chip);
+ if (rc)
+ pr_err("Couldn't set USB current rc = %d\n", rc);
}
- smb1360_set_appropriate_usb_current(chip);
-
- if (prev_therm_lvl == chip->thermal_levels - 1) {
- /*
- * If previously highest value was selected charging must have
- * been disabed. Hence enable charging.
- */
- rc = smb1360_charging_disable(chip, THERMAL, false);
- if (rc < 0) {
- pr_err("Couldn't enable charging rc %d\n", rc);
- goto out;
- }
- }
-out:
mutex_unlock(&chip->current_change_lock);
return rc;
}
@@ -1248,6 +1356,14 @@ static int fg_access_allowed_handler(struct smb1360_chip *chip, u8 rt_stat)
return 0;
}
+static int batt_id_complete_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("batt_id = %x\n", (rt_stat & BATT_ID_RESULT_BIT)
+ >> BATT_ID_SHIFT);
+
+ return 0;
+}
+
struct smb_irq_info {
const char *name;
int (*smb_irq)(struct smb1360_chip *chip,
@@ -1417,10 +1533,8 @@ static struct irq_handler_info handlers[] = {
.name = "fg_data_recovery",
},
{
- .name = "batt_id_result",
- },
- {
.name = "batt_id_complete",
+ .smb_irq = batt_id_complete_handler,
},
},
},
@@ -1428,6 +1542,8 @@ static struct irq_handler_info handlers[] = {
#define IRQ_LATCHED_MASK 0x02
#define IRQ_STATUS_MASK 0x01
+#define BATT_ID_LATCHED_MASK 0x08
+#define BATT_ID_STATUS_MASK 0x07
#define BITS_PER_IRQ 2
static irqreturn_t smb1360_stat_handler(int irq, void *dev_id)
{
@@ -1435,7 +1551,7 @@ static irqreturn_t smb1360_stat_handler(int irq, void *dev_id)
int i, j;
u8 triggered;
u8 changed;
- u8 rt_stat, prev_rt_stat;
+ u8 rt_stat, prev_rt_stat, irq_latched_mask, irq_status_mask;
int rc;
int handler_count = 0;
@@ -1459,12 +1575,19 @@ static irqreturn_t smb1360_stat_handler(int irq, void *dev_id)
}
for (j = 0; j < ARRAY_SIZE(handlers[i].irq_info); j++) {
+ if (handlers[i].stat_reg == IRQ_I_REG && j == 2) {
+ irq_latched_mask = BATT_ID_LATCHED_MASK;
+ irq_status_mask = BATT_ID_STATUS_MASK;
+ } else {
+ irq_latched_mask = IRQ_LATCHED_MASK;
+ irq_status_mask = IRQ_STATUS_MASK;
+ }
triggered = handlers[i].val
- & (IRQ_LATCHED_MASK << (j * BITS_PER_IRQ));
+ & (irq_latched_mask << (j * BITS_PER_IRQ));
rt_stat = handlers[i].val
- & (IRQ_STATUS_MASK << (j * BITS_PER_IRQ));
+ & (irq_status_mask << (j * BITS_PER_IRQ));
prev_rt_stat = handlers[i].prev_val
- & (IRQ_STATUS_MASK << (j * BITS_PER_IRQ));
+ & (irq_status_mask << (j * BITS_PER_IRQ));
changed = prev_rt_stat ^ rt_stat;
if (triggered || changed)
@@ -1500,6 +1623,8 @@ static int show_irq_count(struct seq_file *m, void *data)
for (i = 0; i < ARRAY_SIZE(handlers); i++)
for (j = 0; j < 4; j++) {
+ if (!handlers[i].irq_info[j].name)
+ continue;
seq_printf(m, "%s=%d\t(high=%d low=%d)\n",
handlers[i].irq_info[j].name,
handlers[i].irq_info[j].high
@@ -1770,34 +1895,216 @@ static const struct file_operations irq_stat_debugfs_ops = {
.release = single_release,
};
-#define FIRST_FG_CFG_REG 0x24
-#define LAST_FG_CFG_REG 0x2F
-static int show_fg_cfg_regs(struct seq_file *m, void *data)
+static int data_8(u8 *reg)
{
- struct smb1360_chip *chip = m->private;
- int rc;
+ return reg[0];
+}
+static int data_16(u8 *reg)
+{
+ return (reg[1] << 8) | reg[0];
+}
+static int data_24(u8 *reg)
+{
+ return (reg[2] << 16) | (reg[1] << 8) | reg[0];
+}
+static int data_28(u8 *reg)
+{
+ return ((reg[3] & 0xF) << 24) | (reg[2] << 16) |
+ (reg[1] << 8) | reg[0];
+}
+static int data_32(u8 *reg)
+{
+ return (reg[3] << 24) | (reg[2] << 16) |
+ (reg[1] << 8) | reg[0];
+}
+
+struct fg_regs {
+ int index;
+ int length;
+ char *param_name;
+ int (*calc_func) (u8 *);
+};
+
+static struct fg_regs fg_scratch_pad[] = {
+ {0, 2, "v_current_predicted", data_16},
+ {2, 2, "v_cutoff_predicted", data_16},
+ {4, 2, "v_full_predicted", data_16},
+ {6, 2, "ocv_estimate", data_16},
+ {8, 2, "rslow_drop", data_16},
+ {10, 2, "voltage_old", data_16},
+ {12, 2, "current_old", data_16},
+ {14, 4, "current_average_full", data_32},
+ {18, 2, "temperature", data_16},
+ {20, 2, "temp_last_track", data_16},
+ {22, 2, "ESR_nominal", data_16},
+ {26, 2, "Rslow", data_16},
+ {28, 2, "counter_imptr", data_16},
+ {30, 2, "counter_pulse", data_16},
+ {32, 1, "IRQ_delta_prev", data_8},
+ {33, 1, "cap_learning_counter", data_8},
+ {34, 4, "Vact_int_error", data_24},
+ {38, 3, "SOC_cutoff", data_24},
+ {41, 3, "SOC_full", data_24},
+ {44, 3, "SOC_auto_rechrge_temp", data_24},
+ {47, 3, "Battery_SOC", data_24},
+ {50, 4, "CC_SOC", data_28},
+ {54, 2, "SOC_filtered", data_16},
+ {56, 2, "SOC_Monotonic", data_16},
+ {58, 2, "CC_SOC_coeff", data_16},
+ {60, 2, "nominal_capacity", data_16},
+ {62, 2, "actual_capacity", data_16},
+ {68, 1, "temperature_counter", data_8},
+ {69, 3, "Vbatt_filtered", data_24},
+ {72, 3, "Ibatt_filtered", data_24},
+ {75, 2, "Current_CC_shadow", data_16},
+ {79, 2, "Ibatt_standby", data_16},
+ {82, 1, "Auto_recharge_SOC_threshold", data_8},
+ {83, 2, "System_cutoff_voltage", data_16},
+ {85, 2, "System_CC_to_CV_voltage", data_16},
+ {87, 2, "System_term_current", data_16},
+ {89, 2, "System_fake_term_current", data_16},
+};
+
+static struct fg_regs fg_cfg[] = {
+ {0, 2, "ESR_actual", data_16},
+ {4, 1, "IRQ_SOC_max", data_8},
+ {5, 1, "IRQ_SOC_min", data_8},
+ {6, 1, "IRQ_volt_empty", data_8},
+ {7, 1, "Temp_external", data_8},
+ {8, 1, "IRQ_delta_threshold", data_8},
+ {9, 1, "JIETA_soft_cold", data_8},
+ {10, 1, "JIETA_soft_hot", data_8},
+ {11, 1, "IRQ_volt_min", data_8},
+ {14, 2, "ESR_sys_replace", data_16},
+};
+
+static struct fg_regs fg_shdw[] = {
+ {0, 1, "Latest_battery_info", data_8},
+ {1, 1, "Latest_Msys_SOC", data_8},
+ {2, 2, "Battery_capacity", data_16},
+ {4, 2, "Rslow_drop", data_16},
+ {6, 1, "Latest_SOC", data_8},
+ {7, 1, "Latest_Cutoff_SOC", data_8},
+ {8, 1, "Latest_full_SOC", data_8},
+ {9, 2, "Voltage_shadow", data_16},
+ {11, 2, "Current_shadow", data_16},
+ {13, 2, "Latest_temperature", data_16},
+ {15, 1, "Latest_system_sbits", data_8},
+};
+
+#define FIRST_FG_CFG_REG 0x20
+#define LAST_FG_CFG_REG 0x2F
+#define FIRST_FG_SHDW_REG 0x60
+#define LAST_FG_SHDW_REG 0x6F
+#define FG_SCRATCH_PAD_MAX 90
+#define FG_SCRATCH_PAD_BASE_REG 0x80
+#define SMB1360_I2C_READ_LENGTH 32
+
+static int smb1360_check_cycle_stretch(struct smb1360_chip *chip)
+{
+ int rc = 0;
u8 reg;
- u8 addr;
- for (addr = FIRST_FG_CFG_REG; addr <= LAST_FG_CFG_REG; addr++) {
- rc = smb1360_read(chip, addr, &reg);
- if (!rc)
- seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
+ rc = smb1360_read(chip, STATUS_4_REG, &reg);
+ if (rc) {
+ pr_err("Unable to read status regiseter\n");
+ } else if (reg & CYCLE_STRETCH_ACTIVE_BIT) {
+ /* clear cycle stretch */
+ rc = smb1360_masked_write(chip, CMD_I2C_REG,
+ CYCLE_STRETCH_CLEAR_BIT, CYCLE_STRETCH_CLEAR_BIT);
+ if (rc)
+ pr_err("Unable to clear cycle stretch\n");
}
- return 0;
+ return rc;
}
-static int fg_cfg_debugfs_open(struct inode *inode, struct file *file)
+static int show_fg_regs(struct seq_file *m, void *data)
+{
+ struct smb1360_chip *chip = m->private;
+ int rc, i , j, rem_length;
+ u8 reg[FG_SCRATCH_PAD_MAX];
+
+ rc = smb1360_check_cycle_stretch(chip);
+ if (rc)
+ pr_err("Unable to check cycle-stretch\n");
+
+ rc = smb1360_enable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't request FG access rc=%d\n", rc);
+ return rc;
+ }
+
+ for (i = 0; i < (FG_SCRATCH_PAD_MAX / SMB1360_I2C_READ_LENGTH); i++) {
+ j = i * SMB1360_I2C_READ_LENGTH;
+ rc = smb1360_read_bytes(chip, FG_SCRATCH_PAD_BASE_REG + j,
+ &reg[j], SMB1360_I2C_READ_LENGTH);
+ if (rc) {
+ pr_err("Couldn't read scratch registers rc=%d\n", rc);
+ break;
+ }
+ }
+
+ j = i * SMB1360_I2C_READ_LENGTH;
+ rem_length = FG_SCRATCH_PAD_MAX % SMB1360_I2C_READ_LENGTH;
+ if (rem_length) {
+ rc = smb1360_read_bytes(chip, FG_SCRATCH_PAD_BASE_REG + j,
+ &reg[j], rem_length);
+ if (rc)
+ pr_err("Couldn't read scratch registers rc=%d\n", rc);
+ }
+
+ rc = smb1360_disable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't disable FG access rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_check_cycle_stretch(chip);
+ if (rc)
+ pr_err("Unable to check cycle-stretch\n");
+
+
+ seq_puts(m, "FG scratch-pad registers\n");
+ for (i = 0; i < ARRAY_SIZE(fg_scratch_pad); i++)
+ seq_printf(m, "\t%s = %x\n", fg_scratch_pad[i].param_name,
+ fg_scratch_pad[i].calc_func(&reg[fg_scratch_pad[i].index]));
+
+ rem_length = LAST_FG_CFG_REG - FIRST_FG_CFG_REG + 1;
+ rc = smb1360_read_bytes(chip, FIRST_FG_CFG_REG,
+ &reg[0], rem_length);
+ if (rc)
+ pr_err("Couldn't read config registers rc=%d\n", rc);
+
+ seq_puts(m, "FG config registers\n");
+ for (i = 0; i < ARRAY_SIZE(fg_cfg); i++)
+ seq_printf(m, "\t%s = %x\n", fg_cfg[i].param_name,
+ fg_cfg[i].calc_func(&reg[fg_cfg[i].index]));
+
+ rem_length = LAST_FG_SHDW_REG - FIRST_FG_SHDW_REG + 1;
+ rc = smb1360_read_bytes(chip, FIRST_FG_SHDW_REG,
+ &reg[0], rem_length);
+ if (rc)
+ pr_err("Couldn't read shadow registers rc=%d\n", rc);
+
+ seq_puts(m, "FG shadow registers\n");
+ for (i = 0; i < ARRAY_SIZE(fg_shdw); i++)
+ seq_printf(m, "\t%s = %x\n", fg_shdw[i].param_name,
+ fg_shdw[i].calc_func(&reg[fg_shdw[i].index]));
+
+ return rc;
+}
+
+static int fg_regs_open(struct inode *inode, struct file *file)
{
struct smb1360_chip *chip = inode->i_private;
- return single_open(file, show_fg_cfg_regs, chip);
+ return single_open(file, show_fg_regs, chip);
}
-static const struct file_operations fg_cfg_debugfs_ops = {
+static const struct file_operations fg_regs_debugfs_ops = {
.owner = THIS_MODULE,
- .open = fg_cfg_debugfs_open,
+ .open = fg_regs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
@@ -1889,6 +2196,144 @@ static int smb1360_regulator_init(struct smb1360_chip *chip)
return rc;
}
+static int smb1360_check_batt_profile(struct smb1360_chip *chip)
+{
+ int rc, i, timeout = 50;
+ u8 reg = 0, loaded_profile, new_profile = 0, bid_mask;
+
+ if (!chip->connected_rid) {
+ pr_debug("Skip batt-profile loading connected_rid=%d\n",
+ chip->connected_rid);
+ return 0;
+ }
+
+ rc = smb1360_read(chip, SHDW_FG_BATT_STATUS, &reg);
+ if (rc) {
+ pr_err("Couldn't read FG_BATT_STATUS rc=%d\n", rc);
+ goto fail_profile;
+ }
+
+ loaded_profile = !!(reg & BATTERY_PROFILE_BIT) ?
+ BATTERY_PROFILE_B : BATTERY_PROFILE_A;
+
+ pr_debug("fg_batt_status=%x loaded_profile=%d\n", reg, loaded_profile);
+
+ for (i = 0; i < BATTERY_PROFILE_MAX; i++) {
+ pr_debug("profile=%d profile_rid=%d connected_rid=%d\n", i,
+ chip->profile_rid[i],
+ chip->connected_rid);
+ if (abs(chip->profile_rid[i] - chip->connected_rid) <
+ (div_u64(chip->connected_rid, 10)))
+ break;
+ }
+
+ if (i == BATTERY_PROFILE_MAX) {
+ pr_err("None of the battery-profiles match the connected-RID\n");
+ return 0;
+ } else {
+ if (i == loaded_profile) {
+ pr_debug("Loaded Profile-RID == connected-RID\n");
+ return 0;
+ } else {
+ new_profile = (loaded_profile == BATTERY_PROFILE_A) ?
+ BATTERY_PROFILE_B : BATTERY_PROFILE_A;
+ bid_mask = (new_profile == BATTERY_PROFILE_A) ?
+ BATT_PROFILEA_MASK : BATT_PROFILEB_MASK;
+ pr_info("Loaded Profile-RID != connected-RID, switch-profile old_profile=%d new_profile=%d\n",
+ loaded_profile, new_profile);
+ }
+ }
+
+ /* set the BID mask */
+ rc = smb1360_masked_write(chip, CFG_FG_BATT_CTRL_REG,
+ BATT_PROFILE_SELECT_MASK, bid_mask);
+ if (rc) {
+ pr_err("Couldn't reset battery-profile rc=%d\n", rc);
+ goto fail_profile;
+ }
+
+ /* enable FG access */
+ rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_ACCESS_ENABLED_BIT,
+ FG_ACCESS_ENABLED_BIT);
+ if (rc) {
+ pr_err("Couldn't enable FG access rc=%d\n", rc);
+ goto fail_profile;
+ }
+
+ while (timeout) {
+ /* delay for FG access to be granted */
+ msleep(100);
+ rc = smb1360_read(chip, IRQ_I_REG, &reg);
+ if (rc) {
+ pr_err("Could't read IRQ_I_REG rc=%d\n", rc);
+ goto restore_fg;
+ }
+ if (reg & FG_ACCESS_ALLOWED_BIT)
+ break;
+ timeout--;
+ }
+ if (!timeout) {
+ pr_err("FG access timed-out\n");
+ rc = -EAGAIN;
+ goto restore_fg;
+ }
+
+ /* delay after handshaking for profile-switch to continue */
+ msleep(1500);
+
+ /* reset FG */
+ rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_RESET_BIT,
+ FG_RESET_BIT);
+ if (rc) {
+ pr_err("Couldn't reset FG rc=%d\n", rc);
+ goto restore_fg;
+ }
+
+ /* un-reset FG */
+ rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_RESET_BIT, 0);
+ if (rc) {
+ pr_err("Couldn't un-reset FG rc=%d\n", rc);
+ goto restore_fg;
+ }
+
+ /* disable FG access */
+ rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_ACCESS_ENABLED_BIT, 0);
+ if (rc) {
+ pr_err("Couldn't disable FG access rc=%d\n", rc);
+ goto restore_fg;
+ }
+
+ timeout = 10;
+ while (timeout) {
+ /* delay for profile to change */
+ msleep(500);
+ rc = smb1360_read(chip, SHDW_FG_BATT_STATUS, &reg);
+ if (rc) {
+ pr_err("Could't read FG_BATT_STATUS rc=%d\n", rc);
+ goto restore_fg;
+ }
+
+ reg = !!(reg & BATTERY_PROFILE_BIT);
+ if (reg == new_profile) {
+ pr_info("New profile=%d loaded\n", new_profile);
+ break;
+ }
+ timeout--;
+ }
+
+ if (!timeout) {
+ pr_err("New profile could not be loaded\n");
+ return -EBUSY;
+ }
+
+ return 0;
+
+restore_fg:
+ smb1360_masked_write(chip, CMD_I2C_REG, FG_ACCESS_ENABLED_BIT, 0);
+fail_profile:
+ return rc;
+}
+
static int determine_initial_status(struct smb1360_chip *chip)
{
int rc;
@@ -1943,8 +2388,8 @@ static int determine_initial_status(struct smb1360_chip *chip)
static int smb1360_fg_config(struct smb1360_chip *chip)
{
- int rc, temp;
- u8 reg = 0;
+ int rc = 0, temp, fcc_mah;
+ u8 reg = 0, reg2[2];
/*
* The below IRQ thresholds are not accessible in REV_1
@@ -2019,7 +2464,53 @@ static int smb1360_fg_config(struct smb1360_chip *chip)
}
}
- return 0;
+ /* scratch-pad register config */
+ if (chip->batt_capacity_mah != -EINVAL) {
+ rc = smb1360_enable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't enable FG access rc=%d\n", rc);
+ return rc;
+ }
+ rc = smb1360_read_bytes(chip, NOMINAL_CAPACITY_REG,
+ reg2, 2);
+ if (rc) {
+ pr_err("Failed to read NOM CAPACITY rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+ fcc_mah = (reg2[1] << 8) | reg2[0];
+ if (fcc_mah == chip->batt_capacity_mah) {
+ pr_debug("battery capacity correct\n");
+ goto disable_fg;
+ }
+ /* Update the battery capacity */
+ reg2[1] = (chip->batt_capacity_mah & 0xFF00) >> 8;
+ reg2[0] = (chip->batt_capacity_mah & 0xFF);
+ rc = smb1360_write_bytes(chip, NOMINAL_CAPACITY_REG,
+ reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write batt-capacity rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+ /* Update CC to SOC COEFF */
+ if (chip->cc_soc_coeff != -EINVAL) {
+ reg2[1] = (chip->cc_soc_coeff & 0xFF00) >> 8;
+ reg2[0] = (chip->cc_soc_coeff & 0xFF);
+ rc = smb1360_write_bytes(chip, CC_TO_SOC_COEFF,
+ reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write cc_soc_coeff rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+ }
+disable_fg:
+ /* disable FG access */
+ smb1360_disable_fg_access(chip);
+ }
+
+ return rc;
}
static void smb1360_check_feature_support(struct smb1360_chip *chip)
@@ -2045,6 +2536,51 @@ static void smb1360_check_feature_support(struct smb1360_chip *chip)
}
}
+static int smb1360_enable(struct smb1360_chip *chip, bool enable)
+{
+ int rc = 0;
+ u8 val, shdn_status, shdn_cmd_en, shdn_cmd_polar;
+
+ rc = smb1360_read(chip, SHDN_CTRL_REG, &val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read 0x1A reg rc = %d\n", rc);
+ return rc;
+ }
+ shdn_cmd_en = val & SHDN_CMD_USE_BIT;
+ shdn_cmd_polar = !!(val & SHDN_CMD_POLARITY_BIT);
+ shdn_status = val & SHDN_CMD_BIT;
+
+ val = (shdn_cmd_polar ^ enable) ? SHDN_CMD_BIT : 0;
+
+ if (shdn_cmd_en) {
+ if (shdn_status != val) {
+ rc = smb1360_masked_write(chip, CMD_IL_REG,
+ SHDN_CMD_BIT, val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't shutdown smb1360 rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+ } else {
+ dev_dbg(chip->dev, "SMB not configured for CMD based shutdown\n");
+ }
+
+ return rc;
+}
+
+static inline int smb1360_poweroff(struct smb1360_chip *chip)
+{
+ pr_debug("power off smb1360\n");
+ return smb1360_enable(chip, false);
+}
+
+static inline int smb1360_poweron(struct smb1360_chip *chip)
+{
+ pr_debug("power on smb1360\n");
+ return smb1360_enable(chip, true);
+}
+
static int smb1360_hw_init(struct smb1360_chip *chip)
{
int rc;
@@ -2059,6 +2595,19 @@ static int smb1360_hw_init(struct smb1360_chip *chip)
rc);
return rc;
}
+
+ if (chip->shdn_after_pwroff) {
+ rc = smb1360_poweron(chip);
+ if (rc < 0) {
+ pr_err("smb1360 power on failed\n");
+ return rc;
+ }
+ }
+
+ rc = smb1360_check_batt_profile(chip);
+ if (rc)
+ pr_err("Unable to modify battery profile\n");
+
/*
* set chg en by cmd register, set chg en by writing bit 1,
* enable auto pre to fast
@@ -2222,12 +2771,15 @@ static int smb1360_hw_init(struct smb1360_chip *chip)
/* interrupt enabling - active low */
if (chip->client->irq) {
- mask = CHG_STAT_IRQ_ONLY_BIT | CHG_STAT_ACTIVE_HIGH_BIT
- | CHG_STAT_DISABLE_BIT;
+ mask = CHG_STAT_IRQ_ONLY_BIT
+ | CHG_STAT_ACTIVE_HIGH_BIT
+ | CHG_STAT_DISABLE_BIT
+ | CHG_TEMP_CHG_ERR_BLINK_BIT;
+
if (!chip->pulsed_irq)
reg = CHG_STAT_IRQ_ONLY_BIT;
else
- reg = 0;
+ reg = CHG_TEMP_CHG_ERR_BLINK_BIT;
rc = smb1360_masked_write(chip, CFG_STAT_CTRL_REG, mask, reg);
if (rc < 0) {
dev_err(chip->dev, "Couldn't set irq config rc = %d\n",
@@ -2288,7 +2840,11 @@ static int smb1360_hw_init(struct smb1360_chip *chip)
}
- smb1360_fg_config(chip);
+ rc = smb1360_fg_config(chip);
+ if (rc < 0) {
+ pr_err("Couldn't configure FG rc=%d\n", rc);
+ return rc;
+ }
rc = smb1360_charging_disable(chip, USER, !!chip->charging_disabled);
if (rc)
@@ -2298,6 +2854,78 @@ static int smb1360_hw_init(struct smb1360_chip *chip)
return rc;
}
+static int smb_parse_batt_id(struct smb1360_chip *chip)
+{
+ int rc = 0, rpull = 0, vref = 0;
+ int64_t denom, batt_id_uv;
+ struct device_node *node = chip->dev->of_node;
+ struct qpnp_vadc_result result;
+
+ chip->vadc_dev = qpnp_get_vadc(chip->dev, "smb1360");
+ if (IS_ERR(chip->vadc_dev)) {
+ rc = PTR_ERR(chip->vadc_dev);
+ if (rc == -EPROBE_DEFER)
+ pr_err("vadc not found - defer rc=%d\n", rc);
+ else
+ pr_err("vadc property missing, rc=%d\n", rc);
+
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,profile-a-rid-kohm",
+ &chip->profile_rid[0]);
+ if (rc < 0) {
+ pr_err("Couldn't read profile-a-rid-kohm rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,profile-b-rid-kohm",
+ &chip->profile_rid[1]);
+ if (rc < 0) {
+ pr_err("Couldn't read profile-b-rid-kohm rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,batt-id-vref-uv", &vref);
+ if (rc < 0) {
+ pr_err("Couldn't read batt-id-vref-uv rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,batt-id-rpullup-kohm", &rpull);
+ if (rc < 0) {
+ pr_err("Couldn't read batt-id-rpullup-kohm rc=%d\n", rc);
+ return rc;
+ }
+
+ /* read battery ID */
+ rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX2_BAT_ID, &result);
+ if (rc) {
+ pr_err("error reading batt id channel = %d, rc = %d\n",
+ LR_MUX2_BAT_ID, rc);
+ return rc;
+ }
+ batt_id_uv = result.physical;
+
+ if (batt_id_uv == 0) {
+ /* vadc not correct or batt id line grounded, report 0 kohms */
+ pr_err("batt_id_uv = 0, batt-id grounded using same profile\n");
+ return 0;
+ }
+
+ denom = div64_s64(vref * 1000000LL, batt_id_uv) - 1000000LL;
+ if (denom == 0) {
+ /* batt id connector might be open, return 0 kohms */
+ return 0;
+ }
+ chip->connected_rid = div64_s64(rpull * 1000000LL + denom/2, denom);
+
+ pr_debug("batt_id_voltage = %lld, connected_rid = %d\n",
+ batt_id_uv, chip->connected_rid);
+
+ return 0;
+}
+
static int smb_parse_dt(struct smb1360_chip *chip)
{
int rc;
@@ -2308,6 +2936,15 @@ static int smb_parse_dt(struct smb1360_chip *chip)
return -EINVAL;
}
+ if (of_property_read_bool(node, "qcom,batt-profile-select")) {
+ rc = smb_parse_batt_id(chip);
+ if (rc < 0) {
+ if (rc != -EPROBE_DEFER)
+ pr_err("Unable to parse batt-id rc=%d\n", rc);
+ return rc;
+ }
+ }
+
chip->pulsed_irq = of_property_read_bool(node, "qcom,stat-pulsed-irq");
rc = of_property_read_u32(node, "qcom,float-voltage-mv",
@@ -2350,6 +2987,9 @@ static int smb_parse_dt(struct smb1360_chip *chip)
chip->batt_id_disabled = of_property_read_bool(node,
"qcom,batt-id-disabled");
+ chip->shdn_after_pwroff = of_property_read_bool(node,
+ "qcom,shdn-after-pwroff");
+
if (of_find_property(node, "qcom,thermal-mitigation",
&chip->thermal_levels)) {
chip->thermal_mitigation = devm_kzalloc(chip->dev,
@@ -2394,6 +3034,16 @@ static int smb_parse_dt(struct smb1360_chip *chip)
if (rc < 0)
chip->voltage_empty_mv = -EINVAL;
+ rc = of_property_read_u32(node, "qcom,fg-batt-capacity-mah",
+ &chip->batt_capacity_mah);
+ if (rc < 0)
+ chip->batt_capacity_mah = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-cc-soc-coeff",
+ &chip->cc_soc_coeff);
+ if (rc < 0)
+ chip->cc_soc_coeff = -EINVAL;
+
return 0;
}
@@ -2540,12 +3190,12 @@ static int smb1360_probe(struct i2c_client *client,
"Couldn't create cmd debug file rc = %d\n",
rc);
- ent = debugfs_create_file("fg_config_registers",
+ ent = debugfs_create_file("fg_regs",
S_IFREG | S_IRUGO, chip->debug_root, chip,
- &fg_cfg_debugfs_ops);
+ &fg_regs_debugfs_ops);
if (!ent)
dev_err(chip->dev,
- "Couldn't create fg_config debug file rc = %d\n",
+ "Couldn't create fg_scratch_pad debug file rc = %d\n",
rc);
ent = debugfs_create_x32("address", S_IFREG | S_IWUSR | S_IRUGO,
@@ -2725,6 +3375,19 @@ static int smb1360_resume(struct device *dev)
return 0;
}
+static void smb1360_shutdown(struct i2c_client *client)
+{
+ int rc;
+ struct smb1360_chip *chip = i2c_get_clientdata(client);
+
+ if (chip->shdn_after_pwroff) {
+ rc = smb1360_poweroff(chip);
+ if (rc)
+ pr_err("Couldn't shutdown smb1360, rc = %d\n", rc);
+ pr_info("smb1360 power off\n");
+ }
+}
+
static const struct dev_pm_ops smb1360_pm_ops = {
.resume = smb1360_resume,
.suspend_noirq = smb1360_suspend_noirq,
@@ -2751,6 +3414,7 @@ static struct i2c_driver smb1360_driver = {
},
.probe = smb1360_probe,
.remove = smb1360_remove,
+ .shutdown = smb1360_shutdown,
.id_table = smb1360_id,
};
diff --git a/drivers/staging/android/ion/ion_heap.c b/drivers/staging/android/ion/ion_heap.c
index 482afbfa9ace..e176e2babe22 100644
--- a/drivers/staging/android/ion/ion_heap.c
+++ b/drivers/staging/android/ion/ion_heap.c
@@ -25,6 +25,7 @@
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/highmem.h>
+#include <linux/dma-mapping.h>
#include "ion.h"
#include "ion_priv.h"
@@ -112,16 +113,15 @@ int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
* chunks to minimize the number of memsets and vmaps/vunmaps.
*
* Note that the `pages' array should be composed of all 4K pages.
+ *
+ * NOTE: This function does not guarantee synchronization of the caches
+ * and thus caller is responsible for handling any cache maintenance
+ * operations needed.
*/
int ion_heap_pages_zero(struct page **pages, int num_pages)
{
- int i, j, k, npages_to_vmap;
+ int i, j, npages_to_vmap;
void *ptr = NULL;
- /*
- * It's cheaper just to use writecombine memory and skip the
- * cache vs. using a cache memory and trying to flush it afterwards
- */
- pgprot_t pgprot = pgprot_writecombine(PAGE_KERNEL);
/*
* As an optimization, we manually zero out all of the pages
@@ -137,7 +137,7 @@ int ion_heap_pages_zero(struct page **pages, int num_pages)
for (j = 0; j < MAX_VMAP_RETRIES && npages_to_vmap;
++j) {
ptr = vmap(&pages[i], npages_to_vmap,
- VM_IOREMAP, pgprot);
+ VM_IOREMAP, PAGE_KERNEL);
if (ptr)
break;
else
@@ -146,18 +146,6 @@ int ion_heap_pages_zero(struct page **pages, int num_pages)
if (!ptr)
return -ENOMEM;
- /*
- * We have to invalidate the cache here because there
- * might be dirty lines to these physical pages (which
- * we don't care about) that could get written out at
- * any moment.
- */
- for (k = 0; k < npages_to_vmap; k++) {
- void *p = kmap_atomic(pages[i + k]);
-
- dmac_inv_range(p, p + PAGE_SIZE);
- kunmap_atomic(p);
- }
memset(ptr, 0, npages_to_vmap * PAGE_SIZE);
vunmap(ptr);
}
@@ -165,11 +153,13 @@ int ion_heap_pages_zero(struct page **pages, int num_pages)
return 0;
}
-static int ion_heap_alloc_pages_mem(int page_tbl_size,
- struct pages_mem *pages_mem)
+int ion_heap_alloc_pages_mem(struct pages_mem *pages_mem)
{
struct page **pages;
+ unsigned int page_tbl_size;
+
pages_mem->free_fn = kfree;
+ page_tbl_size = sizeof(struct page *) * (pages_mem->size >> PAGE_SHIFT);
if (page_tbl_size > SZ_8K) {
/*
* Do fallback to ensure we have a balance between
@@ -193,7 +183,7 @@ static int ion_heap_alloc_pages_mem(int page_tbl_size,
return 0;
}
-static void ion_heap_free_pages_mem(struct pages_mem *pages_mem)
+void ion_heap_free_pages_mem(struct pages_mem *pages_mem)
{
pages_mem->free_fn(pages_mem->pages);
}
@@ -203,15 +193,17 @@ int ion_heap_high_order_page_zero(struct page *page, int order)
int i, ret;
struct pages_mem pages_mem;
int npages = 1 << order;
- int page_tbl_size = sizeof(struct page *) * npages;
+ pages_mem.size = npages * PAGE_SIZE;
- if (ion_heap_alloc_pages_mem(page_tbl_size, &pages_mem))
+ if (ion_heap_alloc_pages_mem(&pages_mem))
return -ENOMEM;
for (i = 0; i < (1 << order); ++i)
pages_mem.pages[i] = page + i;
ret = ion_heap_pages_zero(pages_mem.pages, npages);
+ dma_sync_single_for_device(NULL, page_to_phys(page), pages_mem.size,
+ DMA_BIDIRECTIONAL);
ion_heap_free_pages_mem(&pages_mem);
return ret;
}
@@ -220,16 +212,12 @@ int ion_heap_buffer_zero(struct ion_buffer *buffer)
{
struct sg_table *table = buffer->sg_table;
struct scatterlist *sg;
- int i, j, ret = 0, npages = 0, page_tbl_size = 0;
+ int i, j, ret = 0, npages = 0;
struct pages_mem pages_mem;
- for_each_sg(table->sgl, sg, table->nents, i) {
- unsigned long len = sg_dma_len(sg);
- int nrpages = len >> PAGE_SHIFT;
- page_tbl_size += sizeof(struct page *) * nrpages;
- }
+ pages_mem.size = PAGE_ALIGN(buffer->size);
- if (ion_heap_alloc_pages_mem(page_tbl_size, &pages_mem))
+ if (ion_heap_alloc_pages_mem(&pages_mem))
return -ENOMEM;
for_each_sg(table->sgl, sg, table->nents, i) {
@@ -241,6 +229,8 @@ int ion_heap_buffer_zero(struct ion_buffer *buffer)
}
ret = ion_heap_pages_zero(pages_mem.pages, npages);
+ dma_sync_sg_for_device(NULL, table->sgl, table->nents,
+ DMA_BIDIRECTIONAL);
ion_heap_free_pages_mem(&pages_mem);
return ret;
}
diff --git a/drivers/staging/android/ion/ion_page_pool.c b/drivers/staging/android/ion/ion_page_pool.c
index 665a42b73a3b..22422189453e 100644
--- a/drivers/staging/android/ion/ion_page_pool.c
+++ b/drivers/staging/android/ion/ion_page_pool.c
@@ -42,9 +42,6 @@ static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool)
if (ion_heap_high_order_page_zero(page, pool->order))
goto error_free_pages;
- ion_pages_sync_for_device(NULL, page, PAGE_SIZE << pool->order,
- DMA_BIDIRECTIONAL);
-
return page;
error_free_pages:
__free_pages(page, pool->order);
@@ -101,22 +98,25 @@ static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high)
return page;
}
-void *ion_page_pool_alloc(struct ion_page_pool *pool)
+void *ion_page_pool_alloc(struct ion_page_pool *pool, bool *from_pool)
{
struct page *page = NULL;
BUG_ON(!pool);
- mutex_lock(&pool->mutex);
- if (pool->high_count)
- page = ion_page_pool_remove(pool, true);
- else if (pool->low_count)
- page = ion_page_pool_remove(pool, false);
- mutex_unlock(&pool->mutex);
+ *from_pool = true;
- if (!page)
+ if (mutex_trylock(&pool->mutex)) {
+ if (pool->high_count)
+ page = ion_page_pool_remove(pool, true);
+ else if (pool->low_count)
+ page = ion_page_pool_remove(pool, false);
+ mutex_unlock(&pool->mutex);
+ }
+ if (!page) {
page = ion_page_pool_alloc_pages(pool);
-
+ *from_pool = false;
+ }
return page;
}
diff --git a/drivers/staging/android/ion/ion_priv.h b/drivers/staging/android/ion/ion_priv.h
index ea48eb013e02..d4a89a1f4923 100644
--- a/drivers/staging/android/ion/ion_priv.h
+++ b/drivers/staging/android/ion/ion_priv.h
@@ -223,6 +223,7 @@ void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap);
struct pages_mem {
struct page **pages;
+ u32 size;
void (*free_fn) (const void *);
};
@@ -237,6 +238,8 @@ int ion_heap_map_user(struct ion_heap *, struct ion_buffer *,
int ion_heap_pages_zero(struct page **pages, int num_pages);
int ion_heap_buffer_zero(struct ion_buffer *buffer);
int ion_heap_high_order_page_zero(struct page *page, int order);
+int ion_heap_alloc_pages_mem(struct pages_mem *pages_mem);
+void ion_heap_free_pages_mem(struct pages_mem *pages_mem);
/**
* ion_heap_init_deferred_free -- initialize deferred free functionality
@@ -374,7 +377,7 @@ struct ion_page_pool {
struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order);
void ion_page_pool_destroy(struct ion_page_pool *);
-void *ion_page_pool_alloc(struct ion_page_pool *);
+void *ion_page_pool_alloc(struct ion_page_pool *, bool *from_pool);
void ion_page_pool_free(struct ion_page_pool *, struct page *);
/** ion_page_pool_shrink - shrinks the size of the memory cached in the pool
diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c
index 40b0c154f4a5..0563876bfe6e 100644
--- a/drivers/staging/android/ion/ion_system_heap.c
+++ b/drivers/staging/android/ion/ion_system_heap.c
@@ -29,9 +29,9 @@
#include <linux/dma-mapping.h>
#include <trace/events/kmem.h>
-static gfp_t high_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN |
+static gfp_t high_order_gfp_flags = (GFP_HIGHUSER | __GFP_NOWARN |
__GFP_NORETRY) & ~__GFP_WAIT;
-static gfp_t low_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN);
+static gfp_t low_order_gfp_flags = (GFP_HIGHUSER | __GFP_NOWARN);
#ifndef CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS
static const unsigned int orders[] = {9, 8, 4, 0};
@@ -63,13 +63,15 @@ struct ion_system_heap {
struct page_info {
struct page *page;
+ bool from_pool;
unsigned int order;
struct list_head list;
};
static struct page *alloc_buffer_page(struct ion_system_heap *heap,
struct ion_buffer *buffer,
- unsigned long order)
+ unsigned long order,
+ bool *from_pool)
{
bool cached = ion_buffer_cached(buffer);
struct page *page;
@@ -79,7 +81,7 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap,
pool = heap->uncached_pools[order_to_index(order)];
else
pool = heap->cached_pools[order_to_index(order)];
- page = ion_page_pool_alloc(pool);
+ page = ion_page_pool_alloc(pool, from_pool);
if (!page)
return 0;
@@ -113,6 +115,7 @@ static struct page_info *alloc_largest_available(struct ion_system_heap *heap,
struct page *page;
struct page_info *info;
int i;
+ bool from_pool;
for (i = 0; i < num_orders; i++) {
if (size < order_to_size(orders[i]))
@@ -120,7 +123,7 @@ static struct page_info *alloc_largest_available(struct ion_system_heap *heap,
if (max_order < orders[i])
continue;
- page = alloc_buffer_page(heap, buffer, orders[i]);
+ page = alloc_buffer_page(heap, buffer, orders[i], &from_pool);
if (!page)
continue;
@@ -128,11 +131,39 @@ static struct page_info *alloc_largest_available(struct ion_system_heap *heap,
if (info) {
info->page = page;
info->order = orders[i];
+ info->from_pool = from_pool;
}
return info;
}
return NULL;
}
+static unsigned int process_info(struct page_info *info,
+ struct scatterlist *sg,
+ struct scatterlist *sg_sync,
+ struct pages_mem *data, unsigned int i)
+{
+ struct page *page = info->page;
+ unsigned int j;
+
+ if (sg_sync) {
+ sg_set_page(sg_sync, page, (1 << info->order) * PAGE_SIZE, 0);
+ sg_dma_address(sg_sync) = page_to_phys(page);
+ }
+ sg_set_page(sg, page, (1 << info->order) * PAGE_SIZE, 0);
+ /*
+ * This is not correct - sg_dma_address needs a dma_addr_t
+ * that is valid for the the targeted device, but this works
+ * on the currently targeted hardware.
+ */
+ sg_dma_address(sg) = page_to_phys(page);
+ if (data) {
+ for (j = 0; j < (1 << info->order); ++j)
+ data->pages[i++] = nth_page(page, j);
+ }
+ list_del(&info->list);
+ kfree(info);
+ return i;
+}
static int ion_system_heap_allocate(struct ion_heap *heap,
struct ion_buffer *buffer,
@@ -143,54 +174,137 @@ static int ion_system_heap_allocate(struct ion_heap *heap,
struct ion_system_heap,
heap);
struct sg_table *table;
+ struct sg_table table_sync;
struct scatterlist *sg;
+ struct scatterlist *sg_sync;
int ret;
struct list_head pages;
+ struct list_head pages_from_pool;
struct page_info *info, *tmp_info;
int i = 0;
+ unsigned int nents_sync = 0;
unsigned long size_remaining = PAGE_ALIGN(size);
unsigned int max_order = orders[0];
+ struct pages_mem data;
+ unsigned int sz;
if (align > PAGE_SIZE)
return -EINVAL;
+ data.size = 0;
INIT_LIST_HEAD(&pages);
+ INIT_LIST_HEAD(&pages_from_pool);
while (size_remaining > 0) {
info = alloc_largest_available(sys_heap, buffer, size_remaining,
max_order);
if (!info)
goto err;
- list_add_tail(&info->list, &pages);
- size_remaining -= (1 << info->order) * PAGE_SIZE;
+
+ sz = (1 << info->order) * PAGE_SIZE;
+
+ if (info->from_pool) {
+ list_add_tail(&info->list, &pages_from_pool);
+ } else {
+ list_add_tail(&info->list, &pages);
+ data.size += sz;
+ ++nents_sync;
+ }
+ size_remaining -= sz;
max_order = info->order;
i++;
}
+
+ ret = ion_heap_alloc_pages_mem(&data);
+
+ if (ret)
+ goto err;
+
table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table)
- goto err;
+ goto err_free_data_pages;
ret = sg_alloc_table(table, i, GFP_KERNEL);
if (ret)
goto err1;
+ if (nents_sync) {
+ ret = sg_alloc_table(&table_sync, nents_sync, GFP_KERNEL);
+ if (ret)
+ goto err_free_sg;
+ }
+
+ i = 0;
sg = table->sgl;
- list_for_each_entry_safe(info, tmp_info, &pages, list) {
- struct page *page = info->page;
- sg_set_page(sg, page, (1 << info->order) * PAGE_SIZE, 0);
+ sg_sync = table_sync.sgl;
+
+ /*
+ * We now have two separate lists. One list contains pages from the
+ * pool and the other pages from buddy. We want to merge these
+ * together while preserving the ordering of the pages (higher order
+ * first).
+ */
+ do {
+ info = list_first_entry_or_null(&pages, struct page_info, list);
+ tmp_info = list_first_entry_or_null(&pages_from_pool,
+ struct page_info, list);
+ if (info && tmp_info) {
+ if (info->order >= tmp_info->order) {
+ i = process_info(info, sg, sg_sync, &data, i);
+ sg_sync = sg_next(sg_sync);
+ } else {
+ i = process_info(tmp_info, sg, 0, 0, i);
+ }
+ } else if (info) {
+ i = process_info(info, sg, sg_sync, &data, i);
+ sg_sync = sg_next(sg_sync);
+ } else if (tmp_info) {
+ i = process_info(tmp_info, sg, 0, 0, i);
+ } else {
+ BUG();
+ }
sg = sg_next(sg);
- list_del(&info->list);
- kfree(info);
+
+ } while (sg);
+
+ ret = ion_heap_pages_zero(data.pages, data.size >> PAGE_SHIFT);
+ if (ret) {
+ pr_err("Unable to zero pages\n");
+ goto err_free_sg2;
}
+ if (nents_sync)
+ dma_sync_sg_for_device(NULL, table_sync.sgl, table_sync.nents,
+ DMA_BIDIRECTIONAL);
+
buffer->priv_virt = table;
+ if (nents_sync)
+ sg_free_table(&table_sync);
+ ion_heap_free_pages_mem(&data);
return 0;
+err_free_sg2:
+ /* We failed to zero buffers. Bypass pool */
+ buffer->flags |= ION_FLAG_FREED_FROM_SHRINKER;
+
+ for_each_sg(table->sgl, sg, table->nents, i)
+ free_buffer_page(sys_heap, buffer, sg_page(sg),
+ get_order(sg->length));
+ if (nents_sync)
+ sg_free_table(&table_sync);
+err_free_sg:
+ sg_free_table(table);
err1:
kfree(table);
+err_free_data_pages:
+ ion_heap_free_pages_mem(&data);
err:
list_for_each_entry_safe(info, tmp_info, &pages, list) {
free_buffer_page(sys_heap, buffer, info->page, info->order);
kfree(info);
}
+ list_for_each_entry_safe(info, tmp_info, &pages_from_pool, list) {
+ free_buffer_page(sys_heap, buffer, info->page, info->order);
+ kfree(info);
+ }
return -ENOMEM;
}
@@ -430,7 +544,7 @@ static int ion_system_contig_heap_allocate(struct ion_heap *heap,
if (align > (PAGE_SIZE << order))
return -EINVAL;
- page = alloc_pages(low_order_gfp_flags, order);
+ page = alloc_pages(low_order_gfp_flags | __GFP_ZERO, order);
if (!page)
return -ENOMEM;
diff --git a/sound/soc/codecs/msm8x16-wcd.c b/sound/soc/codecs/msm8x16-wcd.c
index 44aa506bf540..0ac902cf0603 100644
--- a/sound/soc/codecs/msm8x16-wcd.c
+++ b/sound/soc/codecs/msm8x16-wcd.c
@@ -345,7 +345,7 @@ static int __msm8x16_wcd_reg_read(struct snd_soc_codec *codec,
ret = msm8x16_wcd_spmi_read(reg, 1, &temp);
else if (MSM8X16_WCD_IS_DIGITAL_REG(reg)) {
mutex_lock(&pdata->cdc_mclk_mutex);
- if (atomic_read(&pdata->dis_work_mclk) == false) {
+ if (atomic_read(&pdata->mclk_enabled) == false) {
pdata->digital_cdc_clk.clk_val = pdata->mclk_freq;
ret = afe_set_digital_codec_core_clock(
AFE_PORT_ID_PRIMARY_MI2S_RX,
@@ -357,8 +357,8 @@ static int __msm8x16_wcd_reg_read(struct snd_soc_codec *codec,
pr_debug("%s: MCLK not enabled\n", __func__);
ret = msm8x16_wcd_ahb_read_device(
msm8x16_wcd, reg, 1, &temp);
- atomic_set(&pdata->dis_work_mclk, true);
- schedule_delayed_work(&pdata->enable_mclk_work, 50);
+ atomic_set(&pdata->mclk_enabled, true);
+ schedule_delayed_work(&pdata->disable_mclk_work, 50);
err:
mutex_unlock(&pdata->cdc_mclk_mutex);
mutex_unlock(&msm8x16_wcd->io_lock);
@@ -395,7 +395,7 @@ static int __msm8x16_wcd_reg_write(struct snd_soc_codec *codec,
ret = msm8x16_wcd_spmi_write(reg, 1, &val);
else if (MSM8X16_WCD_IS_DIGITAL_REG(reg)) {
mutex_lock(&pdata->cdc_mclk_mutex);
- if (atomic_read(&pdata->dis_work_mclk) == false) {
+ if (atomic_read(&pdata->mclk_enabled) == false) {
pr_debug("MCLK not enabled %s:\n", __func__);
pdata->digital_cdc_clk.clk_val = pdata->mclk_freq;
ret = afe_set_digital_codec_core_clock(
@@ -408,8 +408,8 @@ static int __msm8x16_wcd_reg_write(struct snd_soc_codec *codec,
}
ret = msm8x16_wcd_ahb_write_device(
msm8x16_wcd, reg, &val, 1);
- atomic_set(&pdata->dis_work_mclk, true);
- schedule_delayed_work(&pdata->enable_mclk_work, 50);
+ atomic_set(&pdata->mclk_enabled, true);
+ schedule_delayed_work(&pdata->disable_mclk_work, 50);
err:
mutex_unlock(&pdata->cdc_mclk_mutex);
mutex_unlock(&msm8x16_wcd->io_lock);
@@ -775,6 +775,43 @@ static int msm8x16_wcd_pa_gain_get(struct snd_kcontrol *kcontrol,
return 0;
}
+static int msm8x16_wcd_loopback_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct msm8916_asoc_mach_data *pdata = NULL;
+
+ pdata = snd_soc_card_get_drvdata(codec->card);
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ return pdata->lb_mode;
+}
+
+static int msm8x16_wcd_loopback_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct msm8916_asoc_mach_data *pdata = NULL;
+
+ pdata = snd_soc_card_get_drvdata(codec->card);
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ pdata->lb_mode = false;
+ break;
+ case 1:
+ pdata->lb_mode = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int msm8x16_wcd_pa_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1042,6 +1079,12 @@ static int msm8x16_wcd_put_iir_band_audio_mixer(
return 0;
}
+static const char * const msm8x16_wcd_loopback_mode_ctrl_text[] = {
+ "DISABLE", "ENABLE"};
+static const struct soc_enum msm8x16_wcd_loopback_mode_ctl_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_loopback_mode_ctrl_text),
+};
+
static const char * const msm8x16_wcd_ear_pa_boost_ctrl_text[] = {
"DISABLE", "ENABLE"};
static const struct soc_enum msm8x16_wcd_ear_pa_boost_ctl_enum[] = {
@@ -1091,6 +1134,9 @@ static const struct snd_kcontrol_new msm8x16_wcd_snd_controls[] = {
SOC_ENUM_EXT("Speaker Boost", msm8x16_wcd_spk_boost_ctl_enum[0],
msm8x16_wcd_spk_boost_get, msm8x16_wcd_spk_boost_set),
+ SOC_ENUM_EXT("LOOPBACK Mode", msm8x16_wcd_loopback_mode_ctl_enum[0],
+ msm8x16_wcd_loopback_mode_get, msm8x16_wcd_loopback_mode_put),
+
SOC_SINGLE_TLV("ADC1 Volume", MSM8X16_WCD_A_ANALOG_TX_1_EN, 3,
8, 0, analog_gain),
SOC_SINGLE_TLV("ADC2 Volume", MSM8X16_WCD_A_ANALOG_TX_2_EN, 3,
@@ -1890,6 +1936,7 @@ static int msm8x16_wcd_codec_enable_dec(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
+ struct msm8916_asoc_mach_data *pdata = NULL;
unsigned int decimator;
struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
char *dec_name = NULL;
@@ -1900,6 +1947,7 @@ static int msm8x16_wcd_codec_enable_dec(struct snd_soc_dapm_widget *w,
u8 dec_hpf_cut_of_freq;
int offset;
+ pdata = snd_soc_card_get_drvdata(codec->card);
dev_dbg(codec->dev, "%s %d\n", __func__, event);
widget_name = kstrndup(w->name, 15, GFP_KERNEL);
@@ -1983,6 +2031,11 @@ static int msm8x16_wcd_codec_enable_dec(struct snd_soc_dapm_widget *w,
snd_soc_read(codec,
tx_digital_gain_reg[w->shift + offset])
);
+ if (pdata->lb_mode) {
+ pr_debug("%s: loopback mode unmute the DEC\n",
+ __func__);
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00);
+ }
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01);
@@ -3341,37 +3394,23 @@ int msm8x16_wcd_suspend(struct snd_soc_codec *codec)
struct msm8x16_wcd_pdata *msm8x16_pdata = msm8x16->dev->platform_data;
pdata = snd_soc_card_get_drvdata(codec->card);
- pr_debug("%s: mclk cnt = %d, dis_work_mclk = %d"
- "mclk_act = %d\n",
+ pr_debug("%s: mclk cnt = %d, mclk_enabled = %d\n",
__func__, atomic_read(&pdata->mclk_rsc_ref),
- atomic_read(&pdata->dis_work_mclk),
- atomic_read(&pdata->mclk_act));
- mutex_lock(&pdata->cdc_mclk_mutex);
- if ((atomic_read(&pdata->dis_work_mclk) == true) ||
- (atomic_read(&pdata->mclk_rsc_ref) > 0)) {
- pdata->digital_cdc_clk.clk_val = 0;
- afe_set_digital_codec_core_clock(
+ atomic_read(&pdata->mclk_enabled));
+ if (atomic_read(&pdata->mclk_enabled) == true) {
+ cancel_delayed_work_sync(
+ &pdata->disable_mclk_work);
+ mutex_lock(&pdata->cdc_mclk_mutex);
+ if (atomic_read(&pdata->mclk_enabled) == true) {
+ pdata->digital_cdc_clk.clk_val = 0;
+ afe_set_digital_codec_core_clock(
AFE_PORT_ID_PRIMARY_MI2S_RX,
&pdata->digital_cdc_clk);
- /*
- * set mclk activity to resource as
- * it will get updated accordingly going further in this
- * function.
- */
- atomic_set(&pdata->mclk_act, MCLK_SUS_RSC);
- if (atomic_read(&pdata->dis_work_mclk) == true) {
- cancel_delayed_work_sync(
- &pdata->enable_mclk_work);
- atomic_set(&pdata->mclk_act, MCLK_SUS_DIS);
- atomic_set(&pdata->dis_work_mclk, false);
+ atomic_set(&pdata->mclk_enabled, false);
}
- } else
- /*
- * mark no activity on mclk in this suspend
- */
- atomic_set(&pdata->mclk_act, MCLK_SUS_NO_ACT);
+ mutex_unlock(&pdata->cdc_mclk_mutex);
+ }
msm8x16_wcd_disable_static_supplies_to_optimum(msm8x16, msm8x16_pdata);
- mutex_unlock(&pdata->cdc_mclk_mutex);
return 0;
}
@@ -3383,34 +3422,6 @@ int msm8x16_wcd_resume(struct snd_soc_codec *codec)
pdata = snd_soc_card_get_drvdata(codec->card);
msm8x16_wcd_enable_static_supplies_to_optimum(msm8x16, msm8x16_pdata);
- pr_debug("%s: mclk cnt = %d, dis_work_mclk = %d"
- "mclk_act = %d\n",
- __func__, atomic_read(&pdata->mclk_rsc_ref),
- atomic_read(&pdata->dis_work_mclk),
- atomic_read(&pdata->mclk_act));
- if (atomic_read(&pdata->mclk_act) == MCLK_SUS_NO_ACT)
- /*
- * no activity in suspend just return
- */
- return 0;
- mutex_lock(&pdata->cdc_mclk_mutex);
- if ((atomic_read(&pdata->dis_work_mclk) == false) ||
- (atomic_read(&pdata->mclk_rsc_ref) > 0)) {
- pdata->digital_cdc_clk.clk_val = pdata->mclk_freq;
- afe_set_digital_codec_core_clock(
- AFE_PORT_ID_PRIMARY_MI2S_RX,
- &pdata->digital_cdc_clk);
- if (atomic_read(&pdata->mclk_act) == MCLK_SUS_DIS) {
- /*
- * MCLK activity marked as the disabled during suspend
- * this indicated MCLK was enabled to read and write the
- * AHB bus.
- */
- atomic_set(&pdata->dis_work_mclk, true);
- schedule_delayed_work(&pdata->enable_mclk_work, 50);
- }
- }
- mutex_unlock(&pdata->cdc_mclk_mutex);
return 0;
}
diff --git a/sound/soc/codecs/msm8x16-wcd.h b/sound/soc/codecs/msm8x16-wcd.h
index a5cfee9a7fa4..d250df2bc0a0 100644
--- a/sound/soc/codecs/msm8x16-wcd.h
+++ b/sound/soc/codecs/msm8x16-wcd.h
@@ -151,11 +151,11 @@ struct msm8916_asoc_mach_data {
int ext_pa;
int us_euro_gpio;
int mclk_freq;
+ int lb_mode;
atomic_t mclk_rsc_ref;
- atomic_t dis_work_mclk;
- atomic_t mclk_act;
+ atomic_t mclk_enabled;
struct mutex cdc_mclk_mutex;
- struct delayed_work enable_mclk_work;
+ struct delayed_work disable_mclk_work;
struct afe_digital_clk_cfg digital_cdc_clk;
};
diff --git a/sound/soc/msm/msm8x16.c b/sound/soc/msm/msm8x16.c
index a17cdfe1a6ef..c7b153260be7 100644
--- a/sound/soc/msm/msm8x16.c
+++ b/sound/soc/msm/msm8x16.c
@@ -593,28 +593,29 @@ static int msm8x16_enable_codec_ext_clk(struct snd_soc_codec *codec,
atomic_read(&pdata->mclk_rsc_ref));
if (enable) {
if (atomic_inc_return(&pdata->mclk_rsc_ref) == 1) {
+ cancel_delayed_work_sync(
+ &pdata->disable_mclk_work);
mutex_lock(&pdata->cdc_mclk_mutex);
- if (atomic_read(&pdata->dis_work_mclk) == true) {
- cancel_delayed_work_sync(
- &pdata->enable_mclk_work);
- } else {
- pdata->digital_cdc_clk.clk_val = pdata->mclk_freq;
+ if (atomic_read(&pdata->mclk_enabled) == false) {
+ pdata->digital_cdc_clk.clk_val =
+ pdata->mclk_freq;
afe_set_digital_codec_core_clock(
AFE_PORT_ID_PRIMARY_MI2S_RX,
&pdata->digital_cdc_clk);
- atomic_set(&pdata->dis_work_mclk, true);
+ atomic_set(&pdata->mclk_enabled, true);
}
mutex_unlock(&pdata->cdc_mclk_mutex);
}
} else {
+ cancel_delayed_work_sync(&pdata->disable_mclk_work);
mutex_lock(&pdata->cdc_mclk_mutex);
- atomic_set(&pdata->mclk_rsc_ref, 0);
- cancel_delayed_work_sync(&pdata->enable_mclk_work);
- pdata->digital_cdc_clk.clk_val = 0;
- afe_set_digital_codec_core_clock(
- AFE_PORT_ID_PRIMARY_MI2S_RX,
- &pdata->digital_cdc_clk);
- atomic_set(&pdata->dis_work_mclk, false);
+ if (atomic_read(&pdata->mclk_enabled) == true) {
+ pdata->digital_cdc_clk.clk_val = 0;
+ afe_set_digital_codec_core_clock(
+ AFE_PORT_ID_PRIMARY_MI2S_RX,
+ &pdata->digital_cdc_clk);
+ atomic_set(&pdata->mclk_enabled, false);
+ }
mutex_unlock(&pdata->cdc_mclk_mutex);
}
return ret;
@@ -1820,26 +1821,30 @@ static struct snd_soc_card bear_cards[MAX_SND_CARDS] = {
},
};
-void enable_mclk(struct work_struct *work)
+void disable_mclk(struct work_struct *work)
{
struct msm8916_asoc_mach_data *pdata = NULL;
struct delayed_work *dwork;
int ret = 0;
- pr_debug("%s:\n", __func__);
dwork = to_delayed_work(work);
pdata = container_of(dwork, struct msm8916_asoc_mach_data,
- enable_mclk_work);
+ disable_mclk_work);
mutex_lock(&pdata->cdc_mclk_mutex);
- if (atomic_read(&pdata->dis_work_mclk) == true) {
- pr_debug("clock enabled now disable\n");
+ pr_debug("%s: mclk_enabled %d mclk_rsc_ref %d\n", __func__,
+ atomic_read(&pdata->mclk_enabled),
+ atomic_read(&pdata->mclk_rsc_ref));
+
+ if (atomic_read(&pdata->mclk_enabled) == true
+ && atomic_read(&pdata->mclk_rsc_ref) == 0) {
+ pr_debug("Disable the mclk\n");
pdata->digital_cdc_clk.clk_val = 0;
ret = afe_set_digital_codec_core_clock(
AFE_PORT_ID_PRIMARY_MI2S_RX,
&pdata->digital_cdc_clk);
if (ret < 0)
- pr_err("failed to disable the MCLK\n");
- atomic_set(&pdata->dis_work_mclk, false);
+ pr_err("%s failed to disable the MCLK\n", __func__);
+ atomic_set(&pdata->mclk_enabled, false);
}
mutex_unlock(&pdata->cdc_mclk_mutex);
}
@@ -2161,6 +2166,8 @@ static int msm8x16_asoc_machine_probe(struct platform_device *pdev)
pdata->digital_cdc_clk.clk_val = pdata->mclk_freq;
pdata->digital_cdc_clk.clk_root = 5;
pdata->digital_cdc_clk.reserved = 0;
+ /* Initialize loopback mode to false */
+ pdata->lb_mode = false;
msm8x16_setup_hs_jack(pdev, pdata);
@@ -2171,10 +2178,10 @@ static int msm8x16_asoc_machine_probe(struct platform_device *pdev)
if (ret)
goto err;
/* initialize timer */
- INIT_DELAYED_WORK(&pdata->enable_mclk_work, enable_mclk);
+ INIT_DELAYED_WORK(&pdata->disable_mclk_work, disable_mclk);
mutex_init(&pdata->cdc_mclk_mutex);
atomic_set(&pdata->mclk_rsc_ref, 0);
- atomic_set(&pdata->dis_work_mclk, false);
+ atomic_set(&pdata->mclk_enabled, false);
ret = snd_soc_of_parse_audio_routing(card,
"qcom,audio-routing");