xref: /linux/drivers/iio/amplifiers/adl8113.c (revision 505d195b0f96fd613a51b13dde37aa5ad301eb32)
1*b8c7340eSAntoniu Miclaus // SPDX-License-Identifier: GPL-2.0
2*b8c7340eSAntoniu Miclaus /*
3*b8c7340eSAntoniu Miclaus  * ADL8113 Low Noise Amplifier with integrated bypass switches
4*b8c7340eSAntoniu Miclaus  *
5*b8c7340eSAntoniu Miclaus  * Copyright 2025 Analog Devices Inc.
6*b8c7340eSAntoniu Miclaus  */
7*b8c7340eSAntoniu Miclaus 
8*b8c7340eSAntoniu Miclaus #include <linux/array_size.h>
9*b8c7340eSAntoniu Miclaus #include <linux/bitmap.h>
10*b8c7340eSAntoniu Miclaus #include <linux/device/driver.h>
11*b8c7340eSAntoniu Miclaus #include <linux/dev_printk.h>
12*b8c7340eSAntoniu Miclaus #include <linux/err.h>
13*b8c7340eSAntoniu Miclaus #include <linux/gpio/consumer.h>
14*b8c7340eSAntoniu Miclaus #include <linux/iio/iio.h>
15*b8c7340eSAntoniu Miclaus #include <linux/mod_devicetable.h>
16*b8c7340eSAntoniu Miclaus #include <linux/module.h>
17*b8c7340eSAntoniu Miclaus #include <linux/platform_device.h>
18*b8c7340eSAntoniu Miclaus #include <linux/property.h>
19*b8c7340eSAntoniu Miclaus #include <linux/regulator/consumer.h>
20*b8c7340eSAntoniu Miclaus #include <linux/types.h>
21*b8c7340eSAntoniu Miclaus 
22*b8c7340eSAntoniu Miclaus enum adl8113_signal_path {
23*b8c7340eSAntoniu Miclaus 	ADL8113_INTERNAL_AMP,
24*b8c7340eSAntoniu Miclaus 	ADL8113_INTERNAL_BYPASS,
25*b8c7340eSAntoniu Miclaus 	ADL8113_EXTERNAL_A,
26*b8c7340eSAntoniu Miclaus 	ADL8113_EXTERNAL_B,
27*b8c7340eSAntoniu Miclaus };
28*b8c7340eSAntoniu Miclaus 
29*b8c7340eSAntoniu Miclaus struct adl8113_gain_config {
30*b8c7340eSAntoniu Miclaus 	enum adl8113_signal_path path;
31*b8c7340eSAntoniu Miclaus 	int gain_db;
32*b8c7340eSAntoniu Miclaus };
33*b8c7340eSAntoniu Miclaus 
34*b8c7340eSAntoniu Miclaus struct adl8113_state {
35*b8c7340eSAntoniu Miclaus 	struct gpio_descs *gpios;
36*b8c7340eSAntoniu Miclaus 	struct adl8113_gain_config *gain_configs;
37*b8c7340eSAntoniu Miclaus 	unsigned int num_gain_configs;
38*b8c7340eSAntoniu Miclaus 	enum adl8113_signal_path current_path;
39*b8c7340eSAntoniu Miclaus };
40*b8c7340eSAntoniu Miclaus 
41*b8c7340eSAntoniu Miclaus static const char * const adl8113_supply_names[] = {
42*b8c7340eSAntoniu Miclaus 	"vdd1",
43*b8c7340eSAntoniu Miclaus 	"vss2",
44*b8c7340eSAntoniu Miclaus 	"vdd2",
45*b8c7340eSAntoniu Miclaus };
46*b8c7340eSAntoniu Miclaus 
47*b8c7340eSAntoniu Miclaus static int adl8113_set_path(struct adl8113_state *st,
48*b8c7340eSAntoniu Miclaus 			    enum adl8113_signal_path path)
49*b8c7340eSAntoniu Miclaus {
50*b8c7340eSAntoniu Miclaus 	DECLARE_BITMAP(values, 2);
51*b8c7340eSAntoniu Miclaus 	int ret;
52*b8c7340eSAntoniu Miclaus 
53*b8c7340eSAntoniu Miclaus 	/*
54*b8c7340eSAntoniu Miclaus 	 * Determine GPIO values based on signal path.
55*b8c7340eSAntoniu Miclaus 	 * Va: bit 0, Vb: bit 1.
56*b8c7340eSAntoniu Miclaus 	 */
57*b8c7340eSAntoniu Miclaus 	switch (path) {
58*b8c7340eSAntoniu Miclaus 	case ADL8113_INTERNAL_AMP:
59*b8c7340eSAntoniu Miclaus 		bitmap_write(values, 0x00, 0, 2);
60*b8c7340eSAntoniu Miclaus 		break;
61*b8c7340eSAntoniu Miclaus 	case ADL8113_INTERNAL_BYPASS:
62*b8c7340eSAntoniu Miclaus 		bitmap_write(values, 0x03, 0, 2);
63*b8c7340eSAntoniu Miclaus 		break;
64*b8c7340eSAntoniu Miclaus 	case ADL8113_EXTERNAL_A:
65*b8c7340eSAntoniu Miclaus 		bitmap_write(values, 0x02, 0, 2);
66*b8c7340eSAntoniu Miclaus 		break;
67*b8c7340eSAntoniu Miclaus 	case ADL8113_EXTERNAL_B:
68*b8c7340eSAntoniu Miclaus 		bitmap_write(values, 0x01, 0, 2);
69*b8c7340eSAntoniu Miclaus 		break;
70*b8c7340eSAntoniu Miclaus 	default:
71*b8c7340eSAntoniu Miclaus 		return -EINVAL;
72*b8c7340eSAntoniu Miclaus 	}
73*b8c7340eSAntoniu Miclaus 
74*b8c7340eSAntoniu Miclaus 	ret = gpiod_set_array_value_cansleep(st->gpios->ndescs, st->gpios->desc,
75*b8c7340eSAntoniu Miclaus 					     st->gpios->info, values);
76*b8c7340eSAntoniu Miclaus 	if (ret)
77*b8c7340eSAntoniu Miclaus 		return ret;
78*b8c7340eSAntoniu Miclaus 
79*b8c7340eSAntoniu Miclaus 	st->current_path = path;
80*b8c7340eSAntoniu Miclaus 	return 0;
81*b8c7340eSAntoniu Miclaus }
82*b8c7340eSAntoniu Miclaus 
83*b8c7340eSAntoniu Miclaus static int adl8113_find_gain_config(struct adl8113_state *st, int gain_db)
84*b8c7340eSAntoniu Miclaus {
85*b8c7340eSAntoniu Miclaus 	unsigned int i;
86*b8c7340eSAntoniu Miclaus 
87*b8c7340eSAntoniu Miclaus 	for (i = 0; i < st->num_gain_configs; i++) {
88*b8c7340eSAntoniu Miclaus 		if (st->gain_configs[i].gain_db == gain_db)
89*b8c7340eSAntoniu Miclaus 			return i;
90*b8c7340eSAntoniu Miclaus 	}
91*b8c7340eSAntoniu Miclaus 	return -EINVAL;
92*b8c7340eSAntoniu Miclaus }
93*b8c7340eSAntoniu Miclaus 
94*b8c7340eSAntoniu Miclaus static const struct iio_chan_spec adl8113_channels[] = {
95*b8c7340eSAntoniu Miclaus 	{
96*b8c7340eSAntoniu Miclaus 		.type = IIO_VOLTAGE,
97*b8c7340eSAntoniu Miclaus 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN),
98*b8c7340eSAntoniu Miclaus 	},
99*b8c7340eSAntoniu Miclaus };
100*b8c7340eSAntoniu Miclaus 
101*b8c7340eSAntoniu Miclaus static int adl8113_read_raw(struct iio_dev *indio_dev,
102*b8c7340eSAntoniu Miclaus 			    struct iio_chan_spec const *chan,
103*b8c7340eSAntoniu Miclaus 			    int *val, int *val2, long mask)
104*b8c7340eSAntoniu Miclaus {
105*b8c7340eSAntoniu Miclaus 	struct adl8113_state *st = iio_priv(indio_dev);
106*b8c7340eSAntoniu Miclaus 	unsigned int i;
107*b8c7340eSAntoniu Miclaus 
108*b8c7340eSAntoniu Miclaus 	switch (mask) {
109*b8c7340eSAntoniu Miclaus 	case IIO_CHAN_INFO_HARDWAREGAIN:
110*b8c7340eSAntoniu Miclaus 		/* Find current gain configuration */
111*b8c7340eSAntoniu Miclaus 		for (i = 0; i < st->num_gain_configs; i++) {
112*b8c7340eSAntoniu Miclaus 			if (st->gain_configs[i].path == st->current_path) {
113*b8c7340eSAntoniu Miclaus 				*val = st->gain_configs[i].gain_db;
114*b8c7340eSAntoniu Miclaus 				*val2 = 0;
115*b8c7340eSAntoniu Miclaus 				return IIO_VAL_INT_PLUS_MICRO_DB;
116*b8c7340eSAntoniu Miclaus 			}
117*b8c7340eSAntoniu Miclaus 		}
118*b8c7340eSAntoniu Miclaus 		return -EINVAL;
119*b8c7340eSAntoniu Miclaus 	default:
120*b8c7340eSAntoniu Miclaus 		return -EINVAL;
121*b8c7340eSAntoniu Miclaus 	}
122*b8c7340eSAntoniu Miclaus }
123*b8c7340eSAntoniu Miclaus 
124*b8c7340eSAntoniu Miclaus static int adl8113_write_raw(struct iio_dev *indio_dev,
125*b8c7340eSAntoniu Miclaus 			     struct iio_chan_spec const *chan,
126*b8c7340eSAntoniu Miclaus 			     int val, int val2, long mask)
127*b8c7340eSAntoniu Miclaus {
128*b8c7340eSAntoniu Miclaus 	struct adl8113_state *st = iio_priv(indio_dev);
129*b8c7340eSAntoniu Miclaus 	int config_idx;
130*b8c7340eSAntoniu Miclaus 
131*b8c7340eSAntoniu Miclaus 	switch (mask) {
132*b8c7340eSAntoniu Miclaus 	case IIO_CHAN_INFO_HARDWAREGAIN:
133*b8c7340eSAntoniu Miclaus 		if (val2 != 0)
134*b8c7340eSAntoniu Miclaus 			return -EINVAL;
135*b8c7340eSAntoniu Miclaus 
136*b8c7340eSAntoniu Miclaus 		config_idx = adl8113_find_gain_config(st, val);
137*b8c7340eSAntoniu Miclaus 		if (config_idx < 0)
138*b8c7340eSAntoniu Miclaus 			return config_idx;
139*b8c7340eSAntoniu Miclaus 
140*b8c7340eSAntoniu Miclaus 		return adl8113_set_path(st, st->gain_configs[config_idx].path);
141*b8c7340eSAntoniu Miclaus 	default:
142*b8c7340eSAntoniu Miclaus 		return -EINVAL;
143*b8c7340eSAntoniu Miclaus 	}
144*b8c7340eSAntoniu Miclaus }
145*b8c7340eSAntoniu Miclaus 
146*b8c7340eSAntoniu Miclaus static const struct iio_info adl8113_info = {
147*b8c7340eSAntoniu Miclaus 	.read_raw = adl8113_read_raw,
148*b8c7340eSAntoniu Miclaus 	.write_raw = adl8113_write_raw,
149*b8c7340eSAntoniu Miclaus };
150*b8c7340eSAntoniu Miclaus 
151*b8c7340eSAntoniu Miclaus static int adl8113_init_gain_configs(struct device *dev, struct adl8113_state *st)
152*b8c7340eSAntoniu Miclaus {
153*b8c7340eSAntoniu Miclaus 	int external_a_gain, external_b_gain;
154*b8c7340eSAntoniu Miclaus 	unsigned int i;
155*b8c7340eSAntoniu Miclaus 
156*b8c7340eSAntoniu Miclaus 	/*
157*b8c7340eSAntoniu Miclaus 	 * Allocate for all 4 possible paths:
158*b8c7340eSAntoniu Miclaus 	 * - Internal amp and bypass (always present)
159*b8c7340eSAntoniu Miclaus 	 * - External bypass A and B (optional if configured)
160*b8c7340eSAntoniu Miclaus 	 */
161*b8c7340eSAntoniu Miclaus 	st->gain_configs = devm_kcalloc(dev, 4, sizeof(*st->gain_configs),
162*b8c7340eSAntoniu Miclaus 					GFP_KERNEL);
163*b8c7340eSAntoniu Miclaus 	if (!st->gain_configs)
164*b8c7340eSAntoniu Miclaus 		return -ENOMEM;
165*b8c7340eSAntoniu Miclaus 
166*b8c7340eSAntoniu Miclaus 	/* Start filling the gain configurations with data */
167*b8c7340eSAntoniu Miclaus 	i = 0;
168*b8c7340eSAntoniu Miclaus 
169*b8c7340eSAntoniu Miclaus 	/* Always include internal amplifier (14dB) */
170*b8c7340eSAntoniu Miclaus 	st->gain_configs[i++] = (struct adl8113_gain_config) {
171*b8c7340eSAntoniu Miclaus 		.path = ADL8113_INTERNAL_AMP,
172*b8c7340eSAntoniu Miclaus 		.gain_db = 14,
173*b8c7340eSAntoniu Miclaus 	};
174*b8c7340eSAntoniu Miclaus 
175*b8c7340eSAntoniu Miclaus 	/* Always include internal bypass (-2dB insertion loss) */
176*b8c7340eSAntoniu Miclaus 	st->gain_configs[i++] = (struct adl8113_gain_config) {
177*b8c7340eSAntoniu Miclaus 		.path = ADL8113_INTERNAL_BYPASS,
178*b8c7340eSAntoniu Miclaus 		.gain_db = -2,
179*b8c7340eSAntoniu Miclaus 	};
180*b8c7340eSAntoniu Miclaus 
181*b8c7340eSAntoniu Miclaus 	/* Add external bypass A if configured */
182*b8c7340eSAntoniu Miclaus 	if (!device_property_read_u32(dev, "adi,external-bypass-a-gain-db",
183*b8c7340eSAntoniu Miclaus 				      &external_a_gain)) {
184*b8c7340eSAntoniu Miclaus 		st->gain_configs[i++] = (struct adl8113_gain_config) {
185*b8c7340eSAntoniu Miclaus 			.path = ADL8113_EXTERNAL_A,
186*b8c7340eSAntoniu Miclaus 			.gain_db = external_a_gain,
187*b8c7340eSAntoniu Miclaus 		};
188*b8c7340eSAntoniu Miclaus 	}
189*b8c7340eSAntoniu Miclaus 
190*b8c7340eSAntoniu Miclaus 	/* Add external bypass B if configured */
191*b8c7340eSAntoniu Miclaus 	if (!device_property_read_u32(dev, "adi,external-bypass-b-gain-db",
192*b8c7340eSAntoniu Miclaus 				      &external_b_gain)) {
193*b8c7340eSAntoniu Miclaus 		st->gain_configs[i++] = (struct adl8113_gain_config) {
194*b8c7340eSAntoniu Miclaus 			.path = ADL8113_EXTERNAL_B,
195*b8c7340eSAntoniu Miclaus 			.gain_db = external_b_gain,
196*b8c7340eSAntoniu Miclaus 		};
197*b8c7340eSAntoniu Miclaus 	}
198*b8c7340eSAntoniu Miclaus 
199*b8c7340eSAntoniu Miclaus 	st->num_gain_configs = i;
200*b8c7340eSAntoniu Miclaus 
201*b8c7340eSAntoniu Miclaus 	return 0;
202*b8c7340eSAntoniu Miclaus }
203*b8c7340eSAntoniu Miclaus 
204*b8c7340eSAntoniu Miclaus static int adl8113_probe(struct platform_device *pdev)
205*b8c7340eSAntoniu Miclaus {
206*b8c7340eSAntoniu Miclaus 	struct device *dev = &pdev->dev;
207*b8c7340eSAntoniu Miclaus 	struct adl8113_state *st;
208*b8c7340eSAntoniu Miclaus 	struct iio_dev *indio_dev;
209*b8c7340eSAntoniu Miclaus 	int ret;
210*b8c7340eSAntoniu Miclaus 
211*b8c7340eSAntoniu Miclaus 	indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
212*b8c7340eSAntoniu Miclaus 	if (!indio_dev)
213*b8c7340eSAntoniu Miclaus 		return -ENOMEM;
214*b8c7340eSAntoniu Miclaus 
215*b8c7340eSAntoniu Miclaus 	st = iio_priv(indio_dev);
216*b8c7340eSAntoniu Miclaus 
217*b8c7340eSAntoniu Miclaus 	st->gpios = devm_gpiod_get_array(dev, "ctrl", GPIOD_OUT_LOW);
218*b8c7340eSAntoniu Miclaus 	if (IS_ERR(st->gpios))
219*b8c7340eSAntoniu Miclaus 		return dev_err_probe(dev, PTR_ERR(st->gpios),
220*b8c7340eSAntoniu Miclaus 				     "failed to get control GPIOs\n");
221*b8c7340eSAntoniu Miclaus 
222*b8c7340eSAntoniu Miclaus 	if (st->gpios->ndescs != 2)
223*b8c7340eSAntoniu Miclaus 		return dev_err_probe(dev, -EINVAL,
224*b8c7340eSAntoniu Miclaus 				     "expected 2 control GPIOs, got %u\n",
225*b8c7340eSAntoniu Miclaus 				     st->gpios->ndescs);
226*b8c7340eSAntoniu Miclaus 
227*b8c7340eSAntoniu Miclaus 	ret = devm_regulator_bulk_get_enable(dev,
228*b8c7340eSAntoniu Miclaus 					     ARRAY_SIZE(adl8113_supply_names),
229*b8c7340eSAntoniu Miclaus 					     adl8113_supply_names);
230*b8c7340eSAntoniu Miclaus 	if (ret)
231*b8c7340eSAntoniu Miclaus 		return dev_err_probe(dev, ret,
232*b8c7340eSAntoniu Miclaus 				     "failed to get and enable supplies\n");
233*b8c7340eSAntoniu Miclaus 
234*b8c7340eSAntoniu Miclaus 	/* Initialize gain configurations from devicetree */
235*b8c7340eSAntoniu Miclaus 	ret = adl8113_init_gain_configs(dev, st);
236*b8c7340eSAntoniu Miclaus 	if (ret)
237*b8c7340eSAntoniu Miclaus 		return ret;
238*b8c7340eSAntoniu Miclaus 
239*b8c7340eSAntoniu Miclaus 	/* Initialize to internal amplifier path (14dB) */
240*b8c7340eSAntoniu Miclaus 	ret = adl8113_set_path(st, ADL8113_INTERNAL_AMP);
241*b8c7340eSAntoniu Miclaus 	if (ret)
242*b8c7340eSAntoniu Miclaus 		return ret;
243*b8c7340eSAntoniu Miclaus 
244*b8c7340eSAntoniu Miclaus 	indio_dev->info = &adl8113_info;
245*b8c7340eSAntoniu Miclaus 	indio_dev->name = "adl8113";
246*b8c7340eSAntoniu Miclaus 	indio_dev->channels = adl8113_channels;
247*b8c7340eSAntoniu Miclaus 	indio_dev->num_channels = ARRAY_SIZE(adl8113_channels);
248*b8c7340eSAntoniu Miclaus 
249*b8c7340eSAntoniu Miclaus 	return devm_iio_device_register(dev, indio_dev);
250*b8c7340eSAntoniu Miclaus }
251*b8c7340eSAntoniu Miclaus 
252*b8c7340eSAntoniu Miclaus static const struct of_device_id adl8113_of_match[] = {
253*b8c7340eSAntoniu Miclaus 	{ .compatible = "adi,adl8113" },
254*b8c7340eSAntoniu Miclaus 	{ }
255*b8c7340eSAntoniu Miclaus };
256*b8c7340eSAntoniu Miclaus MODULE_DEVICE_TABLE(of, adl8113_of_match);
257*b8c7340eSAntoniu Miclaus 
258*b8c7340eSAntoniu Miclaus static struct platform_driver adl8113_driver = {
259*b8c7340eSAntoniu Miclaus 	.driver = {
260*b8c7340eSAntoniu Miclaus 		.name = "adl8113",
261*b8c7340eSAntoniu Miclaus 		.of_match_table = adl8113_of_match,
262*b8c7340eSAntoniu Miclaus 	},
263*b8c7340eSAntoniu Miclaus 	.probe = adl8113_probe,
264*b8c7340eSAntoniu Miclaus };
265*b8c7340eSAntoniu Miclaus module_platform_driver(adl8113_driver);
266*b8c7340eSAntoniu Miclaus 
267*b8c7340eSAntoniu Miclaus MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>");
268*b8c7340eSAntoniu Miclaus MODULE_DESCRIPTION("Analog Devices ADL8113 Low Noise Amplifier");
269*b8c7340eSAntoniu Miclaus MODULE_LICENSE("GPL");
270