1b475f80bSPeter Rosin /*
2b475f80bSPeter Rosin  * Driver for an envelope detector using a DAC and a comparator
3b475f80bSPeter Rosin  *
4b475f80bSPeter Rosin  * Copyright (C) 2016 Axentia Technologies AB
5b475f80bSPeter Rosin  *
6b475f80bSPeter Rosin  * Author: Peter Rosin <peda@axentia.se>
7b475f80bSPeter Rosin  *
8b475f80bSPeter Rosin  * This program is free software; you can redistribute it and/or modify
9b475f80bSPeter Rosin  * it under the terms of the GNU General Public License version 2 as
10b475f80bSPeter Rosin  * published by the Free Software Foundation.
11b475f80bSPeter Rosin  */
12b475f80bSPeter Rosin 
13b475f80bSPeter Rosin /*
14b475f80bSPeter Rosin  * The DAC is used to find the peak level of an alternating voltage input
15b475f80bSPeter Rosin  * signal by a binary search using the output of a comparator wired to
16b475f80bSPeter Rosin  * an interrupt pin. Like so:
17b475f80bSPeter Rosin  *                           _
18b475f80bSPeter Rosin  *                          | \
19b475f80bSPeter Rosin  *     input +------>-------|+ \
20b475f80bSPeter Rosin  *                          |   \
21b475f80bSPeter Rosin  *            .-------.     |    }---.
22b475f80bSPeter Rosin  *            |       |     |   /    |
23b475f80bSPeter Rosin  *            |    dac|-->--|- /     |
24b475f80bSPeter Rosin  *            |       |     |_/      |
25b475f80bSPeter Rosin  *            |       |              |
26b475f80bSPeter Rosin  *            |       |              |
27b475f80bSPeter Rosin  *            |    irq|------<-------'
28b475f80bSPeter Rosin  *            |       |
29b475f80bSPeter Rosin  *            '-------'
30b475f80bSPeter Rosin  */
31b475f80bSPeter Rosin 
32b475f80bSPeter Rosin #include <linux/completion.h>
33b475f80bSPeter Rosin #include <linux/device.h>
34b475f80bSPeter Rosin #include <linux/err.h>
35b475f80bSPeter Rosin #include <linux/kernel.h>
36b475f80bSPeter Rosin #include <linux/module.h>
37b475f80bSPeter Rosin #include <linux/mutex.h>
38b475f80bSPeter Rosin #include <linux/iio/consumer.h>
39b475f80bSPeter Rosin #include <linux/iio/iio.h>
40b475f80bSPeter Rosin #include <linux/iio/sysfs.h>
41b475f80bSPeter Rosin #include <linux/interrupt.h>
42b475f80bSPeter Rosin #include <linux/irq.h>
43b475f80bSPeter Rosin #include <linux/of.h>
44b475f80bSPeter Rosin #include <linux/of_device.h>
45b475f80bSPeter Rosin #include <linux/platform_device.h>
46b475f80bSPeter Rosin #include <linux/spinlock.h>
47b475f80bSPeter Rosin #include <linux/workqueue.h>
48b475f80bSPeter Rosin 
49b475f80bSPeter Rosin struct envelope {
50b475f80bSPeter Rosin 	spinlock_t comp_lock; /* protects comp */
51b475f80bSPeter Rosin 	int comp;
52b475f80bSPeter Rosin 
53b475f80bSPeter Rosin 	struct mutex read_lock; /* protects everything else */
54b475f80bSPeter Rosin 
55b475f80bSPeter Rosin 	int comp_irq;
56b475f80bSPeter Rosin 	u32 comp_irq_trigger;
57b475f80bSPeter Rosin 	u32 comp_irq_trigger_inv;
58b475f80bSPeter Rosin 
59b475f80bSPeter Rosin 	struct iio_channel *dac;
60b475f80bSPeter Rosin 	struct delayed_work comp_timeout;
61b475f80bSPeter Rosin 
62b475f80bSPeter Rosin 	unsigned int comp_interval;
63b475f80bSPeter Rosin 	bool invert;
64b475f80bSPeter Rosin 	u32 dac_max;
65b475f80bSPeter Rosin 
66b475f80bSPeter Rosin 	int high;
67b475f80bSPeter Rosin 	int level;
68b475f80bSPeter Rosin 	int low;
69b475f80bSPeter Rosin 
70b475f80bSPeter Rosin 	struct completion done;
71b475f80bSPeter Rosin };
72b475f80bSPeter Rosin 
73b475f80bSPeter Rosin /*
74b475f80bSPeter Rosin  * The envelope_detector_comp_latch function works together with the compare
75b475f80bSPeter Rosin  * interrupt service routine below (envelope_detector_comp_isr) as a latch
76b475f80bSPeter Rosin  * (one-bit memory) for if the interrupt has triggered since last calling
77b475f80bSPeter Rosin  * this function.
78b475f80bSPeter Rosin  * The ..._comp_isr function disables the interrupt so that the cpu does not
79b475f80bSPeter Rosin  * need to service a possible interrupt flood from the comparator when no-one
80b475f80bSPeter Rosin  * cares anyway, and this ..._comp_latch function reenables them again if
81b475f80bSPeter Rosin  * needed.
82b475f80bSPeter Rosin  */
83b475f80bSPeter Rosin static int envelope_detector_comp_latch(struct envelope *env)
84b475f80bSPeter Rosin {
85b475f80bSPeter Rosin 	int comp;
86b475f80bSPeter Rosin 
87b475f80bSPeter Rosin 	spin_lock_irq(&env->comp_lock);
88b475f80bSPeter Rosin 	comp = env->comp;
89b475f80bSPeter Rosin 	env->comp = 0;
90b475f80bSPeter Rosin 	spin_unlock_irq(&env->comp_lock);
91b475f80bSPeter Rosin 
92b475f80bSPeter Rosin 	if (!comp)
93b475f80bSPeter Rosin 		return 0;
94b475f80bSPeter Rosin 
95b475f80bSPeter Rosin 	/*
96b475f80bSPeter Rosin 	 * The irq was disabled, and is reenabled just now.
97b475f80bSPeter Rosin 	 * But there might have been a pending irq that
98b475f80bSPeter Rosin 	 * happened while the irq was disabled that fires
99b475f80bSPeter Rosin 	 * just as the irq is reenabled. That is not what
100b475f80bSPeter Rosin 	 * is desired.
101b475f80bSPeter Rosin 	 */
102b475f80bSPeter Rosin 	enable_irq(env->comp_irq);
103b475f80bSPeter Rosin 
104b475f80bSPeter Rosin 	/* So, synchronize this possibly pending irq... */
105b475f80bSPeter Rosin 	synchronize_irq(env->comp_irq);
106b475f80bSPeter Rosin 
107b475f80bSPeter Rosin 	/* ...and redo the whole dance. */
108b475f80bSPeter Rosin 	spin_lock_irq(&env->comp_lock);
109b475f80bSPeter Rosin 	comp = env->comp;
110b475f80bSPeter Rosin 	env->comp = 0;
111b475f80bSPeter Rosin 	spin_unlock_irq(&env->comp_lock);
112b475f80bSPeter Rosin 
113b475f80bSPeter Rosin 	if (comp)
114b475f80bSPeter Rosin 		enable_irq(env->comp_irq);
115b475f80bSPeter Rosin 
116b475f80bSPeter Rosin 	return 1;
117b475f80bSPeter Rosin }
118b475f80bSPeter Rosin 
119b475f80bSPeter Rosin static irqreturn_t envelope_detector_comp_isr(int irq, void *ctx)
120b475f80bSPeter Rosin {
121b475f80bSPeter Rosin 	struct envelope *env = ctx;
122b475f80bSPeter Rosin 
123b475f80bSPeter Rosin 	spin_lock(&env->comp_lock);
124b475f80bSPeter Rosin 	env->comp = 1;
125b475f80bSPeter Rosin 	disable_irq_nosync(env->comp_irq);
126b475f80bSPeter Rosin 	spin_unlock(&env->comp_lock);
127b475f80bSPeter Rosin 
128b475f80bSPeter Rosin 	return IRQ_HANDLED;
129b475f80bSPeter Rosin }
130b475f80bSPeter Rosin 
131b475f80bSPeter Rosin static void envelope_detector_setup_compare(struct envelope *env)
132b475f80bSPeter Rosin {
133b475f80bSPeter Rosin 	int ret;
134b475f80bSPeter Rosin 
135b475f80bSPeter Rosin 	/*
136b475f80bSPeter Rosin 	 * Do a binary search for the peak input level, and stop
137b475f80bSPeter Rosin 	 * when that level is "trapped" between two adjacent DAC
138b475f80bSPeter Rosin 	 * values.
139b475f80bSPeter Rosin 	 * When invert is active, use the midpoint floor so that
140b475f80bSPeter Rosin 	 * env->level ends up as env->low when the termination
141b475f80bSPeter Rosin 	 * criteria below is fulfilled, and use the midpoint
142b475f80bSPeter Rosin 	 * ceiling when invert is not active so that env->level
143b475f80bSPeter Rosin 	 * ends up as env->high in that case.
144b475f80bSPeter Rosin 	 */
145b475f80bSPeter Rosin 	env->level = (env->high + env->low + !env->invert) / 2;
146b475f80bSPeter Rosin 
147b475f80bSPeter Rosin 	if (env->high == env->low + 1) {
148b475f80bSPeter Rosin 		complete(&env->done);
149b475f80bSPeter Rosin 		return;
150b475f80bSPeter Rosin 	}
151b475f80bSPeter Rosin 
152b475f80bSPeter Rosin 	/* Set a "safe" DAC level (if there is such a thing)... */
153b475f80bSPeter Rosin 	ret = iio_write_channel_raw(env->dac, env->invert ? 0 : env->dac_max);
154b475f80bSPeter Rosin 	if (ret < 0)
155b475f80bSPeter Rosin 		goto err;
156b475f80bSPeter Rosin 
157b475f80bSPeter Rosin 	/* ...clear the comparison result... */
158b475f80bSPeter Rosin 	envelope_detector_comp_latch(env);
159b475f80bSPeter Rosin 
160b475f80bSPeter Rosin 	/* ...set the real DAC level... */
161b475f80bSPeter Rosin 	ret = iio_write_channel_raw(env->dac, env->level);
162b475f80bSPeter Rosin 	if (ret < 0)
163b475f80bSPeter Rosin 		goto err;
164b475f80bSPeter Rosin 
165b475f80bSPeter Rosin 	/* ...and wait for a bit to see if the latch catches anything. */
166b475f80bSPeter Rosin 	schedule_delayed_work(&env->comp_timeout,
167b475f80bSPeter Rosin 			      msecs_to_jiffies(env->comp_interval));
168b475f80bSPeter Rosin 	return;
169b475f80bSPeter Rosin 
170b475f80bSPeter Rosin err:
171b475f80bSPeter Rosin 	env->level = ret;
172b475f80bSPeter Rosin 	complete(&env->done);
173b475f80bSPeter Rosin }
174b475f80bSPeter Rosin 
175b475f80bSPeter Rosin static void envelope_detector_timeout(struct work_struct *work)
176b475f80bSPeter Rosin {
177b475f80bSPeter Rosin 	struct envelope *env = container_of(work, struct envelope,
178b475f80bSPeter Rosin 					    comp_timeout.work);
179b475f80bSPeter Rosin 
180b475f80bSPeter Rosin 	/* Adjust low/high depending on the latch content... */
181b475f80bSPeter Rosin 	if (!envelope_detector_comp_latch(env) ^ !env->invert)
182b475f80bSPeter Rosin 		env->low = env->level;
183b475f80bSPeter Rosin 	else
184b475f80bSPeter Rosin 		env->high = env->level;
185b475f80bSPeter Rosin 
186b475f80bSPeter Rosin 	/* ...and continue the search. */
187b475f80bSPeter Rosin 	envelope_detector_setup_compare(env);
188b475f80bSPeter Rosin }
189b475f80bSPeter Rosin 
190b475f80bSPeter Rosin static int envelope_detector_read_raw(struct iio_dev *indio_dev,
191b475f80bSPeter Rosin 				      struct iio_chan_spec const *chan,
192b475f80bSPeter Rosin 				      int *val, int *val2, long mask)
193b475f80bSPeter Rosin {
194b475f80bSPeter Rosin 	struct envelope *env = iio_priv(indio_dev);
195b475f80bSPeter Rosin 	int ret;
196b475f80bSPeter Rosin 
197b475f80bSPeter Rosin 	switch (mask) {
198b475f80bSPeter Rosin 	case IIO_CHAN_INFO_RAW:
199b475f80bSPeter Rosin 		/*
200b475f80bSPeter Rosin 		 * When invert is active, start with high=max+1 and low=0
201b475f80bSPeter Rosin 		 * since we will end up with the low value when the
202b475f80bSPeter Rosin 		 * termination criteria is fulfilled (rounding down). And
203b475f80bSPeter Rosin 		 * start with high=max and low=-1 when invert is not active
204b475f80bSPeter Rosin 		 * since we will end up with the high value in that case.
205b475f80bSPeter Rosin 		 * This ensures that the returned value in both cases are
206b475f80bSPeter Rosin 		 * in the same range as the DAC and is a value that has not
207b475f80bSPeter Rosin 		 * triggered the comparator.
208b475f80bSPeter Rosin 		 */
209b475f80bSPeter Rosin 		mutex_lock(&env->read_lock);
210b475f80bSPeter Rosin 		env->high = env->dac_max + env->invert;
211b475f80bSPeter Rosin 		env->low = -1 + env->invert;
212b475f80bSPeter Rosin 		envelope_detector_setup_compare(env);
213b475f80bSPeter Rosin 		wait_for_completion(&env->done);
214b475f80bSPeter Rosin 		if (env->level < 0) {
215b475f80bSPeter Rosin 			ret = env->level;
216b475f80bSPeter Rosin 			goto err_unlock;
217b475f80bSPeter Rosin 		}
218b475f80bSPeter Rosin 		*val = env->invert ? env->dac_max - env->level : env->level;
219b475f80bSPeter Rosin 		mutex_unlock(&env->read_lock);
220b475f80bSPeter Rosin 
221b475f80bSPeter Rosin 		return IIO_VAL_INT;
222b475f80bSPeter Rosin 
223b475f80bSPeter Rosin 	case IIO_CHAN_INFO_SCALE:
224b475f80bSPeter Rosin 		return iio_read_channel_scale(env->dac, val, val2);
225b475f80bSPeter Rosin 	}
226b475f80bSPeter Rosin 
227b475f80bSPeter Rosin 	return -EINVAL;
228b475f80bSPeter Rosin 
229b475f80bSPeter Rosin err_unlock:
230b475f80bSPeter Rosin 	mutex_unlock(&env->read_lock);
231b475f80bSPeter Rosin 	return ret;
232b475f80bSPeter Rosin }
233b475f80bSPeter Rosin 
234b475f80bSPeter Rosin static ssize_t envelope_show_invert(struct iio_dev *indio_dev,
235b475f80bSPeter Rosin 				    uintptr_t private,
236b475f80bSPeter Rosin 				    struct iio_chan_spec const *ch, char *buf)
237b475f80bSPeter Rosin {
238b475f80bSPeter Rosin 	struct envelope *env = iio_priv(indio_dev);
239b475f80bSPeter Rosin 
240b475f80bSPeter Rosin 	return sprintf(buf, "%u\n", env->invert);
241b475f80bSPeter Rosin }
242b475f80bSPeter Rosin 
243b475f80bSPeter Rosin static ssize_t envelope_store_invert(struct iio_dev *indio_dev,
244b475f80bSPeter Rosin 				     uintptr_t private,
245b475f80bSPeter Rosin 				     struct iio_chan_spec const *ch,
246b475f80bSPeter Rosin 				     const char *buf, size_t len)
247b475f80bSPeter Rosin {
248b475f80bSPeter Rosin 	struct envelope *env = iio_priv(indio_dev);
249b475f80bSPeter Rosin 	unsigned long invert;
250b475f80bSPeter Rosin 	int ret;
251b475f80bSPeter Rosin 	u32 trigger;
252b475f80bSPeter Rosin 
253b475f80bSPeter Rosin 	ret = kstrtoul(buf, 0, &invert);
254b475f80bSPeter Rosin 	if (ret < 0)
255b475f80bSPeter Rosin 		return ret;
256b475f80bSPeter Rosin 	if (invert > 1)
257b475f80bSPeter Rosin 		return -EINVAL;
258b475f80bSPeter Rosin 
259b475f80bSPeter Rosin 	trigger = invert ? env->comp_irq_trigger_inv : env->comp_irq_trigger;
260b475f80bSPeter Rosin 
261b475f80bSPeter Rosin 	mutex_lock(&env->read_lock);
262b475f80bSPeter Rosin 	if (invert != env->invert)
263b475f80bSPeter Rosin 		ret = irq_set_irq_type(env->comp_irq, trigger);
264b475f80bSPeter Rosin 	if (!ret) {
265b475f80bSPeter Rosin 		env->invert = invert;
266b475f80bSPeter Rosin 		ret = len;
267b475f80bSPeter Rosin 	}
268b475f80bSPeter Rosin 	mutex_unlock(&env->read_lock);
269b475f80bSPeter Rosin 
270b475f80bSPeter Rosin 	return ret;
271b475f80bSPeter Rosin }
272b475f80bSPeter Rosin 
273b475f80bSPeter Rosin static ssize_t envelope_show_comp_interval(struct iio_dev *indio_dev,
274b475f80bSPeter Rosin 					   uintptr_t private,
275b475f80bSPeter Rosin 					   struct iio_chan_spec const *ch,
276b475f80bSPeter Rosin 					   char *buf)
277b475f80bSPeter Rosin {
278b475f80bSPeter Rosin 	struct envelope *env = iio_priv(indio_dev);
279b475f80bSPeter Rosin 
280b475f80bSPeter Rosin 	return sprintf(buf, "%u\n", env->comp_interval);
281b475f80bSPeter Rosin }
282b475f80bSPeter Rosin 
283b475f80bSPeter Rosin static ssize_t envelope_store_comp_interval(struct iio_dev *indio_dev,
284b475f80bSPeter Rosin 					    uintptr_t private,
285b475f80bSPeter Rosin 					    struct iio_chan_spec const *ch,
286b475f80bSPeter Rosin 					    const char *buf, size_t len)
287b475f80bSPeter Rosin {
288b475f80bSPeter Rosin 	struct envelope *env = iio_priv(indio_dev);
289b475f80bSPeter Rosin 	unsigned long interval;
290b475f80bSPeter Rosin 	int ret;
291b475f80bSPeter Rosin 
292b475f80bSPeter Rosin 	ret = kstrtoul(buf, 0, &interval);
293b475f80bSPeter Rosin 	if (ret < 0)
294b475f80bSPeter Rosin 		return ret;
295b475f80bSPeter Rosin 	if (interval > 1000)
296b475f80bSPeter Rosin 		return -EINVAL;
297b475f80bSPeter Rosin 
298b475f80bSPeter Rosin 	mutex_lock(&env->read_lock);
299b475f80bSPeter Rosin 	env->comp_interval = interval;
300b475f80bSPeter Rosin 	mutex_unlock(&env->read_lock);
301b475f80bSPeter Rosin 
302b475f80bSPeter Rosin 	return len;
303b475f80bSPeter Rosin }
304b475f80bSPeter Rosin 
305b475f80bSPeter Rosin static const struct iio_chan_spec_ext_info envelope_detector_ext_info[] = {
306b475f80bSPeter Rosin 	{ .name = "invert",
307b475f80bSPeter Rosin 	  .read = envelope_show_invert,
308b475f80bSPeter Rosin 	  .write = envelope_store_invert, },
309b475f80bSPeter Rosin 	{ .name = "compare_interval",
310b475f80bSPeter Rosin 	  .read = envelope_show_comp_interval,
311b475f80bSPeter Rosin 	  .write = envelope_store_comp_interval, },
312b475f80bSPeter Rosin 	{ /* sentinel */ }
313b475f80bSPeter Rosin };
314b475f80bSPeter Rosin 
315b475f80bSPeter Rosin static const struct iio_chan_spec envelope_detector_iio_channel = {
316b475f80bSPeter Rosin 	.type = IIO_ALTVOLTAGE,
317b475f80bSPeter Rosin 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
318b475f80bSPeter Rosin 			    | BIT(IIO_CHAN_INFO_SCALE),
319b475f80bSPeter Rosin 	.ext_info = envelope_detector_ext_info,
320b475f80bSPeter Rosin 	.indexed = 1,
321b475f80bSPeter Rosin };
322b475f80bSPeter Rosin 
323b475f80bSPeter Rosin static const struct iio_info envelope_detector_info = {
324b475f80bSPeter Rosin 	.read_raw = &envelope_detector_read_raw,
325b475f80bSPeter Rosin 	.driver_module = THIS_MODULE,
326b475f80bSPeter Rosin };
327b475f80bSPeter Rosin 
328b475f80bSPeter Rosin static int envelope_detector_probe(struct platform_device *pdev)
329b475f80bSPeter Rosin {
330b475f80bSPeter Rosin 	struct device *dev = &pdev->dev;
331b475f80bSPeter Rosin 	struct iio_dev *indio_dev;
332b475f80bSPeter Rosin 	struct envelope *env;
333b475f80bSPeter Rosin 	enum iio_chan_type type;
334b475f80bSPeter Rosin 	int ret;
335b475f80bSPeter Rosin 
336b475f80bSPeter Rosin 	indio_dev = devm_iio_device_alloc(dev, sizeof(*env));
337b475f80bSPeter Rosin 	if (!indio_dev)
338b475f80bSPeter Rosin 		return -ENOMEM;
339b475f80bSPeter Rosin 
340b475f80bSPeter Rosin 	platform_set_drvdata(pdev, indio_dev);
341b475f80bSPeter Rosin 	env = iio_priv(indio_dev);
342b475f80bSPeter Rosin 	env->comp_interval = 50; /* some sensible default? */
343b475f80bSPeter Rosin 
344b475f80bSPeter Rosin 	spin_lock_init(&env->comp_lock);
345b475f80bSPeter Rosin 	mutex_init(&env->read_lock);
346b475f80bSPeter Rosin 	init_completion(&env->done);
347b475f80bSPeter Rosin 	INIT_DELAYED_WORK(&env->comp_timeout, envelope_detector_timeout);
348b475f80bSPeter Rosin 
349b475f80bSPeter Rosin 	indio_dev->name = dev_name(dev);
350b475f80bSPeter Rosin 	indio_dev->dev.parent = dev;
351b475f80bSPeter Rosin 	indio_dev->dev.of_node = dev->of_node;
352b475f80bSPeter Rosin 	indio_dev->info = &envelope_detector_info;
353b475f80bSPeter Rosin 	indio_dev->channels = &envelope_detector_iio_channel;
354b475f80bSPeter Rosin 	indio_dev->num_channels = 1;
355b475f80bSPeter Rosin 
356b475f80bSPeter Rosin 	env->dac = devm_iio_channel_get(dev, "dac");
357b475f80bSPeter Rosin 	if (IS_ERR(env->dac)) {
358b475f80bSPeter Rosin 		if (PTR_ERR(env->dac) != -EPROBE_DEFER)
359b475f80bSPeter Rosin 			dev_err(dev, "failed to get dac input channel\n");
360b475f80bSPeter Rosin 		return PTR_ERR(env->dac);
361b475f80bSPeter Rosin 	}
362b475f80bSPeter Rosin 
363b475f80bSPeter Rosin 	env->comp_irq = platform_get_irq_byname(pdev, "comp");
364b475f80bSPeter Rosin 	if (env->comp_irq < 0) {
365b475f80bSPeter Rosin 		if (env->comp_irq != -EPROBE_DEFER)
366b475f80bSPeter Rosin 			dev_err(dev, "failed to get compare interrupt\n");
367b475f80bSPeter Rosin 		return env->comp_irq;
368b475f80bSPeter Rosin 	}
369b475f80bSPeter Rosin 
370b475f80bSPeter Rosin 	ret = devm_request_irq(dev, env->comp_irq, envelope_detector_comp_isr,
371b475f80bSPeter Rosin 			       0, "envelope-detector", env);
372b475f80bSPeter Rosin 	if (ret) {
373b475f80bSPeter Rosin 		if (ret != -EPROBE_DEFER)
374b475f80bSPeter Rosin 			dev_err(dev, "failed to request interrupt\n");
375b475f80bSPeter Rosin 		return ret;
376b475f80bSPeter Rosin 	}
377b475f80bSPeter Rosin 	env->comp_irq_trigger = irq_get_trigger_type(env->comp_irq);
378b475f80bSPeter Rosin 	if (env->comp_irq_trigger & IRQF_TRIGGER_RISING)
379b475f80bSPeter Rosin 		env->comp_irq_trigger_inv |= IRQF_TRIGGER_FALLING;
380b475f80bSPeter Rosin 	if (env->comp_irq_trigger & IRQF_TRIGGER_FALLING)
381b475f80bSPeter Rosin 		env->comp_irq_trigger_inv |= IRQF_TRIGGER_RISING;
382b475f80bSPeter Rosin 	if (env->comp_irq_trigger & IRQF_TRIGGER_HIGH)
383b475f80bSPeter Rosin 		env->comp_irq_trigger_inv |= IRQF_TRIGGER_LOW;
384b475f80bSPeter Rosin 	if (env->comp_irq_trigger & IRQF_TRIGGER_LOW)
385b475f80bSPeter Rosin 		env->comp_irq_trigger_inv |= IRQF_TRIGGER_HIGH;
386b475f80bSPeter Rosin 
387b475f80bSPeter Rosin 	ret = iio_get_channel_type(env->dac, &type);
388b475f80bSPeter Rosin 	if (ret < 0)
389b475f80bSPeter Rosin 		return ret;
390b475f80bSPeter Rosin 
391b475f80bSPeter Rosin 	if (type != IIO_VOLTAGE) {
392b475f80bSPeter Rosin 		dev_err(dev, "dac is of the wrong type\n");
393b475f80bSPeter Rosin 		return -EINVAL;
394b475f80bSPeter Rosin 	}
395b475f80bSPeter Rosin 
396b475f80bSPeter Rosin 	ret = iio_read_max_channel_raw(env->dac, &env->dac_max);
397b475f80bSPeter Rosin 	if (ret < 0) {
398b475f80bSPeter Rosin 		dev_err(dev, "dac does not indicate its raw maximum value\n");
399b475f80bSPeter Rosin 		return ret;
400b475f80bSPeter Rosin 	}
401b475f80bSPeter Rosin 
402b475f80bSPeter Rosin 	return devm_iio_device_register(dev, indio_dev);
403b475f80bSPeter Rosin }
404b475f80bSPeter Rosin 
405b475f80bSPeter Rosin static const struct of_device_id envelope_detector_match[] = {
406b475f80bSPeter Rosin 	{ .compatible = "axentia,tse850-envelope-detector", },
407b475f80bSPeter Rosin 	{ /* sentinel */ }
408b475f80bSPeter Rosin };
409b475f80bSPeter Rosin MODULE_DEVICE_TABLE(of, envelope_detector_match);
410b475f80bSPeter Rosin 
411b475f80bSPeter Rosin static struct platform_driver envelope_detector_driver = {
412b475f80bSPeter Rosin 	.probe = envelope_detector_probe,
413b475f80bSPeter Rosin 	.driver = {
414b475f80bSPeter Rosin 		.name = "iio-envelope-detector",
415b475f80bSPeter Rosin 		.of_match_table = envelope_detector_match,
416b475f80bSPeter Rosin 	},
417b475f80bSPeter Rosin };
418b475f80bSPeter Rosin module_platform_driver(envelope_detector_driver);
419b475f80bSPeter Rosin 
420b475f80bSPeter Rosin MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator");
421b475f80bSPeter Rosin MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
422b475f80bSPeter Rosin MODULE_LICENSE("GPL v2");
423