Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5eaa7f68 authored by Yeleswarapu Nagaradhesh's avatar Yeleswarapu Nagaradhesh Committed by Kyle Yan
Browse files

wcd9xxx: refactor wcd9xxx audio codec drivers



Refactor wcd9xxx audio codec driver for better handling
of codec specific functionalities.

CRs-fixed: 1028800
Change-Id: I229ee4a741c5a606e2eb045940f5ee3c4eabf512
Signed-off-by: default avatarPhani Kumar Uppalapati <phaniu@codeaurora.org>
Signed-off-by: default avatarYeleswarapu Nagaradhesh <nagaradh@codeaurora.org>
parent 000c189c
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -354,7 +354,7 @@ Required properties:

Required properties:

 - compatible :                            "qcom,wcd-gpio-ctrl"
 - compatible :                            "qcom,msm-cdc-pinctrl"

 - qcom,cdc-rst-n-gpio :                   TLMM GPIO number

@@ -625,7 +625,7 @@ Example:
	};

	wcd_gpio_ctrl {
		compatible = "qcom,wcd-gpio-ctrl";
		compatible = "qcom,msm-cdc-pinctrl";
		qcom,cdc-rst-n-gpio = <&tlmm 64 0>;
		pinctrl-names = "aud_active", "aud_sleep";
		pinctrl-0 = <&cdc_reset_active>;
+23 −1
Original line number Diff line number Diff line
@@ -1516,10 +1516,29 @@ config MSM_CDC_PINCTRL
	  This driver acts as interface between codec and pinctrl
	  framework.

config MSM_CDC_SUPPLY
	tristate "MSM Codec Power Supply"
	help
	  Enables msm codec power supply driver. The power supply
	  driver provides API support for handling WCD and WSA codec
	  power supply enable or disable. This driver acts as interface
	  between codec and regulator framework.

config WCD9XXX_CODEC_UTIL
	tristate "WCD9XXX Codec Utils"
	select MFD_CORE
	help
	  WCD9XXX Util driver provides APIs for WCD drivers to reset,
	  suspend/resume, regmap bus callback functions and read/write
	  functions. This driver also hides the underlying bus related
	  functionalities.

config WCD9330_CODEC
	tristate "WCD9330 Codec"
	select SLIMBUS
	select MFD_CORE
	select WCD9XXX_CODEC_UTIL
	select MSM_CDC_SUPPLY
	select REGMAP_ALLOW_WRITE_DEBUGFS
	help
	  Enables the WCD9xxx codec core driver. The core driver provides
@@ -1530,9 +1549,12 @@ config WCD9330_CODEC

config WCD9335_CODEC
	tristate "WCD9335 Codec"
	depends on SLIMBUS
	select SLIMBUS
	select SOUNDWIRE_WCD_CTRL
	select MFD_CORE
	select WCD9XXX_CODEC_UTIL
	select MSM_CDC_SUPPLY
	select MSM_CDC_PINCTRL
	select REGMAP_ALLOW_WRITE_DEBUGFS
	help
	  Enables the WCD9xxx codec core driver. The core driver provides
+4 −3
Original line number Diff line number Diff line
@@ -191,11 +191,12 @@ obj-$(CONFIG_MFD_DLN2) += dln2.o
obj-$(CONFIG_MFD_RT5033)	+= rt5033.o
obj-$(CONFIG_MFD_SKY81452)	+= sky81452.o
obj-$(CONFIG_MSM_CDC_PINCTRL)	+= msm-cdc-pinctrl.o
obj-$(CONFIG_MSM_CDC_SUPPLY) += msm-cdc-supply.o
obj-$(CONFIG_WCD9XXX_CODEC_UTIL) += wcd9xxx-utils.o
obj-$(CONFIG_WCD9330_CODEC)	+= wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o\
						wcd9xxx-core-resource.o wcd9330-regmap.o
						wcd9330-regmap.o
obj-$(CONFIG_WCD9335_CODEC)	+= wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o\
					wcd9xxx-core-resource.o wcd9335-regmap.o\
					wcd9335-tables.o wcd-gpio-ctrl.o
					wcd9335-regmap.o wcd9335-tables.o
intel-soc-pmic-objs		:= intel_soc_pmic_core.o intel_soc_pmic_crc.o
intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC)	+= intel_soc_pmic_bxtwc.o
obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
+458 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/mfd/msm-cdc-supply.h>
#include <linux/regulator/consumer.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;

	dev_info(dev, "%s: %s: vol=[%d %d]uV, curr=[%d]uA, ond %d\n",
		 __func__, cdc_vreg->name, cdc_vreg->min_uV, cdc_vreg->max_uV,
		 cdc_vreg->optimum_uA, cdc_vreg->ondemand);

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("%s: device is NULL\n", __func__);
		return -ENODEV;
	}

	if (!cdc_vreg || (num_supplies <= 0)) {
		dev_err(dev, "%s: supply check failed: vreg: %pK, num_supplies: %d\n",
			__func__, cdc_vreg, num_supplies);
		return -EINVAL;
	}

	return 0;
}

