Blame view

drivers/regulator/arizona-micsupp.c 9.92 KB
dfc0c82b3   Axel Lin   regulator: arizon...
1
2
3
4
5
6
7
  // SPDX-License-Identifier: GPL-2.0+
  //
  // arizona-micsupp.c  --  Microphone supply for Arizona devices
  //
  // Copyright 2012 Wolfson Microelectronics PLC.
  //
  // Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
b667a45d9   Mark Brown   regulator: arizon...
8
9
10
11
12
13
  
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/init.h>
  #include <linux/bitops.h>
  #include <linux/err.h>
6f1c9c57b   Charles Keepax   regulator: arizon...
14
  #include <linux/of.h>
b667a45d9   Mark Brown   regulator: arizon...
15
16
17
  #include <linux/platform_device.h>
  #include <linux/regulator/driver.h>
  #include <linux/regulator/machine.h>
36bcdf1bb   Charles Keepax   regulator: arizon...
18
  #include <linux/regulator/of_regulator.h>
b667a45d9   Mark Brown   regulator: arizon...
19
  #include <linux/slab.h>
e6ed90581   Mark Brown   regulator: arizon...
20
21
  #include <linux/workqueue.h>
  #include <sound/soc.h>
b667a45d9   Mark Brown   regulator: arizon...
22
23
24
25
  
  #include <linux/mfd/arizona/core.h>
  #include <linux/mfd/arizona/pdata.h>
  #include <linux/mfd/arizona/registers.h>
7bd7916dc   Richard Fitzgerald   regulator: arizon...
26
27
28
  #include <linux/mfd/madera/core.h>
  #include <linux/mfd/madera/pdata.h>
  #include <linux/mfd/madera/registers.h>
22161f3eb   Richard Fitzgerald   regulator: arizon...
29
  #include <linux/regulator/arizona-micsupp.h>
b667a45d9   Mark Brown   regulator: arizon...
30
31
  struct arizona_micsupp {
  	struct regulator_dev *regulator;
e165983e5   Richard Fitzgerald   regulator: arizon...
32
33
34
35
  	struct regmap *regmap;
  	struct snd_soc_dapm_context **dapm;
  	unsigned int enable_reg;
  	struct device *dev;
b667a45d9   Mark Brown   regulator: arizon...
36
37
38
  
  	struct regulator_consumer_supply supply;
  	struct regulator_init_data init_data;
e6ed90581   Mark Brown   regulator: arizon...
39
40
  
  	struct work_struct check_cp_work;
b667a45d9   Mark Brown   regulator: arizon...
41
  };
