// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #define CODEC_DT_MAX_PROP_SIZE 40 static int msm_cdc_dt_parse_vreg_info(struct device *dev, struct cdc_regulator *cdc_vreg, const char *name, bool is_ond) { char prop_name[CODEC_DT_MAX_PROP_SIZE]; struct device_node *regulator_node = NULL; const __be32 *prop; int len, rc; u32 prop_val; /* Parse supply name */ snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "%s-supply", name); regulator_node = of_parse_phandle(dev->of_node, prop_name, 0); if (!regulator_node) { dev_err(dev, "%s: Looking up %s property in node %s failed", __func__, prop_name, dev->of_node->full_name); rc = -EINVAL; goto done; } cdc_vreg->name = name; cdc_vreg->ondemand = is_ond; /* Parse supply - voltage */ snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-voltage", name); prop = of_get_property(dev->of_node, prop_name, &len); if (!prop || (len != (2 * sizeof(__be32)))) { dev_err(dev, "%s: %s %s property\n", __func__, prop ? "invalid format" : "no", prop_name); rc = -EINVAL; goto done; } else { cdc_vreg->min_uV = be32_to_cpup(&prop[0]); cdc_vreg->max_uV = be32_to_cpup(&prop[1]); } /* Parse supply - current */ snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-current", name); rc = of_property_read_u32(dev->of_node, prop_name, &prop_val); if (rc) { dev_err(dev, "%s: Looking up %s property in node %s failed", __func__, prop_name, dev->of_node->full_name); goto done; } cdc_vreg->optimum_uA = prop_val; /* Parse supply - LPM or NOM mode(default NOM) */ snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-lpm-supported", name); rc = of_property_read_u32(dev->of_node, prop_name, &prop_val); if (rc) { dev_dbg(dev, "%s: Looking up %s property in node %s failed", __func__, prop_name, dev->of_node->full_name); cdc_vreg->lpm_supported = 0; rc = 0; } else { cdc_vreg->lpm_supported = prop_val; } /* Parse supply - retention mode */ snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-rem-supported", name); rc = of_property_read_u32(dev->of_node, prop_name, &prop_val); if (rc) { dev_dbg(dev, "%s: Looking up %s property in node %s failed", __func__, prop_name, dev->of_node->full_name); cdc_vreg->rem_supported = 0; rc = 0; } else { cdc_vreg->rem_supported = prop_val; } dev_info(dev, "%s: %s: vol=[%d %d]uV, curr=[%d]uA, ond %d lpm %d rem %d\n", __func__, cdc_vreg->name, cdc_vreg->min_uV, cdc_vreg->max_uV, cdc_vreg->optimum_uA, cdc_vreg->ondemand, cdc_vreg->lpm_supported, cdc_vreg->rem_supported); done: return rc; } static int msm_cdc_parse_supplies(struct device *dev, struct cdc_regulator *cdc_reg, const char *sup_list, int sup_cnt, bool is_ond) { int idx, rc = 0; const char *name = NULL; for (idx = 0; idx < sup_cnt; idx++) { rc = of_property_read_string_index(dev->of_node, sup_list, idx, &name); if (rc) { dev_err(dev, "%s: read string %s[%d] error (%d)\n", __func__, sup_list, idx, rc); goto done; } dev_dbg(dev, "%s: Found cdc supply %s as part of %s\n", __func__, name, sup_list); rc = msm_cdc_dt_parse_vreg_info(dev, &cdc_reg[idx], name, is_ond); if (rc) { dev_err(dev, "%s: parse %s vreg info failed (%d)\n", __func__, name, rc); goto done; } } done: return rc; } static int msm_cdc_check_supply_param(struct device *dev, struct cdc_regulator *cdc_vreg, int num_supplies) { if (!dev) { pr_err_ratelimited("%s: device is NULL\n", __func__); return -ENODEV; } if (!cdc_vreg || (num_supplies <= 0)) { dev_err_ratelimited(dev, "%s: supply check failed: vreg: %pK, num_supplies: %d\n", __func__, cdc_vreg, num_supplies); return -EINVAL; } return 0; } /* * msm_cdc_is_ondemand_supply: * return if ondemand supply true or not * * @dev: pointer to codec device * @supplies: pointer to regulator bulk data * @cdc_vreg: pointer to platform regulator data * @num_supplies: number of supplies * @supply_name: supply name to be checked * * Return true/false */ bool msm_cdc_is_ondemand_supply(struct device *dev, struct regulator_bulk_data *supplies, struct cdc_regulator *cdc_vreg, int num_supplies, char *supply_name) { bool rc = false; int ret, i; if ((!supply_name) || (!supplies)) { pr_err_ratelimited("%s: either dev or supplies or cdc_vreg is NULL\n", __func__); return rc; } /* input parameter validation */ ret = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); if (ret) return rc; for (i = 0; i < num_supplies; i++) { if (cdc_vreg[i].ondemand && !strcmp(cdc_vreg[i].name, supply_name)) return true; } return rc; } EXPORT_SYMBOL(msm_cdc_is_ondemand_supply); /* * msm_cdc_supply_supports_retention_mode: * On certain hardware configurations, This means that the * PM will disable the supply and remove its power vote * if the PM enters into a suspended state. * * return if supply supports retention mode or not * * @dev: pointer to codec device * @supplies: pointer to regulator bulk data * @cdc_vreg: pointer to platform regulator data * @num_supplies: number of supplies * @supply_name: supply name to be checked * * Return true/false */ bool msm_cdc_supply_supports_retention_mode(struct device *dev, struct regulator_bulk_data *supplies, struct cdc_regulator *cdc_vreg, int num_supplies, char *supply_name) { bool rc = false; int ret, i; if ((!supply_name) || (!supplies)) { pr_err_ratelimited("%s: either dev or supplies or cdc_vreg is NULL\n", __func__); return rc; } /* input parameter validation */ ret = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); if (ret) return rc; for (i = 0; i < num_supplies; i++) { if (cdc_vreg[i].rem_supported && !strcmp(cdc_vreg[i].name, supply_name)) return true; } return rc; } EXPORT_SYMBOL(msm_cdc_supply_supports_retention_mode); /* * msm_cdc_check_supply_vote: * * return true if supply has voted for regulator enable * * @dev: pointer to codec device * @supplies: pointer to regulator bulk data * @cdc_vreg: pointer to platform regulator data * @num_supplies: number of supplies * @supply_name: supply name to be checked * * Return true/false */ bool msm_cdc_check_supply_vote(struct device *dev, struct regulator_bulk_data *supplies, struct cdc_regulator *cdc_vreg, int num_supplies, char *supply_name) { bool rc = false; int ret, i; if ((!supply_name) || (!supplies)) { pr_err_ratelimited("%s: either dev or supplies or cdc_vreg is NULL\n", __func__); return rc; } /* input parameter validation */ ret = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); if (ret) return rc; for (i = 0; i < num_supplies; i++) { if (strcmp(cdc_vreg[i].name, supply_name) != 0) continue; return cdc_vreg[i].vote; } dev_err_ratelimited(dev, "%s: Unable to find vote for supply %s\n", __func__, supply_name); return -EINVAL; } EXPORT_SYMBOL(msm_cdc_check_supply_vote); /* * msm_cdc_set_supply_min_voltage: * Set min supply voltage for particular supply * * @dev: pointer to codec device * @supplies: pointer to regulator bulk data * @cdc_vreg: pointer to platform regulator data * @num_supplies: number of supplies * @supply_name: Supply name to change voltage for * @vval_min: Min voltage to be set in uV * @override_min_vol: True if override min voltage from default * Return error code if unable to set voltage */ int msm_cdc_set_supply_min_voltage(struct device *dev, struct regulator_bulk_data *supplies, struct cdc_regulator *cdc_vreg, int num_supplies, char *supply_name, int vval_min, bool override_min_vol) { int rc = 0, i; if ((!supply_name) || (!supplies)) { pr_err_ratelimited("%s: either dev or supplies or cdc_vreg is NULL\n", __func__); return -EINVAL; } /* input parameter validation */ rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); if (rc) return rc; for (i = 0; i < num_supplies; i++) { if (!strcmp(cdc_vreg[i].name, supply_name)) { if (override_min_vol) regulator_set_voltage(supplies[i].consumer, vval_min, cdc_vreg[i].max_uV); else regulator_set_voltage(supplies[i].consumer, cdc_vreg[i].min_uV, cdc_vreg[i].max_uV); break; } } return rc; } EXPORT_SYMBOL(msm_cdc_set_supply_min_voltage); /* * msm_cdc_disable_ondemand_supply: * Disable codec ondemand supply * * @dev: pointer to codec device * @supplies: pointer to regulator bulk data * @cdc_vreg: pointer to platform regulator data * @num_supplies: number of supplies * @supply_name: Ondemand supply name to be enabled * * Return error code if supply disable is failed */ int msm_cdc_disable_ondemand_supply(struct device *dev, struct regulator_bulk_data *supplies, struct cdc_regulator *cdc_vreg, int num_supplies, char *supply_name) { int rc, i; if ((!supply_name) || (!supplies)) { pr_err_ratelimited("%s: either dev or supplies or cdc_vreg is NULL\n", __func__); return -EINVAL; } /* input parameter validation */ rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); if (rc) return rc; dev_dbg(dev, "%s: Disabling on-demand supply %s\n", __func__, supply_name); for (i = 0; i < num_supplies; i++) { if (cdc_vreg[i].ondemand && !strcmp(cdc_vreg[i].name, supply_name)) { if (!cdc_vreg[i].vote) { dev_err_ratelimited(dev, "%s: Attempted to disable already disabled supply %s\n", __func__, supplies[i].supply); break; } rc = regulator_disable(supplies[i].consumer); if (rc) dev_err_ratelimited(dev, "%s: failed to disable supply %s, err:%d\n", __func__, supplies[i].supply, rc); else cdc_vreg[i].vote = false; break; } } if (i == num_supplies) { dev_err_ratelimited(dev, "%s: not able to find supply %s\n", __func__, supply_name); rc = -EINVAL; } return rc; } EXPORT_SYMBOL(msm_cdc_disable_ondemand_supply); /* * msm_cdc_enable_ondemand_supply: * Enable codec ondemand supply * * @dev: pointer to codec device * @supplies: pointer to regulator bulk data * @cdc_vreg: pointer to platform regulator data * @num_supplies: number of supplies * @supply_name: Ondemand supply name to be enabled * * Return error code if supply enable is failed */ int msm_cdc_enable_ondemand_supply(struct device *dev, struct regulator_bulk_data *supplies, struct cdc_regulator *cdc_vreg, int num_supplies, char *supply_name) { int rc, i; if ((!supply_name) || (!supplies)) { pr_err_ratelimited("%s: either dev or supplies or cdc_vreg is NULL\n", __func__); return -EINVAL; } /* input parameter validation */ rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); if (rc) return rc; dev_dbg(dev, "%s: Enabling on-demand supply %s\n", __func__, supply_name); for (i = 0; i < num_supplies; i++) { if (cdc_vreg[i].ondemand && !strcmp(cdc_vreg[i].name, supply_name)) { if (cdc_vreg[i].vote) { dev_err_ratelimited(dev, "%s: Attempted to enable already enabled supply %s\n", __func__, supplies[i].supply); break; } rc = regulator_enable(supplies[i].consumer); if (rc) dev_err_ratelimited(dev, "%s: failed to enable supply %s, rc: %d\n", __func__, supplies[i].supply, rc); else cdc_vreg[i].vote = true; break; } } if (i == num_supplies) { dev_err_ratelimited(dev, "%s: not able to find supply %s\n", __func__, supply_name); rc = -EINVAL; } return rc; } EXPORT_SYMBOL(msm_cdc_enable_ondemand_supply); /* * msm_cdc_set_supplies_lpm_mode: * Update load for given supply string * * @dev: pointer to codec device * @supplies: pointer to regulator bulk data * @cdc_vreg: pointer to platform regulator data * @num_supplies: number of supplies * @supply_name: supply name to be checked * @min_max: Apply optimum or 0 current * * Return error code if set current fail */ int msm_cdc_set_supplies_lpm_mode(struct device *dev, struct regulator_bulk_data *supplies, struct cdc_regulator *cdc_vreg, int num_supplies, bool flag) { int rc = 0, i; if (!supplies) { pr_err_ratelimited("%s: supplies is NULL\n", __func__); return -EINVAL; } /* input parameter validation */ rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); if (rc) return rc; for (i = 0; i < num_supplies; i++) { if (cdc_vreg[i].lpm_supported) { rc = regulator_set_load( supplies[i].consumer, flag ? 0 : cdc_vreg[i].optimum_uA); if (rc) dev_err_ratelimited(dev, "%s: failed to set supply %s to %s, err:%d\n", __func__, supplies[i].supply, flag ? "LPM" : "NOM", rc); else dev_dbg(dev, "%s: regulator %s load set to %s\n", __func__, supplies[i].supply, flag ? "LPM" : "NOM"); } } return rc; } EXPORT_SYMBOL(msm_cdc_set_supplies_lpm_mode); /* * msm_cdc_disable_static_supplies: * Disable codec static supplies * * @dev: pointer to codec device * @supplies: pointer to regulator bulk data * @cdc_vreg: pointer to platform regulator data * @num_supplies: number of supplies * * Return error code if supply disable is failed */ int msm_cdc_disable_static_supplies(struct device *dev, struct regulator_bulk_data *supplies, struct cdc_regulator *cdc_vreg, int num_supplies) { int rc, i; if ((!dev) || (!supplies) || (!cdc_vreg)) { pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", __func__); return -EINVAL; } /* input parameter validation */ rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); if (rc) return rc; for (i = 0; i < num_supplies; i++) { if (cdc_vreg[i].ondemand) continue; rc = regulator_disable(supplies[i].consumer); if (rc) dev_err(dev, "%s: failed to disable supply %s, err:%d\n", __func__, supplies[i].supply, rc); else { cdc_vreg[i].vote = false; dev_dbg(dev, "%s: disabled regulator %s\n", __func__, supplies[i].supply); } } return rc; } EXPORT_SYMBOL(msm_cdc_disable_static_supplies); /* * msm_cdc_release_supplies: * Release codec power supplies * * @dev: pointer to codec device * @supplies: pointer to regulator bulk data * @cdc_vreg: pointer to platform regulator data * @num_supplies: number of supplies * * Return error code if supply disable is failed */ int msm_cdc_release_supplies(struct device *dev, struct regulator_bulk_data *supplies, struct cdc_regulator *cdc_vreg, int num_supplies) { int rc = 0; int i; if ((!dev) || (!supplies) || (!cdc_vreg)) { pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", __func__); return -EINVAL; } /* input parameter validation */ rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); if (rc) return rc; msm_cdc_disable_static_supplies(dev, supplies, cdc_vreg, num_supplies); for (i = 0; i < num_supplies; i++) { if (regulator_count_voltages(supplies[i].consumer) < 0) continue; regulator_set_voltage(supplies[i].consumer, 0, cdc_vreg[i].max_uV); regulator_set_load(supplies[i].consumer, 0); } return rc; } EXPORT_SYMBOL(msm_cdc_release_supplies); /* * msm_cdc_enable_static_supplies: * Enable codec static supplies * * @dev: pointer to codec device * @supplies: pointer to regulator bulk data * @cdc_vreg: pointer to platform regulator data * @num_supplies: number of supplies * * Return error code if supply enable is failed */ int msm_cdc_enable_static_supplies(struct device *dev, struct regulator_bulk_data *supplies, struct cdc_regulator *cdc_vreg, int num_supplies) { int rc, i; if ((!dev) || (!supplies) || (!cdc_vreg)) { pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", __func__); return -EINVAL; } /* input parameter validation */ rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); if (rc) return rc; for (i = 0; i < num_supplies; i++) { if (cdc_vreg[i].ondemand) continue; rc = regulator_enable(supplies[i].consumer); if (rc) { dev_err(dev, "%s: failed to enable supply %s, rc: %d\n", __func__, supplies[i].supply, rc); break; } else cdc_vreg[i].vote = true; } if (rc) { while (i--) { if (cdc_vreg[i].ondemand) continue; if (regulator_disable(supplies[i].consumer) == 0) cdc_vreg[i].vote = false; else dev_err(dev, "%s: failed to disable supply %s during unwind\n", __func__, supplies[i].supply); } } return rc; } EXPORT_SYMBOL(msm_cdc_enable_static_supplies); /* * msm_cdc_init_supplies: * Initialize codec static supplies * * @dev: pointer to codec device * @supplies: pointer to regulator bulk data * @cdc_vreg: pointer to platform regulator data * @num_supplies: number of supplies * * Return error code if supply init is failed */ int msm_cdc_init_supplies(struct device *dev, struct regulator_bulk_data **supplies, struct cdc_regulator *cdc_vreg, int num_supplies) { return msm_cdc_init_supplies_v2(dev, supplies, cdc_vreg, num_supplies, false); } EXPORT_SYMBOL(msm_cdc_init_supplies); /* * msm_cdc_init_supplies_v2: * Initialize codec static supplies. * Initialize codec dynamic supplies based on vote_regulator_on_demand * * @dev: pointer to codec device * @supplies: pointer to regulator bulk data * @cdc_vreg: pointer to platform regulator data * @num_supplies: number of supplies * @vote_regulator_on_demand: initialize codec dynamic supplies at runtime * * Return error code if supply init is failed */ int msm_cdc_init_supplies_v2(struct device *dev, struct regulator_bulk_data **supplies, struct cdc_regulator *cdc_vreg, int num_supplies, u32 vote_regulator_on_demand) { struct regulator_bulk_data *vsup; int rc; int i; if (!dev || !cdc_vreg) { pr_err("%s: device pointer or dce_vreg is NULL\n", __func__); return -EINVAL; } /* input parameter validation */ rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); if (rc) return rc; vsup = devm_kcalloc(dev, num_supplies, sizeof(struct regulator_bulk_data), GFP_KERNEL); if (!vsup) return -ENOMEM; for (i = 0; i < num_supplies; i++) { if (!cdc_vreg[i].name) { dev_err(dev, "%s: supply name not defined\n", __func__); rc = -EINVAL; goto err_supply; } vsup[i].supply = cdc_vreg[i].name; } rc = devm_regulator_bulk_get(dev, num_supplies, vsup); if (rc) { dev_err(dev, "%s: failed to get supplies (%d)\n", __func__, rc); goto err_supply; } /* Set voltage and current on regulators */ for (i = 0; i < num_supplies; i++) { if (regulator_count_voltages(vsup[i].consumer) < 0) continue; if (cdc_vreg[i].ondemand && vote_regulator_on_demand) continue; cdc_vreg[i].regulator = vsup[i].consumer; rc = regulator_set_voltage(vsup[i].consumer, cdc_vreg[i].min_uV, cdc_vreg[i].max_uV); if (rc) { dev_err(dev, "%s: set regulator voltage failed for %s, err:%d\n", __func__, vsup[i].supply, rc); goto err_supply; } rc = regulator_set_load(vsup[i].consumer, cdc_vreg[i].optimum_uA); if (rc < 0) { dev_err(dev, "%s: set regulator optimum mode failed for %s, err:%d\n", __func__, vsup[i].supply, rc); goto err_supply; } } *supplies = vsup; return 0; err_supply: return rc; } EXPORT_SYMBOL(msm_cdc_init_supplies_v2); /* * msm_cdc_get_power_supplies: * Get codec power supplies from device tree. * Allocate memory to hold regulator data for * all power supplies. * * @dev: pointer to codec device * @cdc_vreg: pointer to codec regulator * @total_num_supplies: total number of supplies read from DT * * Return error code if supply disable is failed */ int msm_cdc_get_power_supplies(struct device *dev, struct cdc_regulator **cdc_vreg, int *total_num_supplies) { const char *static_prop_name = "qcom,cdc-static-supplies"; const char *ond_prop_name = "qcom,cdc-on-demand-supplies"; const char *cp_prop_name = "qcom,cdc-cp-supplies"; int static_sup_cnt = 0; int ond_sup_cnt = 0; int cp_sup_cnt = 0; int num_supplies = 0; struct cdc_regulator *cdc_reg; int rc; if (!dev) { pr_err_ratelimited("%s: device pointer is NULL\n", __func__); return -EINVAL; } static_sup_cnt = of_property_count_strings(dev->of_node, static_prop_name); if (static_sup_cnt < 0) { dev_err_ratelimited(dev, "%s: Failed to get static supplies(%d)\n", __func__, static_sup_cnt); rc = static_sup_cnt; goto err_supply_cnt; } ond_sup_cnt = of_property_count_strings(dev->of_node, ond_prop_name); if (ond_sup_cnt < 0) ond_sup_cnt = 0; cp_sup_cnt = of_property_count_strings(dev->of_node, cp_prop_name); if (cp_sup_cnt < 0) cp_sup_cnt = 0; num_supplies = static_sup_cnt + ond_sup_cnt + cp_sup_cnt; if (num_supplies <= 0) { dev_err_ratelimited(dev, "%s: supply count is 0 or negative\n", __func__); rc = -EINVAL; goto err_supply_cnt; } cdc_reg = devm_kcalloc(dev, num_supplies, sizeof(struct cdc_regulator), GFP_KERNEL); if (!cdc_reg) { rc = -ENOMEM; goto err_mem_alloc; } rc = msm_cdc_parse_supplies(dev, cdc_reg, static_prop_name, static_sup_cnt, false); if (rc) { dev_err_ratelimited(dev, "%s: failed to parse static supplies(%d)\n", __func__, rc); goto err_sup; } rc = msm_cdc_parse_supplies(dev, &cdc_reg[static_sup_cnt], ond_prop_name, ond_sup_cnt, true); if (rc) { dev_err_ratelimited(dev, "%s: failed to parse demand supplies(%d)\n", __func__, rc); goto err_sup; } rc = msm_cdc_parse_supplies(dev, &cdc_reg[static_sup_cnt + ond_sup_cnt], cp_prop_name, cp_sup_cnt, true); if (rc) { dev_err_ratelimited(dev, "%s: failed to parse cp supplies(%d)\n", __func__, rc); goto err_sup; } *cdc_vreg = cdc_reg; *total_num_supplies = num_supplies; return 0; err_sup: err_supply_cnt: err_mem_alloc: return rc; } EXPORT_SYMBOL(msm_cdc_get_power_supplies); /* * msm_cdc_init_wcd_supply: * Initialize wcd supply parameters. * * @np: device node pointer to codec device * @name: power supply name * @cdc_supply: codec supply struct to hold wcd params * * Return error code if init failed */ int msm_cdc_init_wcd_supply(struct device_node *np, const char *name, struct cdc_wcd_supply *cdc_supply) { struct platform_device *pdev = NULL; if (!np || !cdc_supply) return -EINVAL; pdev = of_find_device_by_node(np); if (!pdev) return -EINVAL; cdc_supply->dev = &pdev->dev; cdc_supply->name = name; cdc_supply->component = snd_soc_lookup_component(&pdev->dev, NULL); return 0; } EXPORT_SYMBOL(msm_cdc_init_wcd_supply); /* * msm_cdc_enable_wcd_supply: * Enable/Disable wcd supply. * * @cdc_supply: codec supply struct to hold wcd params * @enable: bool to inform whether to enable or disable * * Return error code if enable/disable failed */ int msm_cdc_enable_wcd_supply(struct cdc_wcd_supply *cdc_supply, bool enable) { struct snd_soc_component *component = cdc_supply->component; int rc; if (!component) { pr_err_ratelimited("%s: Component memory is NULL\n", __func__); return -EINVAL; } if (enable) rc = snd_soc_dapm_force_enable_pin( snd_soc_component_get_dapm(component), cdc_supply->name); else rc = snd_soc_dapm_disable_pin( snd_soc_component_get_dapm(component), cdc_supply->name); if (!rc) snd_soc_dapm_sync(snd_soc_component_get_dapm(component)); else dev_err_ratelimited(component->dev, "%s: micbias %s force %s pin failed\n", __func__, cdc_supply->name, (enable ? "enable" : "disable")); return rc; } EXPORT_SYMBOL(msm_cdc_enable_wcd_supply);