
When codec goes into suspend, disable vdd-px which will cause an unvote in PM. Change-Id: Ia9f958d67fc57dbf3932733797bce7b0eb742363 Signed-off-by: Eric Rosas <quic_erosas@quicinc.com>
907 行
23 KiB
C
907 行
23 KiB
C
// 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 <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <asoc/msm-cdc-supply.h>
|
|
#include <sound/soc.h>
|
|
|
|
#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;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
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;
|
|
|
|
for (i = 0; i < num_supplies; i++) {
|
|
if (cdc_vreg[i].ondemand &&
|
|
!strcmp(cdc_vreg[i].name, supply_name)) {
|
|
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);
|
|
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;
|
|
|
|
for (i = 0; i < num_supplies; i++) {
|
|
if (cdc_vreg[i].ondemand &&
|
|
!strcmp(cdc_vreg[i].name, supply_name)) {
|
|
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);
|
|
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;
|
|
}
|
|
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);
|