Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * arizona-micsupp.c  --  Microphone supply for Arizona devices
  3 *
  4 * Copyright 2012 Wolfson Microelectronics PLC.
  5 *
  6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  7 *
  8 *  This program is free software; you can redistribute  it and/or modify it
  9 *  under  the terms of  the GNU General  Public License as published by the
 10 *  Free Software Foundation;  either version 2 of the  License, or (at your
 11 *  option) any later version.
 12 */
 13
 14#include <linux/module.h>
 15#include <linux/moduleparam.h>
 16#include <linux/init.h>
 17#include <linux/bitops.h>
 18#include <linux/err.h>
 19#include <linux/platform_device.h>
 20#include <linux/regulator/driver.h>
 21#include <linux/regulator/machine.h>
 22#include <linux/gpio.h>
 23#include <linux/slab.h>
 24#include <linux/workqueue.h>
 25#include <sound/soc.h>
 26
 27#include <linux/mfd/arizona/core.h>
 28#include <linux/mfd/arizona/pdata.h>
 29#include <linux/mfd/arizona/registers.h>
 30
 31struct arizona_micsupp {
 32	struct regulator_dev *regulator;
 33	struct arizona *arizona;
 34
 35	struct regulator_consumer_supply supply;
 36	struct regulator_init_data init_data;
 37
 38	struct work_struct check_cp_work;
 39};
 40
 41static void arizona_micsupp_check_cp(struct work_struct *work)
 42{
 43	struct arizona_micsupp *micsupp =
 44		container_of(work, struct arizona_micsupp, check_cp_work);
 45	struct snd_soc_dapm_context *dapm = micsupp->arizona->dapm;
 46	struct arizona *arizona = micsupp->arizona;
 47	struct regmap *regmap = arizona->regmap;
 48	unsigned int reg;
 49	int ret;
 50
 51	ret = regmap_read(regmap, ARIZONA_MIC_CHARGE_PUMP_1, &reg);
 52	if (ret != 0) {
 53		dev_err(arizona->dev, "Failed to read CP state: %d\n", ret);
 54		return;
 55	}
 56
 57	if (dapm) {
 58		if ((reg & (ARIZONA_CPMIC_ENA | ARIZONA_CPMIC_BYPASS)) ==
 59		    ARIZONA_CPMIC_ENA)
 60			snd_soc_dapm_force_enable_pin(dapm, "MICSUPP");
 61		else
 62			snd_soc_dapm_disable_pin(dapm, "MICSUPP");
 63
 64		snd_soc_dapm_sync(dapm);
 65	}
 66}
 67
 68static int arizona_micsupp_enable(struct regulator_dev *rdev)
 69{
 70	struct arizona_micsupp *micsupp = rdev_get_drvdata(rdev);
 71	int ret;
 72
 73	ret = regulator_enable_regmap(rdev);
 74
 75	if (ret == 0)
 76		schedule_work(&micsupp->check_cp_work);
 77
 78	return ret;
 79}
 80
 81static int arizona_micsupp_disable(struct regulator_dev *rdev)
 82{
 83	struct arizona_micsupp *micsupp = rdev_get_drvdata(rdev);
 84	int ret;
 85
 86	ret = regulator_disable_regmap(rdev);
 87	if (ret == 0)
 88		schedule_work(&micsupp->check_cp_work);
 89
 90	return ret;
 91}
 92
 93static int arizona_micsupp_set_bypass(struct regulator_dev *rdev, bool ena)
 94{
 95	struct arizona_micsupp *micsupp = rdev_get_drvdata(rdev);
 96	int ret;
 97
 98	ret = regulator_set_bypass_regmap(rdev, ena);
 99	if (ret == 0)
100		schedule_work(&micsupp->check_cp_work);
101
102	return ret;
103}
104
105static struct regulator_ops arizona_micsupp_ops = {
106	.enable = arizona_micsupp_enable,
107	.disable = arizona_micsupp_disable,
108	.is_enabled = regulator_is_enabled_regmap,
109
110	.list_voltage = regulator_list_voltage_linear_range,
111	.map_voltage = regulator_map_voltage_linear_range,
112
113	.get_voltage_sel = regulator_get_voltage_sel_regmap,
114	.set_voltage_sel = regulator_set_voltage_sel_regmap,
115
116	.get_bypass = regulator_get_bypass_regmap,
117	.set_bypass = arizona_micsupp_set_bypass,
118};
119
120static const struct regulator_linear_range arizona_micsupp_ranges[] = {
121	REGULATOR_LINEAR_RANGE(1700000, 0,    0x1e, 50000),
122	REGULATOR_LINEAR_RANGE(3300000, 0x1f, 0x1f, 0),
123};
124
125static const struct regulator_desc arizona_micsupp = {
126	.name = "MICVDD",
127	.supply_name = "CPVDD",
128	.type = REGULATOR_VOLTAGE,
129	.n_voltages = 32,
130	.ops = &arizona_micsupp_ops,
131
132	.vsel_reg = ARIZONA_LDO2_CONTROL_1,
133	.vsel_mask = ARIZONA_LDO2_VSEL_MASK,
134	.enable_reg = ARIZONA_MIC_CHARGE_PUMP_1,
135	.enable_mask = ARIZONA_CPMIC_ENA,
136	.bypass_reg = ARIZONA_MIC_CHARGE_PUMP_1,
137	.bypass_mask = ARIZONA_CPMIC_BYPASS,
138
139	.linear_ranges = arizona_micsupp_ranges,
140	.n_linear_ranges = ARRAY_SIZE(arizona_micsupp_ranges),
141
142	.enable_time = 3000,
143
144	.owner = THIS_MODULE,
145};
146
147static const struct regulator_linear_range arizona_micsupp_ext_ranges[] = {
148	REGULATOR_LINEAR_RANGE(900000,  0,    0x14, 25000),
149	REGULATOR_LINEAR_RANGE(1500000, 0x15, 0x27, 100000),
150};
151
152static const struct regulator_desc arizona_micsupp_ext = {
153	.name = "MICVDD",
154	.supply_name = "CPVDD",
155	.type = REGULATOR_VOLTAGE,
156	.n_voltages = 40,
157	.ops = &arizona_micsupp_ops,
158
159	.vsel_reg = ARIZONA_LDO2_CONTROL_1,
160	.vsel_mask = ARIZONA_LDO2_VSEL_MASK,
161	.enable_reg = ARIZONA_MIC_CHARGE_PUMP_1,
162	.enable_mask = ARIZONA_CPMIC_ENA,
163	.bypass_reg = ARIZONA_MIC_CHARGE_PUMP_1,
164	.bypass_mask = ARIZONA_CPMIC_BYPASS,
165
166	.linear_ranges = arizona_micsupp_ext_ranges,
167	.n_linear_ranges = ARRAY_SIZE(arizona_micsupp_ext_ranges),
168
169	.enable_time = 3000,
170
171	.owner = THIS_MODULE,
172};
173
174static const struct regulator_init_data arizona_micsupp_default = {
175	.constraints = {
176		.valid_ops_mask = REGULATOR_CHANGE_STATUS |
177				REGULATOR_CHANGE_VOLTAGE |
178				REGULATOR_CHANGE_BYPASS,
179		.min_uV = 1700000,
180		.max_uV = 3300000,
181	},
182
183	.num_consumer_supplies = 1,
184};
185
186static const struct regulator_init_data arizona_micsupp_ext_default = {
187	.constraints = {
188		.valid_ops_mask = REGULATOR_CHANGE_STATUS |
189				REGULATOR_CHANGE_VOLTAGE |
190				REGULATOR_CHANGE_BYPASS,
191		.min_uV = 900000,
192		.max_uV = 3300000,
193	},
194
195	.num_consumer_supplies = 1,
196};
197
198static int arizona_micsupp_probe(struct platform_device *pdev)
199{
200	struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
201	const struct regulator_desc *desc;
202	struct regulator_config config = { };
203	struct arizona_micsupp *micsupp;
204	int ret;
205
206	micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL);
207	if (!micsupp)
208		return -ENOMEM;
209
210	micsupp->arizona = arizona;
211	INIT_WORK(&micsupp->check_cp_work, arizona_micsupp_check_cp);
212
213	/*
214	 * Since the chip usually supplies itself we provide some
215	 * default init_data for it.  This will be overridden with
216	 * platform data if provided.
217	 */
218	switch (arizona->type) {
219	case WM5110:
220		desc = &arizona_micsupp_ext;
221		micsupp->init_data = arizona_micsupp_ext_default;
222		break;
223	default:
224		desc = &arizona_micsupp;
225		micsupp->init_data = arizona_micsupp_default;
226		break;
227	}
228
229	micsupp->init_data.consumer_supplies = &micsupp->supply;
230	micsupp->supply.supply = "MICVDD";
231	micsupp->supply.dev_name = dev_name(arizona->dev);
232
233	config.dev = arizona->dev;
234	config.driver_data = micsupp;
235	config.regmap = arizona->regmap;
236
237	if (arizona->pdata.micvdd)
238		config.init_data = arizona->pdata.micvdd;
239	else
240		config.init_data = &micsupp->init_data;
241
242	/* Default to regulated mode until the API supports bypass */
243	regmap_update_bits(arizona->regmap, ARIZONA_MIC_CHARGE_PUMP_1,
244			   ARIZONA_CPMIC_BYPASS, 0);
245
246	micsupp->regulator = devm_regulator_register(&pdev->dev,
247						     desc,
248						     &config);
249	if (IS_ERR(micsupp->regulator)) {
250		ret = PTR_ERR(micsupp->regulator);
251		dev_err(arizona->dev, "Failed to register mic supply: %d\n",
252			ret);
253		return ret;
254	}
255
256	platform_set_drvdata(pdev, micsupp);
257
258	return 0;
259}
260
261static struct platform_driver arizona_micsupp_driver = {
262	.probe = arizona_micsupp_probe,
263	.driver		= {
264		.name	= "arizona-micsupp",
265		.owner	= THIS_MODULE,
266	},
267};
268
269module_platform_driver(arizona_micsupp_driver);
270
271/* Module information */
272MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
273MODULE_DESCRIPTION("Arizona microphone supply driver");
274MODULE_LICENSE("GPL");
275MODULE_ALIAS("platform:arizona-micsupp");