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