/*
 * 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
			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);
	}

	regulator_bulk_free(num_supplies, supplies);
	devm_kfree(dev, supplies);

	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;
		}
	}

	while (rc && i--)
		if (!cdc_vreg[i].ondemand)
			regulator_disable(supplies[i].consumer);

	return rc;
}
EXPORT_SYMBOL(msm_cdc_enable_static_supplies);

/*
 * msm_cdc_init_supplies:
 *	Initialize codec static supplies with regulator get
 *
 * @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)
{
	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;

		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_set_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_set_supply;
		}
	}

	*supplies = vsup;

	return 0;

err_set_supply:
	for (i = 0; i < num_supplies; i++)
		devm_regulator_put(vsup[i].consumer);
err_supply:
	devm_kfree(dev, vsup);
	return rc;
}
EXPORT_SYMBOL(msm_cdc_init_supplies);

/*
 * 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("%s: device pointer is NULL\n", __func__);
		return -EINVAL;
	}
	static_sup_cnt = of_property_count_strings(dev->of_node,
						   static_prop_name);
	if (IS_ERR_VALUE(static_sup_cnt)) {
		dev_err(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 (IS_ERR_VALUE(ond_sup_cnt))
		ond_sup_cnt = 0;

	cp_sup_cnt = of_property_count_strings(dev->of_node,
					       cp_prop_name);
	if (IS_ERR_VALUE(cp_sup_cnt))
		cp_sup_cnt = 0;

	num_supplies = static_sup_cnt + ond_sup_cnt + cp_sup_cnt;
	if (num_supplies <= 0) {
		dev_err(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(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(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(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:
	devm_kfree(dev, cdc_reg);
err_supply_cnt:
err_mem_alloc:
	return rc;
}
EXPORT_SYMBOL(msm_cdc_get_power_supplies);

drivers/mfd/wcd-gpio-ctrl.c

deleted100644 → 0
+0 −188
Original line number Diff line number Diff line
/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/mfd/wcd9xxx/wcd-gpio-ctrl.h>

struct wcd_gpio_pinctrl_info {
	struct pinctrl *pinctrl;
	struct pinctrl_state *pinctrl_active;
	struct pinctrl_state *pinctrl_sleep;
};

static struct wcd_gpio_pinctrl_info *wcd_gpio_get_gpiodata(
						struct device_node *np)
{
	struct platform_device *pdev;
	struct wcd_gpio_pinctrl_info *gpio_data;

	if (!np) {
		pr_err("%s: device node is null\n", __func__);
		return NULL;
	}

	pdev = of_find_device_by_node(np);
	if (!pdev) {
		pr_err("%s: platform device not found!\n", __func__);
		return NULL;
	}

	gpio_data = dev_get_drvdata(&pdev->dev);
	if (!gpio_data)
		dev_err(&pdev->dev, "%s: cannot find cdc gpio info\n",
			__func__);

	return gpio_data;
}

/*
 * wcd_gpio_ctrl_select_sleep_state: select pinctrl sleep state
 * @np: pointer to struct device_node
 *
 * Returns error code for failure
 */
int wcd_gpio_ctrl_select_sleep_state(struct device_node *np)
{
	struct wcd_gpio_pinctrl_info *gpio_data;

	gpio_data = wcd_gpio_get_gpiodata(np);
	if (!gpio_data)
		return -EINVAL;

	if (!gpio_data->pinctrl_sleep) {
		pr_err("%s: pinctrl sleep state is null\n", __func__);
		return -EINVAL;
	}

	return pinctrl_select_state(gpio_data->pinctrl,
				    gpio_data->pinctrl_sleep);
}
EXPORT_SYMBOL(wcd_gpio_ctrl_select_sleep_state);

