diff mbox

[07/16] regulator: madera-micsupp: Mic supply for Cirrus Logic Madera codecs

Message ID 1491386884-30689-8-git-send-email-rf@opensource.wolfsonmicro.com (mailing list archive)
State New, archived
Headers show

Commit Message

Richard Fitzgerald April 5, 2017, 10:07 a.m. UTC
The adds a driver for the microphone supply regulator on Cirrus Logic
Madera class codecs.

Signed-off-by: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
---
 .../bindings/regulator/madera-micsupp.txt          |  27 +++
 drivers/regulator/Kconfig                          |   8 +
 drivers/regulator/Makefile                         |   1 +
 drivers/regulator/madera-micsupp.c                 | 260 +++++++++++++++++++++
 include/linux/regulator/madera-micsupp.h           |  21 ++
 5 files changed, 317 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/madera-micsupp.txt
 create mode 100644 drivers/regulator/madera-micsupp.c
 create mode 100644 include/linux/regulator/madera-micsupp.h

Comments

Mark Brown April 5, 2017, 1:40 p.m. UTC | #1
On Wed, Apr 05, 2017 at 11:07:55AM +0100, Richard Fitzgerald wrote:
> The adds a driver for the microphone supply regulator on Cirrus Logic
> Madera class codecs.

Again, this appears to have only data and minor code style changes
relative to the existing arizona driver - is there no opportunity for
code sharing here?
Richard Fitzgerald April 5, 2017, 1:53 p.m. UTC | #2
On Wed, 2017-04-05 at 14:40 +0100, Mark Brown wrote:
> On Wed, Apr 05, 2017 at 11:07:55AM +0100, Richard Fitzgerald wrote:
> > The adds a driver for the microphone supply regulator on Cirrus Logic
> > Madera class codecs.
> 
> Again, this appears to have only data and minor code style changes
> relative to the existing arizona driver - is there no opportunity for
> code sharing here?

I'll have a look at how it would work out.
My thought was that these two regulator drivers are so small it wasn't
worth creating an entanglement between arizona and madera for so little
code.
Mark Brown April 6, 2017, 10:57 a.m. UTC | #3
On Wed, Apr 05, 2017 at 02:53:57PM +0100, Richard Fitzgerald wrote:
> On Wed, 2017-04-05 at 14:40 +0100, Mark Brown wrote:

> > Again, this appears to have only data and minor code style changes
> > relative to the existing arizona driver - is there no opportunity for
> > code sharing here?

> I'll have a look at how it would work out.
> My thought was that these two regulator drivers are so small it wasn't
> worth creating an entanglement between arizona and madera for so little
> code.

It's a fairly small amount of code but it's an extremely high proportion
of the code in the driver and doing something a bit unusual, not just
boilerplate.
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/regulator/madera-micsupp.txt b/Documentation/devicetree/bindings/regulator/madera-micsupp.txt
new file mode 100644
index 0000000..08aec7f
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/madera-micsupp.txt
@@ -0,0 +1,27 @@ 
+Cirrus Logic Madera class audio codecs mic supply regulator driver
+
+Only required if the codec has an internal mic supply regulator.
+This is a subnode of the parent mfd node.
+
+See also the core bindings for the parent MFD driver:
+See Documentation/devicetree/bindings/mfd/madera.txt
+
+Required properties:
+  - compatible :  must be "cirrus,madera-micsupp"
+
+Optional subnodes:
+  Standard regulator bindings as described in bindings/regulator/regulator.txt
+
+Example:
+
+codec: cs47l85@0 {
+	compatible = "cirrus,cs47l85";
+
+	micvdd {
+		compatible = "cirrus,madera-micsupp";
+		regulator-min-microvolt = <900000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-allow-bypass = <1>;
+	};
+};
+
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index c96d9a6..4f4b531 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -376,6 +376,14 @@  config REGULATOR_MADERA_LDO1
 	  to supply DCVDD you must include this driver. If you are using an
 	  external DCVDD regulator you do not need this driver.
 
+config REGULATOR_MADERA_MICSUPP
+	tristate "Cirrus Logic Madera codecs mic supply regulator"
+	depends on MFD_MADERA
+	depends on SND_SOC
+	help
+	  Support for the microphone supply regulator on Cirrus Logic
+	  Madera class codecs
+
 config REGULATOR_MAX14577
 	tristate "Maxim 14577/77836 regulator"
 	depends on MFD_MAX14577
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 2db3592..28cdf6e 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -50,6 +50,7 @@  obj-$(CONFIG_REGULATOR_LP8755) += lp8755.o
 obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589.o
 obj-$(CONFIG_REGULATOR_LTC3676) += ltc3676.o
 obj-$(CONFIG_REGULATOR_MADERA_LDO1) += madera-ldo1.o