e6ed90581   Mark Brown   regulator: arizon...
42
43
44
45
  static void arizona_micsupp_check_cp(struct work_struct *work)
  {
  	struct arizona_micsupp *micsupp =
  		container_of(work, struct arizona_micsupp, check_cp_work);
e165983e5   Richard Fitzgerald   regulator: arizon...
46
47
48
  	struct snd_soc_dapm_context *dapm = *micsupp->dapm;
  	struct snd_soc_component *component;
  	unsigned int val;
e6ed90581   Mark Brown   regulator: arizon...
49
  	int ret;
e165983e5   Richard Fitzgerald   regulator: arizon...
50
  	ret = regmap_read(micsupp->regmap, micsupp->enable_reg, &val);
e6ed90581   Mark Brown   regulator: arizon...
51
  	if (ret != 0) {
e165983e5   Richard Fitzgerald   regulator: arizon...
52
53
54
  		dev_err(micsupp->dev,
  			"Failed to read CP state: %d
  ", ret);
e6ed90581   Mark Brown   regulator: arizon...
55
56
57
58
  		return;
  	}
  
  	if (dapm) {
e165983e5   Richard Fitzgerald   regulator: arizon...
59
60
61
  		component = snd_soc_dapm_to_component(dapm);
  
  		if ((val & (ARIZONA_CPMIC_ENA | ARIZONA_CPMIC_BYPASS)) ==
e6ed90581   Mark Brown   regulator: arizon...
62
  		    ARIZONA_CPMIC_ENA)
98cf9965c   Richard Fitzgerald   regulator: arizon...
63
64
  			snd_soc_component_force_enable_pin(component,
  							   "MICSUPP");
e6ed90581   Mark Brown   regulator: arizon...
65
  		else
98cf9965c   Richard Fitzgerald   regulator: arizon...
66
  			snd_soc_component_disable_pin(component, "MICSUPP");
e6ed90581   Mark Brown   regulator: arizon...
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  
  		snd_soc_dapm_sync(dapm);
  	}
  }
  
  static int arizona_micsupp_enable(struct regulator_dev *rdev)
  {
  	struct arizona_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 arizona_micsupp_disable(struct regulator_dev *rdev)
  {
  	struct arizona_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 arizona_micsupp_set_bypass(struct regulator_dev *rdev, bool ena)
  {
  	struct arizona_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;
  }
2773ead13   Bhumika Goyal   regulator: arizon...
108
  static const struct regulator_ops arizona_micsupp_ops = {
e6ed90581   Mark Brown   regulator: arizon...
109
110
  	.enable = arizona_micsupp_enable,
  	.disable = arizona_micsupp_disable,
b667a45d9   Mark Brown   regulator: arizon...
111
  	.is_enabled = regulator_is_enabled_regmap,
71979aa3c   Charles Keepax   regulator: arizon...
112
113
  	.list_voltage = regulator_list_voltage_linear_range,
  	.map_voltage = regulator_map_voltage_linear_range,
b667a45d9   Mark Brown   regulator: arizon...
114
115
116
  
  	.get_voltage_sel = regulator_get_voltage_sel_regmap,
  	.set_voltage_sel = regulator_set_voltage_sel_regmap,
e477ce075   Mark Brown   regulator: arizon...
117
118
  
  	.get_bypass = regulator_get_bypass_regmap,
e6ed90581   Mark Brown   regulator: arizon...
119
  	.set_bypass = arizona_micsupp_set_bypass,
b667a45d9   Mark Brown   regulator: arizon...
120
  };
60ab7f415   Matti Vaittinen   regulator: use li...
121
  static const struct linear_range arizona_micsupp_ranges[] = {
71979aa3c   Charles Keepax   regulator: arizon...
122
123
124
  	REGULATOR_LINEAR_RANGE(1700000, 0,    0x1e, 50000),
  	REGULATOR_LINEAR_RANGE(3300000, 0x1f, 0x1f, 0),
  };
b667a45d9   Mark Brown   regulator: arizon...
125
126
127
128
  static const struct regulator_desc arizona_micsupp = {
  	.name = "MICVDD",
  	.supply_name = "CPVDD",
  	.type = REGULATOR_VOLTAGE,
71979aa3c   Charles Keepax   regulator: arizon...
129
  	.n_voltages = 32,
b667a45d9   Mark Brown   regulator: arizon...
130
131
132
133
134
135
  	.ops = &arizona_micsupp_ops,
  
  	.vsel_reg = ARIZONA_LDO2_CONTROL_1,
  	.vsel_mask = ARIZONA_LDO2_VSEL_MASK,
  	.enable_reg = ARIZONA_MIC_CHARGE_PUMP_1,
  	.enable_mask = ARIZONA_CPMIC_ENA,
e477ce075   Mark Brown   regulator: arizon...
136
137
  	.bypass_reg = ARIZONA_MIC_CHARGE_PUMP_1,
  	.bypass_mask = ARIZONA_CPMIC_BYPASS,
b667a45d9   Mark Brown   regulator: arizon...
138

71979aa3c   Charles Keepax   regulator: arizon...
139
140
  	.linear_ranges = arizona_micsupp_ranges,
  	.n_linear_ranges = ARRAY_SIZE(arizona_micsupp_ranges),
950728185   Mark Brown   regulator: arizon...
141
  	.enable_time = 3000,
b667a45d9   Mark Brown   regulator: arizon...
142
143
  	.owner = THIS_MODULE,
  };
60ab7f415   Matti Vaittinen   regulator: use li...
144
  static const struct linear_range arizona_micsupp_ext_ranges[] = {
d2e7491e2   Charles Keepax   regulator: arizon...
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
  	REGULATOR_LINEAR_RANGE(900000,  0,    0x14, 25000),
  	REGULATOR_LINEAR_RANGE(1500000, 0x15, 0x27, 100000),
  };
  
  static const struct regulator_desc arizona_micsupp_ext = {
  	.name = "MICVDD",
  	.supply_name = "CPVDD",
  	.type = REGULATOR_VOLTAGE,
  	.n_voltages = 40,
  	.ops = &arizona_micsupp_ops,
  
  	.vsel_reg = ARIZONA_LDO2_CONTROL_1,
  	.vsel_mask = ARIZONA_LDO2_VSEL_MASK,
  	.enable_reg = ARIZONA_MIC_CHARGE_PUMP_1,
  	.enable_mask = ARIZONA_CPMIC_ENA,
  	.bypass_reg = ARIZONA_MIC_CHARGE_PUMP_1,
  	.bypass_mask = ARIZONA_CPMIC_BYPASS,
  
  	.linear_ranges = arizona_micsupp_ext_ranges,
  	.n_linear_ranges = ARRAY_SIZE(arizona_micsupp_ext_ranges),
  
  	.enable_time = 3000,
  
  	.owner = THIS_MODULE,
  };
b667a45d9   Mark Brown   regulator: arizon...
170
171
172
  static const struct regulator_init_data arizona_micsupp_default = {
  	.constraints = {
  		.valid_ops_mask = REGULATOR_CHANGE_STATUS |
9fc50a2ea   Mark Brown   regulator: arizon...
173
174
  				REGULATOR_CHANGE_VOLTAGE |
  				REGULATOR_CHANGE_BYPASS,
b667a45d9   Mark Brown   regulator: arizon...
175
176
177
178
179
180
  		.min_uV = 1700000,
  		.max_uV = 3300000,
  	},
  
  	.num_consumer_supplies = 1,
  };
d2e7491e2   Charles Keepax   regulator: arizon...
181
182
183
184
185
186
187
188
189
190
191
  static const struct regulator_init_data arizona_micsupp_ext_default = {
  	.constraints = {
  		.valid_ops_mask = REGULATOR_CHANGE_STATUS |
  				REGULATOR_CHANGE_VOLTAGE |
  				REGULATOR_CHANGE_BYPASS,
  		.min_uV = 900000,
  		.max_uV = 3300000,
  	},
  
  	.num_consumer_supplies = 1,
  };
7bd7916dc   Richard Fitzgerald   regulator: arizon...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
  static const struct regulator_desc madera_micsupp = {
  	.name = "MICVDD",
  	.supply_name = "CPVDD1",
  	.type = REGULATOR_VOLTAGE,
  	.n_voltages = 40,
  	.ops = &arizona_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 = arizona_micsupp_ext_ranges,
  	.n_linear_ranges = ARRAY_SIZE(arizona_micsupp_ext_ranges),
  
  	.enable_time = 3000,
  
  	.owner = THIS_MODULE,
  };
22161f3eb   Richard Fitzgerald   regulator: arizon...
213
  static int arizona_micsupp_of_get_pdata(struct arizona_micsupp_pdata *pdata,
072e78b12   Javier Martinez Canillas   regulator: of: Ad...
214
215
  					struct regulator_config *config,
  					const struct regulator_desc *desc)
36bcdf1bb   Charles Keepax   regulator: arizon...
216
  {
36bcdf1bb   Charles Keepax   regulator: arizon...
217
218
219
  	struct arizona_micsupp *micsupp = config->driver_data;
  	struct device_node *np;
  	struct regulator_init_data *init_data;
22161f3eb   Richard Fitzgerald   regulator: arizon...
220
  	np = of_get_child_by_name(config->dev->of_node, "micvdd");
36bcdf1bb   Charles Keepax   regulator: arizon...
221
222
223
  
  	if (np) {
  		config->of_node = np;
22161f3eb   Richard Fitzgerald   regulator: arizon...
224
  		init_data = of_get_regulator_init_data(config->dev, np, desc);
36bcdf1bb   Charles Keepax   regulator: arizon...
225
226
227
228
  
  		if (init_data) {
  			init_data->consumer_supplies = &micsupp->supply;
  			init_data->num_consumer_supplies = 1;
22161f3eb   Richard Fitzgerald   regulator: arizon...
229
  			pdata->init_data = init_data;
36bcdf1bb   Charles Keepax   regulator: arizon...
230
231
232
233
234
  		}
  	}
  
  	return 0;
  }
7d8d14b51   Richard Fitzgerald   regulator: arizon...
235
236
237
238
  static int arizona_micsupp_common_init(struct platform_device *pdev,
  				       struct arizona_micsupp *micsupp,
  				       const struct regulator_desc *desc,
  				       struct arizona_micsupp_pdata *pdata)
b667a45d9   Mark Brown   regulator: arizon...
239
  {
b667a45d9   Mark Brown   regulator: arizon...
240
  	struct regulator_config config = { };
b667a45d9   Mark Brown   regulator: arizon...
241
  	int ret;
e6ed90581   Mark Brown   regulator: arizon...
242
  	INIT_WORK(&micsupp->check_cp_work, arizona_micsupp_check_cp);
b667a45d9   Mark Brown   regulator: arizon...
243

b667a45d9   Mark Brown   regulator: arizon...
244
245
  	micsupp->init_data.consumer_supplies = &micsupp->supply;
  	micsupp->supply.supply = "MICVDD";
7d8d14b51   Richard Fitzgerald   regulator: arizon...
246
  	micsupp->supply.dev_name = dev_name(micsupp->dev);
e165983e5   Richard Fitzgerald   regulator: arizon...
247
  	micsupp->enable_reg = desc->enable_reg;
b667a45d9   Mark Brown   regulator: arizon...
248

7d8d14b51   Richard Fitzgerald   regulator: arizon...
249
  	config.dev = micsupp->dev;
b667a45d9   Mark Brown   regulator: arizon...
250
  	config.driver_data = micsupp;
7d8d14b51   Richard Fitzgerald   regulator: arizon...
251
  	config.regmap = micsupp->regmap;
b667a45d9   Mark Brown   regulator: arizon...
252

36bcdf1bb   Charles Keepax   regulator: arizon...
253
  	if (IS_ENABLED(CONFIG_OF)) {
7d8d14b51   Richard Fitzgerald   regulator: arizon...
254
  		if (!dev_get_platdata(micsupp->dev)) {
22161f3eb   Richard Fitzgerald   regulator: arizon...
255
256
  			ret = arizona_micsupp_of_get_pdata(pdata, &config,
  							   desc);
36bcdf1bb   Charles Keepax   regulator: arizon...
257
258
259
260
  			if (ret < 0)
  				return ret;
  		}
  	}
22161f3eb   Richard Fitzgerald   regulator: arizon...
261
262
  	if (pdata->init_data)
  		config.init_data = pdata->init_data;
b667a45d9   Mark Brown   regulator: arizon...
263
264
  	else
  		config.init_data = &micsupp->init_data;
7d8d14b51   Richard Fitzgerald   regulator: arizon...
265
266
  	/* Default to regulated mode */
  	regmap_update_bits(micsupp->regmap, micsupp->enable_reg,
6dc027c97   Mark Brown   regulator: arizon...
267
  			   ARIZONA_CPMIC_BYPASS, 0);
b6b7709cf   Mark Brown   regulator: arizon...
268
  	micsupp->regulator = devm_regulator_register(&pdev->dev,
d2e7491e2   Charles Keepax   regulator: arizon...
269
  						     desc,
b6b7709cf   Mark Brown   regulator: arizon...
270
  						     &config);
a7b976ae4   Charles Keepax   regulator: arizon...
271
272
  
  	of_node_put(config.of_node);
b667a45d9   Mark Brown   regulator: arizon...
273
274
  	if (IS_ERR(micsupp->regulator)) {
  		ret = PTR_ERR(micsupp->regulator);
7d8d14b51   Richard Fitzgerald   regulator: arizon...
275
276
  		dev_err(micsupp->dev, "Failed to register mic supply: %d
  ",
b667a45d9   Mark Brown   regulator: arizon...
277
278
279
280
281
282
283
284
  			ret);
  		return ret;
  	}
  
  	platform_set_drvdata(pdev, micsupp);
  
  	return 0;
  }
7d8d14b51   Richard Fitzgerald   regulator: arizon...
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
  static int arizona_micsupp_probe(struct platform_device *pdev)
  {
  	struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
  	const struct regulator_desc *desc;
  	struct arizona_micsupp *micsupp;
  
  	micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL);
  	if (!micsupp)
  		return -ENOMEM;
  
  	micsupp->regmap = arizona->regmap;
  	micsupp->dapm = &arizona->dapm;
  	micsupp->dev = arizona->dev;
  
  	/*
  	 * Since the chip usually supplies itself we provide some
  	 * default init_data for it.  This will be overridden with
  	 * platform data if provided.
  	 */
  	switch (arizona->type) {
  	case WM5110:
  	case WM8280:
  		desc = &arizona_micsupp_ext;
  		micsupp->init_data = arizona_micsupp_ext_default;
  		break;
  	default:
  		desc = &arizona_micsupp;
  		micsupp->init_data = arizona_micsupp_default;
  		break;
  	}
  
  	return arizona_micsupp_common_init(pdev, micsupp, desc,
  					   &arizona->pdata.micvdd);
  }
7bd7916dc   Richard Fitzgerald   regulator: arizon...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
  static int madera_micsupp_probe(struct platform_device *pdev)
  {
  	struct madera *madera = dev_get_drvdata(pdev->dev.parent);
  	struct arizona_micsupp *micsupp;
  
  	micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL);
  	if (!micsupp)
  		return -ENOMEM;
  
  	micsupp->regmap = madera->regmap;
  	micsupp->dapm = &madera->dapm;
  	micsupp->dev = madera->dev;
  	micsupp->init_data = arizona_micsupp_ext_default;
  
  	return arizona_micsupp_common_init(pdev, micsupp, &madera_micsupp,
  					   &madera->pdata.micvdd);
  }
b667a45d9   Mark Brown   regulator: arizon...
336
337
  static struct platform_driver arizona_micsupp_driver = {
  	.probe = arizona_micsupp_probe,
b667a45d9   Mark Brown   regulator: arizon...
338
339
  	.driver		= {
  		.name	= "arizona-micsupp",
b667a45d9   Mark Brown   regulator: arizon...
340
341
  	},
  };
7bd7916dc   Richard Fitzgerald   regulator: arizon...
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
  static struct platform_driver madera_micsupp_driver = {
  	.probe = madera_micsupp_probe,
  	.driver		= {
  		.name	= "madera-micsupp",
  	},
  };
  
  static struct platform_driver * const arizona_micsupp_drivers[] = {
  	&arizona_micsupp_driver,
  	&madera_micsupp_driver,
  };
  
  static int __init arizona_micsupp_init(void)
  {
  	return platform_register_drivers(arizona_micsupp_drivers,
  					 ARRAY_SIZE(arizona_micsupp_drivers));
  }
  module_init(arizona_micsupp_init);
  
  static void __exit arizona_micsupp_exit(void)
  {
  	platform_unregister_drivers(arizona_micsupp_drivers,
  				    ARRAY_SIZE(arizona_micsupp_drivers));
  }
  module_exit(arizona_micsupp_exit);
b667a45d9   Mark Brown   regulator: arizon...
367
368
369
370
371
372
  
  /* Module information */
  MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
  MODULE_DESCRIPTION("Arizona microphone supply driver");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:arizona-micsupp");
7bd7916dc   Richard Fitzgerald   regulator: arizon...
373
  MODULE_ALIAS("platform:madera-micsupp");