/*
 * wcd_gpio_ctrl_select_active_state: select pinctrl active state
 * @np: pointer to struct device_node
 *
 * Returns error code for failure
 */
int wcd_gpio_ctrl_select_active_state(struct device_node *np)
{
	struct wcd_gpio_pinctrl_info *gpio_data;

	gpio_data = wcd_gpio_get_gpiodata(np);
	if (!gpio_data)
		return -EINVAL;

	if (!gpio_data->pinctrl_active) {
		pr_err("%s: pinctrl active state is null\n", __func__);
		return -EINVAL;
	}

	return pinctrl_select_state(gpio_data->pinctrl,
				    gpio_data->pinctrl_active);
}
EXPORT_SYMBOL(wcd_gpio_ctrl_select_active_state);

static int wcd_gpio_ctrl_probe(struct platform_device *pdev)
{
	int ret = 0;
	struct wcd_gpio_pinctrl_info *gpio_data;

	gpio_data = devm_kzalloc(&pdev->dev,
				 sizeof(struct wcd_gpio_pinctrl_info),
				 GFP_KERNEL);
	if (!gpio_data)
		return -ENOMEM;

	gpio_data->pinctrl = devm_pinctrl_get(&pdev->dev);
	if (IS_ERR_OR_NULL(gpio_data->pinctrl)) {
		dev_err(&pdev->dev, "%s: Cannot get cdc gpio pinctrl:%ld\n",
			__func__, PTR_ERR(gpio_data->pinctrl));
		ret = PTR_ERR(gpio_data->pinctrl);
		goto err_pctrl_get;
	}

	gpio_data->pinctrl_active = pinctrl_lookup_state(
					gpio_data->pinctrl, "aud_active");
	if (IS_ERR_OR_NULL(gpio_data->pinctrl_active)) {
		dev_err(&pdev->dev, "%s: Cannot get aud_active pinctrl state:%ld\n",
			__func__, PTR_ERR(gpio_data->pinctrl_active));
		ret = PTR_ERR(gpio_data->pinctrl_active);
		goto err_lookup_state;
	}

	gpio_data->pinctrl_sleep = pinctrl_lookup_state(
					gpio_data->pinctrl, "aud_sleep");
	if (IS_ERR_OR_NULL(gpio_data->pinctrl_sleep)) {
		dev_err(&pdev->dev, "%s: Cannot get aud_sleep pinctrl state:%ld\n",
			__func__, PTR_ERR(gpio_data->pinctrl_sleep));
		ret = PTR_ERR(gpio_data->pinctrl_sleep);
		goto err_lookup_state;
	}

	/* Set pinctrl state to aud_sleep by default */
	ret = pinctrl_select_state(gpio_data->pinctrl,
				   gpio_data->pinctrl_sleep);
	if (ret)
		dev_err(&pdev->dev, "%s: set cdc gpio sleep state fail: %d\n",
			__func__, ret);

	dev_set_drvdata(&pdev->dev, gpio_data);
	return 0;

err_lookup_state:
	devm_pinctrl_put(gpio_data->pinctrl);
err_pctrl_get:
	devm_kfree(&pdev->dev, gpio_data);
	return ret;
}

static int wcd_gpio_ctrl_remove(struct platform_device *pdev)
{
	struct wcd_gpio_pinctrl_info *gpio_data;

	gpio_data = dev_get_drvdata(&pdev->dev);

	if (gpio_data && gpio_data->pinctrl)
		devm_pinctrl_put(gpio_data->pinctrl);

	devm_kfree(&pdev->dev, gpio_data);

	return 0;
}

static const struct of_device_id wcd_gpio_ctrl_match[] = {
	{.compatible = "qcom,wcd-gpio-ctrl"},
	{}
};

static struct platform_driver wcd_gpio_ctrl_driver = {
	.driver = {
		.name = "wcd-gpio-ctrl",
		.owner = THIS_MODULE,
		.of_match_table = wcd_gpio_ctrl_match,
	},
	.probe = wcd_gpio_ctrl_probe,
	.remove = wcd_gpio_ctrl_remove,
};
module_platform_driver(wcd_gpio_ctrl_driver);

MODULE_DESCRIPTION("WCD GPIO Control module platform driver");
MODULE_LICENSE("GPL v2");
Loading