Unverified Commit b8c442b3 authored by Mark Brown's avatar Mark Brown
Browse files

Add support for IIO devices in ASoC

Merge series from Herve Codina <herve.codina@bootlin.com>:

Several weeks ago, I sent a series [1] for adding a potentiometer as an
auxiliary device in ASoC. The feedback was that the potentiometer should
be directly handled in IIO (as other potentiometers) and something more
generic should be present in ASoC in order to have a binding to import
some IIO devices into sound cards.

The series related to the IIO potentiometer device is already applied.

This series introduces audio-iio-aux. Its goal is to offer the binding
between IIO and ASoC.
It exposes attached IIO devices as ASoC auxiliary devices and allows to
control them through mixer controls.

On my system, the IIO device is a potentiometer and it is present in an
amplifier design present in the audio path.
parents c7a0f10b 6d8ad35d
Loading
Loading
Loading
Loading
+64 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/audio-iio-aux.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Audio IIO auxiliary

maintainers:
  - Herve Codina <herve.codina@bootlin.com>

description:
  Auxiliary device based on Industrial I/O device channels

allOf:
  - $ref: dai-common.yaml#

properties:
  compatible:
    const: audio-iio-aux

  io-channels:
    description:
      Industrial I/O device channels used

  io-channel-names:
    description:
      Industrial I/O channel names related to io-channels.
      These names are used to provides sound controls, widgets and routes names.

  snd-control-invert-range:
    $ref: /schemas/types.yaml#/definitions/uint32-array
    description: |
      A list of 0/1 flags defining whether or not the related channel is
      inverted
    items:
      enum: [0, 1]
      default: 0
      description: |
        Invert the sound control value compared to the IIO channel raw value.
          - 1: The related sound control value is inverted meaning that the
               minimum sound control value correspond to the maximum IIO channel
               raw value and the maximum sound control value correspond to the
               minimum IIO channel raw value.
          - 0: The related sound control value is not inverted meaning that the
               minimum (resp maximum) sound control value correspond to the
               minimum (resp maximum) IIO channel raw value.

required:
  - compatible
  - io-channels
  - io-channel-names

unevaluatedProperties: false

examples:
  - |
    iio-aux {
        compatible = "audio-iio-aux";
        io-channels = <&iio 0>, <&iio 1>, <&iio 2>, <&iio 3>;
        io-channel-names = "CH0", "CH1", "CH2", "CH3";
        /* Invert CH1 and CH2 */
        snd-control-invert-range = <0 1 1 0>;
    };
+53 −0
Original line number Diff line number Diff line
@@ -148,6 +148,15 @@ definitions:
    required:
      - sound-dai

  additional-devs:
    type: object
    description:
      Additional devices used by the simple audio card.
    patternProperties:
      '^iio-aux(-.+)?$':
        type: object
        $ref: audio-iio-aux.yaml#

properties:
  compatible:
    contains:
@@ -187,6 +196,8 @@ properties:
    $ref: "#/definitions/mclk-fs"
  simple-audio-card,aux-devs:
    $ref: "#/definitions/aux-devs"
  simple-audio-card,additional-devs:
    $ref: "#/definitions/additional-devs"
  simple-audio-card,convert-rate:
    $ref: "#/definitions/convert-rate"
  simple-audio-card,convert-channels:
@@ -359,6 +370,48 @@ examples:
        };
    };

# --------------------
# route audio to/from a codec through an amplifier
# designed with a potentiometer driven by IIO:
# --------------------
  - |
    sound {
        compatible = "simple-audio-card";

        simple-audio-card,aux-devs = <&amp_in>, <&amp_out>;
        simple-audio-card,routing =
            "CODEC LEFTIN", "AMP_IN LEFT OUT",
            "CODEC RIGHTIN", "AMP_IN RIGHT OUT",
            "AMP_OUT LEFT IN", "CODEC LEFTOUT",
            "AMP_OUT RIGHT IN", "CODEC RIGHTOUT";

        simple-audio-card,additional-devs {
            amp_out: iio-aux-out {
                compatible = "audio-iio-aux";
                io-channels = <&pot_out 0>, <&pot_out 1>;
                io-channel-names = "LEFT", "RIGHT";
                snd-control-invert-range = <1 1>;
                sound-name-prefix = "AMP_OUT";
            };

            amp_in: iio_aux-in {
                compatible = "audio-iio-aux";
                io-channels = <&pot_in 0>, <&pot_in 1>;
                io-channel-names = "LEFT", "RIGHT";
                sound-name-prefix = "AMP_IN";
            };
        };

        simple-audio-card,cpu {
            sound-dai = <&cpu>;
        };

        simple-audio-card,codec {
            sound-dai = <&codec>;
            clocks = <&clocks>;
        };
    };

