diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index 1b8ad1298176..5be76b21be1c 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -339,6 +339,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 { @@ -589,6 +601,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 4f31ed880f88..7a1913151b33 100644 --- 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 44217e83d611..f4538495ac4b 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -101,7 +101,7 @@ obj-$(CONFIG_WCD9330_CODEC) += wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o\ wcd9xxx-core-resource.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 obj-$(CONFIG_SND_SOC_MSM8X16_WCD) += wcd9xxx-core.o wcd9xxx-irq.o\ wcd9xxx-slimslave.o wcd9xxx-core-resource.o obj-$(CONFIG_SND_SOC_MSM8909_WCD) += wcd9xxx-core.o wcd9xxx-irq.o\ diff --git a/drivers/mfd/wcd-gpio-ctrl.c b/drivers/mfd/wcd-gpio-ctrl.c new file mode 100644 index 000000000000..5620f007b82d --- /dev/null +++ b/drivers/mfd/wcd-gpio-ctrl.c @@ -0,0 +1,189 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 55f82c85e97b..55bace63d80f 100644 --- a/drivers/mfd/wcd9xxx-core.c +++ b/drivers/mfd/wcd9xxx-core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -1116,6 +1117,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->slim_device_bootup && !pdata->use_pinctrl) { ret = gpio_request(wcd9xxx->reset_gpio, "CDC_RESET"); @@ -1158,6 +1181,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); @@ -1685,6 +1714,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, @@ -2135,11 +2173,18 @@ static int wcd9xxx_i2c_probe(struct i2c_client *client, ret = -EINVAL; goto fail; } - 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) { @@ -2149,7 +2194,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->slim_device_bootup = true; if (client->dev.of_node) wcd9xxx->mclk_rate = pdata->mclk_rate; @@ -2580,15 +2624,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); @@ -2816,12 +2864,17 @@ static int wcd9xxx_slim_probe(struct slim_device *slim) wcd9xxx->dev = &slim->dev; wcd9xxx->mclk_rate = pdata->mclk_rate; wcd9xxx->slim_device_bootup = 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 61856f0032ec..fe1ac7a863b6 100644 --- a/include/linux/mfd/wcd9xxx/core.h +++ b/include/linux/mfd/wcd9xxx/core.h @@ -264,6 +264,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 9c3508f79245..a209efaa5710 100644 --- a/include/linux/mfd/wcd9xxx/pdata.h +++ b/include/linux/mfd/wcd9xxx/pdata.h @@ -176,6 +176,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 +#include + +#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