+obj-$(CONFIG_REGULATOR_MADERA_MICSUPP) += madera-micsupp.o
 obj-$(CONFIG_REGULATOR_MAX14577) += max14577-regulator.o
 obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
 obj-$(CONFIG_REGULATOR_MAX77620) += max77620-regulator.o
diff --git a/drivers/regulator/madera-micsupp.c b/drivers/regulator/madera-micsupp.c
new file mode 100644
index 0000000..ac8bcbd
--- /dev/null
+++ b/drivers/regulator/madera-micsupp.c
@@ -0,0 +1,260 @@ 
+/*
+ * madera-micsupp.c  --  Driver for the mic supply regulator on Madera codecs
+ *
+ * Copyright 2015-2017 Cirrus Logic
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+
+#include <linux/regulator/madera-micsupp.h>
+
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/pdata.h>
+#include <linux/mfd/madera/registers.h>
+
+struct madera_micsupp {
+	struct regulator_dev *regulator;
+	struct madera *madera;
+
+	struct regulator_consumer_supply supply;
+	struct regulator_init_data init_data;
+
+	struct work_struct check_cp_work;
+};
+
+static void madera_micsupp_check_cp(struct work_struct *work)
+{
+	struct madera_micsupp *micsupp =
+		container_of(work, struct madera_micsupp, check_cp_work);
+	struct snd_soc_dapm_context *dapm = micsupp->madera->dapm;
+	struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+	struct madera *madera = micsupp->madera;
+	struct regmap *regmap = madera->regmap;
+	unsigned int reg;
+	int ret;
+
+	if (dapm) {
+		ret = regmap_read(regmap, MADERA_MIC_CHARGE_PUMP_1, &reg);
+		if (ret) {
+			dev_err(madera->dev,
+				"Failed to read CP state: %d\n", ret);
+			return;
+		}
+
+		reg &= MADERA_CPMIC_ENA | MADERA_CPMIC_BYPASS;
+		if (reg == MADERA_CPMIC_ENA)
+			snd_soc_component_force_enable_pin(component,
+							   "MICSUPP");
+		else
+			snd_soc_component_disable_pin(component, "MICSUPP");
+
+		snd_soc_dapm_sync(dapm);
+	}
+}
+
+static int madera_micsupp_enable(struct regulator_dev *rdev)
+{
+	struct madera_micsupp *micsupp = rdev_get_drvdata(rdev);
+	int ret;
+
+	ret = regulator_enable_regmap(rdev);
+	if (ret == 0)
+		schedule_work(&micsupp->check_cp_work);
+
+	return ret;
+}
+
+static int madera_micsupp_disable(struct regulator_dev *rdev)
+{
+	struct madera_micsupp *micsupp = rdev_get_drvdata(rdev);
+	int ret;
+
+	ret = regulator_disable_regmap(rdev);
+	if (ret == 0)
+		schedule_work(&micsupp->check_cp_work);
+
+	return ret;
+}
+
+static int madera_micsupp_set_bypass(struct regulator_dev *rdev, bool ena)
+{
+	struct madera_micsupp *micsupp = rdev_get_drvdata(rdev);
+	int ret;
+
+	ret = regulator_set_bypass_regmap(rdev, ena);
+	if (ret == 0)
+		schedule_work(&micsupp->check_cp_work);
+
+	return ret;
+}
+
+static const struct regulator_ops madera_micsupp_ops = {
+	.enable = madera_micsupp_enable,
+	.disable = madera_micsupp_disable,
+	.is_enabled = regulator_is_enabled_regmap,
+
+	.list_voltage = regulator_list_voltage_linear_range,
+	.map_voltage = regulator_map_voltage_linear_range,
+
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+
+	.get_bypass = regulator_get_bypass_regmap,
+	.set_bypass = madera_micsupp_set_bypass,
+};
+
+static const struct regulator_linear_range madera_micsupp_ranges[] = {
+	REGULATOR_LINEAR_RANGE(900000,  0,    0x14, 25000),
+	REGULATOR_LINEAR_RANGE(1500000, 0x15, 0x27, 100000),
+};
+
+static const struct regulator_desc madera_micsupp = {
+	.name = "MICVDD",
+	.supply_name = "CPVDD1",
+	.type = REGULATOR_VOLTAGE,
+	.n_voltages = 40,
+	.ops = &madera_micsupp_ops,
+
+	.vsel_reg = MADERA_LDO2_CONTROL_1,
+	.vsel_mask = MADERA_LDO2_VSEL_MASK,
+	.enable_reg = MADERA_MIC_CHARGE_PUMP_1,
+	.enable_mask = MADERA_CPMIC_ENA,
+	.bypass_reg = MADERA_MIC_CHARGE_PUMP_1,
+	.bypass_mask = MADERA_CPMIC_BYPASS,
+
+	.linear_ranges = madera_micsupp_ranges,
+	.n_linear_ranges = ARRAY_SIZE(madera_micsupp_ranges),
+
+	.enable_time = 3000,
+
+	.owner = THIS_MODULE,
+};
+
+static const struct regulator_init_data madera_micsupp_default = {
+	.constraints = {
+		.valid_ops_mask = REGULATOR_CHANGE_STATUS |
+				REGULATOR_CHANGE_VOLTAGE |
+				REGULATOR_CHANGE_BYPASS,
+		.min_uV = 900000,
+		.max_uV = 3300000,
+	},
+
+	.num_consumer_supplies = 1,
+};
+
+static int madera_micsupp_of_get_pdata(struct madera *madera,
+					struct regulator_config *config,
+					const struct regulator_desc *desc)
+{
+	struct madera_pdata *pdata = &madera->pdata;
+	struct madera_micsupp *micsupp = config->driver_data;
+	struct regulator_init_data *init_data;
+
+	init_data = of_get_regulator_init_data(config->dev, config->of_node,
+						desc);
+
+	if (init_data) {
+		init_data->consumer_supplies = &micsupp->supply;
+		init_data->num_consumer_supplies = 1;
+		pdata->micsupp.init_data = init_data;
+	}
+
+	return 0;
+}
+
+static int madera_micsupp_probe(struct platform_device *pdev)
+{
+	struct madera *madera = dev_get_drvdata(pdev->dev.parent);
+	const struct regulator_desc *desc;
+	struct regulator_config config = { };
+	struct madera_micsupp *micsupp;
+	int ret;
+
+	micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL);
+	if (!micsupp)
+		return -ENOMEM;
+
+	micsupp->madera = madera;
+	INIT_WORK(&micsupp->check_cp_work, madera_micsupp_check_cp);
+
+	/*
+	 * Since the chip usually supplies itself we provide some
+	 * default init_data for it.  This will be overridden with
+	 * platform data if provided.
+	 */
+	desc = &madera_micsupp;
+	micsupp->init_data = madera_micsupp_default;
+
+	micsupp->init_data.consumer_supplies = &micsupp->supply;
+	micsupp->supply.supply = "MICVDD";
+	micsupp->supply.dev_name = dev_name(madera->dev);
+
+	config.dev = madera->dev;
+	config.of_node = config.dev->of_node;
+	config.driver_data = micsupp;
+	config.regmap = madera->regmap;
+
+	if (IS_ENABLED(CONFIG_OF)) {
+		if (!dev_get_platdata(madera->dev)) {
+			ret = madera_micsupp_of_get_pdata(madera, &config,
+							  desc);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	if (madera->pdata.micsupp.init_data)
+		config.init_data = madera->pdata.micsupp.init_data;
+	else
+		config.init_data = &micsupp->init_data;
+
+	/* Default to regulated mode, in case bypass is not in constraints */
+	regmap_update_bits(madera->regmap, MADERA_MIC_CHARGE_PUMP_1,
+			   MADERA_CPMIC_BYPASS, 0);
+
+	micsupp->regulator = devm_regulator_register(&pdev->dev, desc,
+						     &config);
+
+	of_node_put(config.of_node);
+
+	if (IS_ERR(micsupp->regulator)) {
+		ret = PTR_ERR(micsupp->regulator);
+		dev_err(madera->dev, "Failed to register mic supply: %d\n",
+			ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, micsupp);
+
+	return 0;
+}
+
+static struct platform_driver madera_micsupp_driver = {
+	.probe = madera_micsupp_probe,
+	.driver		= {
+		.name	= "madera-micsupp",
+	},
+};
+
+module_platform_driver(madera_micsupp_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("Madera microphone supply driver");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:madera-micsupp");
diff --git a/include/linux/regulator/madera-micsupp.h b/include/linux/regulator/madera-micsupp.h
new file mode 100644
index 0000000..9913bc6
--- /dev/null
+++ b/include/linux/regulator/madera-micsupp.h
@@ -0,0 +1,21 @@ 
+/*
+ * Platform data for Madera codecs MICSUPP regulator
+ *
+ * Copyright 2016-2017 Cirrus Logic
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef MADERA_MICSUPP_H
+#define MADERA_MICSUPP_H
+
+struct regulator_init_data;
+
+struct madera_micsupp_pdata {
+	/** Regulator configuration for MICSUPP */
+	const struct regulator_init_data *init_data;
+};
+
+#endif