# --------------------
# Sampling Rate Conversion
# --------------------
+73 −13
Original line number Diff line number Diff line
@@ -5,9 +5,10 @@
 */
#include <linux/err.h>
#include <linux/export.h>
#include <linux/minmax.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/mutex.h>

#include <linux/iio/iio.h>
#include <linux/iio/iio-opaque.h>
@@ -849,15 +850,14 @@ static int iio_channel_read_max(struct iio_channel *chan,
				int *val, int *val2, int *type,
				enum iio_chan_info_enum info)
{
	int unused;
	const int *vals;
	int length;
	int ret;

	if (!val2)
		val2 = &unused;

	ret = iio_channel_read_avail(chan, &vals, type, &length, info);
	if (ret < 0)
		return ret;

	switch (ret) {
	case IIO_AVAIL_RANGE:
		switch (*type) {
@@ -866,6 +866,7 @@ static int iio_channel_read_max(struct iio_channel *chan,
			break;
		default:
			*val = vals[4];
			if (val2)
				*val2 = vals[5];
		}
		return 0;
@@ -875,20 +876,16 @@ static int iio_channel_read_max(struct iio_channel *chan,
			return -EINVAL;
		switch (*type) {
		case IIO_VAL_INT:
			*val = vals[--length];
			while (length) {
				if (vals[--length] > *val)
					*val = vals[length];
			}
			*val = max_array(vals, length);
			break;
		default:
			/* FIXME: learn about max for other iio values */
			/* TODO: learn about max for other iio values */
			return -EINVAL;
		}
		return 0;

	default:
		return ret;
		return -EINVAL;
	}
}

@@ -912,6 +909,69 @@ int iio_read_max_channel_raw(struct iio_channel *chan, int *val)
}
EXPORT_SYMBOL_GPL(iio_read_max_channel_raw);

static int iio_channel_read_min(struct iio_channel *chan,
				int *val, int *val2, int *type,
				enum iio_chan_info_enum info)
{
	const int *vals;
	int length;
	int ret;

	ret = iio_channel_read_avail(chan, &vals, type, &length, info);
	if (ret < 0)
		return ret;

