diff options
-rwxr-xr-x | Documentation/devicetree/bindings/sound/qcom-audio-dev.txt | 21 | ||||
-rwxr-xr-x | Documentation/devicetree/bindings/sound/taiko_codec.txt | 5 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 2 | ||||
-rw-r--r-- | drivers/mfd/wcd-gpio-ctrl.c | 188 | ||||
-rw-r--r-- | drivers/mfd/wcd9xxx-core.c | 85 | ||||
-rwxr-xr-x | include/linux/mfd/wcd9xxx/core.h | 1 | ||||
-rwxr-xr-x | include/linux/mfd/wcd9xxx/pdata.h | 1 | ||||
-rw-r--r-- | include/linux/mfd/wcd9xxx/wcd-gpio-ctrl.h | 34 |
8 files changed, 320 insertions, 17 deletions
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index 9513a8f5c6d3..f3bb265da565 100755 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -328,6 +328,18 @@ Required properties: - elemental-addr: slimbus slave enumeration address. +* wcd_gpio_ctrl + +Required properties: + + - compatible : "qcom,wcd-gpio-ctrl" + + - qcom,cdc-rst-n-gpio : TLMM GPIO number + + - pinctrl-names: Pinctrl state names for each pin + group configuration. + - pinctrl-x: Defines pinctrl state for each pin + group. Example: qcom,msm-pcm { @@ -579,6 +591,15 @@ Example: compatible = "qcom,msm_dai_slim"; elemental-addr = [ff ff ff fe 17 02]; }; + + wcd_gpio_ctrl { + compatible = "qcom,wcd-gpio-ctrl"; + qcom,cdc-rst-n-gpio = <&tlmm 64 0>; + pinctrl-names = "aud_active", "aud_sleep"; + pinctrl-0 = <&cdc_reset_active>; + pinctrl-1 = <&cdc_reset_sleep>; + }; + * MSM8916 ASoC Machine driver Required properties: diff --git a/Documentation/devicetree/bindings/sound/taiko_codec.txt b/Documentation/devicetree/bindings/sound/taiko_codec.txt index 0b38ba2b7b95..3bf9e7c74305 100755 --- a/Documentation/devicetree/bindings/sound/taiko_codec.txt +++ b/Documentation/devicetree/bindings/sound/taiko_codec.txt @@ -8,6 +8,11 @@ Required properties: - elemental-addr: codec slimbus slave PGD enumeration address.(48 bits) - qcom,cdc-reset-gpio: gpio used for codec SOC reset. + If this property is not defined, it is expected + to atleast have "qcom,wcd-rst-n-gpio" to be defined. + - qcom,wcd-rst-gpio-node: Phandle reference to the DT node having codec reset gpio + configuration. If this property is not defined, it is + expected to atleast define "qcom,cdc-reset-gpio" property. - cdc-vdd-buck-supply: phandle of buck supply's regulator device tree node. - qcom,cdc-vdd-buck-voltage: buck supply's voltage level min and max in mV. diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 778dc28bf570..f5f7b51ea2a8 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -193,7 +193,7 @@ obj-$(CONFIG_WCD9330_CODEC) += wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o\ wcd9xxx-core-resource.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 + wcd9335-tables.o wcd-gpio-ctrl.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 diff --git a/drivers/mfd/wcd-gpio-ctrl.c b/drivers/mfd/wcd-gpio-ctrl.c new file mode 100644 index 000000000000..d5e6caa4d519 --- /dev/null +++ b/drivers/mfd/wcd-gpio-ctrl.c @@ -0,0 +1,188 @@ +/* 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"); diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c index ebd1cdadb78c..c521eff89abb 100644 --- a/drivers/mfd/wcd9xxx-core.c +++ b/drivers/mfd/wcd9xxx-core.c @@ -23,6 +23,7 @@ #include <linux/mfd/wcd9xxx/core-resource.h> #include <linux/mfd/wcd9xxx/pdata.h> #include <linux/mfd/wcd9xxx/wcd9xxx_registers.h> +#include <linux/mfd/wcd9xxx/wcd-gpio-ctrl.h> #include <linux/mfd/wcd9335/registers.h> #include <linux/delay.h> @@ -1173,6 +1174,28 @@ static int wcd9xxx_reset(struct wcd9xxx *wcd9xxx) int ret; struct wcd9xxx_pdata *pdata = wcd9xxx->dev->platform_data; + if (wcd9xxx->wcd_rst_np) { + /* use pinctrl and call into wcd-rst-gpio driver */ + ret = wcd_gpio_ctrl_select_sleep_state(wcd9xxx->wcd_rst_np); + if (ret) { + pr_err("%s: wcd sleep pinctrl state fail!\n", + __func__); + return ret; + } + /* 20ms sleep required after pulling the reset gpio to LOW */ + msleep(20); + ret = wcd_gpio_ctrl_select_active_state(wcd9xxx->wcd_rst_np); + if (ret) { + pr_err("%s: wcd active pinctrl state fail!\n", + __func__); + return ret; + } + /* 20ms sleep required after pulling the reset gpio to HIGH */ + msleep(20); + + return 0; + } + if (wcd9xxx->reset_gpio && wcd9xxx->dev_up && !pdata->use_pinctrl) { ret = gpio_request(wcd9xxx->reset_gpio, "CDC_RESET"); @@ -1215,6 +1238,12 @@ static int wcd9xxx_reset(struct wcd9xxx *wcd9xxx) static void wcd9xxx_free_reset(struct wcd9xxx *wcd9xxx) { struct wcd9xxx_pdata *pdata = wcd9xxx->dev->platform_data; + + if (wcd9xxx->wcd_rst_np) { + wcd_gpio_ctrl_select_sleep_state(wcd9xxx->wcd_rst_np); + return; + } + if (wcd9xxx->reset_gpio) { if (!pdata->use_pinctrl) { gpio_free(wcd9xxx->reset_gpio); @@ -1743,6 +1772,15 @@ static void wcd9xxx_set_reset_pin_state(struct wcd9xxx *wcd9xxx, struct wcd9xxx_pdata *pdata, bool active) { + if (wcd9xxx->wcd_rst_np) { + if (active) + wcd_gpio_ctrl_select_active_state(wcd9xxx->wcd_rst_np); + else + wcd_gpio_ctrl_select_sleep_state(wcd9xxx->wcd_rst_np); + + return; + } + if (pdata->use_pinctrl) { if (active == true) pinctrl_select_state(pinctrl_info.pinctrl, @@ -2219,11 +2257,18 @@ static int wcd9xxx_i2c_probe(struct i2c_client *client, goto err_codec; } } - ret = extcodec_get_pinctrl(&client->dev); - if (ret < 0) - pdata->use_pinctrl = false; - else + wcd9xxx->reset_gpio = pdata->reset_gpio; + wcd9xxx->wcd_rst_np = pdata->wcd_rst_np; + + if (!wcd9xxx->wcd_rst_np) { + ret = extcodec_get_pinctrl(&client->dev); + if (ret < 0) + pdata->use_pinctrl = false; + else + pdata->use_pinctrl = true; + } else { pdata->use_pinctrl = true; + } if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { @@ -2233,7 +2278,6 @@ static int wcd9xxx_i2c_probe(struct i2c_client *client, } dev_set_drvdata(&client->dev, wcd9xxx); wcd9xxx->dev = &client->dev; - wcd9xxx->reset_gpio = pdata->reset_gpio; wcd9xxx->dev_up = true; if (client->dev.of_node) wcd9xxx->mclk_rate = pdata->mclk_rate; @@ -2672,15 +2716,19 @@ static struct wcd9xxx_pdata *wcd9xxx_populate_dt_pdata(struct device *dev) if (ret) goto err; - pdata->reset_gpio = of_get_named_gpio(dev->of_node, + pdata->wcd_rst_np = of_parse_phandle(dev->of_node, + "qcom,wcd-rst-gpio-node", 0); + if (!pdata->wcd_rst_np) { + pdata->reset_gpio = of_get_named_gpio(dev->of_node, "qcom,cdc-reset-gpio", 0); - if (pdata->reset_gpio < 0) { - dev_err(dev, "Looking up %s property in node %s failed %d\n", - "qcom, cdc-reset-gpio", dev->of_node->full_name, - pdata->reset_gpio); - goto err; + if (pdata->reset_gpio < 0) { + dev_err(dev, "Looking up %s property in node %s failed %d\n", + "qcom, cdc-reset-gpio", + dev->of_node->full_name, pdata->reset_gpio); + goto err; + } + dev_dbg(dev, "%s: reset gpio %d", __func__, pdata->reset_gpio); } - dev_dbg(dev, "%s: reset gpio %d", __func__, pdata->reset_gpio); ret = of_property_read_u32(dev->of_node, "qcom,cdc-mclk-clk-rate", &mclk_rate); @@ -2924,12 +2972,17 @@ static int wcd9xxx_slim_probe(struct slim_device *slim) wcd9xxx->dev = &slim->dev; wcd9xxx->mclk_rate = pdata->mclk_rate; wcd9xxx->dev_up = true; + wcd9xxx->wcd_rst_np = pdata->wcd_rst_np; - ret = extcodec_get_pinctrl(&slim->dev); - if (ret < 0) - pdata->use_pinctrl = false; - else + if (!wcd9xxx->wcd_rst_np) { + ret = extcodec_get_pinctrl(&slim->dev); + if (ret < 0) + pdata->use_pinctrl = false; + else + pdata->use_pinctrl = true; + } else { pdata->use_pinctrl = true; + } ret = wcd9xxx_init_supplies(wcd9xxx, pdata); if (ret) { diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h index 77ed7a86c60d..eed0328b3f59 100755 --- a/include/linux/mfd/wcd9xxx/core.h +++ b/include/linux/mfd/wcd9xxx/core.h @@ -260,6 +260,7 @@ struct wcd9xxx { u8 version; int reset_gpio; + struct device_node *wcd_rst_np; int (*read_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg, int bytes, void *dest, bool interface_reg); diff --git a/include/linux/mfd/wcd9xxx/pdata.h b/include/linux/mfd/wcd9xxx/pdata.h index 7dc7d42013fb..791cb7f1654a 100755 --- a/include/linux/mfd/wcd9xxx/pdata.h +++ b/include/linux/mfd/wcd9xxx/pdata.h @@ -178,6 +178,7 @@ struct wcd9xxx_pdata { int irq_base; int num_irqs; int reset_gpio; + struct device_node *wcd_rst_np; struct wcd9xxx_amic amic_settings; struct slim_device slimbus_slave_device; struct wcd9xxx_micbias_setting micbias; diff --git a/include/linux/mfd/wcd9xxx/wcd-gpio-ctrl.h b/include/linux/mfd/wcd9xxx/wcd-gpio-ctrl.h new file mode 100644 index 000000000000..1260c33d1003 --- /dev/null +++ b/include/linux/mfd/wcd9xxx/wcd-gpio-ctrl.h @@ -0,0 +1,34 @@ +/* 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. + */ + +#ifndef __MFD_CDC_GPIO_CTRL_H_ +#define __MFD_CDC_GPIO_CTRL_H_ + +#include <linux/types.h> +#include <linux/of.h> + +#ifdef CONFIG_WCD9335_CODEC +extern int wcd_gpio_ctrl_select_sleep_state(struct device_node *); +extern int wcd_gpio_ctrl_select_active_state(struct device_node *); + +#else +int wcd_gpio_ctrl_select_sleep_state(struct device_node *np) +{ + return 0; +} +int wcd_gpio_ctrl_select_active_state(struct device_node *np) +{ + return 0; +} +#endif + +#endif |