diff options
-rw-r--r-- | Documentation/devicetree/bindings/power/smb1360-charger-fg.txt | 34 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-msm-v2.c | 6 | ||||
-rw-r--r-- | drivers/power/smb1360-charger-fg.c | 796 | ||||
-rw-r--r-- | drivers/staging/android/ion/ion_heap.c | 52 | ||||
-rw-r--r-- | drivers/staging/android/ion/ion_page_pool.c | 24 | ||||
-rw-r--r-- | drivers/staging/android/ion/ion_priv.h | 5 | ||||
-rw-r--r-- | drivers/staging/android/ion/ion_system_heap.c | 142 | ||||
-rw-r--r-- | sound/soc/codecs/msm8x16-wcd.c | 131 | ||||
-rw-r--r-- | sound/soc/codecs/msm8x16-wcd.h | 6 | ||||
-rw-r--r-- | sound/soc/msm/msm8x16.c | 51 |
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, ®); + 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, ®); - if (!rc) - seq_printf(m, "0x%02x = 0x%02x\n", addr, reg); + rc = smb1360_read(chip, STATUS_4_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, + ®[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, + ®[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(®[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, + ®[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(®[fg_cfg[i].index])); + + rem_length = LAST_FG_SHDW_REG - FIRST_FG_SHDW_REG + 1; + rc = smb1360_read_bytes(chip, FIRST_FG_SHDW_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(®[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, ®); + 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, ®); + 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, ®); + 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"); |