	switch (ret) {
	case IIO_AVAIL_RANGE:
		switch (*type) {
		case IIO_VAL_INT:
			*val = vals[0];
			break;
		default:
			*val = vals[0];
			if (val2)
				*val2 = vals[1];
		}
		return 0;

	case IIO_AVAIL_LIST:
		if (length <= 0)
			return -EINVAL;
		switch (*type) {
		case IIO_VAL_INT:
			*val = min_array(vals, length);
			break;
		default:
			/* TODO: learn about min for other iio values */
			return -EINVAL;
		}
		return 0;

	default:
		return -EINVAL;
	}
}

int iio_read_min_channel_raw(struct iio_channel *chan, int *val)
{
	struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
	int ret;
	int type;

	mutex_lock(&iio_dev_opaque->info_exist_lock);
	if (!chan->indio_dev->info) {
		ret = -ENODEV;
		goto err_unlock;
	}

	ret = iio_channel_read_min(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
err_unlock:
	mutex_unlock(&iio_dev_opaque->info_exist_lock);

	return ret;
}
EXPORT_SYMBOL_GPL(iio_read_min_channel_raw);

int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type)
{
	struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
+27 −10
Original line number Diff line number Diff line
@@ -201,8 +201,9 @@ struct iio_dev
 * @chan:		The channel being queried.
 * @val:		Value read back.
 *
 * Note raw reads from iio channels are in adc counts and hence
 * scale will need to be applied if standard units required.
 * Note, if standard units are required, raw reads from iio channels
 * need the offset (default 0) and scale (default 1) to be applied
 * as (raw + offset) * scale.
 */
int iio_read_channel_raw(struct iio_channel *chan,
			 int *val);
@@ -212,8 +213,9 @@ int iio_read_channel_raw(struct iio_channel *chan,
 * @chan:		The channel being queried.
 * @val:		Value read back.
 *
 * Note raw reads from iio channels are in adc counts and hence
 * scale will need to be applied if standard units required.
 * Note, if standard units are required, raw reads from iio channels
 * need the offset (default 0) and scale (default 1) to be applied
 * as (raw + offset) * scale.
 *
 * In opposit to the normal iio_read_channel_raw this function
 * returns the average of multiple reads.
@@ -281,8 +283,9 @@ int iio_read_channel_attribute(struct iio_channel *chan, int *val,
 * @chan:		The channel being queried.
 * @val:		Value being written.
 *
 * Note raw writes to iio channels are in dac counts and hence
 * scale will need to be applied if standard units required.
 * Note that for raw writes to iio channels, if the value provided is
 * in standard units, the affect of the scale and offset must be removed
 * as (value / scale) - offset.
 */
int iio_write_channel_raw(struct iio_channel *chan, int val);

@@ -292,11 +295,24 @@ int iio_write_channel_raw(struct iio_channel *chan, int val);
 * @chan:		The channel being queried.
 * @val:		Value read back.
 *
 * Note raw reads from iio channels are in adc counts and hence
 * scale will need to be applied if standard units are required.
 * Note, if standard units are required, raw reads from iio channels
 * need the offset (default 0) and scale (default 1) to be applied
 * as (raw + offset) * scale.
 */
int iio_read_max_channel_raw(struct iio_channel *chan, int *val);

/**
 * iio_read_min_channel_raw() - read minimum available raw value from a given
 *				channel, i.e. the minimum possible value.
 * @chan:		The channel being queried.
 * @val:		Value read back.
 *
 * Note, if standard units are required, raw reads from iio channels
 * need the offset (default 0) and scale (default 1) to be applied
 * as (raw + offset) * scale.
 */
int iio_read_min_channel_raw(struct iio_channel *chan, int *val);

/**
 * iio_read_avail_channel_raw() - read available raw values from a given channel
 * @chan:		The channel being queried.
@@ -308,8 +324,9 @@ int iio_read_max_channel_raw(struct iio_channel *chan, int *val);
 * For ranges, three vals are always returned; min, step and max.
 * For lists, all the possible values are enumerated.
 *
 * Note raw available values from iio channels are in adc counts and
 * hence scale will need to be applied if standard units are required.
 * Note, if standard units are required, raw available values from iio
 * channels need the offset (default 0) and scale (default 1) to be applied
 * as (raw + offset) * scale.
 */
int iio_read_avail_channel_raw(struct iio_channel *chan,
			       const int **vals, int *length);
+64 −0
Original line number Diff line number Diff line
@@ -133,6 +133,70 @@
 */
#define max_t(type, x, y)	__careful_cmp((type)(x), (type)(y), >)

/*
 * Remove a const qualifier from integer types
 * _Generic(foo, type-name: association, ..., default: association) performs a
 * comparison against the foo type (not the qualified type).
 * Do not use the const keyword in the type-name as it will not match the
 * unqualified type of foo.
 */
#define __unconst_integer_type_cases(type)	\
	unsigned type:  (unsigned type)0,	\
	signed type:    (signed type)0

#define __unconst_integer_typeof(x) typeof(			\
	_Generic((x),						\
		char: (char)0,					\
		__unconst_integer_type_cases(char),		\
		__unconst_integer_type_cases(short),		\
		__unconst_integer_type_cases(int),		\
		__unconst_integer_type_cases(long),		\
		__unconst_integer_type_cases(long long),	\
		default: (x)))

/*
 * Do not check the array parameter using __must_be_array().
 * In the following legit use-case where the "array" passed is a simple pointer,
 * __must_be_array() will return a failure.
 * --- 8< ---
 * int *buff
 * ...
 * min = min_array(buff, nb_items);
 * --- 8< ---
 *
 * The first typeof(&(array)[0]) is needed in order to support arrays of both
 * 'int *buff' and 'int buff[N]' types.
 *
 * The array can be an array of const items.
 * typeof() keeps the const qualifier. Use __unconst_integer_typeof() in order
 * to discard the const qualifier for the __element variable.
 */
#define __minmax_array(op, array, len) ({				\
	typeof(&(array)[0]) __array = (array);				\
	typeof(len) __len = (len);					\
	__unconst_integer_typeof(__array[0]) __element = __array[--__len]; \
	while (__len--)							\
		__element = op(__element, __array[__len]);		\
	__element; })

/**
 * min_array - return minimum of values present in an array
 * @array: array
 * @len: array length
 *
 * Note that @len must not be zero (empty array).
 */
#define min_array(array, len) __minmax_array(min, array, len)

/**
 * max_array - return maximum of values present in an array
 * @array: array
 * @len: array length
 *
 * Note that @len must not be zero (empty array).
 */
#define max_array(array, len) __minmax_array(max, array, len)

/**
 * clamp_t - return a value clamped to a given range using a given type
 * @type: the type of variable to use
Loading