summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xDocumentation/devicetree/bindings/sound/qcom-audio-dev.txt21
-rwxr-xr-xDocumentation/devicetree/bindings/sound/taiko_codec.txt5
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/wcd-gpio-ctrl.c188
-rw-r--r--drivers/mfd/wcd9xxx-core.c85
-rwxr-xr-xinclude/linux/mfd/wcd9xxx/core.h1
-rwxr-xr-xinclude/linux/mfd/wcd9xxx/pdata.h1
-rw-r--r--include/linux/mfd/wcd9xxx/wcd-gpio-ctrl.h34
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